.clang-format0100644 0000000 0000000 00000000535 13756501734 012176 0ustar000000000 0000000 BasedOnStyle: Google AccessModifierOffset: -4 AlignOperands: false AllowShortFunctionsOnASingleLine: Inline AlwaysBreakBeforeMultilineStrings: false ColumnLimit: 100 CommentPragmas: NOLINT:.* ConstructorInitializerIndentWidth: 6 ContinuationIndentWidth: 8 IndentWidth: 4 PenaltyBreakBeforeFirstCallParameter: 100000 SpacesBeforeTrailingComments: 1 Android.bp0100644 0000000 0000000 00000000603 13756501734 011522 0ustar000000000 0000000 ndk_headers { name: "libandroid_headers", from: "include/android", to: "android", srcs: ["include/android/**/*.h"], license: "NOTICE", } subdirs = [ "cmds/*", "headers", "libs/*", "opengl", "services/*", "vulkan", ] cc_library_headers { name: "libandroid_sensor_headers", vendor: true, export_include_dirs: ["include_sensor"], } CleanSpec.mk0100644 0000000 0000000 00000005327 13756501734 012015 0ustar000000000 0000000 # Copyright (C) 2018 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. # # If you don't need to do a full clean build but would like to touch # a file or delete some intermediate files, add a clean step to the end # of the list. These steps will only be run once, if they haven't been # run before. # # E.g.: # $(call add-clean-step, touch -c external/sqlite/sqlite3.h) # $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) # # Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with # files that are missing or have been moved. # # Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. # Use $(OUT_DIR) to refer to the "out" directory. # # If you need to re-do something that's already mentioned, just copy # the command and add it to the bottom of the list. E.g., if a change # that you made last week required touching a file and a change you # made today requires touching the same file, just copy the old # touch step and add it to the end of the list. # # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ # For example: #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libdvr.so" -print0 | xargs -0 rm -f) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvr_intermediates) $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/thermalserviced) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/thermalservice.rc) $(call add-clean-step, find $(PRODUCT_OUT) -type f -name "gpuservice*" -print0 | xargs -0 rm -f) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/gpuservice_intermediates) MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501734 012743 0ustar000000000 0000000 NOTICE0100644 0000000 0000000 00000042420 13756501734 010526 0ustar000000000 0000000 ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for the Android-specific code. == ========================================================================= Android Code Copyright 2005-2008 The Android Open Source Project This product includes software developed as part of The Android Open Source Project (http://source.android.com). ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for Apache Commons code. == ========================================================================= Apache Commons Copyright 1999-2006 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for Jakarta Commons Logging. == ========================================================================= Jakarta Commons Logging (JCL) Copyright 2005,2006 The Apache Software Foundation. This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for the Nuance code. == ========================================================================= These files are Copyright 2007 Nuance Communications, but released under the Apache2 License. ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for the Media Codecs code. == ========================================================================= Media Codecs These files are Copyright 1998 - 2009 PacketVideo, but released under the Apache2 License. ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for the TagSoup code. == ========================================================================= This file is part of TagSoup and is Copyright 2002-2008 by John Cowan. TagSoup is licensed under the Apache License, Version 2.0. You may obtain a copy of this license at http://www.apache.org/licenses/LICENSE-2.0 . You may also have additional legal rights not granted by this license. TagSoup is distributed in the hope that it will be useful, but unless required by applicable law or agreed to in writing, TagSoup is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied; not even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for Additional Codecs code. == ========================================================================= Additional Codecs These files are Copyright 2003-2010 VisualOn, but released under the Apache2 License. ========================================================================= == NOTICE file corresponding to the section 4 d of == == the Apache License, Version 2.0, == == in this case for the Audio Effects code. == ========================================================================= Audio Effects These files are Copyright (C) 2004-2010 NXP Software and Copyright (C) 2010 The Android Open Source Project, but released under the Apache2 License. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/ . Unicode Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, and http://www.unicode.org/cldr/data/. NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. COPYRIGHT AND PERMISSION NOTICE Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html. Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that (a) the above copyright notice(s) and this permission notice appear with all copies of the Data Files or Software, (b) both the above copyright notice(s) and this permission notice appear in associated documentation, and (c) there is clear notice in each modified Data File or in the Software as well as in the documentation associated with the Data File(s) or Software that the data or software has been modified. THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. PREUPLOAD.cfg0100644 0000000 0000000 00000002070 13756501734 011633 0ustar000000000 0000000 [Builtin Hooks] clang_format = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp libs/binder/ndk/ libs/graphicsenv/ libs/gui/ libs/renderengine/ libs/ui/ libs/vr/ services/bufferhub/ services/surfaceflinger/ services/vr/ [Hook Scripts] owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" installd_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/installd/" dumpstate_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/dumpstate/" dumpsys_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/dumpsys/" # bugreports matches both cmds/bugreport and cmds/bugreportz bugreports_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/bugreport" aidl/0040755 0000000 0000000 00000000000 13756501734 010534 5ustar000000000 0000000 aidl/binder/0040755 0000000 0000000 00000000000 13756501734 011777 5ustar000000000 0000000 aidl/binder/android/0040755 0000000 0000000 00000000000 13756501734 013417 5ustar000000000 0000000 aidl/binder/android/os/0040755 0000000 0000000 00000000000 13756501734 014040 5ustar000000000 0000000 aidl/binder/android/os/PersistableBundle.aidl0100644 0000000 0000000 00000001306 13756501734 020277 0ustar000000000 0000000 /* ** ** Copyright 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 android.os; parcelable PersistableBundle cpp_header "binder/PersistableBundle.h"; aidl/gui/0040755 0000000 0000000 00000000000 13756501734 011320 5ustar000000000 0000000 aidl/gui/android/0040755 0000000 0000000 00000000000 13756501734 012740 5ustar000000000 0000000 aidl/gui/android/view/0040755 0000000 0000000 00000000000 13756501734 013712 5ustar000000000 0000000 aidl/gui/android/view/Surface.aidl0100644 0000000 0000000 00000001346 13756501734 016136 0ustar000000000 0000000 /* //device/java/android/android/view/Surface.aidl ** ** Copyright 2007, 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.view; parcelable Surface cpp_header "gui/view/Surface.h"; build/0040755 0000000 0000000 00000000000 13756501734 010722 5ustar000000000 0000000 build/phone-hdpi-512-dalvik-heap.mk0100644 0000000 0000000 00000001675 13756501734 016004 0ustar000000000 0000000 # # Copyright (C) 2010 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. # # Provides overrides to configure the Dalvik heap for a standard high density # phone with around 512MB total RAM. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=5m \ dalvik.vm.heapgrowthlimit=48m \ dalvik.vm.heapsize=128m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=2m build/phone-hdpi-dalvik-heap.mk0100644 0000000 0000000 00000001572 13756501734 015473 0ustar000000000 0000000 # # Copyright (C) 2010 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. # # Provides overrides to configure the Dalvik heap for a standard high density phone. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=5m \ dalvik.vm.heapsize=32m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=2m build/phone-xhdpi-1024-dalvik-heap.mk0100644 0000000 0000000 00000001616 13756501734 016246 0ustar000000000 0000000 # # Copyright (C) 2011 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. # # Provides overrides to configure the Dalvik heap for a xhdpi phone PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=8m \ dalvik.vm.heapgrowthlimit=96m \ dalvik.vm.heapsize=256m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=8m build/phone-xhdpi-2048-dalvik-heap.mk0100644 0000000 0000000 00000001726 13756501734 016257 0ustar000000000 0000000 # # Copyright (C) 2012 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. # # Provides overrides to configure the Dalvik heap for a 2G phone # 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=8m \ dalvik.vm.heapgrowthlimit=192m \ dalvik.vm.heapsize=512m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=8m build/tablet-10in-xhdpi-2048-dalvik-heap.mk0100644 0000000 0000000 00000001634 13756501734 017164 0ustar000000000 0000000 # # Copyright (C) 2012 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. # # Provides overrides to configure the Dalvik heap for a standard tablet device. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=16m \ dalvik.vm.heapgrowthlimit=192m \ dalvik.vm.heapsize=512m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=8m build/tablet-7in-hdpi-1024-dalvik-heap.mk0100644 0000000 0000000 00000001632 13756501734 016711 0ustar000000000 0000000 # # Copyright (C) 2010 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. # # Provides overrides to configure the Dalvik heap for a standard tablet device. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=8m \ dalvik.vm.heapgrowthlimit=80m \ dalvik.vm.heapsize=384m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=8m build/tablet-7in-xhdpi-2048-dalvik-heap.mk0100644 0000000 0000000 00000001635 13756501734 017113 0ustar000000000 0000000 # # 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. # # Provides overrides to configure the Dalvik heap for a 320dpi 7" tablet device. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=16m \ dalvik.vm.heapgrowthlimit=192m \ dalvik.vm.heapsize=512m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=8m build/tablet-dalvik-heap.mk0100644 0000000 0000000 00000001632 13756501734 014710 0ustar000000000 0000000 # # Copyright (C) 2010 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. # # Provides overrides to configure the Dalvik heap for a standard tablet device. PRODUCT_PROPERTY_OVERRIDES += \ dalvik.vm.heapstartsize=5m \ dalvik.vm.heapgrowthlimit=48m \ dalvik.vm.heapsize=256m \ dalvik.vm.heaptargetutilization=0.75 \ dalvik.vm.heapminfree=512k \ dalvik.vm.heapmaxfree=2m cmds/0040755 0000000 0000000 00000000000 13756501734 010551 5ustar000000000 0000000 cmds/atrace/0040755 0000000 0000000 00000000000 13756501734 012010 5ustar000000000 0000000 cmds/atrace/Android.bp0100644 0000000 0000000 00000001130 13756501734 013703 0ustar000000000 0000000 // Copyright 2012 The Android Open Source Project cc_binary { name: "atrace", srcs: ["atrace.cpp"], cflags: [ "-Wall", "-Werror", ], shared_libs: [ "libbinder", "libhwbinder", "libhidlbase", "libhidltransport", "liblog", "libutils", "libcutils", "libz", "libbase", "libpdx_default_transport", "android.hardware.atrace@1.0", ], init_rc: ["atrace.rc"], product_variables: { debuggable: { init_rc: ["atrace_userdebug.rc"], }, }, } cmds/atrace/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501734 015130 0ustar000000000 0000000 cmds/atrace/NOTICE0100644 0000000 0000000 00000024702 13756501734 012716 0ustar000000000 0000000 Copyright (c) 2012, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS cmds/atrace/TEST_MAPPING0100644 0000000 0000000 00000000114 13756501734 013656 0ustar000000000 0000000 { "presubmit": [ { "name": "CtsAtraceHostTestCases" } ] } cmds/atrace/atrace.cpp0100644 0000000 0000000 00000137035 13756501734 013761 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #define LOG_TAG "atrace" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace android; using pdx::default_transport::ServiceUtility; using hardware::hidl_vec; using hardware::hidl_string; using hardware::Return; using hardware::atrace::V1_0::IAtraceDevice; using hardware::atrace::V1_0::Status; using hardware::atrace::V1_0::toString; using std::string; #define MAX_SYS_FILES 11 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags"; const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated"; const char* k_traceAppsNumberProperty = "debug.atrace.app_number"; const char* k_traceAppsPropertyTemplate = "debug.atrace.app_%d"; const char* k_coreServiceCategory = "core_services"; const char* k_pdxServiceCategory = "pdx"; const char* k_coreServicesProp = "ro.atrace.core.services"; typedef enum { OPT, REQ } requiredness ; struct TracingCategory { // The name identifying the category. const char* name; // A longer description of the category. const char* longname; // The userland tracing tags that the category enables. uint64_t tags; // The fname==NULL terminated list of /sys/ files that the category // enables. struct { // Whether the file must be writable in order to enable the tracing // category. requiredness required; // The path to the enable file. const char* path; } sysfiles[MAX_SYS_FILES]; }; /* Tracing categories */ static const TracingCategory k_categories[] = { { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } }, { "input", "Input", ATRACE_TAG_INPUT, { } }, { "view", "View System", ATRACE_TAG_VIEW, { } }, { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } }, { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } }, { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } }, { "sm", "Sync Manager", ATRACE_TAG_SYNC_MANAGER, { } }, { "audio", "Audio", ATRACE_TAG_AUDIO, { } }, { "video", "Video", ATRACE_TAG_VIDEO, { } }, { "camera", "Camera", ATRACE_TAG_CAMERA, { } }, { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } }, { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } }, { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } }, { "rs", "RenderScript", ATRACE_TAG_RS, { } }, { "bionic", "Bionic C Library", ATRACE_TAG_BIONIC, { } }, { "power", "Power Management", ATRACE_TAG_POWER, { } }, { "pm", "Package Manager", ATRACE_TAG_PACKAGE_MANAGER, { } }, { "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } }, { "database", "Database", ATRACE_TAG_DATABASE, { } }, { "network", "Network", ATRACE_TAG_NETWORK, { } }, { "adb", "ADB", ATRACE_TAG_ADB, { } }, { "vibrator", "Vibrator", ATRACE_TAG_VIBRATOR, { } }, { "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } }, { "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } }, { "rro", "Runtime Resource Overlay", ATRACE_TAG_RRO, { } }, { k_coreServiceCategory, "Core services", 0, { } }, { k_pdxServiceCategory, "PDX services", 0, { } }, { "sched", "CPU Scheduling", 0, { { REQ, "events/sched/sched_switch/enable" }, { REQ, "events/sched/sched_wakeup/enable" }, { OPT, "events/sched/sched_waking/enable" }, { OPT, "events/sched/sched_blocked_reason/enable" }, { OPT, "events/sched/sched_cpu_hotplug/enable" }, { OPT, "events/sched/sched_pi_setprio/enable" }, { OPT, "events/sched/sched_process_exit/enable" }, { OPT, "events/cgroup/enable" }, { OPT, "events/oom/oom_score_adj_update/enable" }, { OPT, "events/task/task_rename/enable" }, { OPT, "events/task/task_newtask/enable" }, } }, { "irq", "IRQ Events", 0, { { REQ, "events/irq/enable" }, { OPT, "events/ipi/enable" }, } }, { "irqoff", "IRQ-disabled code section tracing", 0, { { REQ, "events/preemptirq/irq_enable/enable" }, { REQ, "events/preemptirq/irq_disable/enable" }, } }, { "preemptoff", "Preempt-disabled code section tracing", 0, { { REQ, "events/preemptirq/preempt_enable/enable" }, { REQ, "events/preemptirq/preempt_disable/enable" }, } }, { "i2c", "I2C Events", 0, { { REQ, "events/i2c/enable" }, { REQ, "events/i2c/i2c_read/enable" }, { REQ, "events/i2c/i2c_write/enable" }, { REQ, "events/i2c/i2c_result/enable" }, { REQ, "events/i2c/i2c_reply/enable" }, { OPT, "events/i2c/smbus_read/enable" }, { OPT, "events/i2c/smbus_write/enable" }, { OPT, "events/i2c/smbus_result/enable" }, { OPT, "events/i2c/smbus_reply/enable" }, } }, { "freq", "CPU Frequency", 0, { { REQ, "events/power/cpu_frequency/enable" }, { OPT, "events/power/clock_set_rate/enable" }, { OPT, "events/power/clock_disable/enable" }, { OPT, "events/power/clock_enable/enable" }, { OPT, "events/clk/clk_set_rate/enable" }, { OPT, "events/clk/clk_disable/enable" }, { OPT, "events/clk/clk_enable/enable" }, { OPT, "events/power/cpu_frequency_limits/enable" }, { OPT, "events/power/suspend_resume/enable" }, } }, { "membus", "Memory Bus Utilization", 0, { { REQ, "events/memory_bus/enable" }, } }, { "idle", "CPU Idle", 0, { { REQ, "events/power/cpu_idle/enable" }, } }, { "disk", "Disk I/O", 0, { { OPT, "events/f2fs/f2fs_sync_file_enter/enable" }, { OPT, "events/f2fs/f2fs_sync_file_exit/enable" }, { OPT, "events/f2fs/f2fs_write_begin/enable" }, { OPT, "events/f2fs/f2fs_write_end/enable" }, { OPT, "events/ext4/ext4_da_write_begin/enable" }, { OPT, "events/ext4/ext4_da_write_end/enable" }, { OPT, "events/ext4/ext4_sync_file_enter/enable" }, { OPT, "events/ext4/ext4_sync_file_exit/enable" }, { REQ, "events/block/block_rq_issue/enable" }, { REQ, "events/block/block_rq_complete/enable" }, } }, { "mmc", "eMMC commands", 0, { { REQ, "events/mmc/enable" }, } }, { "load", "CPU Load", 0, { { REQ, "events/cpufreq_interactive/enable" }, } }, { "sync", "Synchronization", 0, { // linux kernel < 4.9 { OPT, "events/sync/enable" }, // linux kernel == 4.9.x { OPT, "events/fence/enable" }, // linux kernel > 4.9 { OPT, "events/dma_fence/enable" }, } }, { "workq", "Kernel Workqueues", 0, { { REQ, "events/workqueue/enable" }, } }, { "memreclaim", "Kernel Memory Reclaim", 0, { { REQ, "events/vmscan/mm_vmscan_direct_reclaim_begin/enable" }, { REQ, "events/vmscan/mm_vmscan_direct_reclaim_end/enable" }, { REQ, "events/vmscan/mm_vmscan_kswapd_wake/enable" }, { REQ, "events/vmscan/mm_vmscan_kswapd_sleep/enable" }, { OPT, "events/lowmemorykiller/enable" }, } }, { "regulators", "Voltage and Current Regulators", 0, { { REQ, "events/regulator/enable" }, } }, { "binder_driver", "Binder Kernel driver", 0, { { REQ, "events/binder/binder_transaction/enable" }, { REQ, "events/binder/binder_transaction_received/enable" }, { REQ, "events/binder/binder_transaction_alloc_buf/enable" }, { OPT, "events/binder/binder_set_priority/enable" }, } }, { "binder_lock", "Binder global lock trace", 0, { { OPT, "events/binder/binder_lock/enable" }, { OPT, "events/binder/binder_locked/enable" }, { OPT, "events/binder/binder_unlock/enable" }, } }, { "pagecache", "Page cache", 0, { { REQ, "events/filemap/enable" }, } }, { "memory", "Memory", 0, { { OPT, "events/kmem/rss_stat/enable" }, { OPT, "events/kmem/ion_heap_grow/enable" }, { OPT, "events/kmem/ion_heap_shrink/enable" }, } }, }; struct TracingVendorCategory { // The name identifying the category. std::string name; // A longer description of the category. std::string description; // If the category is enabled through command. bool enabled; TracingVendorCategory(string &&name, string &&description, bool enabled) : name(std::move(name)) , description(std::move(description)) , enabled(enabled) {} }; /* Command line options */ static int g_traceDurationSeconds = 5; static bool g_traceOverwrite = false; static int g_traceBufferSizeKB = 2048; static bool g_compress = false; static bool g_nohup = false; static int g_initialSleepSecs = 0; static const char* g_categoriesFile = nullptr; static const char* g_kernelTraceFuncs = nullptr; static const char* g_debugAppCmdLine = ""; static const char* g_outputFile = nullptr; /* Global state */ static bool g_tracePdx = false; static bool g_traceAborted = false; static bool g_categoryEnables[arraysize(k_categories)] = {}; static std::string g_traceFolder; static sp g_atraceHal; static std::vector g_vendorCategories; /* Sys file paths */ static const char* k_traceClockPath = "trace_clock"; static const char* k_traceBufferSizePath = "buffer_size_kb"; #if 0 // TODO: Re-enable after stabilization static const char* k_traceCmdlineSizePath = "saved_cmdlines_size"; #endif static const char* k_tracingOverwriteEnablePath = "options/overwrite"; static const char* k_currentTracerPath = "current_tracer"; static const char* k_printTgidPath = "options/print-tgid"; static const char* k_recordTgidPath = "options/record-tgid"; static const char* k_funcgraphAbsTimePath = "options/funcgraph-abstime"; static const char* k_funcgraphCpuPath = "options/funcgraph-cpu"; static const char* k_funcgraphProcPath = "options/funcgraph-proc"; static const char* k_funcgraphFlatPath = "options/funcgraph-flat"; static const char* k_ftraceFilterPath = "set_ftrace_filter"; static const char* k_tracingOnPath = "tracing_on"; static const char* k_tracePath = "trace"; static const char* k_traceStreamPath = "trace_pipe"; static const char* k_traceMarkerPath = "trace_marker"; // Check whether a file exists. static bool fileExists(const char* filename) { return access((g_traceFolder + filename).c_str(), F_OK) != -1; } // Check whether a file is writable. static bool fileIsWritable(const char* filename) { return access((g_traceFolder + filename).c_str(), W_OK) != -1; } // Truncate a file. static bool truncateFile(const char* path) { // This uses creat rather than truncate because some of the debug kernel // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by // calls to truncate, but they are cleared by calls to creat. int traceFD = creat((g_traceFolder + path).c_str(), 0); if (traceFD == -1) { fprintf(stderr, "error truncating %s: %s (%d)\n", (g_traceFolder + path).c_str(), strerror(errno), errno); return false; } close(traceFD); return true; } static bool _writeStr(const char* filename, const char* str, int flags) { std::string fullFilename = g_traceFolder + filename; int fd = open(fullFilename.c_str(), flags); if (fd == -1) { fprintf(stderr, "error opening %s: %s (%d)\n", fullFilename.c_str(), strerror(errno), errno); return false; } bool ok = true; ssize_t len = strlen(str); if (write(fd, str, len) != len) { fprintf(stderr, "error writing to %s: %s (%d)\n", fullFilename.c_str(), strerror(errno), errno); ok = false; } close(fd); return ok; } // Write a string to a file, returning true if the write was successful. static bool writeStr(const char* filename, const char* str) { return _writeStr(filename, str, O_WRONLY); } // Append a string to a file, returning true if the write was successful. static bool appendStr(const char* filename, const char* str) { return _writeStr(filename, str, O_APPEND|O_WRONLY); } static void writeClockSyncMarker() { char buffer[128]; int len = 0; int fd = open((g_traceFolder + k_traceMarkerPath).c_str(), O_WRONLY); if (fd == -1) { fprintf(stderr, "error opening %s: %s (%d)\n", k_traceMarkerPath, strerror(errno), errno); return; } float now_in_seconds = systemTime(CLOCK_MONOTONIC) / 1000000000.0f; len = snprintf(buffer, 128, "trace_event_clock_sync: parent_ts=%f\n", now_in_seconds); if (write(fd, buffer, len) != len) { fprintf(stderr, "error writing clock sync marker %s (%d)\n", strerror(errno), errno); } int64_t realtime_in_ms = systemTime(CLOCK_REALTIME) / 1000000; len = snprintf(buffer, 128, "trace_event_clock_sync: realtime_ts=%" PRId64 "\n", realtime_in_ms); if (write(fd, buffer, len) != len) { fprintf(stderr, "error writing clock sync marker %s (%d)\n", strerror(errno), errno); } close(fd); } // Enable or disable a kernel option by writing a "1" or a "0" into a /sys // file. static bool setKernelOptionEnable(const char* filename, bool enable) { return writeStr(filename, enable ? "1" : "0"); } // Check whether the category is supported on the device with the current // rootness. A category is supported only if all its required /sys/ files are // writable and if enabling the category will enable one or more tracing tags // or /sys/ files. static bool isCategorySupported(const TracingCategory& category) { if (strcmp(category.name, k_coreServiceCategory) == 0) { return !android::base::GetProperty(k_coreServicesProp, "").empty(); } if (strcmp(category.name, k_pdxServiceCategory) == 0) { return true; } bool ok = category.tags != 0; for (int i = 0; i < MAX_SYS_FILES; i++) { const char* path = category.sysfiles[i].path; bool req = category.sysfiles[i].required == REQ; if (path != nullptr) { if (fileIsWritable(path)) { ok = true; } else if (req) { return false; } } } return ok; } // Check whether the category would be supported on the device if the user // were root. This function assumes that root is able to write to any file // that exists. It performs the same logic as isCategorySupported, but it // uses file existence rather than writability in the /sys/ file checks. static bool isCategorySupportedForRoot(const TracingCategory& category) { bool ok = category.tags != 0; for (int i = 0; i < MAX_SYS_FILES; i++) { const char* path = category.sysfiles[i].path; bool req = category.sysfiles[i].required == REQ; if (path != nullptr) { if (req) { if (!fileExists(path)) { return false; } else { ok = true; } } else { ok |= fileExists(path); } } } return ok; } // Enable or disable overwriting of the kernel trace buffers. Disabling this // will cause tracing to stop once the trace buffers have filled up. static bool setTraceOverwriteEnable(bool enable) { return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable); } // Set the user initiated trace property static bool setUserInitiatedTraceProperty(bool enable) { if (!android::base::SetProperty(k_userInitiatedTraceProperty, enable ? "1" : "")) { fprintf(stderr, "error setting user initiated strace system property\n"); return false; } return true; } // Enable or disable kernel tracing. static bool setTracingEnabled(bool enable) { return setKernelOptionEnable(k_tracingOnPath, enable); } // Clear the contents of the kernel trace. static bool clearTrace() { return truncateFile(k_tracePath); } // Set the size of the kernel's trace buffer in kilobytes. static bool setTraceBufferSizeKB(int size) { char str[32] = "1"; if (size < 1) { size = 1; } snprintf(str, 32, "%d", size); return writeStr(k_traceBufferSizePath, str); } #if 0 // TODO: Re-enable after stabilization // Set the default size of cmdline hashtable static bool setCmdlineSize() { if (fileExists(k_traceCmdlineSizePath)) { return writeStr(k_traceCmdlineSizePath, "8192"); } return true; } #endif // Set the clock to the best available option while tracing. Use 'boot' if it's // available; otherwise, use 'mono'. If neither are available use 'global'. // Any write to the trace_clock sysfs file will reset the buffer, so only // update it if the requested value is not the current value. static bool setClock() { std::ifstream clockFile((g_traceFolder + k_traceClockPath).c_str(), O_RDONLY); std::string clockStr((std::istreambuf_iterator(clockFile)), std::istreambuf_iterator()); std::string newClock; if (clockStr.find("boot") != std::string::npos) { newClock = "boot"; } else if (clockStr.find("mono") != std::string::npos) { newClock = "mono"; } else { newClock = "global"; } size_t begin = clockStr.find('[') + 1; size_t end = clockStr.find(']'); if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) { return true; } return writeStr(k_traceClockPath, newClock.c_str()); } static bool setPrintTgidEnableIfPresent(bool enable) { // Pre-4.13 this was options/print-tgid as an android-specific option. // In 4.13+ this is an upstream option called options/record-tgid // Both options produce the same ftrace format change if (fileExists(k_printTgidPath)) { return setKernelOptionEnable(k_printTgidPath, enable); } if (fileExists(k_recordTgidPath)) { return setKernelOptionEnable(k_recordTgidPath, enable); } return true; } // Poke all the binder-enabled processes in the system to get them to re-read // their system properties. static bool pokeBinderServices() { sp sm = defaultServiceManager(); Vector services = sm->listServices(); for (size_t i = 0; i < services.size(); i++) { sp obj = sm->checkService(services[i]); if (obj != nullptr) { Parcel data; if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data, nullptr, 0) != OK) { if (false) { // XXX: For some reason this fails on tablets trying to // poke the "phone" service. It's not clear whether some // are expected to fail. String8 svc(services[i]); fprintf(stderr, "error poking binder service %s\n", svc.string()); return false; } } } } return true; } // Poke all the HAL processes in the system to get them to re-read // their system properties. static void pokeHalServices() { using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; using ::android::hardware::hidl_string; using ::android::hardware::Return; sp sm = ::android::hardware::defaultServiceManager(); if (sm == nullptr) { fprintf(stderr, "failed to get IServiceManager to poke hal services\n"); return; } auto listRet = sm->list([&](const auto &interfaces) { for (size_t i = 0; i < interfaces.size(); i++) { string fqInstanceName = interfaces[i]; string::size_type n = fqInstanceName.find('/'); if (n == std::string::npos || interfaces[i].size() == n+1) continue; hidl_string fqInterfaceName = fqInstanceName.substr(0, n); hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos); Return> interfaceRet = sm->get(fqInterfaceName, instanceName); if (!interfaceRet.isOk()) { // ignore continue; } sp interface = interfaceRet; if (interface == nullptr) { // ignore continue; } auto notifyRet = interface->notifySyspropsChanged(); if (!notifyRet.isOk()) { // ignore } } }); if (!listRet.isOk()) { // TODO(b/34242478) fix this when we determine the correct ACL //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str()); } } // Set the trace tags that userland tracing uses, and poke the running // processes to pick up the new value. static bool setTagsProperty(uint64_t tags) { std::string value = android::base::StringPrintf("%#" PRIx64, tags); if (!android::base::SetProperty(k_traceTagsProperty, value)) { fprintf(stderr, "error setting trace tags system property\n"); return false; } return true; } static void clearAppProperties() { if (!android::base::SetProperty(k_traceAppsNumberProperty, "")) { fprintf(stderr, "failed to clear system property: %s", k_traceAppsNumberProperty); } } // Set the system property that indicates which apps should perform // application-level tracing. static bool setAppCmdlineProperty(char* cmdline) { int i = 0; char* start = cmdline; while (start != nullptr) { char* end = strchr(start, ','); if (end != nullptr) { *end = '\0'; end++; } std::string key = android::base::StringPrintf(k_traceAppsPropertyTemplate, i); if (!android::base::SetProperty(key, start)) { fprintf(stderr, "error setting trace app %d property to %s\n", i, key.c_str()); clearAppProperties(); return false; } start = end; i++; } std::string value = android::base::StringPrintf("%d", i); if (!android::base::SetProperty(k_traceAppsNumberProperty, value)) { fprintf(stderr, "error setting trace app number property to %s\n", value.c_str()); clearAppProperties(); return false; } return true; } // Disable all /sys/ enable files. static bool disableKernelTraceEvents() { bool ok = true; for (size_t i = 0; i < arraysize(k_categories); i++) { const TracingCategory &c = k_categories[i]; for (int j = 0; j < MAX_SYS_FILES; j++) { const char* path = c.sysfiles[j].path; if (path != nullptr && fileIsWritable(path)) { ok &= setKernelOptionEnable(path, false); } } } return ok; } // Verify that the comma separated list of functions are being traced by the // kernel. static bool verifyKernelTraceFuncs(const char* funcs) { std::string buf; if (!android::base::ReadFileToString(g_traceFolder + k_ftraceFilterPath, &buf)) { fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath, strerror(errno), errno); return false; } String8 funcList = String8::format("\n%s",buf.c_str()); // Make sure that every function listed in funcs is in the list we just // read from the kernel, except for wildcard inputs. bool ok = true; char* myFuncs = strdup(funcs); char* func = strtok(myFuncs, ","); while (func) { if (!strchr(func, '*')) { String8 fancyFunc = String8::format("\n%s\n", func); bool found = funcList.find(fancyFunc.string(), 0) >= 0; if (!found || func[0] == '\0') { fprintf(stderr, "error: \"%s\" is not a valid kernel function " "to trace.\n", func); ok = false; } } func = strtok(nullptr, ","); } free(myFuncs); return ok; } // Set the comma separated list of functions that the kernel is to trace. static bool setKernelTraceFuncs(const char* funcs) { bool ok = true; if (funcs == nullptr || funcs[0] == '\0') { // Disable kernel function tracing. if (fileIsWritable(k_currentTracerPath)) { ok &= writeStr(k_currentTracerPath, "nop"); } if (fileIsWritable(k_ftraceFilterPath)) { ok &= truncateFile(k_ftraceFilterPath); } } else { // Enable kernel function tracing. ok &= writeStr(k_currentTracerPath, "function_graph"); ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true); ok &= setKernelOptionEnable(k_funcgraphCpuPath, true); ok &= setKernelOptionEnable(k_funcgraphProcPath, true); ok &= setKernelOptionEnable(k_funcgraphFlatPath, true); // Set the requested filter functions. ok &= truncateFile(k_ftraceFilterPath); char* myFuncs = strdup(funcs); char* func = strtok(myFuncs, ","); while (func) { ok &= appendStr(k_ftraceFilterPath, func); func = strtok(nullptr, ","); } free(myFuncs); // Verify that the set functions are being traced. if (ok) { ok &= verifyKernelTraceFuncs(funcs); } } return ok; } static bool setCategoryEnable(const char* name) { bool vendor_found = false; for (auto &c : g_vendorCategories) { if (strcmp(name, c.name.c_str()) == 0) { c.enabled = true; vendor_found = true; } } for (size_t i = 0; i < arraysize(k_categories); i++) { const TracingCategory& c = k_categories[i]; if (strcmp(name, c.name) == 0) { if (isCategorySupported(c)) { g_categoryEnables[i] = true; return true; } else { if (isCategorySupportedForRoot(c)) { fprintf(stderr, "error: category \"%s\" requires root " "privileges.\n", name); } else { fprintf(stderr, "error: category \"%s\" is not supported " "on this device.\n", name); } return false; } } } if (vendor_found) { return true; } fprintf(stderr, "error: unknown tracing category \"%s\"\n", name); return false; } static bool setCategoriesEnableFromFile(const char* categories_file) { if (!categories_file) { return true; } Tokenizer* tokenizer = nullptr; if (Tokenizer::open(String8(categories_file), &tokenizer) != NO_ERROR) { return false; } bool ok = true; while (!tokenizer->isEol()) { String8 token = tokenizer->nextToken(" "); if (token.isEmpty()) { tokenizer->skipDelimiters(" "); continue; } ok &= setCategoryEnable(token.string()); } delete tokenizer; return ok; } static bool setUpUserspaceTracing() { bool ok = true; // Set up the tags property. uint64_t tags = 0; for (size_t i = 0; i < arraysize(k_categories); i++) { if (g_categoryEnables[i]) { const TracingCategory &c = k_categories[i]; tags |= c.tags; } } ok &= setTagsProperty(tags); bool coreServicesTagEnabled = false; for (size_t i = 0; i < arraysize(k_categories); i++) { if (strcmp(k_categories[i].name, k_coreServiceCategory) == 0) { coreServicesTagEnabled = g_categoryEnables[i]; } // Set whether to poke PDX services in this session. if (strcmp(k_categories[i].name, k_pdxServiceCategory) == 0) { g_tracePdx = g_categoryEnables[i]; } } std::string packageList(g_debugAppCmdLine); if (coreServicesTagEnabled) { if (!packageList.empty()) { packageList += ","; } packageList += android::base::GetProperty(k_coreServicesProp, ""); } ok &= setAppCmdlineProperty(&packageList[0]); ok &= pokeBinderServices(); pokeHalServices(); if (g_tracePdx) { ok &= ServiceUtility::PokeServices(); } return ok; } static void cleanUpUserspaceTracing() { setTagsProperty(0); clearAppProperties(); pokeBinderServices(); pokeHalServices(); if (g_tracePdx) { ServiceUtility::PokeServices(); } } // Set all the kernel tracing settings to the desired state for this trace // capture. static bool setUpKernelTracing() { bool ok = true; ok &= setUserInitiatedTraceProperty(true); // Set up the tracing options. ok &= setCategoriesEnableFromFile(g_categoriesFile); ok &= setTraceOverwriteEnable(g_traceOverwrite); ok &= setTraceBufferSizeKB(g_traceBufferSizeKB); // TODO: Re-enable after stabilization //ok &= setCmdlineSize(); ok &= setClock(); ok &= setPrintTgidEnableIfPresent(true); ok &= setKernelTraceFuncs(g_kernelTraceFuncs); // Disable all the sysfs enables. This is done as a separate loop from // the enables to allow the same enable to exist in multiple categories. ok &= disableKernelTraceEvents(); // Enable all the sysfs enables that are in an enabled category. for (size_t i = 0; i < arraysize(k_categories); i++) { if (g_categoryEnables[i]) { const TracingCategory &c = k_categories[i]; for (int j = 0; j < MAX_SYS_FILES; j++) { const char* path = c.sysfiles[j].path; bool required = c.sysfiles[j].required == REQ; if (path != nullptr) { if (fileIsWritable(path)) { ok &= setKernelOptionEnable(path, true); } else if (required) { fprintf(stderr, "error writing file %s\n", path); ok = false; } } } } } return ok; } // Reset all the kernel tracing settings to their default state. static void cleanUpKernelTracing() { // Disable all tracing that we're able to. disableKernelTraceEvents(); // Set the options back to their defaults. setTraceOverwriteEnable(true); setTraceBufferSizeKB(1); setPrintTgidEnableIfPresent(false); setKernelTraceFuncs(nullptr); setUserInitiatedTraceProperty(false); } // Enable tracing in the kernel. static bool startTrace() { return setTracingEnabled(true); } // Disable tracing in the kernel. static void stopTrace() { setTracingEnabled(false); } // Read data from the tracing pipe and forward to stdout static void streamTrace() { char trace_data[4096]; int traceFD = open((g_traceFolder + k_traceStreamPath).c_str(), O_RDWR); if (traceFD == -1) { fprintf(stderr, "error opening %s: %s (%d)\n", k_traceStreamPath, strerror(errno), errno); return; } while (!g_traceAborted) { ssize_t bytes_read = read(traceFD, trace_data, 4096); if (bytes_read > 0) { write(STDOUT_FILENO, trace_data, bytes_read); fflush(stdout); } else { if (!g_traceAborted) { fprintf(stderr, "read returned %zd bytes err %d (%s)\n", bytes_read, errno, strerror(errno)); } break; } } } // Read the current kernel trace and write it to stdout. static void dumpTrace(int outFd) { ALOGI("Dumping trace"); int traceFD = open((g_traceFolder + k_tracePath).c_str(), O_RDWR); if (traceFD == -1) { fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath, strerror(errno), errno); return; } if (g_compress) { z_stream zs; memset(&zs, 0, sizeof(zs)); int result = deflateInit(&zs, Z_DEFAULT_COMPRESSION); if (result != Z_OK) { fprintf(stderr, "error initializing zlib: %d\n", result); close(traceFD); return; } constexpr size_t bufSize = 64*1024; std::unique_ptr in(new uint8_t[bufSize]); std::unique_ptr out(new uint8_t[bufSize]); if (!in || !out) { fprintf(stderr, "couldn't allocate buffers\n"); close(traceFD); return; } int flush = Z_NO_FLUSH; zs.next_out = reinterpret_cast(out.get()); zs.avail_out = bufSize; do { if (zs.avail_in == 0) { // More input is needed. result = read(traceFD, in.get(), bufSize); if (result < 0) { fprintf(stderr, "error reading trace: %s (%d)\n", strerror(errno), errno); result = Z_STREAM_END; break; } else if (result == 0) { flush = Z_FINISH; } else { zs.next_in = reinterpret_cast(in.get()); zs.avail_in = result; } } if (zs.avail_out == 0) { // Need to write the output. result = write(outFd, out.get(), bufSize); if ((size_t)result < bufSize) { fprintf(stderr, "error writing deflated trace: %s (%d)\n", strerror(errno), errno); result = Z_STREAM_END; // skip deflate error message zs.avail_out = bufSize; // skip the final write break; } zs.next_out = reinterpret_cast(out.get()); zs.avail_out = bufSize; } } while ((result = deflate(&zs, flush)) == Z_OK); if (result != Z_STREAM_END) { fprintf(stderr, "error deflating trace: %s\n", zs.msg); } if (zs.avail_out < bufSize) { size_t bytes = bufSize - zs.avail_out; result = write(outFd, out.get(), bytes); if ((size_t)result < bytes) { fprintf(stderr, "error writing deflated trace: %s (%d)\n", strerror(errno), errno); } } result = deflateEnd(&zs); if (result != Z_OK) { fprintf(stderr, "error cleaning up zlib: %d\n", result); } } else { char buf[4096]; ssize_t rc; while ((rc = TEMP_FAILURE_RETRY(read(traceFD, buf, sizeof(buf)))) > 0) { if (!android::base::WriteFully(outFd, buf, rc)) { fprintf(stderr, "error writing trace: %s\n", strerror(errno)); break; } } if (rc == -1) { fprintf(stderr, "error dumping trace: %s\n", strerror(errno)); } } close(traceFD); } static void handleSignal(int /*signo*/) { if (!g_nohup) { g_traceAborted = true; } } static void registerSigHandler() { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = handleSignal; sigaction(SIGHUP, &sa, nullptr); sigaction(SIGINT, &sa, nullptr); sigaction(SIGQUIT, &sa, nullptr); sigaction(SIGTERM, &sa, nullptr); } static void listSupportedCategories() { for (size_t i = 0; i < arraysize(k_categories); i++) { const TracingCategory& c = k_categories[i]; if (isCategorySupported(c)) { printf(" %10s - %s\n", c.name, c.longname); } } for (const auto &c : g_vendorCategories) { printf(" %10s - %s (HAL)\n", c.name.c_str(), c.description.c_str()); } } // Print the command usage help to stderr. static void showHelp(const char *cmd) { fprintf(stderr, "usage: %s [options] [categories...]\n", cmd); fprintf(stderr, "options include:\n" " -a appname enable app-level tracing for a comma " "separated list of cmdlines; * is a wildcard matching any process\n" " -b N use a trace buffer size of N KB\n" " -c trace into a circular buffer\n" " -f filename use the categories written in a file as space-separated\n" " values in a line\n" " -k fname,... trace the listed kernel functions\n" " -n ignore signals\n" " -s N sleep for N seconds before tracing [default 0]\n" " -t N trace for N seconds [default 5]\n" " -z compress the trace dump\n" " --async_start start circular trace and return immediately\n" " --async_dump dump the current contents of circular trace buffer\n" " --async_stop stop tracing and dump the current contents of circular\n" " trace buffer\n" " --stream stream trace to stdout as it enters the trace buffer\n" " Note: this can take significant CPU time, and is best\n" " used for measuring things that are not affected by\n" " CPU performance, like pagecache usage.\n" " --list_categories\n" " list the available tracing categories\n" " -o filename write the trace to the specified file instead\n" " of stdout.\n" ); } bool findTraceFiles() { static const std::string debugfs_path = "/sys/kernel/debug/tracing/"; static const std::string tracefs_path = "/sys/kernel/tracing/"; static const std::string trace_file = "trace_marker"; bool tracefs = access((tracefs_path + trace_file).c_str(), F_OK) != -1; bool debugfs = access((debugfs_path + trace_file).c_str(), F_OK) != -1; if (!tracefs && !debugfs) { fprintf(stderr, "Error: Did not find trace folder\n"); return false; } if (tracefs) { g_traceFolder = tracefs_path; } else { g_traceFolder = debugfs_path; } return true; } void initVendorCategories() { g_atraceHal = IAtraceDevice::getService(); if (g_atraceHal == nullptr) { // No atrace HAL return; } Return ret = g_atraceHal->listCategories( [](const auto& list) { g_vendorCategories.reserve(list.size()); for (const auto& category : list) { g_vendorCategories.emplace_back(category.name, category.description, false); } }); if (!ret.isOk()) { fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); } } static bool setUpVendorTracing() { if (g_atraceHal == nullptr) { // No atrace HAL return true; } std::vector categories; for (const auto &c : g_vendorCategories) { if (c.enabled) { categories.emplace_back(c.name); } } if (!categories.size()) { return true; } auto ret = g_atraceHal->enableCategories(categories); if (!ret.isOk()) { fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); return false; } else if (ret != Status::SUCCESS) { fprintf(stderr, "calling atrace HAL failed: %s\n", toString(ret).c_str()); return false; } return true; } static bool cleanUpVendorTracing() { if (g_atraceHal == nullptr) { // No atrace HAL return true; } if (!g_vendorCategories.size()) { // No vendor categories return true; } auto ret = g_atraceHal->disableAllCategories(); if (!ret.isOk()) { fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); return false; } else if (ret != Status::SUCCESS) { fprintf(stderr, "calling atrace HAL failed: %s\n", toString(ret).c_str()); return false; } return true; } int main(int argc, char **argv) { bool async = false; bool traceStart = true; bool traceStop = true; bool traceDump = true; bool traceStream = false; bool onlyUserspace = false; if (argc == 2 && 0 == strcmp(argv[1], "--help")) { showHelp(argv[0]); exit(0); } if (!findTraceFiles()) { fprintf(stderr, "No trace folder found\n"); exit(-1); } initVendorCategories(); for (;;) { int ret; int option_index = 0; static struct option long_options[] = { {"async_start", no_argument, nullptr, 0 }, {"async_stop", no_argument, nullptr, 0 }, {"async_dump", no_argument, nullptr, 0 }, {"only_userspace", no_argument, nullptr, 0 }, {"list_categories", no_argument, nullptr, 0 }, {"stream", no_argument, nullptr, 0 }, {nullptr, 0, nullptr, 0 } }; ret = getopt_long(argc, argv, "a:b:cf:k:ns:t:zo:", long_options, &option_index); if (ret < 0) { for (int i = optind; i < argc; i++) { if (!setCategoryEnable(argv[i])) { fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); exit(1); } } break; } switch(ret) { case 'a': g_debugAppCmdLine = optarg; break; case 'b': g_traceBufferSizeKB = atoi(optarg); break; case 'c': g_traceOverwrite = true; break; case 'f': g_categoriesFile = optarg; break; case 'k': g_kernelTraceFuncs = optarg; break; case 'n': g_nohup = true; break; case 's': g_initialSleepSecs = atoi(optarg); break; case 't': g_traceDurationSeconds = atoi(optarg); break; case 'z': g_compress = true; break; case 'o': g_outputFile = optarg; break; case 0: if (!strcmp(long_options[option_index].name, "async_start")) { async = true; traceStop = false; traceDump = false; g_traceOverwrite = true; } else if (!strcmp(long_options[option_index].name, "async_stop")) { async = true; traceStart = false; } else if (!strcmp(long_options[option_index].name, "async_dump")) { async = true; traceStart = false; traceStop = false; } else if (!strcmp(long_options[option_index].name, "only_userspace")) { onlyUserspace = true; } else if (!strcmp(long_options[option_index].name, "stream")) { traceStream = true; traceDump = false; } else if (!strcmp(long_options[option_index].name, "list_categories")) { listSupportedCategories(); exit(0); } break; default: fprintf(stderr, "\n"); showHelp(argv[0]); exit(-1); break; } } if (onlyUserspace) { if (!async || !(traceStart || traceStop)) { fprintf(stderr, "--only_userspace can only be used with " "--async_start or --async_stop\n"); exit(1); } } registerSigHandler(); if (g_initialSleepSecs > 0) { sleep(g_initialSleepSecs); } bool ok = true; if (traceStart) { ok &= setUpUserspaceTracing(); } if (ok && traceStart && !onlyUserspace) { ok &= setUpKernelTracing(); ok &= setUpVendorTracing(); ok &= startTrace(); } if (ok && traceStart) { if (!traceStream && !onlyUserspace) { printf("capturing trace..."); fflush(stdout); } // We clear the trace after starting it because tracing gets enabled for // each CPU individually in the kernel. Having the beginning of the trace // contain entries from only one CPU can cause "begin" entries without a // matching "end" entry to show up if a task gets migrated from one CPU to // another. if (!onlyUserspace) ok = clearTrace(); writeClockSyncMarker(); if (ok && !async && !traceStream) { // Sleep to allow the trace to be captured. struct timespec timeLeft; timeLeft.tv_sec = g_traceDurationSeconds; timeLeft.tv_nsec = 0; do { if (g_traceAborted) { break; } } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR); } if (traceStream) { streamTrace(); } } // Stop the trace and restore the default settings. if (traceStop && !onlyUserspace) stopTrace(); if (ok && traceDump && !onlyUserspace) { if (!g_traceAborted) { printf(" done\n"); fflush(stdout); int outFd = STDOUT_FILENO; if (g_outputFile) { outFd = open(g_outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); } if (outFd == -1) { printf("Failed to open '%s', err=%d", g_outputFile, errno); } else { dprintf(outFd, "TRACE:\n"); dumpTrace(outFd); if (g_outputFile) { close(outFd); } } } else { printf("\ntrace aborted.\n"); fflush(stdout); } clearTrace(); } else if (!ok) { fprintf(stderr, "unable to start tracing\n"); } // Reset the trace buffer size to 1. if (traceStop) { cleanUpVendorTracing(); cleanUpUserspaceTracing(); if (!onlyUserspace) cleanUpKernelTracing(); } return g_traceAborted ? 1 : 0; } cmds/atrace/atrace.rc0100644 0000000 0000000 00000031776 13756501734 013610 0ustar000000000 0000000 ## Permissions to allow system-wide tracing to the kernel trace buffer. ## on late-init # Allow writing to the kernel trace log. chmod 0222 /sys/kernel/debug/tracing/trace_marker chmod 0222 /sys/kernel/tracing/trace_marker # Scheduler tracepoints require schedstats=enable write /proc/sys/kernel/sched_schedstats 1 # Grant unix world read/write permissions to kernel tracepoints. # Access control to these files is now entirely in selinux policy. chmod 0666 /sys/kernel/debug/tracing/trace_clock chmod 0666 /sys/kernel/tracing/trace_clock chmod 0666 /sys/kernel/debug/tracing/buffer_size_kb chmod 0666 /sys/kernel/tracing/buffer_size_kb chmod 0666 /sys/kernel/debug/tracing/options/overwrite chmod 0666 /sys/kernel/tracing/options/overwrite chmod 0666 /sys/kernel/debug/tracing/options/print-tgid chmod 0666 /sys/kernel/tracing/options/print-tgid chmod 0666 /sys/kernel/debug/tracing/options/record-tgid chmod 0666 /sys/kernel/tracing/options/record-tgid chmod 0666 /sys/kernel/debug/tracing/saved_cmdlines_size chmod 0666 /sys/kernel/tracing/saved_cmdlines_size chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_switch/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_switch/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_wakeup/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_pi_setprio/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable chmod 0666 /sys/kernel/tracing/events/cgroup/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_idle/enable chmod 0666 /sys/kernel/tracing/events/power/cpu_idle/enable chmod 0666 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable chmod 0666 /sys/kernel/tracing/events/power/suspend_resume/enable chmod 0666 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chmod 0666 /sys/kernel/tracing/events/cpufreq_interactive/enable chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable chmod 0666 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable chmod 0666 /sys/kernel/debug/tracing/tracing_on chmod 0666 /sys/kernel/tracing/tracing_on chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction_received/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction_alloc_buf/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_lock/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_lock/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_locked/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_locked/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_unlock/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/enable chmod 0666 /sys/kernel/tracing/events/i2c/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable chmod 0666 /sys/kernel/tracing/events/i2c/i2c_read/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable chmod 0666 /sys/kernel/tracing/events/i2c/i2c_write/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable chmod 0666 /sys/kernel/tracing/events/i2c/i2c_result/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable chmod 0666 /sys/kernel/tracing/events/i2c/i2c_reply/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable chmod 0666 /sys/kernel/tracing/events/i2c/smbus_read/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable chmod 0666 /sys/kernel/tracing/events/i2c/smbus_write/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable chmod 0666 /sys/kernel/tracing/events/i2c/smbus_result/enable chmod 0666 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable chmod 0666 /sys/kernel/tracing/events/i2c/smbus_reply/enable chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/enable chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/enable chmod 0666 /sys/kernel/debug/tracing/events/sync/enable chmod 0666 /sys/kernel/tracing/events/sync/enable chmod 0666 /sys/kernel/debug/tracing/events/fence/enable chmod 0666 /sys/kernel/tracing/events/fence/enable chmod 0666 /sys/kernel/debug/tracing/events/dma_fence/enable chmod 0666 /sys/kernel/tracing/events/dma_fence/enable chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/enable chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/enable chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow/enable chmod 0666 /sys/kernel/tracing/events/kmem/ion_heap_grow/enable chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_shrink/enable chmod 0666 /sys/kernel/tracing/events/kmem/ion_heap_shrink/enable chmod 0666 /sys/kernel/debug/tracing/events/signal/signal_generate/enable chmod 0666 /sys/kernel/tracing/events/signal/signal_generate/enable chmod 0666 /sys/kernel/debug/tracing/events/signal/signal_deliver/enable chmod 0666 /sys/kernel/tracing/events/signal/signal_deliver/enable chmod 0666 /sys/kernel/debug/tracing/events/mm_event/mm_event_record/enable chmod 0666 /sys/kernel/tracing/events/mm_event/mm_event_record/enable chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/lowmemory_kill/enable chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable chmod 0666 /sys/kernel/debug/tracing/events/task/task_rename/enable chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable # disk chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_get_data_block/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_iget/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_iget/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_exit/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_write_begin/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_write_end/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_da_write_begin/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_da_write_end/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_enter/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_enter/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_exit/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_exit/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_load_inode/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_load_inode/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_sync_file_enter/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_sync_file_exit/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable chmod 0666 /sys/kernel/tracing/events/block/block_rq_issue/enable chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_issue/enable chmod 0666 /sys/kernel/tracing/events/block/block_rq_complete/enable chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable # filemap events for iorapd chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_add_to_page_cache/enable chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_add_to_page_cache/enable chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable # Tracing disabled by default write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 # Read and truncate the kernel trace. chmod 0666 /sys/kernel/debug/tracing/trace chmod 0666 /sys/kernel/tracing/trace # Read and truncate the per-CPU kernel trace. # Cannot use wildcards in .rc files. Update this if there is a phone with # more CPUs. chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu0/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu0/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu1/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu1/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu2/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu2/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu3/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu3/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu4/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu4/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu5/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu5/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu6/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu6/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu7/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu7/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu8/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu8/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu9/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu9/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu10/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu10/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu11/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu11/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu12/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu12/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu13/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu13/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu14/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu14/trace chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace on property:persist.debug.atrace.boottrace=1 start boottrace # Run atrace with the categories written in a file service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categories disabled oneshot cmds/atrace/atrace_userdebug.rc0100644 0000000 0000000 00000002232 13756501734 015636 0ustar000000000 0000000 ## Permissions to allow additional system-wide tracing to the kernel trace buffer. ## The default list of permissions is set in frameworks/native/cmds/atrace/atrace.rc # Grant unix world read/write permissions to enable kernel tracepoints. # Access control to these files is now entirely in selinux policy. on post-fs # On userdebug allow to enable any event via the generic # set_event interface: # echo sched/foo > set_event == echo 1 > events/sched/foo/enable. chmod 0666 /sys/kernel/tracing/set_event chmod 0666 /sys/kernel/debug/tracing/set_event chmod 0666 /sys/kernel/tracing/events/workqueue/enable chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable chmod 0666 /sys/kernel/tracing/events/regulator/enable chmod 0666 /sys/kernel/debug/tracing/events/regulator/enable chmod 0666 /sys/kernel/tracing/events/filemap/enable chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable # irq chmod 0666 /sys/kernel/tracing/events/irq/enable chmod 0666 /sys/kernel/debug/tracing/events/irq/enable chmod 0666 /sys/kernel/tracing/events/ipi/enable chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable cmds/bugreport/0040755 0000000 0000000 00000000000 13756501734 012562 5ustar000000000 0000000 cmds/bugreport/Android.bp0100644 0000000 0000000 00000000204 13756501734 014456 0ustar000000000 0000000 cc_binary { name: "bugreport", srcs: ["bugreport.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: ["libcutils"], } cmds/bugreport/OWNERS0100644 0000000 0000000 00000000131 13756501734 013512 0ustar000000000 0000000 set noparent felipeal@google.com nandana@google.com jsharkey@android.com enh@google.com cmds/bugreport/bugreport.cpp0100644 0000000 0000000 00000006367 13756501734 015310 0ustar000000000 0000000 /* * Copyright (C) 2009 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. */ #include #include #include #include #include #include #include // This program will trigger the dumpstate service to start a call to // dumpstate, then connect to the dumpstate local client to read the // output. All of the dumpstate output is written to stdout, including // any errors encountered while reading/writing the output. int main() { fprintf(stderr, "=============================================================================\n"); fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport instead\n"); fprintf(stderr, "=============================================================================\n\n\n"); // Start the dumpstate service. property_set("ctl.start", "dumpstate"); // Socket will not be available until service starts. int s; for (int i = 0; i < 20; i++) { s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if (s >= 0) break; // Try again in 1 second. sleep(1); } if (s == -1) { printf("Failed to connect to dumpstate service: %s\n", strerror(errno)); return 1; } // Set a timeout so that if nothing is read in 3 minutes, we'll stop // reading and quit. No timeout in dumpstate is longer than 60 seconds, // so this gives lots of leeway in case of unforeseen time outs. struct timeval tv; tv.tv_sec = 3 * 60; tv.tv_usec = 0; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno)); } while (1) { char buffer[65536]; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer))); if (bytes_read == 0) { break; } else if (bytes_read == -1) { // EAGAIN really means time out, so change the errno. if (errno == EAGAIN) { errno = ETIMEDOUT; } printf("\nBugreport read terminated abnormally (%s).\n", strerror(errno)); break; } ssize_t bytes_to_send = bytes_read; ssize_t bytes_written; do { bytes_written = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send, bytes_to_send)); if (bytes_written == -1) { printf("Failed to write data to stdout: read %zd, trying to send %zd (%s)\n", bytes_read, bytes_to_send, strerror(errno)); return 1; } bytes_to_send -= bytes_written; } while (bytes_written != 0 && bytes_to_send > 0); } close(s); return 0; } cmds/bugreportz/0040755 0000000 0000000 00000000000 13756501734 012754 5ustar000000000 0000000 cmds/bugreportz/.clang-format0100644 0000000 0000000 00000000440 13756501734 015322 0ustar000000000 0000000 BasedOnStyle: Google AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: false AccessModifierOffset: -2 ColumnLimit: 100 CommentPragmas: NOLINT:.* DerivePointerAlignment: false IndentWidth: 4 PointerAlignment: Left TabWidth: 4 UseTab: Never PenaltyExcessCharacter: 32 cmds/bugreportz/Android.bp0100644 0000000 0000000 00000001135 13756501734 014654 0ustar000000000 0000000 // bugreportz // ========== cc_binary { name: "bugreportz", srcs: [ "bugreportz.cpp", "main.cpp", ], cflags: [ "-Werror", "-Wall", ], shared_libs: [ "libbase", "libcutils", ], } // bugreportz_test // =============== cc_test { name: "bugreportz_test", test_suites: ["device-tests"], cflags: [ "-Werror", "-Wall", ], srcs: [ "bugreportz.cpp", "bugreportz_test.cpp", ], static_libs: ["libgmock"], shared_libs: [ "libbase", "libutils", ], } cmds/bugreportz/AndroidTest.xml0100644 0000000 0000000 00000002323 13756501734 015713 0ustar000000000 0000000 cmds/bugreportz/OWNERS0100644 0000000 0000000 00000000131 13756501734 013704 0ustar000000000 0000000 set noparent felipeal@google.com nandana@google.com jsharkey@android.com enh@google.com cmds/bugreportz/bugreportz.cpp0100644 0000000 0000000 00000004536 13756501734 015670 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include #include #include #include "bugreportz.h" static constexpr char BEGIN_PREFIX[] = "BEGIN:"; static constexpr char PROGRESS_PREFIX[] = "PROGRESS:"; static void write_line(const std::string& line, bool show_progress) { if (line.empty()) return; // When not invoked with the -p option, it must skip BEGIN and PROGRESS lines otherwise it // will break adb (which is expecting either OK or FAIL). if (!show_progress && (android::base::StartsWith(line, PROGRESS_PREFIX) || android::base::StartsWith(line, BEGIN_PREFIX))) return; android::base::WriteStringToFd(line, STDOUT_FILENO); } int bugreportz(int s, bool show_progress) { std::string line; while (1) { char buffer[65536]; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer))); if (bytes_read == 0) { break; } else if (bytes_read == -1) { // EAGAIN really means time out, so change the errno. if (errno == EAGAIN) { errno = ETIMEDOUT; } printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno)); return EXIT_FAILURE; } // Writes line by line. for (int i = 0; i < bytes_read; i++) { char c = buffer[i]; line.append(1, c); if (c == '\n') { write_line(line, show_progress); line.clear(); } } } // Process final line, in case it didn't finish with newline write_line(line, show_progress); return EXIT_SUCCESS; } cmds/bugreportz/bugreportz.h0100644 0000000 0000000 00000001513 13756501734 015325 0ustar000000000 0000000 // Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef BUGREPORTZ_H #define BUGREPORTZ_H // Calls dumpstate using the given socket and output its result to stdout. // Ownership of the socket is not transferred. int bugreportz(int s, bool show_progress); #endif // BUGREPORTZ_H cmds/bugreportz/bugreportz_test.cpp0100644 0000000 0000000 00000010013 13756501734 016712 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include #include "bugreportz.h" using ::testing::StrEq; using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStdout; class BugreportzTest : public ::testing::Test { public: // Creates the pipe used to communicate with bugreportz() void SetUp() { int fds[2]; ASSERT_EQ(0, pipe(fds)); read_fd_ = fds[0]; write_fd_ = fds[1]; } // Closes the pipe FDs. // If a FD is closed manually during a test, set it to -1 to prevent TearDown() trying to close // it again. void TearDown() { for (int fd : {read_fd_, write_fd_}) { if (fd >= 0) { close(fd); } } } // Emulates dumpstate output by writing to the socket passed to bugreportz() void WriteToSocket(const std::string& data) { if (write_fd_ < 0) { ADD_FAILURE() << "cannot write '" << data << "' because socket is already closed"; return; } int expected = data.length(); int actual = write(write_fd_, data.data(), data.length()); ASSERT_EQ(expected, actual) << "wrong number of bytes written to socket"; } void AssertStdoutEquals(const std::string& expected) { ASSERT_THAT(stdout_, StrEq(expected)) << "wrong stdout output"; } // Calls bugreportz() using the internal pipe. // // Tests must call WriteToSocket() to set what's written prior to calling it, since the writing // end of the pipe will be closed before calling bugreportz() (otherwise that function would // hang). void Bugreportz(bool show_progress) { close(write_fd_); write_fd_ = -1; CaptureStdout(); int status = bugreportz(read_fd_, show_progress); close(read_fd_); read_fd_ = -1; stdout_ = GetCapturedStdout(); ASSERT_EQ(0, status) << "bugrepotz() call failed (stdout: " << stdout_ << ")"; } private: int read_fd_; int write_fd_; std::string stdout_; }; // Tests 'bugreportz', without any argument - it will ignore progress lines. TEST_F(BugreportzTest, NoArgument) { WriteToSocket("BEGIN:THE IGNORED PATH WARS HAS!\n"); // Should be ommited. WriteToSocket("What happens on 'dumpstate',"); WriteToSocket("stays on 'bugreportz'.\n"); WriteToSocket("PROGRESS:Y U NO OMITTED?\n"); // Should be ommited. WriteToSocket("But "); WriteToSocket("PROGRESS IN THE MIDDLE"); // Ok - not starting a line. WriteToSocket(" is accepted\n"); Bugreportz(false); AssertStdoutEquals( "What happens on 'dumpstate',stays on 'bugreportz'.\n" "But PROGRESS IN THE MIDDLE is accepted\n"); } // Tests 'bugreportz -p' - it will just echo dumpstate's output to stdout TEST_F(BugreportzTest, WithProgress) { WriteToSocket("BEGIN:I AM YOUR PATH\n"); WriteToSocket("What happens on 'dumpstate',"); WriteToSocket("stays on 'bugreportz'.\n"); WriteToSocket("PROGRESS:IS INEVITABLE\n"); WriteToSocket("PROG"); WriteToSocket("RESS:IS NOT AUTOMATIC\n"); WriteToSocket("Newline is optional"); Bugreportz(true); AssertStdoutEquals( "BEGIN:I AM YOUR PATH\n" "What happens on 'dumpstate',stays on 'bugreportz'.\n" "PROGRESS:IS INEVITABLE\n" "PROGRESS:IS NOT AUTOMATIC\n" "Newline is optional"); } cmds/bugreportz/main.cpp0100644 0000000 0000000 00000006264 13756501734 014411 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include #include #include #include "bugreportz.h" static constexpr char VERSION[] = "1.1"; static void show_usage() { fprintf(stderr, "usage: bugreportz [-h | -v]\n" " -h: to display this help message\n" " -p: display progress\n" " -v: to display the version\n" " or no arguments to generate a zipped bugreport\n"); } static void show_version() { fprintf(stderr, "%s\n", VERSION); } int main(int argc, char* argv[]) { bool show_progress = false; if (argc > 1) { /* parse arguments */ int c; while ((c = getopt(argc, argv, "hpv")) != -1) { switch (c) { case 'h': show_usage(); return EXIT_SUCCESS; case 'p': show_progress = true; break; case 'v': show_version(); return EXIT_SUCCESS; default: show_usage(); return EXIT_FAILURE; } } } // TODO: code below was copy-and-pasted from bugreport.cpp (except by the // timeout value); // should be reused instead. // Start the dumpstatez service. property_set("ctl.start", "dumpstatez"); // Socket will not be available until service starts. int s; for (int i = 0; i < 20; i++) { s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if (s >= 0) break; // Try again in 1 second. sleep(1); } if (s == -1) { printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno)); return EXIT_FAILURE; } // Set a timeout so that if nothing is read in 10 minutes, we'll stop // reading and quit. No timeout in dumpstate is longer than 60 seconds, // so this gives lots of leeway in case of unforeseen time outs. struct timeval tv; tv.tv_sec = 10 * 60; tv.tv_usec = 0; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { fprintf(stderr, "WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n", strerror(errno)); } int ret = bugreportz(s, show_progress); if (close(s) == -1) { fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); ret = EXIT_FAILURE; } return ret; } cmds/bugreportz/readme.md0100644 0000000 0000000 00000001355 13756501734 014534 0ustar000000000 0000000 # bugreportz protocol `bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using the simple protocol defined below. # Version 1.1 On version 1.1, in addition to the `OK` and `FAILURE` lines, when `bugreportz` is invoked with `-p`, it outputs the following lines: - `BEGIN:` right away. - `PROGRESS:/` as `dumpstate` progresses (where `` is the current progress units out of a max of ``). ## Version 1.0 On version 1.0, `bugreportz` does not generate any output on `stdout` until the bugreport is finished, when it then prints one line with the result: - `OK:` in case of success. - `FAIL:` in case of failure. cmds/cmd/0040755 0000000 0000000 00000000000 13756501734 011314 5ustar000000000 0000000 cmds/cmd/Android.bp0100644 0000000 0000000 00000001076 13756501734 013220 0ustar000000000 0000000 cc_library_static { name: "libcmd", srcs: ["cmd.cpp"], export_include_dirs: ["."], shared_libs: [ "libutils", "liblog", "libselinux", "libbinder", ], cflags: [ "-Wall", "-Werror", "-DXP_UNIX", ], } cc_binary { name: "cmd", srcs: ["main.cpp"], static_libs: [ "libcmd", ], shared_libs: [ "libutils", "liblog", "libselinux", "libbinder", ], cflags: [ "-Wall", "-Werror", "-DXP_UNIX", ], } cmds/cmd/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501734 014434 0ustar000000000 0000000 cmds/cmd/NOTICE0100644 0000000 0000000 00000024707 13756501734 012227 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS cmds/cmd/cmd.cpp0100644 0000000 0000000 00000017604 13756501734 012570 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "cmd" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "selinux/selinux.h" #include "selinux/android.h" #include "cmd.h" #define DEBUG 0 using namespace android; static int sort_func(const String16* lhs, const String16* rhs) { return lhs->compare(*rhs); } struct SecurityContext_Delete { void operator()(security_context_t p) const { freecon(p); } }; typedef std::unique_ptr Unique_SecurityContext; class MyShellCallback : public BnShellCallback { public: TextOutput& mErrorLog; bool mActive = true; MyShellCallback(TextOutput& errorLog) : mErrorLog(errorLog) {} virtual int openFile(const String16& path, const String16& seLinuxContext, const String16& mode) { String8 path8(path); char cwd[256]; getcwd(cwd, 256); String8 fullPath(cwd); fullPath.appendPath(path8); if (!mActive) { mErrorLog << "Open attempt after active for: " << fullPath << endl; return -EPERM; } #if DEBUG ALOGD("openFile: %s, full=%s", path8.string(), fullPath.string()); #endif int flags = 0; bool checkRead = false; bool checkWrite = false; if (mode == u"w") { flags = O_WRONLY|O_CREAT|O_TRUNC; checkWrite = true; } else if (mode == u"w+") { flags = O_RDWR|O_CREAT|O_TRUNC; checkRead = checkWrite = true; } else if (mode == u"r") { flags = O_RDONLY; checkRead = true; } else if (mode == u"r+") { flags = O_RDWR; checkRead = checkWrite = true; } else { mErrorLog << "Invalid mode requested: " << mode.string() << endl; return -EINVAL; } int fd = open(fullPath.string(), flags, S_IRWXU|S_IRWXG); #if DEBUG ALOGD("openFile: fd=%d", fd); #endif if (fd < 0) { return fd; } if (is_selinux_enabled() && seLinuxContext.size() > 0) { String8 seLinuxContext8(seLinuxContext); security_context_t tmp = nullptr; getfilecon(fullPath.string(), &tmp); Unique_SecurityContext context(tmp); if (checkWrite) { int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), "file", "write", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux write check!"); #endif close(fd); mErrorLog << "System server has no access to write file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl; return -EPERM; } } if (checkRead) { int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), "file", "read", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux read check!"); #endif close(fd); mErrorLog << "System server has no access to read file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl; return -EPERM; } } } return fd; } }; class MyResultReceiver : public BnResultReceiver { public: Mutex mMutex; Condition mCondition; bool mHaveResult = false; int32_t mResult = 0; virtual void send(int32_t resultCode) { AutoMutex _l(mMutex); mResult = resultCode; mHaveResult = true; mCondition.signal(); } int32_t waitForResult() { AutoMutex _l(mMutex); while (!mHaveResult) { mCondition.wait(mMutex); } return mResult; } }; int cmdMain(const std::vector& argv, TextOutput& outputLog, TextOutput& errorLog, int in, int out, int err, RunMode runMode) { sp proc = ProcessState::self(); proc->startThreadPool(); #if DEBUG ALOGD("cmd: starting"); #endif sp sm = defaultServiceManager(); if (runMode == RunMode::kStandalone) { fflush(stdout); } if (sm == nullptr) { ALOGW("Unable to get default service manager!"); errorLog << "cmd: Unable to get default service manager!" << endl; return 20; } int argc = argv.size(); if (argc == 0) { errorLog << "cmd: No service specified; use -l to list all services" << endl; return 20; } if ((argc == 1) && (argv[0] == "-l")) { Vector services = sm->listServices(); services.sort(sort_func); outputLog << "Currently running services:" << endl; for (size_t i=0; i service = sm->checkService(services[i]); if (service != nullptr) { outputLog << " " << services[i] << endl; } } return 0; } const auto cmd = argv[0]; Vector args; String16 serviceName = String16(cmd.data(), cmd.size()); for (int i = 1; i < argc; i++) { args.add(String16(argv[i].data(), argv[i].size())); } sp service = sm->checkService(serviceName); if (service == nullptr) { if (runMode == RunMode::kStandalone) { ALOGW("Can't find service %.*s", static_cast(cmd.size()), cmd.data()); } errorLog << "cmd: Can't find service: " << cmd << endl; return 20; } sp cb = new MyShellCallback(errorLog); sp result = new MyResultReceiver(); #if DEBUG ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err); #endif // TODO: block until a result is returned to MyResultReceiver. status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result); if (error < 0) { const char* errstr; switch (error) { case BAD_TYPE: errstr = "Bad type"; break; case FAILED_TRANSACTION: errstr = "Failed transaction"; break; case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break; case UNEXPECTED_NULL: errstr = "Unexpected null"; break; default: errstr = strerror(-error); break; } if (runMode == RunMode::kStandalone) { ALOGW("Failure calling service %.*s: %s (%d)", static_cast(cmd.size()), cmd.data(), errstr, -error); } outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error) << ")" << endl; return error; } cb->mActive = false; status_t res = result->waitForResult(); #if DEBUG ALOGD("result=%d", (int)res); #endif return res; } cmds/cmd/cmd.h0100644 0000000 0000000 00000001653 13756501734 012232 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include #include enum class RunMode { kStandalone, kLibrary, }; int cmdMain(const std::vector& argv, android::TextOutput& outputLog, android::TextOutput& errorLog, int in, int out, int err, RunMode runMode); cmds/cmd/main.cpp0100644 0000000 0000000 00000002104 13756501734 012736 0ustar000000000 0000000 /* * 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. */ #include #include "cmd.h" int main(int argc, char* const argv[]) { signal(SIGPIPE, SIG_IGN); std::vector arguments; arguments.reserve(argc - 1); // 0th argument is a program name, skipping. for (int i = 1; i < argc; ++i) { arguments.emplace_back(argv[i]); } return cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, RunMode::kStandalone); } cmds/dumpstate/0040755 0000000 0000000 00000000000 13756501734 012557 5ustar000000000 0000000 cmds/dumpstate/.clang-format0100644 0000000 0000000 00000000440 13756501734 015125 0ustar000000000 0000000 BasedOnStyle: Google AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: false AccessModifierOffset: -2 ColumnLimit: 100 CommentPragmas: NOLINT:.* DerivePointerAlignment: false IndentWidth: 4 PointerAlignment: Left TabWidth: 4 UseTab: Never PenaltyExcessCharacter: 32 cmds/dumpstate/Android.bp0100644 0000000 0000000 00000007513 13756501734 014465 0ustar000000000 0000000 // // Copyright (C) 2017 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. cc_defaults { name: "dumpstate_cflag_defaults", cflags: [ "-Wall", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-variable", "-Wunused-parameter", ], } cc_library_shared { name: "libdumpstateutil", defaults: ["dumpstate_cflag_defaults"], vendor_available: true, vndk: { enabled: true, }, srcs: [ "DumpstateInternal.cpp", "DumpstateUtil.cpp", ], shared_libs: [ "libbase", "liblog", ], export_include_dirs: ["."], export_shared_lib_headers: [ "libbase", ], } cc_library_shared { name: "libdumpstateaidl", defaults: ["dumpstate_cflag_defaults"], shared_libs: [ "libbinder", "libutils", ], aidl: { local_include_dirs: ["binder"], export_aidl_headers: true, }, srcs: [ ":dumpstate_aidl", ], export_include_dirs: ["binder"], } filegroup { name: "dumpstate_aidl", srcs: [ "binder/android/os/IDumpstateListener.aidl", "binder/android/os/IDumpstateToken.aidl", "binder/android/os/IDumpstate.aidl", ], path: "binder", } cc_defaults { name: "dumpstate_defaults", defaults: ["dumpstate_cflag_defaults"], shared_libs: [ "android.hardware.dumpstate@1.0", "libziparchive", "libbase", "libbinder", "libcrypto", "libcutils", "libdebuggerd_client", "libdumpstateaidl", "libdumpstateutil", "libdumputils", "libhidlbase", "libhidltransport", "liblog", "libutils", ], srcs: [ "DumpstateSectionReporter.cpp", "DumpstateService.cpp", "utils.cpp", ], static_libs: [ "libincidentcompanion", "libdumpsys", "libserviceutils", ], } cc_binary { name: "dumpstate", defaults: ["dumpstate_defaults"], srcs: [ "dumpstate.cpp", "main.cpp", ], required: [ "atrace", "df", "getprop", "ip", "iptables", "ip6tables", "kill", "librank", "logcat", "lsmod", "lsof", "netstat", "parse_radio_log", "printenv", "procrank", "screencap", "showmap", "ss", "storaged", "top", "uptime", "vdc", "vril-dump", ], init_rc: ["dumpstate.rc"], } cc_test { name: "dumpstate_test", defaults: ["dumpstate_defaults"], srcs: [ "dumpstate.cpp", "tests/dumpstate_test.cpp", ], static_libs: ["libgmock"], } cc_test { name: "dumpstate_smoke_test", defaults: ["dumpstate_defaults"], srcs: [ "dumpstate.cpp", "tests/dumpstate_smoke_test.cpp", ], static_libs: ["libgmock"], } // =======================# // dumpstate_test_fixture # // =======================# cc_test { name: "dumpstate_test_fixture", test_suites: ["device-tests"], cflags: [ "-Wall", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-variable", "-Wunused-parameter", ], srcs: ["tests/dumpstate_test_fixture.cpp"], data: ["tests/testdata/**/*"], } cmds/dumpstate/AndroidTest.xml0100644 0000000 0000000 00000002357 13756501734 015525 0ustar000000000 0000000 cmds/dumpstate/DumpstateInternal.cpp0100644 0000000 0000000 00000014640 13756501734 016730 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "dumpstate" #include "DumpstateInternal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include uint64_t Nanotime() { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return static_cast(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec); } // Switches to non-root user and group. bool DropRootUser() { struct group* grp = getgrnam("shell"); gid_t shell_gid = grp != nullptr ? grp->gr_gid : 0; struct passwd* pwd = getpwnam("shell"); uid_t shell_uid = pwd != nullptr ? pwd->pw_uid : 0; if (!shell_gid || !shell_uid) { MYLOGE("Unable to get AID_SHELL: %s\n", strerror(errno)); return false; } if (getgid() == shell_gid && getuid() == shell_uid) { MYLOGD("drop_root_user(): already running as Shell\n"); return true; } /* ensure we will keep capabilities when we drop root */ if (prctl(PR_SET_KEEPCAPS, 1) < 0) { MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); return false; } static const std::vector group_names{ "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"}; std::vector groups(group_names.size(), 0); for (size_t i = 0; i < group_names.size(); ++i) { grp = getgrnam(group_names[i].c_str()); groups[i] = grp != nullptr ? grp->gr_gid : 0; if (groups[i] == 0) { MYLOGE("Unable to get required gid '%s': %s\n", group_names[i].c_str(), strerror(errno)); return false; } } if (setgroups(groups.size(), groups.data()) != 0) { MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno)); return false; } if (setgid(shell_gid) != 0) { MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno)); return false; } if (setuid(shell_uid) != 0) { MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno)); return false; } struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; memset(&capheader, 0, sizeof(capheader)); memset(&capdata, 0, sizeof(capdata)); capheader.version = _LINUX_CAPABILITY_VERSION_3; capheader.pid = 0; if (capget(&capheader, &capdata[0]) != 0) { MYLOGE("capget failed: %s\n", strerror(errno)); return false; } const uint32_t cap_syslog_mask = CAP_TO_MASK(CAP_SYSLOG); const uint32_t cap_syslog_index = CAP_TO_INDEX(CAP_SYSLOG); bool has_cap_syslog = (capdata[cap_syslog_index].effective & cap_syslog_mask) != 0; memset(&capdata, 0, sizeof(capdata)); if (has_cap_syslog) { // Only attempt to keep CAP_SYSLOG if it was present to begin with. capdata[cap_syslog_index].permitted |= cap_syslog_mask; capdata[cap_syslog_index].effective |= cap_syslog_mask; } if (capset(&capheader, &capdata[0]) != 0) { MYLOGE("capset({%#x, %#x}) failed: %s\n", capdata[0].effective, capdata[1].effective, strerror(errno)); return false; } return true; } int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd, bool dry_run) { const char* path = path_string.c_str(); if (!title.empty()) { dprintf(out_fd, "------ %s (%s", title.c_str(), path); struct stat st; // Only show the modification time of non-device files. size_t path_len = strlen(path); if ((path_len < 6 || memcmp(path, "/proc/", 6)) && (path_len < 5 || memcmp(path, "/sys/", 5)) && (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) { char stamp[80]; time_t mtime = st.st_mtime; strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime)); dprintf(out_fd, ": %s", stamp); } dprintf(out_fd, ") ------\n"); fsync(out_fd); } if (dry_run) { if (out_fd != STDOUT_FILENO) { // There is no title, but we should still print a dry-run message dprintf(out_fd, "%s: skipped on dry run\n", path); } else if (!title.empty()) { dprintf(out_fd, "\t(skipped on dry run)\n"); } fsync(out_fd); return 0; } bool newline = false; while (true) { uint64_t start_time = Nanotime(); pollfd fds[] = { { .fd = fd, .events = POLLIN } }; int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000)); if (ret == -1) { dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno)); newline = true; break; } else if (ret == 0) { uint64_t elapsed = Nanotime() - start_time; dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC); newline = true; break; } else { char buffer[65536]; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); if (bytes_read > 0) { android::base::WriteFully(out_fd, buffer, bytes_read); newline = (buffer[bytes_read - 1] == '\n'); } else { if (bytes_read == -1) { dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno)); newline = true; } break; } } } if (!newline) dprintf(out_fd, "\n"); if (!title.empty()) dprintf(out_fd, "\n"); return 0; } cmds/dumpstate/DumpstateInternal.h0100644 0000000 0000000 00000003476 13756501734 016402 0ustar000000000 0000000 /* * 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. */ #ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_ #define FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_ #include #include // TODO: rename macros to DUMPSTATE_LOGXXX #ifndef MYLOGD #define MYLOGD(...) \ fprintf(stderr, __VA_ARGS__); \ ALOGD(__VA_ARGS__); #endif #ifndef MYLOGI #define MYLOGI(...) \ fprintf(stderr, __VA_ARGS__); \ ALOGI(__VA_ARGS__); #endif #ifndef MYLOGW #define MYLOGW(...) \ fprintf(stderr, __VA_ARGS__); \ ALOGW(__VA_ARGS__); #endif #ifndef MYLOGE #define MYLOGE(...) \ fprintf(stderr, __VA_ARGS__); \ ALOGE(__VA_ARGS__); #endif // Internal functions used by .cpp files on multiple build targets. // TODO: move to android::os::dumpstate::internal namespace // TODO: use functions from instead const uint64_t NANOS_PER_SEC = 1000000000; uint64_t Nanotime(); // Switches to non-root user and group. bool DropRootUser(); // TODO: move to .cpp as static once is not used by utils.cpp anymore. int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd, bool dry_run = false); #endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_INTERNAL_H_ cmds/dumpstate/DumpstateSectionReporter.cpp0100644 0000000 0000000 00000003006 13756501734 020275 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #define LOG_TAG "dumpstate" #include "DumpstateSectionReporter.h" namespace android { namespace os { namespace dumpstate { DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title, sp listener, bool sendReport) : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) { started_ = std::chrono::steady_clock::now(); } DumpstateSectionReporter::~DumpstateSectionReporter() { if ((listener_ != nullptr) && (sendReport_)) { auto elapsed = std::chrono::duration_cast( std::chrono::steady_clock::now() - started_); listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count()); } } } // namespace dumpstate } // namespace os } // namespace android cmds/dumpstate/DumpstateSectionReporter.h0100644 0000000 0000000 00000003344 13756501734 017747 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ #define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ #include #include namespace android { namespace os { namespace dumpstate { /* * Helper class used to report per section details to a listener. * * Typical usage: * * DumpstateSectionReporter sectionReporter(title, listener, sendReport); * sectionReporter.setSize(5000); * */ class DumpstateSectionReporter { public: DumpstateSectionReporter(const std::string& title, sp listener, bool sendReport); ~DumpstateSectionReporter(); void setStatus(status_t status) { status_ = status; } void setSize(int size) { size_ = size; } private: std::string title_; android::sp listener_; bool sendReport_; status_t status_; int size_; std::chrono::time_point started_; }; } // namespace dumpstate } // namespace os } // namespace android #endif // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ cmds/dumpstate/DumpstateService.cpp0100644 0000000 0000000 00000021415 13756501734 016552 0ustar000000000 0000000 /** * 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. */ #define LOG_TAG "dumpstate" #include "DumpstateService.h" #include #include #include "android/os/BnDumpstate.h" #include "DumpstateInternal.h" using android::base::StringPrintf; namespace android { namespace os { namespace { struct DumpstateInfo { public: Dumpstate* ds = nullptr; int32_t calling_uid = -1; std::string calling_package; }; static binder::Status exception(uint32_t code, const std::string& msg) { MYLOGE("%s (%d) ", msg.c_str(), code); return binder::Status::fromExceptionCode(code, String8(msg.c_str())); } // Creates a bugreport and exits, thus preserving the oneshot nature of the service. // Note: takes ownership of data. [[noreturn]] static void* dumpstate_thread_main(void* data) { std::unique_ptr ds_info(static_cast(data)); ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package); MYLOGD("Finished taking a bugreport. Exiting.\n"); exit(0); } [[noreturn]] static void signalErrorAndExit(sp listener, int error_code) { listener->onError(error_code); exit(0); } class DumpstateToken : public BnDumpstateToken {}; } // namespace DumpstateService::DumpstateService() : ds_(nullptr) { } char const* DumpstateService::getServiceName() { return "dumpstate"; } status_t DumpstateService::Start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService::publish(); if (ret != android::OK) { return ret; } sp ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); return android::OK; } // Note: this method is part of the old flow and is not expected to be used in combination // with startBugreport. binder::Status DumpstateService::setListener(const std::string& name, const sp& listener, bool getSectionDetails, sp* returned_token) { *returned_token = nullptr; if (name.empty()) { MYLOGE("setListener(): name not set\n"); return binder::Status::ok(); } if (listener == nullptr) { MYLOGE("setListener(): listener not set\n"); return binder::Status::ok(); } std::lock_guard lock(lock_); if (ds_ == nullptr) { ds_ = &(Dumpstate::GetInstance()); } if (ds_->listener_ != nullptr) { MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_->listener_name_.c_str()); return binder::Status::ok(); } ds_->listener_name_ = name; ds_->listener_ = listener; ds_->report_section_ = getSectionDetails; *returned_token = new DumpstateToken(); return binder::Status::ok(); } binder::Status DumpstateService::startBugreport(int32_t calling_uid, const std::string& calling_package, const android::base::unique_fd& bugreport_fd, const android::base::unique_fd& screenshot_fd, int bugreport_mode, const sp& listener) { MYLOGI("startBugreport() with mode: %d\n", bugreport_mode); // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a // time. std::lock_guard lock(lock_); if (ds_ != nullptr) { MYLOGE("Error! There is already a bugreport in progress. Returning."); if (listener != nullptr) { listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); } return exception(binder::Status::EX_SERVICE_SPECIFIC, "There is already a bugreport in progress"); } // From here on, all conditions that indicate we are done with this incoming request should // result in exiting the service to free it up for next invocation. if (listener == nullptr) { MYLOGE("Invalid input: no listener"); exit(0); } if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_REMOTE && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WEAR && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) { MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); } if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) { // TODO(b/111441001): screenshot fd should be optional MYLOGE("Invalid filedescriptor"); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); } std::unique_ptr options = std::make_unique(); options->Initialize(static_cast(bugreport_mode), bugreport_fd, screenshot_fd); ds_ = &(Dumpstate::GetInstance()); ds_->SetOptions(std::move(options)); ds_->listener_ = listener; DumpstateInfo* ds_info = new DumpstateInfo(); ds_info->ds = ds_; ds_info->calling_uid = calling_uid; ds_info->calling_package = calling_package; pthread_t thread; status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info); if (err != 0) { delete ds_info; ds_info = nullptr; MYLOGE("Could not create a thread"); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR); } return binder::Status::ok(); } binder::Status DumpstateService::cancelBugreport() { // This is a no-op since the cancellation is done from java side via setting sys properties. // See BugreportManagerServiceImpl. // TODO(b/111441001): maybe make native and java sides use different binder interface // to avoid these annoyances. return binder::Status::ok(); } status_t DumpstateService::dump(int fd, const Vector&) { if (ds_ == nullptr) { dprintf(fd, "Bugreport not in progress yet"); return NO_ERROR; } std::string destination = ds_->options_->bugreport_fd.get() != -1 ? StringPrintf("[fd:%d]", ds_->options_->bugreport_fd.get()) : ds_->bugreport_internal_dir_.c_str(); dprintf(fd, "id: %d\n", ds_->id_); dprintf(fd, "pid: %d\n", ds_->pid_); dprintf(fd, "update_progress: %s\n", ds_->options_->do_progress_updates ? "true" : "false"); dprintf(fd, "update_progress_threshold: %d\n", ds_->update_progress_threshold_); dprintf(fd, "last_updated_progress: %d\n", ds_->last_updated_progress_); dprintf(fd, "progress:\n"); ds_->progress_->Dump(fd, " "); dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str()); dprintf(fd, "version: %s\n", ds_->version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", destination.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str()); dprintf(fd, "log_path: %s\n", ds_->log_path_.c_str()); dprintf(fd, "tmp_path: %s\n", ds_->tmp_path_.c_str()); dprintf(fd, "path: %s\n", ds_->path_.c_str()); dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str()); dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str()); dprintf(fd, "name: %s\n", ds_->name_.c_str()); dprintf(fd, "now: %ld\n", ds_->now_); dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false"); dprintf(fd, "listener: %s\n", ds_->listener_name_.c_str()); dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str()); dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str()); return NO_ERROR; } } // namespace os } // namespace android cmds/dumpstate/DumpstateService.h0100644 0000000 0000000 00000004223 13756501734 016215 0ustar000000000 0000000 /** * 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. */ #ifndef ANDROID_OS_DUMPSTATE_H_ #define ANDROID_OS_DUMPSTATE_H_ #include #include #include #include #include "android/os/BnDumpstate.h" #include "android/os/BnDumpstateToken.h" #include "dumpstate.h" namespace android { namespace os { class DumpstateService : public BinderService, public BnDumpstate { public: DumpstateService(); static status_t Start(); static char const* getServiceName(); status_t dump(int fd, const Vector& args) override; binder::Status setListener(const std::string& name, const sp& listener, bool getSectionDetails, sp* returned_token) override; binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package, const android::base::unique_fd& bugreport_fd, const android::base::unique_fd& screenshot_fd, int bugreport_mode, const sp& listener) override; // No-op binder::Status cancelBugreport(); private: // Dumpstate object which contains all the bugreporting logic. // Note that dumpstate is a oneshot service, so this object is meant to be used at most for // one bugreport. // This service does not own this object. Dumpstate* ds_; std::mutex lock_; }; } // namespace os } // namespace android #endif // ANDROID_OS_DUMPSTATE_H_ cmds/dumpstate/DumpstateUtil.cpp0100644 0000000 0000000 00000031333 13756501734 016067 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "dumpstate" #include "DumpstateUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "DumpstateInternal.h" namespace android { namespace os { namespace dumpstate { namespace { static constexpr const char* kSuPath = "/system/xbin/su"; static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { sigset_t child_mask, old_mask; sigemptyset(&child_mask); sigaddset(&child_mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) { printf("*** sigprocmask failed: %s\n", strerror(errno)); return false; } timespec ts; ts.tv_sec = MSEC_TO_SEC(timeout_ms); ts.tv_nsec = (timeout_ms % 1000) * 1000000; int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts)); int saved_errno = errno; // Set the signals back the way they were. if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) { printf("*** sigprocmask failed: %s\n", strerror(errno)); if (ret == 0) { return false; } } if (ret == -1) { errno = saved_errno; if (errno == EAGAIN) { errno = ETIMEDOUT; } else { printf("*** sigtimedwait failed: %s\n", strerror(errno)); } return false; } pid_t child_pid = waitpid(pid, status, WNOHANG); if (child_pid != pid) { if (child_pid != -1) { printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid); } else { printf("*** waitpid failed: %s\n", strerror(errno)); } return false; } return true; } } // unnamed namespace CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build(); CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build(); CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout_ms) : values(timeout_ms) { } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() { values.always_ = true; return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() { if (!PropertiesHelper::IsUnroot()) { values.account_mode_ = SU_ROOT; } return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() { if (!PropertiesHelper::IsUserBuild()) { return AsRoot(); } return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() { values.account_mode_ = DROP_ROOT; return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() { values.output_mode_ = REDIRECT_TO_STDERR; return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log( const std::string& message) { values.logging_message_ = message; return *this; } CommandOptions CommandOptions::CommandOptionsBuilder::Build() { return CommandOptions(values); } CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout_ms) : timeout_ms_(timeout_ms), always_(false), account_mode_(DONT_DROP_ROOT), output_mode_(NORMAL_OUTPUT), logging_message_("") { } CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) { } int64_t CommandOptions::Timeout() const { return MSEC_TO_SEC(values.timeout_ms_); } int64_t CommandOptions::TimeoutInMs() const { return values.timeout_ms_; } bool CommandOptions::Always() const { return values.always_; } PrivilegeMode CommandOptions::PrivilegeMode() const { return values.account_mode_; } OutputMode CommandOptions::OutputMode() const { return values.output_mode_; } std::string CommandOptions::LoggingMessage() const { return values.logging_message_; } CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout_sec) { return CommandOptions::CommandOptionsBuilder(SEC_TO_MSEC(timeout_sec)); } CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeoutInMs(int64_t timeout_ms) { return CommandOptions::CommandOptionsBuilder(timeout_ms); } std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -1; int PropertiesHelper::unroot_ = -1; bool PropertiesHelper::IsUserBuild() { if (build_type_.empty()) { build_type_ = android::base::GetProperty("ro.build.type", "user"); } return "user" == build_type_; } bool PropertiesHelper::IsDryRun() { if (dry_run_ == -1) { dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0; } return dry_run_ == 1; } bool PropertiesHelper::IsUnroot() { if (unroot_ == -1) { unroot_ = android::base::GetBoolProperty("dumpstate.unroot", false) ? 1 : 0; } return unroot_ == 1; } int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); if (fd.get() < 0) { int err = errno; if (title.empty()) { dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err)); } else { dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err)); } fsync(out_fd); return -1; } return DumpFileFromFdToFd(title, path, fd.get(), out_fd, PropertiesHelper::IsDryRun()); } int RunCommandToFd(int fd, const std::string& title, const std::vector& full_command, const CommandOptions& options) { if (full_command.empty()) { MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str()); return -1; } int size = full_command.size() + 1; // null terminated int starting_index = 0; if (options.PrivilegeMode() == SU_ROOT) { starting_index = 2; // "su" "root" size += starting_index; } std::vector args; args.resize(size); std::string command_string; if (options.PrivilegeMode() == SU_ROOT) { args[0] = kSuPath; command_string += kSuPath; args[1] = "root"; command_string += " root "; } for (size_t i = 0; i < full_command.size(); i++) { args[i + starting_index] = full_command[i].data(); command_string += args[i + starting_index]; if (i != full_command.size() - 1) { command_string += " "; } } args[size - 1] = nullptr; const char* command = command_string.c_str(); if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) { dprintf(fd, "Skipping '%s' on user build.\n", command); return 0; } if (!title.empty()) { dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command); fsync(fd); } const std::string& logging_message = options.LoggingMessage(); if (!logging_message.empty()) { MYLOGI(logging_message.c_str(), command_string.c_str()); } bool silent = (options.OutputMode() == REDIRECT_TO_STDERR); bool redirecting_to_fd = STDOUT_FILENO != fd; if (PropertiesHelper::IsDryRun() && !options.Always()) { if (!title.empty()) { dprintf(fd, "\t(skipped on dry run)\n"); } else if (redirecting_to_fd) { // There is no title, but we should still print a dry-run message dprintf(fd, "%s: skipped on dry run\n", command_string.c_str()); } fsync(fd); return 0; } const char* path = args[0]; uint64_t start = Nanotime(); pid_t pid = fork(); /* handle error case */ if (pid < 0) { if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno)); MYLOGE("*** fork: %s\n", strerror(errno)); return pid; } /* handle child case */ if (pid == 0) { if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) { if (!silent) { dprintf(fd, "*** failed to drop root before running %s: %s\n", command, strerror(errno)); } MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno)); return -1; } if (silent) { // Redirects stdout to stderr TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO)); } else if (redirecting_to_fd) { // Redirect stdout to fd TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO)); close(fd); } /* make sure the child dies when dumpstate dies */ prctl(PR_SET_PDEATHSIG, SIGKILL); /* just ignore SIGPIPE, will go down with parent's */ struct sigaction sigact; memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sigact, nullptr); execvp(path, (char**)args.data()); // execvp's result will be handled after waitpid_with_timeout() below, but // if it failed, it's safer to exit dumpstate. MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno)); // Must call _exit (instead of exit), otherwise it will corrupt the zip // file. _exit(EXIT_FAILURE); } /* handle parent case */ int status; bool ret = waitpid_with_timeout(pid, options.TimeoutInMs(), &status); fsync(fd); uint64_t elapsed = Nanotime() - start; if (!ret) { if (errno == ETIMEDOUT) { if (!silent) dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command, static_cast(elapsed) / NANOS_PER_SEC, pid); MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command, static_cast(elapsed) / NANOS_PER_SEC, pid); } else { if (!silent) dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command, static_cast(elapsed) / NANOS_PER_SEC, pid); MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command, static_cast(elapsed) / NANOS_PER_SEC, pid); } kill(pid, SIGTERM); if (!waitpid_with_timeout(pid, 5000, nullptr)) { kill(pid, SIGKILL); if (!waitpid_with_timeout(pid, 5000, nullptr)) { if (!silent) dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid); MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid); } } return -1; } if (WIFSIGNALED(status)) { if (!silent) dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status)); MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status)); } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { status = WEXITSTATUS(status); if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status); MYLOGE("*** command '%s' failed: exit code %d\n", command, status); } return status; } int GetPidByName(const std::string& ps_name) { DIR* proc_dir; struct dirent* ps; unsigned int pid; std::string cmdline; if (!(proc_dir = opendir("/proc"))) { MYLOGE("Can't open /proc\n"); return -1; } while ((ps = readdir(proc_dir))) { if (!(pid = atoi(ps->d_name))) { continue; } android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline); if (cmdline.find(ps_name) == std::string::npos) { continue; } else { closedir(proc_dir); return pid; } } MYLOGE("can't find the pid\n"); closedir(proc_dir); return -1; } } // namespace dumpstate } // namespace os } // namespace android cmds/dumpstate/DumpstateUtil.h0100644 0000000 0000000 00000015277 13756501734 015545 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_OS_DUMPSTATE_UTIL_H_ #define ANDROID_OS_DUMPSTATE_UTIL_H_ #include #include /* * Converts seconds to milliseconds. */ #define SEC_TO_MSEC(second) (second * 1000) /* * Converts milliseconds to seconds. */ #define MSEC_TO_SEC(millisecond) (millisecond / 1000) namespace android { namespace os { namespace dumpstate { /* * Defines the Linux account that should be executing a command. */ enum PrivilegeMode { /* Explicitly change the `uid` and `gid` to be `shell`.*/ DROP_ROOT, /* Don't change the `uid` and `gid`. */ DONT_DROP_ROOT, /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */ SU_ROOT }; /* * Defines what should happen with the main output stream (`stdout` or fd) of a command. */ enum OutputMode { /* Don't change main output. */ NORMAL_OUTPUT, /* Redirect main output to `stderr`. */ REDIRECT_TO_STDERR }; /* * Value object used to set command options. * * Typically constructed using a builder with chained setters. Examples: * * CommandOptions::WithTimeout(20).AsRoot().Build(); * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build(); * * Although the builder could be used to dynamically set values. Example: * * CommandOptions::CommandOptionsBuilder options = * CommandOptions::WithTimeout(10); * if (!is_user_build()) { * options.AsRoot(); * } * RunCommand("command", {"args"}, options.Build()); */ class CommandOptions { private: class CommandOptionsValues { private: explicit CommandOptionsValues(int64_t timeout_ms); int64_t timeout_ms_; bool always_; PrivilegeMode account_mode_; OutputMode output_mode_; std::string logging_message_; friend class CommandOptions; friend class CommandOptionsBuilder; }; explicit CommandOptions(const CommandOptionsValues& values); const CommandOptionsValues values; public: class CommandOptionsBuilder { public: /* Sets the command to always run, even on `dry-run` mode. */ CommandOptionsBuilder& Always(); /* * Sets the command's PrivilegeMode as `SU_ROOT` unless overridden by system property * 'dumpstate.unroot'. */ CommandOptionsBuilder& AsRoot(); /* * Runs AsRoot() on userdebug builds. No-op on user builds since 'su' is * not available. This is used for commands that return some useful information even * when run as shell. */ CommandOptionsBuilder& AsRootIfAvailable(); /* Sets the command's PrivilegeMode as `DROP_ROOT` */ CommandOptionsBuilder& DropRoot(); /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */ CommandOptionsBuilder& RedirectStderr(); /* When not empty, logs a message before executing the command. * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */ CommandOptionsBuilder& Log(const std::string& message); /* Builds the command options. */ CommandOptions Build(); private: explicit CommandOptionsBuilder(int64_t timeout_ms); CommandOptionsValues values; friend class CommandOptions; }; /** Gets the command timeout in seconds. */ int64_t Timeout() const; /** Gets the command timeout in milliseconds. */ int64_t TimeoutInMs() const; /* Checks whether the command should always be run, even on dry-run mode. */ bool Always() const; /** Gets the PrivilegeMode of the command. */ PrivilegeMode PrivilegeMode() const; /** Gets the OutputMode of the command. */ OutputMode OutputMode() const; /** Gets the logging message header, it any. */ std::string LoggingMessage() const; /** Creates a builder with the requied timeout in seconds. */ static CommandOptionsBuilder WithTimeout(int64_t timeout_sec); /** Creates a builder with the requied timeout in milliseconds. */ static CommandOptionsBuilder WithTimeoutInMs(int64_t timeout_ms); // Common options. static CommandOptions DEFAULT; static CommandOptions AS_ROOT; }; /* * System properties helper. */ class PropertiesHelper { friend class DumpstateBaseTest; public: /* * Gets whether device is running a `user` build. */ static bool IsUserBuild(); /* * When running in dry-run mode, skips the real dumps and just print the section headers. * * Useful when debugging dumpstate or other bugreport-related activities. * * Dry-run mode is enabled by setting the system property `dumpstate.dry_run` to true. */ static bool IsDryRun(); /** * Checks whether root availability should be overridden. * * Useful to verify how dumpstate would work in a device with an user build. */ static bool IsUnroot(); private: static std::string build_type_; static int dry_run_; static int unroot_; }; /* * Forks a command, waits for it to finish, and returns its status. * * |fd| file descriptor that receives the command's 'stdout'. * |title| description of the command printed on `stdout` (or empty to skip * description). * |full_command| array containing the command (first entry) and its arguments. * Must contain at least one element. * |options| optional argument defining the command's behavior. */ int RunCommandToFd(int fd, const std::string& title, const std::vector& full_command, const CommandOptions& options = CommandOptions::DEFAULT); /* * Dumps the contents of a file into a file descriptor. * * |fd| file descriptor where the file is dumped into. * |title| description of the command printed on `stdout` (or empty to skip * description). * |path| location of the file to be dumped. */ int DumpFileToFd(int fd, const std::string& title, const std::string& path); /* * Finds the process id by process name. * |ps_name| the process name we want to search for */ int GetPidByName(const std::string& ps_name); } // namespace dumpstate } // namespace os } // namespace android #endif // ANDROID_OS_DUMPSTATE_UTIL_H_ cmds/dumpstate/OWNERS0100644 0000000 0000000 00000000131 13756501734 013507 0ustar000000000 0000000 set noparent felipeal@google.com nandana@google.com jsharkey@android.com enh@google.com cmds/dumpstate/README.md0100644 0000000 0000000 00000005266 13756501734 014044 0ustar000000000 0000000 # `dumpstate` development tips ## To build `dumpstate` Do a full build first: ``` m -j dumpstate ``` Then incremental ones: ``` mmm -j frameworks/native/cmds/dumpstate ``` If you're working on device-specific code, you might need to build them as well. Example: ``` mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate ``` ## To build, deploy, and take a bugreport ``` mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb push ${OUT}/system/lib64/*dumpstate*.so /system/lib64/ && adb shell am bug-report ``` Make sure that the device is remounted before running the above command. * If you're working with `userdebug` variant, you may need to run the following to remount your device: ``` adb root && adb remount -R && adb wait-for-device && adb root && adb remount ``` * If you're working with `eng` variant, you may need to run the following to remount your device: ``` adb root && adb remount ``` ## To build, deploy, and run unit tests First create `/data/nativetest64`: ``` adb shell mkdir /data/nativetest64 ``` Then run: ``` mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test ``` And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`): ``` mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs ``` ## To take quick bugreports ``` adb shell setprop dumpstate.dry_run true ``` ## To emulate a device with user build ``` adb shell setprop dumpstate.unroot true ``` ## To change the `dumpstate` version ``` adb shell setprop dumpstate.version VERSION_NAME ``` Example: ``` adb shell setprop dumpstate.version split-dumpsys && adb shell dumpstate -v ``` Then to restore the default version: ``` adb shell setprop dumpstate.version default ``` ## Code style and formatting Use the style defined at the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and make sure to run the following command prior to `repo upload`: ``` git clang-format --style=file HEAD~ ``` ## Useful Bash tricks ``` export BR_DIR=/bugreports alias br='adb shell cmd activity bug-report' alias ls_bugs='adb shell ls -l ${BR_DIR}/' unzip_bug() { adb pull ${BR_DIR}/$1 && emacs $1 && mv $1 /tmp } less_bug() { adb pull ${BR_DIR}/$1 && less $1 && mv $1 /tmp } rm_bugs() { if [ -z "${BR_DIR}" ] ; then echo "Variable BR_DIR not set"; else adb shell rm -rf ${BR_DIR}/*; fi } ``` cmds/dumpstate/binder/0040755 0000000 0000000 00000000000 13756501734 014022 5ustar000000000 0000000 cmds/dumpstate/binder/android/0040755 0000000 0000000 00000000000 13756501734 015442 5ustar000000000 0000000 cmds/dumpstate/binder/android/os/0040755 0000000 0000000 00000000000 13756501734 016063 5ustar000000000 0000000 cmds/dumpstate/binder/android/os/IDumpstate.aidl0100644 0000000 0000000 00000007024 13756501734 020775 0ustar000000000 0000000 /** * 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 android.os; import android.os.IDumpstateListener; import android.os.IDumpstateToken; /** * Binder interface for the currently running dumpstate process. * {@hide} */ interface IDumpstate { // TODO: remove method once startBugReport is used by Shell. /* * Sets the listener for this dumpstate progress. * * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already * set (the listener behaves like a Highlander: There Can be Only One). * Set {@code getSectionDetails} to true in order to receive callbacks with per section * progress details */ IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener, boolean getSectionDetails); // NOTE: If you add to or change these modes, please also change the corresponding enums // in system server, in BugreportParams.java. // These modes encapsulate a set of run time options for generating bugreports. // Takes a bugreport without user interference. const int BUGREPORT_MODE_FULL = 0; // Interactive bugreport, i.e. triggered by the user. const int BUGREPORT_MODE_INTERACTIVE = 1; // Remote bugreport triggered by DevicePolicyManager, for e.g. const int BUGREPORT_MODE_REMOTE = 2; // Bugreport triggered on a wear device. const int BUGREPORT_MODE_WEAR = 3; // Bugreport limited to only telephony info. const int BUGREPORT_MODE_TELEPHONY = 4; // Bugreport limited to only wifi info. const int BUGREPORT_MODE_WIFI = 5; // Default mode. const int BUGREPORT_MODE_DEFAULT = 6; /* * Starts a bugreport in the background. * *

Shows the user a dialog to get consent for sharing the bugreport with the calling * application. If they deny {@link IDumpstateListener#onError} will be called. If they * consent and bugreport generation is successful artifacts will be copied to the given fds and * {@link IDumpstateListener#onFinished} will be called. If there * are errors in bugreport generation {@link IDumpstateListener#onError} will be called. * * @param callingUid UID of the original application that requested the report. * @param callingPackage package of the original application that requested the report. * @param bugreportFd the file to which the zipped bugreport should be written * @param screenshotFd the file to which screenshot should be written; optional * @param bugreportMode the mode that specifies other run time options; must be one of above * @param listener callback for updates; optional */ void startBugreport(int callingUid, @utf8InCpp String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, int bugreportMode, IDumpstateListener listener); /* * Cancels the bugreport currently in progress. */ void cancelBugreport(); } cmds/dumpstate/binder/android/os/IDumpstateListener.aidl0100644 0000000 0000000 00000005345 13756501734 022507 0ustar000000000 0000000 /** * 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 android.os; /** * Listener for dumpstate events. * *

When bugreport creation is complete one of {@code onError} or {@code onFinished} is called. * *

These methods are synchronous by design in order to make dumpstate's lifecycle simpler * to handle. * * {@hide} */ interface IDumpstateListener { /** * Called when there is a progress update. * * @param progress the progress in [0, 100] */ void onProgress(int progress); // NOTE: If you add to or change these error codes, please also change the corresponding enums // in system server, in BugreportManager.java. /* Options specified are invalid or incompatible */ const int BUGREPORT_ERROR_INVALID_INPUT = 1; /* Bugreport encountered a runtime error */ const int BUGREPORT_ERROR_RUNTIME_ERROR = 2; /* User denied consent to share the bugreport with the specified app */ const int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; /* The request to get user consent timed out */ const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; /* There is currently a bugreport running. The caller should try again later. */ const int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; /** * Called on an error condition with one of the error codes listed above. */ void onError(int errorCode); /** * Called when taking bugreport finishes successfully. */ void onFinished(); // TODO(b/111441001): Remove old methods when not used anymore. void onProgressUpdated(int progress); void onMaxProgressUpdated(int maxProgress); /** * Called after every section is complete. * * @param name section name * @param status values from status_t * {@code OK} section completed successfully * {@code TIMEOUT} dump timed out * {@code != OK} error * @param size size in bytes, may be invalid if status != OK * @param durationMs duration in ms */ void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs); } cmds/dumpstate/binder/android/os/IDumpstateToken.aidl0100644 0000000 0000000 00000001375 13756501734 022001 0ustar000000000 0000000 /** * 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 android.os; /** * Token used by the IDumpstateListener to watch for dumpstate death. * {@hide} */ interface IDumpstateToken { } cmds/dumpstate/bugreport-format.md0100644 0000000 0000000 00000012721 13756501734 016400 0ustar000000000 0000000 # Bugreport file format This document specifies the format of the bugreport files generated by the bugreport services (like `bugreport` and `bugreportplus`) and delivered to the end user (i.e., it doesn’t include other tools like `adb bugreport`). A _bugreport_ is initially generated by dumpstate, then processed by **Shell**, which in turn delivers it to the end user through a `ACTION_SEND_MULTIPLE` intent; the end user then select which app (like an email client) handles such intent. ## Text file (Pre-M) Prior to _Android M (Marshmallow)_, `dumpstate` generates a flat .txt file named _bugreport-DATE.txt_ (where _DATE_ is date the bugreport was generated, in the format _YYYY-MM-DD-HH-MM-SS_), and Shell simply propagates it as an attachment in the `ACTION_SEND_MULTIPLE` intent. ## Version 0 (Android M) On _Android M (Marshmallow)_, dumpstate still generates a flat _bugreport-DATE.txt_ file, but then **Shell** creates a zip file called _bugreport-DATE.zip_ containing a _bugreport-DATE.txt_ entry and sends that file as the `ACTION_SEND_MULTIPLE` attachment. ## Version 1.0 (Android N) On _Android N (Nougat)_, `dumpstate` generates a zip file directly (unless there is a failure, in which case it reverts to the flat file that is zipped by **Shell** and hence the end result is the _v0_ format). The zip file is by default called _bugreport-BUILD_ID-DATE.zip_ and it contains a _bugreport-BUILD_ID-DATE.txt_ entry, although the end user can change the name (through **Shell**), in which case they would be called _bugreport-BUILD_ID-NEW_NAME.zip_ and _bugreport-BUILD_ID-NEW_NAME.txt_ respectively. The zip file also contains 2 metadata entries generated by `dumpstate`: - `version.txt`: whose value is **1.0**. - `main-entry.txt`: whose value is the name of the flat text entry (i.e., _bugreport-BUILD_ID-DATE.txt_ or _bugreport-NEW_NAME.txt_). `dumpstate` can also copy files from the device’s filesystem into the zip file under the `FS` folder. For example, a `/dirA/dirB/fileC` file in the device would generate a `FS/dirA/dirB/fileC` entry in the zip file. When systrace is enabled, the zip file will contain a `systrace.txt` file as well. The flat file also has some minor changes: - Tombstone files were removed and added to the zip file. - The duration of each section is printed in the report. - Some dumpsys sections (memory and cpuinfo) are reported earlier in the file. Besides the files generated by `dumpstate`, **Shell** can also add 2 other files upon the end user’s request: - `title.txt`: whose value is a single-line summary of the problem. - `description.txt`: whose value is a multi-line, detailed description of the problem. ## Android O versions On _Android O (Oreo)_, the following changes were made: - The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-split-anr`). ## Version 2.0 (Android P) On _Android P_, the following changes were made: - Framework services are dumped by priority. Supported priorities can be specified when registering the service. If a service does not specify its priority, its assumed to be NORMAL. Supported priorities: - CRITICAL - services that must dump first, and fast (under 100ms). Ex: cpuinfo. - HIGH - services that also must dump first, but can take longer (under 250ms) to dump. Ex: meminfo. - NORMAL - services that have no rush to dump and can take a long time (under 10s). Format changes: - Two additional dumpsys sections are generated. The two new sections can be identified by their HEADER `DUMPSYS CRITICAL` and `DUMPSYS HIGH`. - Services in the new sections will have a new header containing the priority. `DUMP OF SERVICE CRITICAL ` and `DUMP OF SERVICE HIGH `. For example, cpuinfo will now move to `DUMPSYS CRITICAL` and will have a header `DUMP OF SERVICE CRITICAL CPUINFO`. - Bug report will contain proto dumps from all supporting services. Support can be specified when registering framework services. Format changes: - All protos will be generated into separate files per service, per priority. The files will be stored in `proto/(_CRITICAL|_HIGH|).proto` - ANR trace feature has been pushed to version `3.0-dev-split-anr` ## Intermediate versions During development, the versions will be suffixed with _-devX_ or _-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the changes become stable. For example, the initial version during _Android N_ development was **1.0-dev1**. When `dumpsys` was split in 2 sections but not all tools were ready to parse that format, the version was named **1.0-dev2**, which had to be passed to `dumpsys` explicitly (by setting the `dumpstate.version` system property). Once that format became stable and tools knew how to parse it, the default version became **1.0-dev2**. Similarly, if changes in the file format are made after the initial release of Android defining that format, then a new _sub-version_ will be used. For example, if after _Android N_ launches changes are made for the next _N_ release, the version will be called **1.1** or something like that. Determining version and main entry ----------------------------------------------- Tools parsing the zipped bugreport file can use the following algorithm to determine the bugreport format version and its main entry: ``` If [entries contain "version.txt"] version = read("version.txt") main_entry = read("main_entry.txt") else version = 0 main_entry = entries[0] fi ``` cmds/dumpstate/dumpstate.cpp0100644 0000000 0000000 00000337350 13756501734 015301 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #define LOG_TAG "dumpstate" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DumpstateInternal.h" #include "DumpstateSectionReporter.h" #include "DumpstateService.h" #include "dumpstate.h" using ::android::hardware::dumpstate::V1_0::IDumpstateDevice; using ::std::literals::chrono_literals::operator""ms; using ::std::literals::chrono_literals::operator""s; // TODO: remove once moved to namespace using android::defaultServiceManager; using android::Dumpsys; using android::INVALID_OPERATION; using android::IServiceManager; using android::OK; using android::sp; using android::status_t; using android::String16; using android::String8; using android::TIMED_OUT; using android::UNKNOWN_ERROR; using android::Vector; using android::base::StringPrintf; using android::os::IDumpstateListener; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::DumpstateSectionReporter; using android::os::dumpstate::GetPidByName; using android::os::dumpstate::PropertiesHelper; typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult; /* read before root is shed */ static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = nullptr; static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000; // TODO: variables and functions below should be part of dumpstate object static std::set mount_points; void add_mountinfo(); #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops" #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0" #define BLK_DEV_SYS_DIR "/sys/block" #define RECOVERY_DIR "/cache/recovery" #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" #define LOGPERSIST_DATA_DIR "/data/misc/logd" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" #define WLUTIL "/vendor/xbin/wlutil" #define WMTRACE_DATA_DIR "/data/misc/wmtrace" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. // // File: system/core/debuggerd/tombstoned/tombstoned.cpp static const std::string TOMBSTONE_DIR = "/data/tombstones/"; static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_"; static const std::string ANR_DIR = "/data/anr/"; static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); #define RETURN_IF_USER_DENIED_CONSENT() \ if (ds.IsUserConsentDenied()) { \ MYLOGE("Returning early as user denied consent to share bugreport with calling app."); \ return Dumpstate::RunStatus::USER_CONSENT_DENIED; \ } // Runs func_ptr, but checks user consent before and after running it. Returns USER_CONSENT_DENIED // if consent is found to be denied. #define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(func_ptr, ...) \ RETURN_IF_USER_DENIED_CONSENT(); \ func_ptr(__VA_ARGS__); \ RETURN_IF_USER_DENIED_CONSENT(); namespace android { namespace os { namespace { static int Open(std::string path, int flags, mode_t mode = 0) { int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd == -1) { MYLOGE("open(%s, %s)\n", path.c_str(), strerror(errno)); } return fd; } static int OpenForRead(std::string path) { return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); } bool CopyFile(int in_fd, int out_fd) { char buf[4096]; ssize_t byte_count; while ((byte_count = TEMP_FAILURE_RETRY(read(in_fd, buf, sizeof(buf)))) > 0) { if (!android::base::WriteFully(out_fd, buf, byte_count)) { return false; } } return (byte_count != -1); } static bool CopyFileToFd(const std::string& input_file, int out_fd) { MYLOGD("Going to copy file (%s) to %d\n", input_file.c_str(), out_fd); // Obtain a handle to the source file. android::base::unique_fd in_fd(OpenForRead(input_file)); if (out_fd != -1 && in_fd.get() != -1) { if (CopyFile(in_fd.get(), out_fd)) { return true; } MYLOGE("Failed to copy file: %s\n", strerror(errno)); } return false; } static bool UnlinkAndLogOnError(const std::string& file) { if (unlink(file.c_str())) { MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno)); return false; } return true; } static bool IsFileEmpty(const std::string& file_path) { std::ifstream file(file_path, std::ios::binary | std::ios::ate); if(file.bad()) { MYLOGE("Cannot open file: %s\n", file_path.c_str()); return true; } return file.tellg() <= 0; } int64_t GetModuleMetadataVersion() { auto binder = defaultServiceManager()->getService(android::String16("package_native")); if (binder == nullptr) { MYLOGE("Failed to retrieve package_native service"); return 0L; } auto package_service = android::interface_cast(binder); std::string package_name; auto status = package_service->getModuleMetadataPackageName(&package_name); if (!status.isOk()) { MYLOGE("Failed to retrieve module metadata package name: %s", status.toString8().c_str()); return 0L; } MYLOGD("Module metadata package name: %s", package_name.c_str()); int64_t version_code; status = package_service->getVersionCodeForPackage(android::String16(package_name.c_str()), &version_code); if (!status.isOk()) { MYLOGE("Failed to retrieve module metadata version: %s", status.toString8().c_str()); return 0L; } return version_code; } } // namespace } // namespace os } // namespace android static int RunCommand(const std::string& title, const std::vector& fullCommand, const CommandOptions& options = CommandOptions::DEFAULT) { return ds.RunCommand(title, fullCommand, options); } static void RunDumpsys(const std::string& title, const std::vector& dumpsysArgs, const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS, long dumpsysTimeoutMs = 0) { return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs); } static int DumpFile(const std::string& title, const std::string& path) { return ds.DumpFile(title, path); } // Relative directory (inside the zip) for all files copied as-is into the bugreport. static const std::string ZIP_ROOT_DIR = "FS"; static const std::string kProtoPath = "proto/"; static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { "dumpstate_board.txt", "dumpstate_board.bin" }; static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles); static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options"; static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id"; static constexpr char PROPERTY_VERSION[] = "dumpstate.version"; static constexpr char PROPERTY_EXTRA_TITLE[] = "dumpstate.options.title"; static constexpr char PROPERTY_EXTRA_DESCRIPTION[] = "dumpstate.options.description"; static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build(); /* * Returns a vector of dump fds under |dir_path| with a given |file_prefix|. * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime| * is set, the vector only contains files that were written in the last 30 minutes. * If |limit_by_count| is set, the vector only contains the ten latest files. */ static std::vector GetDumpFds(const std::string& dir_path, const std::string& file_prefix, bool limit_by_mtime, bool limit_by_count = true) { const time_t thirty_minutes_ago = ds.now_ - 60 * 30; std::unique_ptr dump_dir(opendir(dir_path.c_str()), closedir); if (dump_dir == nullptr) { MYLOGW("Unable to open directory %s: %s\n", dir_path.c_str(), strerror(errno)); return std::vector(); } std::vector dump_data; struct dirent* entry = nullptr; while ((entry = readdir(dump_dir.get()))) { if (entry->d_type != DT_REG) { continue; } const std::string base_name(entry->d_name); if (base_name.find(file_prefix) != 0) { continue; } const std::string abs_path = dir_path + base_name; android::base::unique_fd fd( TEMP_FAILURE_RETRY(open(abs_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK))); if (fd == -1) { MYLOGW("Unable to open dump file %s: %s\n", abs_path.c_str(), strerror(errno)); break; } struct stat st = {}; if (fstat(fd, &st) == -1) { MYLOGW("Unable to stat dump file %s: %s\n", abs_path.c_str(), strerror(errno)); continue; } if (limit_by_mtime && st.st_mtime < thirty_minutes_ago) { MYLOGI("Excluding stale dump file: %s\n", abs_path.c_str()); continue; } dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime}); } // Sort in descending modification time so that we only keep the newest // reports if |limit_by_count| is true. std::sort(dump_data.begin(), dump_data.end(), [](const DumpData& d1, const DumpData& d2) { return d1.mtime > d2.mtime; }); if (limit_by_count && dump_data.size() > 10) { dump_data.erase(dump_data.begin() + 10, dump_data.end()); } return dump_data; } static bool AddDumps(const std::vector::const_iterator start, const std::vector::const_iterator end, const char* type_name, const bool add_to_zip) { bool dumped = false; for (auto it = start; it != end; ++it) { const std::string& name = it->name; const int fd = it->fd; dumped = true; // Seek to the beginning of the file before dumping any data. A given // DumpData entry might be dumped multiple times in the report. // // For example, the most recent ANR entry is dumped to the body of the // main entry and it also shows up as a separate entry in the bugreport // ZIP file. if (lseek(fd, 0, SEEK_SET) != static_cast(0)) { MYLOGE("Unable to add %s to zip file, lseek failed: %s\n", name.c_str(), strerror(errno)); } if (ds.IsZipping() && add_to_zip) { if (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd, /* timeout = */ 0ms) != OK) { MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str()); } } else { dump_file_from_fd(type_name, name.c_str(), fd); } } return dumped; } // for_each_pid() callback to get mount info about a process. void do_mountinfo(int pid, const char* name __attribute__((unused))) { char path[PATH_MAX]; // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points // are added. snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid); char linkname[PATH_MAX]; ssize_t r = readlink(path, linkname, PATH_MAX); if (r == -1) { MYLOGE("Unable to read link for %s: %s\n", path, strerror(errno)); return; } linkname[r] = '\0'; if (mount_points.find(linkname) == mount_points.end()) { // First time this mount point was found: add it snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid); if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) { mount_points.insert(linkname); } else { MYLOGE("Unable to add mountinfo %s to zip file\n", path); } } } void add_mountinfo() { if (!ds.IsZipping()) return; std::string title = "MOUNT INFO"; mount_points.clear(); DurationReporter duration_reporter(title, true); for_each_pid(do_mountinfo, nullptr); MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size()); } static void dump_dev_files(const char *title, const char *driverpath, const char *filename) { DIR *d; struct dirent *de; char path[PATH_MAX]; d = opendir(driverpath); if (d == nullptr) { return; } while ((de = readdir(d))) { if (de->d_type != DT_LNK) { continue; } snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename); DumpFile(title, path); } closedir(d); } // dump anrd's trace and add to the zip file. // 1. check if anrd is running on this device. // 2. send a SIGUSR1 to its pid which will dump anrd's trace. // 3. wait until the trace generation completes and add to the zip file. static bool dump_anrd_trace() { unsigned int pid; char buf[50], path[PATH_MAX]; struct dirent *trace; struct stat st; DIR *trace_dir; int retry = 5; long max_ctime = 0, old_mtime; long long cur_size = 0; const char *trace_path = "/data/misc/anrd/"; if (!ds.IsZipping()) { MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n"); return false; } // find anrd's pid if it is running. pid = GetPidByName("/system/bin/anrd"); if (pid > 0) { if (stat(trace_path, &st) == 0) { old_mtime = st.st_mtime; } else { MYLOGE("Failed to find: %s\n", trace_path); return false; } // send SIGUSR1 to the anrd to generate a trace. sprintf(buf, "%u", pid); if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf}, CommandOptions::WithTimeout(1).Build())) { MYLOGE("anrd signal timed out. Please manually collect trace\n"); return false; } while (retry-- > 0 && old_mtime == st.st_mtime) { sleep(1); stat(trace_path, &st); } if (retry < 0 && old_mtime == st.st_mtime) { MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path); return false; } // identify the trace file by its creation time. if (!(trace_dir = opendir(trace_path))) { MYLOGE("Can't open trace file under %s\n", trace_path); } while ((trace = readdir(trace_dir))) { if (strcmp(trace->d_name, ".") == 0 || strcmp(trace->d_name, "..") == 0) { continue; } sprintf(path, "%s%s", trace_path, trace->d_name); if (stat(path, &st) == 0) { if (st.st_ctime > max_ctime) { max_ctime = st.st_ctime; sprintf(buf, "%s", trace->d_name); } } } closedir(trace_dir); // Wait until the dump completes by checking the size of the trace. if (max_ctime > 0) { sprintf(path, "%s%s", trace_path, buf); while(true) { sleep(1); if (stat(path, &st) == 0) { if (st.st_size == cur_size) { break; } else if (st.st_size > cur_size) { cur_size = st.st_size; } else { return false; } } else { MYLOGE("Cant stat() %s anymore\n", path); return false; } } // Add to the zip file. if (!ds.AddZipEntry("anrd_trace.txt", path)) { MYLOGE("Unable to add anrd_trace file %s to zip file\n", path); } else { android::os::UnlinkAndLogOnError(path); return true; } } else { MYLOGE("Can't stats any trace file under %s\n", trace_path); } } return false; } static bool skip_not_stat(const char *path) { static const char stat[] = "/stat"; size_t len = strlen(path); if (path[len - 1] == '/') { /* Directory? */ return false; } return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */ } static bool skip_none(const char* path __attribute__((unused))) { return false; } unsigned long worst_write_perf = 20000; /* in KB/s */ // // stat offsets // Name units description // ---- ----- ----------- // read I/Os requests number of read I/Os processed #define __STAT_READ_IOS 0 // read merges requests number of read I/Os merged with in-queue I/O #define __STAT_READ_MERGES 1 // read sectors sectors number of sectors read #define __STAT_READ_SECTORS 2 // read ticks milliseconds total wait time for read requests #define __STAT_READ_TICKS 3 // write I/Os requests number of write I/Os processed #define __STAT_WRITE_IOS 4 // write merges requests number of write I/Os merged with in-queue I/O #define __STAT_WRITE_MERGES 5 // write sectors sectors number of sectors written #define __STAT_WRITE_SECTORS 6 // write ticks milliseconds total wait time for write requests #define __STAT_WRITE_TICKS 7 // in_flight requests number of I/Os currently in flight #define __STAT_IN_FLIGHT 8 // io_ticks milliseconds total time this block device has been active #define __STAT_IO_TICKS 9 // time_in_queue milliseconds total wait time for all requests #define __STAT_IN_QUEUE 10 #define __STAT_NUMBER_FIELD 11 // // read I/Os, write I/Os // ===================== // // These values increment when an I/O request completes. // // read merges, write merges // ========================= // // These values increment when an I/O request is merged with an // already-queued I/O request. // // read sectors, write sectors // =========================== // // These values count the number of sectors read from or written to this // block device. The "sectors" in question are the standard UNIX 512-byte // sectors, not any device- or filesystem-specific block size. The // counters are incremented when the I/O completes. #define SECTOR_SIZE 512 // // read ticks, write ticks // ======================= // // These values count the number of milliseconds that I/O requests have // waited on this block device. If there are multiple I/O requests waiting, // these values will increase at a rate greater than 1000/second; for // example, if 60 read requests wait for an average of 30 ms, the read_ticks // field will increase by 60*30 = 1800. // // in_flight // ========= // // This value counts the number of I/O requests that have been issued to // the device driver but have not yet completed. It does not include I/O // requests that are in the queue but not yet issued to the device driver. // // io_ticks // ======== // // This value counts the number of milliseconds during which the device has // had I/O requests queued. // // time_in_queue // ============= // // This value counts the number of milliseconds that I/O requests have waited // on this block device. If there are multiple I/O requests waiting, this // value will increase as the product of the number of milliseconds times the // number of requests waiting (see "read ticks" above for an example). #define S_TO_MS 1000 // static int dump_stat_from_fd(const char *title __unused, const char *path, int fd) { unsigned long long fields[__STAT_NUMBER_FIELD]; bool z; char *cp, *buffer = nullptr; size_t i = 0; FILE *fp = fdopen(dup(fd), "rb"); getline(&buffer, &i, fp); fclose(fp); if (!buffer) { return -errno; } i = strlen(buffer); while ((i > 0) && (buffer[i - 1] == '\n')) { buffer[--i] = '\0'; } if (!*buffer) { free(buffer); return 0; } z = true; for (cp = buffer, i = 0; i < (sizeof(fields) / sizeof(fields[0])); ++i) { fields[i] = strtoull(cp, &cp, 10); if (fields[i] != 0) { z = false; } } if (z) { /* never accessed */ free(buffer); return 0; } if (!strncmp(path, BLK_DEV_SYS_DIR, sizeof(BLK_DEV_SYS_DIR) - 1)) { path += sizeof(BLK_DEV_SYS_DIR) - 1; } printf("%-30s:%9s%9s%9s%9s%9s%9s%9s%9s%9s%9s%9s\n%-30s:\t%s\n", "Block-Dev", "R-IOs", "R-merg", "R-sect", "R-wait", "W-IOs", "W-merg", "W-sect", "W-wait", "in-fli", "activ", "T-wait", path, buffer); free(buffer); if (fields[__STAT_IO_TICKS]) { unsigned long read_perf = 0; unsigned long read_ios = 0; if (fields[__STAT_READ_TICKS]) { unsigned long long divisor = fields[__STAT_READ_TICKS] * fields[__STAT_IO_TICKS]; read_perf = ((unsigned long long)SECTOR_SIZE * fields[__STAT_READ_SECTORS] * fields[__STAT_IN_QUEUE] + (divisor >> 1)) / divisor; read_ios = ((unsigned long long)S_TO_MS * fields[__STAT_READ_IOS] * fields[__STAT_IN_QUEUE] + (divisor >> 1)) / divisor; } unsigned long write_perf = 0; unsigned long write_ios = 0; if (fields[__STAT_WRITE_TICKS]) { unsigned long long divisor = fields[__STAT_WRITE_TICKS] * fields[__STAT_IO_TICKS]; write_perf = ((unsigned long long)SECTOR_SIZE * fields[__STAT_WRITE_SECTORS] * fields[__STAT_IN_QUEUE] + (divisor >> 1)) / divisor; write_ios = ((unsigned long long)S_TO_MS * fields[__STAT_WRITE_IOS] * fields[__STAT_IN_QUEUE] + (divisor >> 1)) / divisor; } unsigned queue = (fields[__STAT_IN_QUEUE] + (fields[__STAT_IO_TICKS] >> 1)) / fields[__STAT_IO_TICKS]; if (!write_perf && !write_ios) { printf("%-30s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n", path, read_perf, read_ios, queue); } else { printf("%-30s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n", path, read_perf, read_ios, write_perf, write_ios, queue); } /* bugreport timeout factor adjustment */ if ((write_perf > 1) && (write_perf < worst_write_perf)) { worst_write_perf = write_perf; } } return 0; } static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000; /* timeout in ms to read a list of buffers */ static unsigned long logcat_timeout(const std::vector& buffers) { unsigned long timeout_ms = 0; for (const auto& buffer : buffers) { log_id_t id = android_name_to_log_id(buffer.c_str()); unsigned long property_size = __android_logger_get_buffer_size(id); /* Engineering margin is ten-fold our guess */ timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf; } return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) { } android::binder::Status Dumpstate::ConsentCallback::onReportApproved() { std::lock_guard lock(lock_); result_ = APPROVED; MYLOGD("User approved consent to share bugreport\n"); return android::binder::Status::ok(); } android::binder::Status Dumpstate::ConsentCallback::onReportDenied() { std::lock_guard lock(lock_); result_ = DENIED; MYLOGW("User denied consent to share bugreport\n"); return android::binder::Status::ok(); } UserConsentResult Dumpstate::ConsentCallback::getResult() { std::lock_guard lock(lock_); return result_; } uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const { return Nanotime() - start_time_; } void Dumpstate::PrintHeader() const { std::string build, fingerprint, radio, bootloader, network; char date[80]; build = android::base::GetProperty("ro.build.display.id", "(unknown)"); fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)"); radio = android::base::GetProperty("gsm.version.baseband", "(unknown)"); bootloader = android::base::GetProperty("ro.bootloader", "(unknown)"); network = android::base::GetProperty("gsm.operator.alpha", "(unknown)"); strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_)); printf("========================================================\n"); printf("== dumpstate: %s\n", date); printf("========================================================\n"); printf("\n"); printf("Build: %s\n", build.c_str()); // NOTE: fingerprint entry format is important for other tools. printf("Build fingerprint: '%s'\n", fingerprint.c_str()); printf("Bootloader: %s\n", bootloader.c_str()); printf("Radio: %s\n", radio.c_str()); printf("Network: %s\n", network.c_str()); int64_t module_metadata_version = android::os::GetModuleMetadataVersion(); if (module_metadata_version != 0) { printf("Module Metadata version: %" PRId64 "\n", module_metadata_version); } printf("Kernel: "); DumpFileToFd(STDOUT_FILENO, "", "/proc/version"); printf("Command line: %s\n", strtok(cmdline_buf, "\n")); printf("Uptime: "); RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"}, CommandOptions::WithTimeout(1).Always().Build()); printf("Bugreport format version: %s\n", version_.c_str()); printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_, PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->extra_options.c_str()); printf("\n"); } // List of file extensions that can cause a zip file attachment to be rejected by some email // service providers. static const std::set PROBLEMATIC_FILE_EXTENSIONS = { ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp", ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct", ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh" }; status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, std::chrono::milliseconds timeout = 0ms) { if (!IsZipping()) { MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n", entry_name.c_str()); return INVALID_OPERATION; } std::string valid_name = entry_name; // Rename extension if necessary. size_t idx = entry_name.rfind('.'); if (idx != std::string::npos) { std::string extension = entry_name.substr(idx); std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) { valid_name = entry_name + ".renamed"; MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str()); } } // Logging statement below is useful to time how long each entry takes, but it's too verbose. // MYLOGD("Adding zip entry %s\n", entry_name.c_str()); int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress, get_mtime(fd, ds.now_)); if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(), ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; } bool finished_entry = false; auto finish_entry = [this, &finished_entry] { if (!finished_entry) { // This should only be called when we're going to return an earlier error, // which would've been logged. This may imply the file is already corrupt // and any further logging from FinishEntry is more likely to mislead than // not. this->zip_writer_->FinishEntry(); } }; auto scope_guard = android::base::make_scope_guard(finish_entry); auto start = std::chrono::steady_clock::now(); auto end = start + timeout; struct pollfd pfd = {fd, POLLIN}; std::vector buffer(65536); while (1) { if (timeout.count() > 0) { // lambda to recalculate the timeout. auto time_left_ms = [end]() { auto now = std::chrono::steady_clock::now(); auto diff = std::chrono::duration_cast(end - now); return std::max(diff.count(), 0LL); }; int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { MYLOGE("Error in poll while adding from fd to zip entry %s:%s\n", entry_name.c_str(), strerror(errno)); return -errno; } else if (rc == 0) { MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms\n", entry_name.c_str(), strerror(errno), timeout.count()); return TIMED_OUT; } } ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size())); if (bytes_read == 0) { break; } else if (bytes_read == -1) { MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno)); return -errno; } err = zip_writer_->WriteBytes(buffer.data(), bytes_read); if (err) { MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; } } err = zip_writer_->FinishEntry(); finished_entry = true; if (err != 0) { MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; } return OK; } bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) { android::base::unique_fd fd( TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); if (fd == -1) { MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno)); return false; } return (AddZipEntryFromFd(entry_name, fd.get()) == OK); } /* adds a file to the existing zipped bugreport */ static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) { return (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) == OK) ? 0 : 1; } void Dumpstate::AddDir(const std::string& dir, bool recursive) { if (!IsZipping()) { MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str()); return; } MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive); DurationReporter duration_reporter(dir, true); dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd); } bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) { if (!IsZipping()) { MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n", entry_name.c_str()); return false; } MYLOGD("Adding zip text entry %s\n", entry_name.c_str()); int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_); if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(), ZipWriter::ErrorCodeString(err)); return false; } err = zip_writer_->WriteBytes(content.c_str(), content.length()); if (err != 0) { MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(), ZipWriter::ErrorCodeString(err)); return false; } err = zip_writer_->FinishEntry(); if (err != 0) { MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); return false; } return true; } static void DoKmsg() { struct stat st; if (!stat(PSTORE_LAST_KMSG, &st)) { /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */ DumpFile("LAST KMSG", PSTORE_LAST_KMSG); } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) { DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG); } else { /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */ DumpFile("LAST KMSG", "/proc/last_kmsg"); } } static void DoKernelLogcat() { unsigned long timeout_ms = logcat_timeout({"kernel"}); RunCommand( "KERNEL LOG", {"logcat", "-b", "kernel", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build()); } static void DoLogcat() { unsigned long timeout_ms; // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); // calculate timeout timeout_ms = logcat_timeout({"main", "system", "crash"}); RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build()); timeout_ms = logcat_timeout({"events"}); RunCommand( "EVENT LOG", {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build()); timeout_ms = logcat_timeout({"stats"}); RunCommand( "STATS LOG", {"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build()); timeout_ms = logcat_timeout({"radio"}); RunCommand( "RADIO LOG", {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeoutInMs(timeout_ms).Build()); RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */ RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}); } static void DumpIpTablesAsRoot() { RunCommand("IPTABLES", {"iptables", "-L", "-nvx"}); RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"}); RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"}); /* no ip6 nat */ RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"}); RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"}); RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"}); RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"}); } static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) { MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path, anr_traces_dir.c_str()); // If we're here, dump_traces_path will always be a temporary file // (created with mkostemp or similar) that contains dumps taken earlier // on in the process. if (dump_traces_path != nullptr) { if (add_to_zip) { ds.AddZipEntry(ZIP_ROOT_DIR + anr_traces_dir + "/traces-just-now.txt", dump_traces_path); } else { MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n", dump_traces_path); ds.DumpFile("VM TRACES JUST NOW", dump_traces_path); } const int ret = unlink(dump_traces_path); if (ret == -1) { MYLOGW("Error unlinking temporary trace path %s: %s\n", dump_traces_path, strerror(errno)); } } // Add a specific message for the first ANR Dump. if (ds.anr_data_.size() > 0) { AddDumps(ds.anr_data_.begin(), ds.anr_data_.begin() + 1, "VM TRACES AT LAST ANR", add_to_zip); // The "last" ANR will always be included as separate entry in the zip file. In addition, // it will be present in the body of the main entry if |add_to_zip| == false. // // Historical ANRs are always included as separate entries in the bugreport zip file. AddDumps(ds.anr_data_.begin() + ((add_to_zip) ? 1 : 0), ds.anr_data_.end(), "HISTORICAL ANR", true /* add_to_zip */); } else { printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str()); } } static void AddAnrTraceFiles() { const bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR; std::string anr_traces_dir = "/data/anr"; AddAnrTraceDir(add_to_zip, anr_traces_dir); RunCommand("ANR FILES", {"ls", "-lt", ANR_DIR}); // Slow traces for slow operations. struct stat st; int i = 0; while (true) { const std::string slow_trace_path = anr_traces_dir + android::base::StringPrintf("slow%02d.txt", i); if (stat(slow_trace_path.c_str(), &st)) { // No traces file at this index, done with the files. break; } ds.DumpFile("VM TRACES WHEN SLOW", slow_trace_path.c_str()); i++; } } static void DumpBlockStatFiles() { DurationReporter duration_reporter("DUMP BLOCK STAT"); std::unique_ptr> dirptr(opendir(BLK_DEV_SYS_DIR), closedir); if (dirptr == nullptr) { MYLOGE("Failed to open %s: %s\n", BLK_DEV_SYS_DIR, strerror(errno)); return; } printf("------ DUMP BLOCK STAT ------\n\n"); while (struct dirent *d = readdir(dirptr.get())) { if ((d->d_name[0] == '.') && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) || (d->d_name[1] == '\0'))) { continue; } const std::string new_path = android::base::StringPrintf("%s/%s", BLK_DEV_SYS_DIR, d->d_name); printf("------ BLOCK STAT (%s) ------\n", new_path.c_str()); dump_files("", new_path.c_str(), skip_not_stat, dump_stat_from_fd); printf("\n"); } return; } static void DumpPacketStats() { DumpFile("NETWORK DEV INFO", "/proc/net/dev"); DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all"); DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt"); DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl"); DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats"); } static void DumpIpAddrAndRules() { /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */ RunCommand("NETWORK INTERFACES", {"ip", "link"}); RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"}); RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"}); RunCommand("IP RULES", {"ip", "rule", "show"}); RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); } static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { auto start = std::chrono::steady_clock::now(); sp sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); Vector args; Dumpsys::setServiceArgs(args, /* asProto = */ false, priority); Vector services = dumpsys.listServices(priority, /* supports_proto = */ false); for (const String16& service : services) { RETURN_IF_USER_DENIED_CONSENT(); std::string path(title); path.append(" - ").append(String8(service).c_str()); DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); size_t bytes_written = 0; status_t status = dumpsys.startDumpThread(service, args); if (status == OK) { dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); std::chrono::duration elapsed_seconds; status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, /* as_proto = */ false, elapsed_seconds, bytes_written); section_reporter.setSize(bytes_written); dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); bool dump_complete = (status == OK); dumpsys.stopDumpThread(dump_complete); } section_reporter.setStatus(status); auto elapsed_duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - start); if (elapsed_duration > timeout) { MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(), elapsed_duration.count()); break; } } return Dumpstate::RunStatus::OK; } static void RunDumpsysText(const std::string& title, int priority, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { DurationReporter duration_reporter(title); dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); fsync(STDOUT_FILENO); RunDumpsysTextByPriority(title, priority, timeout, service_timeout); } /* Dump all services registered with Normal or Default priority. */ static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& title, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { DurationReporter duration_reporter(title); dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); fsync(STDOUT_FILENO); RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, timeout, service_timeout); RETURN_IF_USER_DENIED_CONSENT(); return RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, service_timeout); } static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { if (!ds.IsZipping()) { MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); return Dumpstate::RunStatus::OK; } sp sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); Vector args; Dumpsys::setServiceArgs(args, /* asProto = */ true, priority); DurationReporter duration_reporter(title); auto start = std::chrono::steady_clock::now(); Vector services = dumpsys.listServices(priority, /* supports_proto = */ true); for (const String16& service : services) { RETURN_IF_USER_DENIED_CONSENT(); std::string path(kProtoPath); path.append(String8(service).c_str()); if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { path.append("_CRITICAL"); } else if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) { path.append("_HIGH"); } path.append(kProtoExt); DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); status_t status = dumpsys.startDumpThread(service, args); if (status == OK) { status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); bool dumpTerminated = (status == OK); dumpsys.stopDumpThread(dumpTerminated); } ZipWriter::FileEntry file_entry; ds.zip_writer_->GetLastEntry(&file_entry); section_reporter.setSize(file_entry.compressed_size); section_reporter.setStatus(status); auto elapsed_duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - start); if (elapsed_duration > timeout) { MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(), elapsed_duration.count()); break; } } return Dumpstate::RunStatus::OK; } // Runs dumpsys on services that must dump first and will take less than 100ms to dump. static Dumpstate::RunStatus RunDumpsysCritical() { RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, /* timeout= */ 5s, /* service_timeout= */ 500ms); RETURN_IF_USER_DENIED_CONSENT(); return RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, /* timeout= */ 5s, /* service_timeout= */ 500ms); } // Runs dumpsys on services that must dump first but can take up to 250ms to dump. static Dumpstate::RunStatus RunDumpsysHigh() { // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both // high priority. Reduce timeout once they are able to dump in a shorter time or // moved to a parallel task. RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, /* timeout= */ 90s, /* service_timeout= */ 30s); RETURN_IF_USER_DENIED_CONSENT(); return RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, /* timeout= */ 5s, /* service_timeout= */ 1s); } // Runs dumpsys on services that must dump but can take up to 10s to dump. static Dumpstate::RunStatus RunDumpsysNormal() { RunDumpsysTextNormalPriority("DUMPSYS", /* timeout= */ 90s, /* service_timeout= */ 10s); RETURN_IF_USER_DENIED_CONSENT(); return RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, /* timeout= */ 90s, /* service_timeout= */ 10s); } static void DumpHals() { if (!ds.IsZipping()) { RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); return; } DurationReporter duration_reporter("DUMP HALS"); RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); using android::hidl::manager::V1_0::IServiceManager; using android::hardware::defaultServiceManager; sp sm = defaultServiceManager(); if (sm == nullptr) { MYLOGE("Could not retrieve hwservicemanager to dump hals.\n"); return; } auto ret = sm->list([&](const auto& interfaces) { for (const std::string& interface : interfaces) { std::string cleanName = interface; std::replace_if(cleanName.begin(), cleanName.end(), [](char c) { return !isalnum(c) && std::string("@-_:.").find(c) == std::string::npos; }, '_'); const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; { auto fd = android::base::unique_fd( TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); if (fd < 0) { MYLOGE("Could not open %s to dump additional hal information.\n", path.c_str()); continue; } RunCommandToFd(fd, "", {"lshal", "debug", "-E", interface}, CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); bool empty = 0 == lseek(fd, 0, SEEK_END); if (!empty) { ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path); } } unlink(path.c_str()); } }); if (!ret.isOk()) { MYLOGE("Could not list hals from hwservicemanager.\n"); } } // Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent // via the consent they are shown. Ignores other errors that occur while running various // commands. The consent checking is currently done around long running tasks, which happen to // be distributed fairly evenly throughout the function. static Dumpstate::RunStatus dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); // Dump various things. Note that anything that takes "long" (i.e. several seconds) should // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK). dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); RunCommand("UPTIME", {"uptime"}); DumpBlockStatFiles(); dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd"); DumpFile("MEMORY INFO", "/proc/meminfo"); RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20); DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat"); DumpFile("VMALLOC INFO", "/proc/vmallocinfo"); DumpFile("SLAB INFO", "/proc/slabinfo"); DumpFile("ZONEINFO", "/proc/zoneinfo"); DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo"); DumpFile("BUDDYINFO", "/proc/buddyinfo"); DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index"); DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources"); DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); DumpFile("KERNEL SYNC", "/d/sync"); RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, CommandOptions::AS_ROOT); DumpHals(); RunCommand("PRINTENV", {"printenv"}); RunCommand("NETSTAT", {"netstat", "-nW"}); struct stat s; if (stat("/proc/modules", &s) != 0) { MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n"); } else { RunCommand("LSMOD", {"lsmod"}); } if (__android_logger_property_get_bool( "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) { DoKernelLogcat(); } else { do_dmesg(); } RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); /* Dump Bluetooth HCI logs */ ds.AddDir("/data/misc/bluetooth/logs", true); if (!ds.do_early_screenshot_) { MYLOGI("taking late screenshot\n"); ds.TakeScreenshot(); } DoLogcat(); AddAnrTraceFiles(); // NOTE: tombstones are always added as separate entries in the zip archive // and are not interspersed with the main report. const bool tombstones_dumped = AddDumps(ds.tombstone_data_.begin(), ds.tombstone_data_.end(), "TOMBSTONE", true /* add_to_zip */); if (!tombstones_dumped) { printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR.c_str()); } DumpPacketStats(); RunDumpsys("EBPF MAP STATS", {"netd", "trafficcontroller"}); DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"}); RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"}); RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"}); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh); RunCommand("SYSTEM PROPERTIES", {"getprop"}); RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"}); /* Binder state is expensive to look at as it uses a lot of memory. */ DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log"); DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log"); DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions"); DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); RunDumpsys("WINSCOPE TRACE", {"window", "trace"}); /* Add window and surface trace files. */ if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(WMTRACE_DATA_DIR, false); } RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard); /* Migrate the ril_dumpstate to a device specific dumpstate? */ int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0); if (rilDumpstateTimeout > 0) { // su does not exist on user builds, so try running without it. // This way any implementations of vril-dump that do not require // root can run on user builds. CommandOptions::CommandOptionsBuilder options = CommandOptions::WithTimeout(rilDumpstateTimeout); if (!PropertiesHelper::IsUserBuild()) { options.AsRoot(); } RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build()); } printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal); printf("========================================================\n"); printf("== Checkins\n"); printf("========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"}); RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}); RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}); printf("========================================================\n"); printf("== Running Application Activities\n"); printf("========================================================\n"); // The following dumpsys internally collects output from running apps, so it can take a long // time. So let's extend the timeout. const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Running Application Services (platform)\n"); printf("========================================================\n"); RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Running Application Services (non-platform)\n"); printf("========================================================\n"); RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Running Application Providers (platform)\n"); printf("========================================================\n"); RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Running Application Providers (non-platform)\n"); printf("========================================================\n"); RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Dropbox crashes\n"); printf("========================================================\n"); RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"}); RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"}); printf("========================================================\n"); printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(), ds.progress_->GetMax(), ds.progress_->GetInitialMax()); printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); printf("========================================================\n"); printf("== Obtaining statsd metadata\n"); printf("========================================================\n"); // This differs from the usual dumpsys stats, which is the stats report data. RunDumpsys("STATSDSTATS", {"stats", "--metadata"}); return Dumpstate::RunStatus::OK; } /* * Dumps state for the default case; drops root after it's no longer necessary. * * Returns RunStatus::OK if everything went fine. * Returns RunStatus::ERROR if there was an error. * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport * with the caller. */ static Dumpstate::RunStatus DumpstateDefault() { // Try to dump anrd trace if the daemon is running. dump_anrd_trace(); // Invoking the following dumpsys calls before DumpTraces() to try and // keep the system stats as close to its initial state as possible. RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical); /* collect stack traces from Dalvik and native processes (needs root) */ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path); /* Run some operations that require root. */ ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()); ds.AddDir(RECOVERY_DIR, true); ds.AddDir(RECOVERY_DATA_DIR, true); ds.AddDir(UPDATE_ENGINE_LOG_DIR, true); ds.AddDir(LOGPERSIST_DATA_DIR, false); if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(PROFILE_DATA_DIR_CUR, true); ds.AddDir(PROFILE_DATA_DIR_REF, true); } add_mountinfo(); DumpIpTablesAsRoot(); // Capture any IPSec policies in play. No keys are exposed here. RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build()); // Dump IPsec stats. No keys are exposed here. DumpFile("XFRM STATS", XFRM_STAT_PROC_FILE); // Run ss as root so we can see socket marks. RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build()); // Run iotop as root to show top 100 IO threads RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"}); // Gather shared memory buffer info if the product implements it struct stat st; if (!stat("/product/bin/dmabuf_dump", &st)) { RunCommand("Dmabuf dump", {"/product/bin/dmabuf_dump"}); } if (!DropRootUser()) { return Dumpstate::RunStatus::ERROR; } RETURN_IF_USER_DENIED_CONSENT(); return dumpstate(); } // This method collects common dumpsys for telephony and wifi static void DumpstateRadioCommon() { DumpIpTablesAsRoot(); ds.AddDir(LOGPERSIST_DATA_DIR, false); if (!DropRootUser()) { return; } do_dmesg(); DoLogcat(); DumpPacketStats(); DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); DumpHals(); RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); } // This method collects dumpsys for telephony debugging only static void DumpstateTelephonyOnly() { DurationReporter duration_reporter("DUMPSTATE"); const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); DumpstateRadioCommon(); RunCommand("SYSTEM PROPERTIES", {"getprop"}); printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); printf("========================================================\n"); printf("== Running Application Services\n"); printf("========================================================\n"); RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"}); printf("========================================================\n"); printf("== Running Application Services (non-platform)\n"); printf("========================================================\n"); RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Checkins\n"); printf("========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } // This method collects dumpsys for wifi debugging only static void DumpstateWifiOnly() { DurationReporter duration_reporter("DUMPSTATE"); DumpstateRadioCommon(); printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { DurationReporter duration_reporter("DUMP TRACES"); const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX"; const size_t buf_size = temp_file_pattern.length() + 1; std::unique_ptr file_name_buf(new char[buf_size]); memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size); // Create a new, empty file to receive all trace dumps. // // TODO: This can be simplified once we remove support for the old style // dumps. We can have a file descriptor passed in to dump_traces instead // of creating a file, closing it and then reopening it again. android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC)); if (fd < 0) { MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno)); return RunStatus::OK; } // Nobody should have access to this temporary file except dumpstate, but we // temporarily grant 'read' to 'others' here because this file is created // when tombstoned is still running as root, but dumped after dropping. This // can go away once support for old style dumping has. const int chmod_ret = fchmod(fd, 0666); if (chmod_ret < 0) { MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno)); return RunStatus::OK; } std::unique_ptr proc(opendir("/proc"), closedir); if (proc.get() == nullptr) { MYLOGE("opendir /proc failed: %s\n", strerror(errno)); return RunStatus::OK; } // Number of times process dumping has timed out. If we encounter too many // failures, we'll give up. int timeout_failures = 0; bool dalvik_found = false; const std::set hal_pids = get_interesting_hal_pids(); struct dirent* d; while ((d = readdir(proc.get()))) { RETURN_IF_USER_DENIED_CONSENT(); int pid = atoi(d->d_name); if (pid <= 0) { continue; } const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid); std::string exe; if (!android::base::Readlink(link_name, &exe)) { continue; } bool is_java_process; if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") { // Don't bother dumping backtraces for the zygote. if (IsZygote(pid)) { continue; } dalvik_found = true; is_java_process = true; } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) { is_java_process = false; } else { // Probably a native process we don't care about, continue. continue; } // If 3 backtrace dumps fail in a row, consider debuggerd dead. if (timeout_failures == 3) { dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n"); break; } const uint64_t start = Nanotime(); const int ret = dump_backtrace_to_file_timeout( pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, is_java_process ? 5 : 20, fd); if (ret == -1) { // For consistency, the header and footer to this message match those // dumped by debuggerd in the success case. dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid); dprintf(fd, "Dump failed, likely due to a timeout.\n"); dprintf(fd, "---- end %d ----", pid); timeout_failures++; continue; } // We've successfully dumped stack traces, reset the failure count // and write a summary of the elapsed time to the file and continue with the // next process. timeout_failures = 0; dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native", pid, (float)(Nanotime() - start) / NANOS_PER_SEC); } if (!dalvik_found) { MYLOGE("Warning: no Dalvik processes found to dump stacks\n"); } *path = file_name_buf.release(); return RunStatus::OK; } void Dumpstate::DumpstateBoard() { DurationReporter duration_reporter("dumpstate_board()"); printf("========================================================\n"); printf("== Board\n"); printf("========================================================\n"); if (!IsZipping()) { MYLOGD("Not dumping board info because it's not a zipped bugreport\n"); return; } std::vector paths; std::vector>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(), kDumpstateBoardFiles[i].c_str())); remover.emplace_back(android::base::make_scope_guard( std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i]))); } sp dumpstate_device(IDumpstateDevice::getService()); if (dumpstate_device == nullptr) { MYLOGE("No IDumpstateDevice implementation\n"); return; } using ScopedNativeHandle = std::unique_ptr>; ScopedNativeHandle handle(native_handle_create(static_cast(paths.size()), 0), [](native_handle_t* handle) { native_handle_close(handle); native_handle_delete(handle); }); if (handle == nullptr) { MYLOGE("Could not create native_handle\n"); return; } // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); android::base::unique_fd fd(TEMP_FAILURE_RETRY( open(paths[i].c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); if (fd < 0) { MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno)); return; } handle.get()->data[i] = fd.release(); } // Given that bugreport is required to diagnose failures, it's better to // set an arbitrary amount of timeout for IDumpstateDevice than to block the // rest of bugreport. In the timeout case, we will kill dumpstate board HAL // and grab whatever dumped std::packaged_task dumpstate_task([paths, dumpstate_device, &handle]() -> bool { android::hardware::Return status = dumpstate_device->dumpstateBoard(handle.get()); if (!status.isOk()) { MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); return false; } return true; }); auto result = dumpstate_task.get_future(); std::thread(std::move(dumpstate_task)).detach(); constexpr size_t timeout_sec = 30; if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec); if (!android::base::SetProperty("ctl.interface_restart", android::base::StringPrintf("%s/default", IDumpstateDevice::descriptor))) { MYLOGE("Couldn't restart dumpstate HAL\n"); } } // Wait some time for init to kill dumpstate vendor HAL constexpr size_t killing_timeout_sec = 10; if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { MYLOGE("killing dumpstateBoard timed out after %zus, continue and " "there might be racing in content\n", killing_timeout_sec); } auto file_sizes = std::make_unique(paths.size()); for (size_t i = 0; i < paths.size(); i++) { struct stat s; if (fstat(handle.get()->data[i], &s) == -1) { MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), strerror(errno)); file_sizes[i] = -1; continue; } file_sizes[i] = s.st_size; } for (size_t i = 0; i < paths.size(); i++) { if (file_sizes[i] == -1) { continue; } if (file_sizes[i] == 0) { MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str()); continue; } AddZipEntry(kDumpstateBoardFiles[i], paths[i]); } printf("*** See dumpstate-board.txt entry ***\n"); } static void ShowUsage() { fprintf(stderr, "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] " "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n" " -h: display this help message\n" " -b: play sound file instead of vibrate, at beginning of job\n" " -e: play sound file instead of vibrate, at end of job\n" " -o: write to file (instead of stdout)\n" " -d: append date to filename (requires -o)\n" " -p: capture screenshot to filename.png (requires -o)\n" " -z: generate zipped file (requires -o)\n" " -s: write output to control socket (for init)\n" " -S: write file location to control socket (for init; requires -o and -z)\n" " -q: disable vibrate\n" " -B: send broadcast when finished (requires -o)\n" " -P: send broadcast when started and update system properties on " "progress (requires -o and -B)\n" " -R: take bugreport in remote mode (requires -o, -z, -d and -B, " "shouldn't be used with -P)\n" " -w: start binder service and make it wait for a call to startBugreport\n" " -v: prints the dumpstate header and exit\n"); } static void register_sig_handler() { signal(SIGPIPE, SIG_IGN); } bool Dumpstate::FinishZipFile() { std::string entry_name = base_name_ + "-" + name_ + ".txt"; MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(), tmp_path_.c_str()); // Final timestamp char date[80]; time_t the_real_now_please_stand_up = time(nullptr); strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up)); MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date, the_real_now_please_stand_up - ds.now_); if (!ds.AddZipEntry(entry_name, tmp_path_)) { MYLOGE("Failed to add text entry to .zip file\n"); return false; } if (!AddTextZipEntry("main_entry.txt", entry_name)) { MYLOGE("Failed to add main_entry.txt to .zip file\n"); return false; } // Add log file (which contains stderr output) to zip... fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n"); if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) { MYLOGE("Failed to add dumpstate log to .zip file\n"); return false; } // TODO: Should truncate the existing file. // ... and re-open it for further logging. if (!redirect_to_existing_file(stderr, const_cast(ds.log_path_.c_str()))) { return false; } fprintf(stderr, "\n"); int32_t err = zip_writer_->Finish(); if (err != 0) { MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err)); return false; } // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor. ds.zip_file.reset(nullptr); MYLOGD("Removing temporary file %s\n", tmp_path_.c_str()) android::os::UnlinkAndLogOnError(tmp_path_); return true; } static std::string SHA256_file_hash(const std::string& filepath) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOFOLLOW))); if (fd == -1) { MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno)); return nullptr; } SHA256_CTX ctx; SHA256_Init(&ctx); std::vector buffer(65536); while (1) { ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd.get(), buffer.data(), buffer.size())); if (bytes_read == 0) { break; } else if (bytes_read == -1) { MYLOGE("read(%s): %s\n", filepath.c_str(), strerror(errno)); return nullptr; } SHA256_Update(&ctx, buffer.data(), bytes_read); } uint8_t hash[SHA256_DIGEST_LENGTH]; SHA256_Final(hash, &ctx); char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1]; for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) { sprintf(hash_buffer + (i * 2), "%02x", hash[i]); } hash_buffer[sizeof(hash_buffer) - 1] = 0; return std::string(hash_buffer); } static void SendBroadcast(const std::string& action, const std::vector& args) { // clang-format off std::vector am = {"/system/bin/cmd", "activity", "broadcast", "--user", "0", "--receiver-foreground", "--receiver-include-background", "-a", action}; // clang-format on am.insert(am.end(), args.begin(), args.end()); RunCommand("", am, CommandOptions::WithTimeout(20) .Log("Sending broadcast: '%s'\n") .Always() .DropRoot() .RedirectStderr() .Build()); } static void Vibrate(int duration_ms) { // clang-format off RunCommand("", {"cmd", "vibrator", "vibrate", std::to_string(duration_ms), "dumpstate"}, CommandOptions::WithTimeout(10) .Log("Vibrate: '%s'\n") .Always() .Build()); // clang-format on } static void MaybeResolveSymlink(std::string* path) { std::string resolved_path; if (android::base::Readlink(*path, &resolved_path)) { *path = resolved_path; } } /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter * if we are writing zip files and adds the version file. */ static void PrepareToWriteToFile() { MaybeResolveSymlink(&ds.bugreport_internal_dir_); std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str()); if (ds.options_->do_add_date) { char date[80]; strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); ds.name_ = date; } else { ds.name_ = "undated"; } if (ds.options_->telephony_only) { ds.base_name_ += "-telephony"; } else if (ds.options_->wifi_only) { ds.base_name_ += "-wifi"; } if (ds.options_->do_fb) { ds.screenshot_path_ = ds.GetPath(".png"); } ds.tmp_path_ = ds.GetPath(".tmp"); ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); std::string destination = ds.options_->bugreport_fd.get() != -1 ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get()) : ds.bugreport_internal_dir_.c_str(); MYLOGD( "Bugreport dir: %s\n" "Base name: %s\n" "Suffix: %s\n" "Log path: %s\n" "Temporary path: %s\n" "Screenshot path: %s\n", destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); if (ds.options_->do_zip_file) { ds.path_ = ds.GetPath(".zip"); MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); create_parent_dirs(ds.path_.c_str()); ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); if (ds.zip_file == nullptr) { MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); } else { ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); } ds.AddTextZipEntry("version.txt", ds.version_); } } /* * Finalizes writing to the file by renaming or zipping the tmp file to the final location, * printing zipped file status, etc. */ static void FinalizeFile() { /* check if user changed the suffix using system properties */ std::string name = android::base::GetProperty(android::base::StringPrintf("dumpstate.%d.name", ds.pid_), ""); bool change_suffix = false; if (!name.empty()) { /* must whitelist which characters are allowed, otherwise it could cross directories */ std::regex valid_regex("^[-_a-zA-Z0-9]+$"); if (std::regex_match(name.c_str(), valid_regex)) { change_suffix = true; } else { MYLOGE("invalid suffix provided by user: %s\n", name.c_str()); } } if (change_suffix) { MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str()); ds.name_ = name; if (!ds.screenshot_path_.empty()) { std::string new_screenshot_path = ds.GetPath(".png"); if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) { MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(), new_screenshot_path.c_str(), strerror(errno)); } else { ds.screenshot_path_ = new_screenshot_path; } } } bool do_text_file = true; if (ds.options_->do_zip_file) { if (!ds.FinishZipFile()) { MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); do_text_file = true; } else { do_text_file = false; // If the user has changed the suffix, we need to change the zip file name. std::string new_path = ds.GetPath(".zip"); if (ds.path_ != new_path) { MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str()); if (rename(ds.path_.c_str(), new_path.c_str())) { MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(), strerror(errno)); } else { ds.path_ = new_path; } } } } if (do_text_file) { ds.path_ = ds.GetPath(".txt"); MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(), ds.tmp_path_.c_str()); if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) { MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(), strerror(errno)); ds.path_.clear(); } } if (ds.options_->use_control_socket) { if (do_text_file) { dprintf(ds.control_socket_fd_, "FAIL:could not create zip file, check %s " "for more details\n", ds.log_path_.c_str()); } else { dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str()); } } } /* Broadcasts that we are done with the bugreport */ static void SendBugreportFinishedBroadcast() { // TODO(b/111441001): use callback instead of broadcast. if (!ds.path_.empty()) { MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); // clang-format off std::vector am_args = { "--receiver-permission", "android.permission.DUMP", "--ei", "android.intent.extra.ID", std::to_string(ds.id_), "--ei", "android.intent.extra.PID", std::to_string(ds.pid_), "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()), "--es", "android.intent.extra.BUGREPORT", ds.path_, "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ }; // clang-format on if (ds.options_->do_fb && !android::os::IsFileEmpty(ds.screenshot_path_)) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.SCREENSHOT"); am_args.push_back(ds.screenshot_path_); } if (!ds.options_->notification_title.empty()) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.TITLE"); am_args.push_back(ds.options_->notification_title); if (!ds.options_->notification_description.empty()) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.DESCRIPTION"); am_args.push_back(ds.options_->notification_description); } } if (ds.options_->is_remote_mode) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH"); am_args.push_back(SHA256_file_hash(ds.path_)); SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); } else { SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); } } else { MYLOGE("Skipping finished broadcast because bugreport could not be generated\n"); } } static inline const char* ModeToString(Dumpstate::BugreportMode mode) { switch (mode) { case Dumpstate::BugreportMode::BUGREPORT_FULL: return "BUGREPORT_FULL"; case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: return "BUGREPORT_INTERACTIVE"; case Dumpstate::BugreportMode::BUGREPORT_REMOTE: return "BUGREPORT_REMOTE"; case Dumpstate::BugreportMode::BUGREPORT_WEAR: return "BUGREPORT_WEAR"; case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: return "BUGREPORT_TELEPHONY"; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return "BUGREPORT_WIFI"; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return "BUGREPORT_DEFAULT"; } } static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) { options->extra_options = ModeToString(mode); switch (mode) { case Dumpstate::BugreportMode::BUGREPORT_FULL: options->do_broadcast = true; options->do_fb = true; break; case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: // Currently, the dumpstate binder is only used by Shell to update progress. options->do_start_service = true; options->do_progress_updates = true; options->do_fb = false; options->do_broadcast = true; break; case Dumpstate::BugreportMode::BUGREPORT_REMOTE: options->do_vibrate = false; options->is_remote_mode = true; options->do_fb = false; options->do_broadcast = true; break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: options->do_start_service = true; options->do_progress_updates = true; options->do_zip_file = true; options->do_fb = true; options->do_broadcast = true; break; case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; options->do_fb = false; options->do_broadcast = true; break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; options->do_zip_file = true; options->do_fb = false; options->do_broadcast = true; break; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: break; } } static Dumpstate::BugreportMode getBugreportModeFromProperty() { // If the system property is not set, it's assumed to be a default bugreport. Dumpstate::BugreportMode mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; std::string extra_options = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, ""); if (!extra_options.empty()) { // Framework uses a system property to override some command-line args. // Currently, it contains the type of the requested bugreport. if (extra_options == "bugreportplus") { mode = Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE; } else if (extra_options == "bugreportfull") { mode = Dumpstate::BugreportMode::BUGREPORT_FULL; } else if (extra_options == "bugreportremote") { mode = Dumpstate::BugreportMode::BUGREPORT_REMOTE; } else if (extra_options == "bugreportwear") { mode = Dumpstate::BugreportMode::BUGREPORT_WEAR; } else if (extra_options == "bugreporttelephony") { mode = Dumpstate::BugreportMode::BUGREPORT_TELEPHONY; } else if (extra_options == "bugreportwifi") { mode = Dumpstate::BugreportMode::BUGREPORT_WIFI; } else { MYLOGE("Unknown extra option: %s\n", extra_options.c_str()); } // Reset the property android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, ""); } return mode; } // TODO: Move away from system properties when we have options passed via binder calls. /* Sets runtime options from the system properties and then clears those properties. */ static void SetOptionsFromProperties(Dumpstate::DumpOptions* options) { Dumpstate::BugreportMode mode = getBugreportModeFromProperty(); SetOptionsFromMode(mode, options); options->notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, ""); if (!options->notification_title.empty()) { // Reset the property android::base::SetProperty(PROPERTY_EXTRA_TITLE, ""); options->notification_description = android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); if (!options->notification_description.empty()) { // Reset the property android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); } MYLOGD("notification (title: %s, description: %s)\n", options->notification_title.c_str(), options->notification_description.c_str()); } } static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI("do_zip_file: %d\n", options.do_zip_file); MYLOGI("do_add_date: %d\n", options.do_add_date); MYLOGI("do_vibrate: %d\n", options.do_vibrate); MYLOGI("use_socket: %d\n", options.use_socket); MYLOGI("use_control_socket: %d\n", options.use_control_socket); MYLOGI("do_fb: %d\n", options.do_fb); MYLOGI("do_broadcast: %d\n", options.do_broadcast); MYLOGI("is_remote_mode: %d\n", options.is_remote_mode); MYLOGI("show_header_only: %d\n", options.show_header_only); MYLOGI("do_start_service: %d\n", options.do_start_service); MYLOGI("telephony_only: %d\n", options.telephony_only); MYLOGI("wifi_only: %d\n", options.wifi_only); MYLOGI("do_progress_updates: %d\n", options.do_progress_updates); MYLOGI("fd: %d\n", options.bugreport_fd.get()); MYLOGI("extra_options: %s\n", options.extra_options.c_str()); MYLOGI("args: %s\n", options.args.c_str()); MYLOGI("notification_title: %s\n", options.notification_title.c_str()); MYLOGI("notification_description: %s\n", options.notification_description.c_str()); } void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd_in, const android::base::unique_fd& screenshot_fd_in) { // In the new API world, date is always added; output is always a zip file. // TODO(111441001): remove these options once they are obsolete. do_add_date = true; do_zip_file = true; // Duplicate the fds because the passed in fds don't outlive the binder transaction. bugreport_fd.reset(dup(bugreport_fd_in.get())); screenshot_fd.reset(dup(screenshot_fd_in.get())); extra_options = ModeToString(bugreport_mode); SetOptionsFromMode(bugreport_mode, this); } Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) { RunStatus status = RunStatus::OK; int c; while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) { switch (c) { // clang-format off case 'd': do_add_date = true; break; case 'z': do_zip_file = true; break; // o=use_outfile not supported anymore. // TODO(b/111441001): Remove when all callers have migrated. case 'o': break; case 's': use_socket = true; break; case 'S': use_control_socket = true; break; case 'v': show_header_only = true; break; case 'q': do_vibrate = false; break; case 'p': do_fb = true; break; case 'P': do_progress_updates = true; break; case 'R': is_remote_mode = true; break; case 'B': do_broadcast = true; break; case 'V': break; // compatibility no-op case 'w': // This was already processed break; case 'h': status = RunStatus::HELP; break; default: fprintf(stderr, "Invalid option: %c\n", c); status = RunStatus::INVALID_INPUT; break; // clang-format on } } // TODO: use helper function to convert argv into a string for (int i = 0; i < argc; i++) { args += argv[i]; if (i < argc - 1) { args += " "; } } // Reset next index used by getopt so this can be called multiple times, for eg, in tests. optind = 1; SetOptionsFromProperties(this); return status; } bool Dumpstate::DumpOptions::ValidateOptions() const { if (bugreport_fd.get() != -1 && !do_zip_file) { return false; } if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !OutputToFile()) { return false; } if (use_control_socket && !do_zip_file) { return false; } if (do_progress_updates && !do_broadcast) { return false; } if (is_remote_mode && (do_progress_updates || !do_broadcast || !do_zip_file || !do_add_date)) { return false; } return true; } void Dumpstate::SetOptions(std::unique_ptr options) { options_ = std::move(options); } Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) { Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package); if (listener_ != nullptr) { switch (status) { case Dumpstate::RunStatus::OK: listener_->onFinished(); break; case Dumpstate::RunStatus::HELP: break; case Dumpstate::RunStatus::INVALID_INPUT: listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); break; case Dumpstate::RunStatus::ERROR: listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR); break; case Dumpstate::RunStatus::USER_CONSENT_DENIED: listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT); break; case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT: listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); break; } } return status; } /* * Dumps relevant information to a bugreport based on the given options. * * The bugreport can be dumped to a file or streamed to a socket. * * How dumping to file works: * stdout is redirected to a temporary file. This will later become the main bugreport entry. * stderr is redirected a log file. * * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final * text file. * * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also * gets added to the archive. * * Bugreports are first generated in a local directory and later copied to the caller's fd if * supplied. */ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, const std::string& calling_package) { LogDumpOptions(*options_); if (!options_->ValidateOptions()) { MYLOGE("Invalid options specified\n"); return RunStatus::INVALID_INPUT; } /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we"); if (oom_adj) { fputs("-1000", oom_adj); fclose(oom_adj); } else { /* fallback to kernels <= 2.6.35 */ oom_adj = fopen("/proc/self/oom_adj", "we"); if (oom_adj) { fputs("-17", oom_adj); fclose(oom_adj); } } if (version_ == VERSION_DEFAULT) { version_ = VERSION_CURRENT; } if (version_ != VERSION_CURRENT && version_ != VERSION_SPLIT_ANR) { MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n", version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(), VERSION_SPLIT_ANR.c_str()); return RunStatus::INVALID_INPUT; } if (options_->show_header_only) { PrintHeader(); return RunStatus::OK; } if (options_->bugreport_fd.get() != -1) { // If the output needs to be copied over to the caller's fd, get user consent. android::String16 package(calling_package.c_str()); CheckUserConsent(calling_uid, package); } // Redirect output if needed bool is_redirecting = options_->OutputToFile(); // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) : ""; progress_.reset(new Progress(stats_path)); /* gets the sequential id */ uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0); id_ = ++last_id; android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id)); MYLOGI("begin\n"); register_sig_handler(); // TODO(b/111441001): maybe skip if already started? if (options_->do_start_service) { MYLOGI("Starting 'dumpstate' service\n"); android::status_t ret; if ((ret = android::os::DumpstateService::Start()) != android::OK) { MYLOGE("Unable to start DumpstateService: %d\n", ret); } } if (PropertiesHelper::IsDryRun()) { MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n"); } MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", id_, options_->args.c_str(), options_->extra_options.c_str()); MYLOGI("bugreport format version: %s\n", version_.c_str()); do_early_screenshot_ = options_->do_progress_updates; // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. if (options_->use_socket) { if (!redirect_to_socket(stdout, "dumpstate")) { return ERROR; } } if (options_->use_control_socket) { MYLOGD("Opening control socket\n"); control_socket_fd_ = open_socket("dumpstate"); if (control_socket_fd_ == -1) { return ERROR; } options_->do_progress_updates = 1; } if (is_redirecting) { PrepareToWriteToFile(); if (options_->do_progress_updates) { if (options_->do_broadcast) { // clang-format off std::vector am_args = { "--receiver-permission", "android.permission.DUMP", "--es", "android.intent.extra.NAME", name_, "--ei", "android.intent.extra.ID", std::to_string(id_), "--ei", "android.intent.extra.PID", std::to_string(pid_), "--ei", "android.intent.extra.MAX", std::to_string(progress_->GetMax()), }; // clang-format on SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); } if (options_->use_control_socket) { dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); } } } /* read /proc/cmdline before dropping root */ FILE *cmdline = fopen("/proc/cmdline", "re"); if (cmdline) { fgets(cmdline_buf, sizeof(cmdline_buf), cmdline); fclose(cmdline); } if (options_->do_vibrate) { Vibrate(150); } if (options_->do_fb && do_early_screenshot_) { if (screenshot_path_.empty()) { // should not have happened MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n"); } else { MYLOGI("taking early screenshot\n"); TakeScreenshot(); } } if (options_->do_zip_file && zip_file != nullptr) { if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(), strerror(errno)); } } int dup_stdout_fd; int dup_stderr_fd; if (is_redirecting) { // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); if (!redirect_to_file(stderr, const_cast(log_path_.c_str()))) { return ERROR; } if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), strerror(errno)); } // Redirect stdout to tmp_path_. This is the main bugreport entry and will be // moved into zip file later, if zipping. TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout))); // TODO: why not write to a file instead of stdout to overcome this problem? /* TODO: rather than generating a text file now and zipping it later, it would be more efficient to redirect stdout to the zip entry directly, but the libziparchive doesn't support that option yet. */ if (!redirect_to_file(stdout, const_cast(tmp_path_.c_str()))) { return ERROR; } if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", tmp_path_.c_str(), strerror(errno)); } } // Don't buffer stdout setvbuf(stdout, nullptr, _IONBF, 0); // NOTE: there should be no stdout output until now, otherwise it would break the header. // In particular, DurationReport objects should be created passing 'title, NULL', so their // duration is logged into MYLOG instead. PrintHeader(); if (options_->telephony_only) { DumpstateTelephonyOnly(); DumpstateBoard(); } else if (options_->wifi_only) { DumpstateWifiOnly(); } else { // Dump state for the default case. This also drops root. RunStatus s = DumpstateDefault(); if (s != RunStatus::OK) { if (s == RunStatus::USER_CONSENT_TIMED_OUT) { HandleUserConsentDenied(); } return s; } } /* close output if needed */ if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); } // Rename, and/or zip the (now complete) .tmp file within the internal directory. if (options_->OutputToFile()) { FinalizeFile(); } // Share the final file with the caller if the user has consented. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; if (options_->bugreport_fd.get() != -1) { status = CopyBugreportIfUserConsented(); if (status != Dumpstate::RunStatus::OK && status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) { // Do an early return if there were errors. We make an exception for consent // timing out because it's possible the user got distracted. In this case the // bugreport is not shared but made available for manual retrieval. MYLOGI("User denied consent. Returning\n"); return status; } if (options_->do_fb && options_->screenshot_fd.get() != -1) { bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_, options_->screenshot_fd.get()); if (copy_succeeded) { android::os::UnlinkAndLogOnError(screenshot_path_); } } if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) { MYLOGI( "Did not receive user consent yet." " Will not copy the bugreport artifacts to caller.\n"); const String16 incidentcompanion("incidentcompanion"); sp ics(defaultServiceManager()->getService(incidentcompanion)); if (ics != nullptr) { MYLOGD("Canceling user consent request via incidentcompanion service\n"); android::interface_cast(ics)->cancelAuthorization( consent_callback_.get()); } else { MYLOGD("Unable to cancel user consent; incidentcompanion service unavailable\n"); } } } /* vibrate a few but shortly times to let user know it's finished */ if (options_->do_vibrate) { for (int i = 0; i < 3; i++) { Vibrate(75); usleep((75 + 50) * 1000); } } /* tell activity manager we're done */ if (options_->do_broadcast) { SendBugreportFinishedBroadcast(); // Note that listener_ is notified in Run(); } MYLOGD("Final progress: %d/%d (estimated %d)\n", progress_->Get(), progress_->GetMax(), progress_->GetInitialMax()); progress_->Save(); MYLOGI("done (id %d)\n", id_); if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); } if (options_->use_control_socket && control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); close(control_socket_fd_); } tombstone_data_.clear(); anr_data_.clear(); return (consent_callback_ != nullptr && consent_callback_->getResult() == UserConsentResult::UNAVAILABLE) ? USER_CONSENT_TIMED_OUT : RunStatus::OK; } void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) { consent_callback_ = new ConsentCallback(); const String16 incidentcompanion("incidentcompanion"); sp ics(defaultServiceManager()->getService(incidentcompanion)); if (ics != nullptr) { MYLOGD("Checking user consent via incidentcompanion service\n"); android::interface_cast(ics)->authorizeReport( calling_uid, calling_package, String16(), String16(), 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get()); } else { MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n"); } } bool Dumpstate::IsUserConsentDenied() const { return ds.consent_callback_ != nullptr && ds.consent_callback_->getResult() == UserConsentResult::DENIED; } void Dumpstate::CleanupFiles() { android::os::UnlinkAndLogOnError(tmp_path_); android::os::UnlinkAndLogOnError(screenshot_path_); android::os::UnlinkAndLogOnError(path_); } Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() { MYLOGD("User denied consent; deleting files and returning\n"); CleanupFiles(); return USER_CONSENT_DENIED; } Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { // If the caller has asked to copy the bugreport over to their directory, we need explicit // user consent. UserConsentResult consent_result = consent_callback_->getResult(); if (consent_result == UserConsentResult::UNAVAILABLE) { // User has not responded yet. uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs(); if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) { uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000; MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds); sleep(delay_seconds); } consent_result = consent_callback_->getResult(); } if (consent_result == UserConsentResult::DENIED) { // User has explicitly denied sharing with the app. To be safe delete the // internal bugreport & tmp files. return HandleUserConsentDenied(); } if (consent_result == UserConsentResult::APPROVED) { bool copy_succeeded = android::os::CopyFileToFd(path_, options_->bugreport_fd.get()); if (copy_succeeded) { android::os::UnlinkAndLogOnError(path_); } return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR; } else if (consent_result == UserConsentResult::UNAVAILABLE) { // consent_result is still UNAVAILABLE. The user has likely not responded yet. // Since we do not have user consent to share the bugreport it does not get // copied over to the calling app but remains in the internal directory from // where the user can manually pull it. return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT; } // Unknown result; must be a programming error. MYLOGE("Unknown user consent result:%d\n", consent_result); return Dumpstate::RunStatus::ERROR; } Dumpstate::RunStatus Dumpstate::ParseCommandlineAndRun(int argc, char* argv[]) { std::unique_ptr options = std::make_unique(); Dumpstate::RunStatus status = options->Initialize(argc, argv); if (status == Dumpstate::RunStatus::OK) { SetOptions(std::move(options)); // When directly running dumpstate binary, the output is not expected to be written // to any external file descriptor. assert(options_->bugreport_fd.get() == -1); // calling_uid and calling_package are for user consent to share the bugreport with // an app; they are irrelvant here because bugreport is only written to a local // directory, and not shared. status = Run(-1 /* calling_uid */, "" /* calling_package */); } return status; } /* Main entry point for dumpstate binary. */ int run_main(int argc, char* argv[]) { Dumpstate::RunStatus status = ds.ParseCommandlineAndRun(argc, argv); switch (status) { case Dumpstate::RunStatus::OK: exit(0); case Dumpstate::RunStatus::HELP: ShowUsage(); exit(0); case Dumpstate::RunStatus::INVALID_INPUT: fprintf(stderr, "Invalid combination of args\n"); ShowUsage(); exit(1); case Dumpstate::RunStatus::ERROR: FALLTHROUGH_INTENDED; case Dumpstate::RunStatus::USER_CONSENT_DENIED: FALLTHROUGH_INTENDED; case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT: exit(2); } } cmds/dumpstate/dumpstate.h0100644 0000000 0000000 00000047413 13756501734 014744 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ #define FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DumpstateUtil.h" // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to // std::vector // TODO: remove once not used #define MAX_ARGS_ARRAY_SIZE 1000 // TODO: move everything under this namespace // TODO: and then remove explicitly android::os::dumpstate:: prefixes namespace android { namespace os { struct DumpstateOptions; namespace dumpstate { class DumpstateTest; class ProgressTest; } // namespace dumpstate } // namespace os } // namespace android class ZipWriter; // TODO: remove once moved to HAL #ifdef __cplusplus extern "C" { #endif /* * Helper class used to report how long it takes for a section to finish. * * Typical usage: * * DurationReporter duration_reporter(title); * */ class DurationReporter { public: explicit DurationReporter(const std::string& title, bool logcat_only = false); ~DurationReporter(); private: std::string title_; bool logcat_only_; uint64_t started_; DISALLOW_COPY_AND_ASSIGN(DurationReporter); }; /* * Keeps track of current progress and estimated max, saving stats on file to tune up future runs. * * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall * progress can be calculated by dividing the estimate max progress by the current progress. * * The estimated max progress is initially set to a value (`kDefaultMax) defined empirically, but * it's adjusted after each dumpstate run by storing the average duration in a file. * */ class Progress { friend class android::os::dumpstate::ProgressTest; friend class android::os::dumpstate::DumpstateTest; public: /* * Default estimation of the max duration of a bugreport generation. * * It does not need to match the exact sum of all sections, but ideally it should to be slight * more than such sum: a value too high will cause the bugreport to finish before the user * expected (for example, jumping from 70% to 100%), while a value too low will cause the * progress to get stuck at an almost-finished value (like 99%) for a while. * * This constant is only used when the average duration from previous runs cannot be used. */ static const int kDefaultMax; explicit Progress(const std::string& path = ""); // Gets the current progress. int32_t Get() const; // Gets the current estimated max progress. int32_t GetMax() const; // Gets the initial estimated max progress. int32_t GetInitialMax() const; // Increments progress (ignored if not positive). // Returns `true` if the max progress increased as well. bool Inc(int32_t delta); // Persist the stats. void Save(); void Dump(int fd, const std::string& prefix) const; private: Progress(int32_t initial_max, float growth_factor, const std::string& path = ""); // Used by test cases. Progress(int32_t initial_max, int32_t progress, float growth_factor); // Used by test cases. void Load(); int32_t initial_max_; int32_t progress_; int32_t max_; float growth_factor_; int32_t n_runs_; int32_t average_max_; std::string path_; }; /* * List of supported zip format versions. * * See bugreport-format.md for more info. */ static std::string VERSION_CURRENT = "2.0"; /* * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version * will be bumped to 3.0. */ static std::string VERSION_SPLIT_ANR = "3.0-dev-split-anr"; /* * "Alias" for the current version. */ static std::string VERSION_DEFAULT = "default"; /* * Directory used by Dumpstate binary to keep its local files. */ static const std::string DUMPSTATE_DIRECTORY = "/bugreports"; /* * Structure that contains the information of an open dump file. */ struct DumpData { // Path of the file. std::string name; // Open file descriptor for the file. android::base::unique_fd fd; // Modification time of the file. time_t mtime; }; /* * Main class driving a bugreport generation. * * Currently, it only contains variables that are accessed externally, but gradually the functions * that are spread accross utils.cpp and dumpstate.cpp will be moved to it. */ class Dumpstate { friend class DumpstateTest; public: enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT }; // The mode under which the bugreport should be run. Each mode encapsulates a few options. enum BugreportMode { BUGREPORT_FULL = android::os::IDumpstate::BUGREPORT_MODE_FULL, BUGREPORT_INTERACTIVE = android::os::IDumpstate::BUGREPORT_MODE_INTERACTIVE, BUGREPORT_REMOTE = android::os::IDumpstate::BUGREPORT_MODE_REMOTE, BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR, BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY, BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI, BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT }; static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS; static Dumpstate& GetInstance(); /* Checkes whether dumpstate is generating a zipped bugreport. */ bool IsZipping() const; /* * Forks a command, waits for it to finish, and returns its status. * * |title| description of the command printed on `stdout` (or empty to skip * description). * |full_command| array containing the command (first entry) and its arguments. * Must contain at least one element. * |options| optional argument defining the command's behavior. */ int RunCommand(const std::string& title, const std::vector& fullCommand, const android::os::dumpstate::CommandOptions& options = android::os::dumpstate::CommandOptions::DEFAULT); /* * Runs `dumpsys` with the given arguments, automatically setting its timeout * (`-T` argument) * according to the command options. * * |title| description of the command printed on `stdout` (or empty to skip * description). * |dumpsys_args| `dumpsys` arguments (except `-t`). * |options| optional argument defining the command's behavior. * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the * timeout from `options`) */ void RunDumpsys(const std::string& title, const std::vector& dumpsys_args, const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS, long dumpsys_timeout_ms = 0); /* * Prints the contents of a file. * * |title| description of the command printed on `stdout` (or empty to skip * description). * |path| location of the file to be dumped. */ int DumpFile(const std::string& title, const std::string& path); /* * Adds a new entry to the existing zip file. * */ bool AddZipEntry(const std::string& entry_name, const std::string& entry_path); /* * Adds a new entry to the existing zip file. * * |entry_name| destination path of the new entry. * |fd| file descriptor to read from. * |timeout| timeout to terminate the read if not completed. Set * value of 0s (default) to disable timeout. */ android::status_t AddZipEntryFromFd(const std::string& entry_name, int fd, std::chrono::milliseconds timeout); /* * Adds a text entry to the existing zip file. */ bool AddTextZipEntry(const std::string& entry_name, const std::string& content); /* * Adds all files from a directory to the zipped bugreport file. */ void AddDir(const std::string& dir, bool recursive); /* * Takes a screenshot and save it to the given `path`. * * If `path` is empty, uses a standard path based on the bugreport name. */ void TakeScreenshot(const std::string& path = ""); ///////////////////////////////////////////////////////////////////// // TODO: members below should be private once refactor is finished // ///////////////////////////////////////////////////////////////////// // TODO: temporary method until Dumpstate object is properly set void SetProgress(std::unique_ptr progress); // Dumps Dalvik and native stack traces, sets the trace file location to path // if it succeeded. // Note that it returns early if user consent is denied with status USER_CONSENT_DENIED. // Returns OK in all other cases. RunStatus DumpTraces(const char** path); void DumpstateBoard(); /* * Updates the overall progress of the bugreport generation by the given weight increment. */ void UpdateProgress(int32_t delta); /* Prints the dumpstate header on `stdout`. */ void PrintHeader() const; /* * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the * temporary file. */ bool FinishZipFile(); /* Constructs a full path inside directory with file name formatted using the given suffix. */ std::string GetPath(const std::string& directory, const std::string& suffix) const; /* Constructs a full path inside bugreport_internal_dir_ with file name formatted using the * given suffix. */ std::string GetPath(const std::string& suffix) const; /* Returns true if the current version supports priority dump feature. */ bool CurrentVersionSupportsPriorityDumps() const; struct DumpOptions; /* Main entry point for running a complete bugreport. */ RunStatus Run(int32_t calling_uid, const std::string& calling_package); RunStatus ParseCommandlineAndRun(int argc, char* argv[]); /* Sets runtime options. */ void SetOptions(std::unique_ptr options); /* * Returns true if user consent is necessary and has been denied. * Consent is only necessary if the caller has asked to copy over the bugreport to a file they * provided. */ bool IsUserConsentDenied() const; /* * Structure to hold options that determine the behavior of dumpstate. */ struct DumpOptions { bool do_add_date = false; bool do_zip_file = false; bool do_vibrate = true; // Writes bugreport content to a socket; only flatfile format is supported. bool use_socket = false; bool use_control_socket = false; bool do_fb = false; bool do_broadcast = false; bool is_remote_mode = false; bool show_header_only = false; bool do_start_service = false; bool telephony_only = false; bool wifi_only = false; // Whether progress updates should be published. bool do_progress_updates = false; // File descriptor to output zip file. android::base::unique_fd bugreport_fd; // File descriptor to screenshot file. android::base::unique_fd screenshot_fd; // TODO: rename to MODE. // Extra options passed as system property. std::string extra_options; // Command-line arguments as string std::string args; // Notification title and description std::string notification_title; std::string notification_description; /* Initializes options from commandline arguments and system properties. */ RunStatus Initialize(int argc, char* argv[]); /* Initializes options from the requested mode. */ void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd, const android::base::unique_fd& screenshot_fd); /* Returns true if the options set so far are consistent. */ bool ValidateOptions() const; /* Returns if options specified require writing bugreport to a file */ bool OutputToFile() const { // If we are not writing to socket, we will write to a file. If bugreport_fd is // specified, it is preferred. If not bugreport is written to /bugreports. return !use_socket; } }; // TODO: initialize fields on constructor // dumpstate id - unique after each device reboot. uint32_t id_; // dumpstate pid pid_t pid_; // Runtime options. std::unique_ptr options_; // How frequently the progess should be updated;the listener will only be notificated when the // delta from the previous update is more than the threshold. int32_t update_progress_threshold_ = 100; // Last progress that triggered a listener updated int32_t last_updated_progress_; // Whether it should take an screenshot earlier in the process. bool do_early_screenshot_ = false; std::unique_ptr progress_; // When set, defines a socket file-descriptor use to report progress to bugreportz. int control_socket_fd_ = -1; // Bugreport format version; std::string version_ = VERSION_CURRENT; time_t now_; // Base name (without suffix or extensions) of the bugreport files, typically // `bugreport-BUILD_ID`. std::string base_name_; // Name is the suffix part of the bugreport files - it's typically the date (when invoked with // `-d`), but it could be changed by the user.. std::string name_; std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; // Full path of the temporary file containing the bugreport, inside bugreport_internal_dir_. // At the very end this file is pulled into the zip file. std::string tmp_path_; // Full path of the file containing the dumpstate logs, inside bugreport_internal_dir_. // This is useful for debugging. std::string log_path_; // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_. std::string path_; // TODO: If temporary this should be removed at the end. // Full path of the temporary file containing the screenshot (when requested). std::string screenshot_path_; // Pointer to the zipped file. std::unique_ptr zip_file{nullptr, fclose}; // Pointer to the zip structure. std::unique_ptr zip_writer_; // Binder object listening to progress. android::sp listener_; std::string listener_name_; bool report_section_; // List of open tombstone dump files. std::vector tombstone_data_; // List of open ANR dump files. std::vector anr_data_; // A callback to IncidentCompanion service, which checks user consent for sharing the // bugreport with the calling app. If the user has not responded yet to the dialog it will // be neither confirmed nor denied. class ConsentCallback : public android::os::BnIncidentAuthListener { public: ConsentCallback(); android::binder::Status onReportApproved() override; android::binder::Status onReportDenied() override; enum ConsentResult { APPROVED, DENIED, UNAVAILABLE }; ConsentResult getResult(); // Returns the time since creating this listener uint64_t getElapsedTimeMs() const; private: ConsentResult result_; uint64_t start_time_; std::mutex lock_; }; private: RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package); void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package); // Removes the in progress files output files (tmp file, zip/txt file, screenshot), // but leaves the log file alone. void CleanupFiles(); RunStatus HandleUserConsentDenied(); // Copies bugreport artifacts over to the caller's directories provided there is user consent. RunStatus CopyBugreportIfUserConsented(); // Used by GetInstance() only. explicit Dumpstate(const std::string& version = VERSION_CURRENT); android::sp consent_callback_; DISALLOW_COPY_AND_ASSIGN(Dumpstate); }; // for_each_pid_func = void (*)(int, const char*); // for_each_tid_func = void (*)(int, int, const char*); typedef void(for_each_pid_func)(int, const char*); typedef void(for_each_tid_func)(int, int, const char*); /* saves the the contents of a file as a long */ int read_file_as_long(const char *path, long int *output); /* prints the contents of the fd * fd must have been opened with the flag O_NONBLOCK. */ int dump_file_from_fd(const char *title, const char *path, int fd); /* calls skip to gate calling dump_from_fd recursively * in the specified directory. dump_from_fd defaults to * dump_file_from_fd above when set to NULL. skip defaults * to false when set to NULL. dump_from_fd will always be * called with title NULL. */ int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), int (*dump_from_fd)(const char* title, const char* path, int fd)); /** opens a socket and returns its file descriptor */ int open_socket(const char *service); /* * Redirects 'redirect' to a service control socket. * * Returns true if redirect succeeds. */ bool redirect_to_socket(FILE* redirect, const char* service); /* * Redirects 'redirect' to a file indicated by 'path', truncating it. * * Returns true if redirect succeeds. */ bool redirect_to_file(FILE* redirect, char* path); /* * Redirects 'redirect' to an existing file indicated by 'path', appending it. * * Returns true if redirect succeeds. */ bool redirect_to_existing_file(FILE* redirect, char* path); /* create leading directories, if necessary */ void create_parent_dirs(const char *path); /* for each process in the system, run the specified function */ void for_each_pid(for_each_pid_func func, const char *header); /* for each thread in the system, run the specified function */ void for_each_tid(for_each_tid_func func, const char *header); /* Displays a blocked processes in-kernel wait channel */ void show_wchan(int pid, int tid, const char *name); /* Displays a processes times */ void show_showtime(int pid, const char *name); /* Runs "showmap" for a process */ void do_showmap(int pid, const char *name); /* Gets the dmesg output for the kernel */ void do_dmesg(); /* Prints the contents of all the routing tables, both IPv4 and IPv6. */ void dump_route_tables(); /* Play a sound via Stagefright */ void play_sound(const char *path); /* Checks if a given path is a directory. */ bool is_dir(const char* pathname); /** Gets the last modification time of a file, or default time if file is not found. */ time_t get_mtime(int fd, time_t default_mtime); /* Dumps eMMC Extended CSD data. */ void dump_emmc_ecsd(const char *ext_csd_path); /** Gets command-line arguments. */ void format_args(int argc, const char *argv[], std::string *args); /** Main entry point for dumpstate. */ int run_main(int argc, char* argv[]); #ifdef __cplusplus } #endif #endif /* FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ */ cmds/dumpstate/dumpstate.rc0100644 0000000 0000000 00000001437 13756501734 015115 0ustar000000000 0000000 on boot # Allow bugreports access to eMMC 5.0 stats chown root mount /sys/kernel/debug/mmc0/mmc0:0001/ext_csd chmod 0440 /sys/kernel/debug/mmc0/mmc0:0001/ext_csd service dumpstate /system/bin/dumpstate -s class main socket dumpstate stream 0660 shell log disabled oneshot # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once # it is finished. service dumpstatez /system/bin/dumpstate -S -d -z \ -o /data/user_de/0/com.android.shell/files/bugreports/bugreport socket dumpstate stream 0660 shell log class main disabled oneshot # bugreportd starts dumpstate binder service and makes it wait for a listener to connect. service bugreportd /system/bin/dumpstate -w class main disabled oneshot cmds/dumpstate/main.cpp0100644 0000000 0000000 00000004116 13756501734 014206 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #define LOG_TAG "dumpstate" #include #include "DumpstateInternal.h" #include "DumpstateService.h" #include "dumpstate.h" namespace { // Returns true if we should start the service and wait for a listener // to bind with bugreport options. bool ShouldStartServiceAndWait(int argc, char* argv[]) { bool do_wait = false; int c; // Keep flags in sync with Dumpstate::DumpOptions::Initialize. while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) { switch (c) { case 'w': do_wait = true; break; default: // Ignore all other options break; } } // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to // parse bugreport options. optind = 1; return do_wait; } } // namespace int main(int argc, char* argv[]) { if (ShouldStartServiceAndWait(argc, argv)) { int ret; if ((ret = android::os::DumpstateService::Start()) != android::OK) { MYLOGE("Unable to start 'dumpstate' service: %d", ret); exit(1); } MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()"); // Waits forever for an incoming connection. // TODO(b/111441001): should this time out? android::IPCThreadState::self()->joinThreadPool(); return 0; } else { return run_main(argc, argv); } } cmds/dumpstate/tests/0040755 0000000 0000000 00000000000 13756501734 013721 5ustar000000000 0000000 cmds/dumpstate/tests/dumpstate_smoke_test.cpp0100644 0000000 0000000 00000043075 13756501734 020676 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "dumpstate.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) namespace android { namespace os { namespace dumpstate { using ::testing::Test; using ::std::literals::chrono_literals::operator""s; using android::base::unique_fd; class DumpstateListener; namespace { sp GetDumpstateService() { return android::interface_cast( android::defaultServiceManager()->getService(String16("dumpstate"))); } int OpenForWrite(const std::string& filename) { return TEMP_FAILURE_RETRY(open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); } } // namespace struct SectionInfo { std::string name; status_t status; int32_t size_bytes; int32_t duration_ms; }; /** * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the * section details generated by dumpstate are added to a vector to be used by Tests later. */ class DumpstateListener : public BnDumpstateListener { public: DumpstateListener(int fd, std::shared_ptr> sections) : out_fd_(fd), sections_(sections) { } DumpstateListener(int fd) : out_fd_(fd) { } binder::Status onProgress(int32_t progress) override { dprintf(out_fd_, "\rIn progress %d", progress); return binder::Status::ok(); } binder::Status onError(int32_t error_code) override { std::lock_guard lock(lock_); error_code_ = error_code; dprintf(out_fd_, "\rError code %d", error_code); return binder::Status::ok(); } binder::Status onFinished() override { std::lock_guard lock(lock_); is_finished_ = true; dprintf(out_fd_, "\rFinished"); return binder::Status::ok(); } binder::Status onProgressUpdated(int32_t progress) override { dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_); return binder::Status::ok(); } binder::Status onMaxProgressUpdated(int32_t max_progress) override { std::lock_guard lock(lock_); max_progress_ = max_progress; return binder::Status::ok(); } binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, int32_t duration_ms) override { std::lock_guard lock(lock_); if (sections_.get() != nullptr) { sections_->push_back({name, status, size_bytes, duration_ms}); } return binder::Status::ok(); } bool getIsFinished() { std::lock_guard lock(lock_); return is_finished_; } int getErrorCode() { std::lock_guard lock(lock_); return error_code_; } private: int out_fd_; int max_progress_ = 5000; int error_code_ = -1; bool is_finished_ = false; std::shared_ptr> sections_; std::mutex lock_; }; /** * Generates bug report and provide access to the bug report file and other info for other tests. * Since bug report generation is slow, the bugreport is only generated once. */ class ZippedBugreportGenerationTest : public Test { public: static std::shared_ptr> sections; static Dumpstate& ds; static std::chrono::milliseconds duration; static void SetUpTestCase() { property_set("dumpstate.options", "bugreportplus"); // clang-format off char* argv[] = { (char*)"dumpstate", (char*)"-d", (char*)"-z", (char*)"-B", (char*)"-o", (char*)dirname(android::base::GetExecutablePath().c_str()) }; // clang-format on sp listener(new DumpstateListener(dup(fileno(stdout)), sections)); ds.listener_ = listener; ds.listener_name_ = "Smokey"; ds.report_section_ = true; auto start = std::chrono::steady_clock::now(); ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv); auto end = std::chrono::steady_clock::now(); duration = std::chrono::duration_cast(end - start); } static const char* getZipFilePath() { return ds.GetPath(".zip").c_str(); } }; std::shared_ptr> ZippedBugreportGenerationTest::sections = std::make_shared>(); Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance(); std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s; TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) { EXPECT_EQ(access(getZipFilePath(), F_OK), 0); } TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) { struct stat st; EXPECT_EQ(stat(getZipFilePath(), &st), 0); EXPECT_GE(st.st_size, 3000000 /* 3MB */); EXPECT_LE(st.st_size, 30000000 /* 30MB */); } TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) { EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time " << duration.count() << " s."; EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time " << duration.count() << " s."; } /** * Run tests on contents of zipped bug report. */ class ZippedBugReportContentsTest : public Test { public: ZipArchiveHandle handle; void SetUp() { ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0); } void TearDown() { CloseArchive(handle); } void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) { ZipEntry entry; EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0); EXPECT_GT(entry.uncompressed_length, minsize); EXPECT_LT(entry.uncompressed_length, maxsize); } }; TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) { ZipEntry mainEntryLoc; // contains main entry name file EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0); char* buf = new char[mainEntryLoc.uncompressed_length]; ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length); delete[] buf; // contains main entry file FileExists(buf, 1000000U, 50000000U); } TEST_F(ZippedBugReportContentsTest, ContainsVersion) { ZipEntry entry; // contains main entry name file EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0); char* buf = new char[entry.uncompressed_length + 1]; ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length); buf[entry.uncompressed_length] = 0; EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str()); delete[] buf; } TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) { FileExists("dumpstate_board.bin", 1000000U, 80000000U); FileExists("dumpstate_board.txt", 100000U, 1000000U); } // Spot check on some files pulled from the file system TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { // FS/proc/*/mountinfo size > 0 FileExists("FS/proc/1/mountinfo", 0U, 100000U); // FS/data/misc/profiles/cur/0/*/primary.prof size > 0 FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U); } /** * Runs tests on section data generated by dumpstate and captured by DumpstateListener. */ class BugreportSectionTest : public Test { public: int numMatches(const std::string& substring) { int matches = 0; for (auto const& section : *ZippedBugreportGenerationTest::sections) { if (section.name.find(substring) != std::string::npos) { matches++; } } return matches; } void SectionExists(const std::string& sectionName, int minsize) { for (auto const& section : *ZippedBugreportGenerationTest::sections) { if (sectionName == section.name) { EXPECT_GE(section.size_bytes, minsize); return; } } FAIL() << sectionName << " not found."; } }; // Test all sections are generated without timeouts or errors TEST_F(BugreportSectionTest, GeneratedWithoutErrors) { for (auto const& section : *ZippedBugreportGenerationTest::sections) { EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status; } } TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { int numSections = numMatches("DUMPSYS CRITICAL"); EXPECT_GE(numSections, 3); } TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) { int numSections = numMatches("DUMPSYS HIGH"); EXPECT_GE(numSections, 2); } TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) { int allSections = numMatches("DUMPSYS"); int criticalSections = numMatches("DUMPSYS CRITICAL"); int highSections = numMatches("DUMPSYS HIGH"); int normalSections = allSections - criticalSections - highSections; EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections << "High:" << highSections << "Normal:" << normalSections << ")"; } TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) { int numSections = numMatches("proto/"); EXPECT_GE(numSections, 1); } // Test if some critical sections are being generated. TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) { SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000); } TEST_F(BugreportSectionTest, ActivitySectionsGenerated) { SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000); SectionExists("DUMPSYS - activity", /* bytes= */ 10000); } TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) { SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000); } TEST_F(BugreportSectionTest, WindowSectionGenerated) { SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000); } TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) { SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000); SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000); } TEST_F(BugreportSectionTest, MeminfoSectionGenerated) { SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000); } TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) { SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000); } TEST_F(BugreportSectionTest, WifiSectionGenerated) { SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); } class DumpstateBinderTest : public Test { protected: void SetUp() override { // In case there is a stray service, stop it first. property_set("ctl.stop", "bugreportd"); // dry_run results in a faster bugreport. property_set("dumpstate.dry_run", "true"); // We need to receive some async calls later. Ensure we have binder threads. ProcessState::self()->startThreadPool(); } void TearDown() override { property_set("ctl.stop", "bugreportd"); property_set("dumpstate.dry_run", ""); unlink("/data/local/tmp/tmp.zip"); unlink("/data/local/tmp/tmp.png"); } // Waits until listener gets the callbacks. void WaitTillExecutionComplete(DumpstateListener* listener) { // Wait till one of finished, error or timeout. static const int kBugreportTimeoutSeconds = 120; int i = 0; while (!listener->getIsFinished() && listener->getErrorCode() == -1 && i < kBugreportTimeoutSeconds) { sleep(1); i++; } } }; TEST_F(DumpstateBinderTest, Baseline) { // In the beginning dumpstate binder service is not running. sp ds_binder(GetDumpstateService()); EXPECT_EQ(ds_binder, nullptr); // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service // and makes it wait. property_set("dumpstate.dry_run", "true"); property_set("ctl.start", "bugreportd"); // Now we are able to retrieve dumpstate binder service. ds_binder = GetDumpstateService(); EXPECT_NE(ds_binder, nullptr); // Prepare arguments unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip")); unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png")); EXPECT_NE(bugreport_fd.get(), -1); EXPECT_NE(screenshot_fd.get(), -1); sp listener(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener); // startBugreport is an async call. Verify binder call succeeded first, then wait till listener // gets expected callbacks. EXPECT_TRUE(status.isOk()); WaitTillExecutionComplete(listener.get()); // Bugreport generation requires user consent, which we cannot get in a test set up, // so instead of getting is_finished_, we are more likely to get a consent error. EXPECT_TRUE( listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT || listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); // The service should have died on its own, freeing itself up for a new invocation. sleep(2); ds_binder = GetDumpstateService(); EXPECT_EQ(ds_binder, nullptr); } TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) { // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service // and makes it wait. property_set("ctl.start", "bugreportd"); sp ds_binder(GetDumpstateService()); EXPECT_NE(ds_binder, nullptr); // Prepare arguments unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); EXPECT_NE(bugreport_fd.get(), -1); EXPECT_NE(screenshot_fd.get(), -1); // Call startBugreport with bad arguments. sp listener(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, 2000, // invalid bugreport mode listener); EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); // The service should have died, freeing itself up for a new invocation. sleep(2); ds_binder = GetDumpstateService(); EXPECT_EQ(ds_binder, nullptr); } TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service // and makes it wait. property_set("dumpstate.dry_run", "true"); property_set("ctl.start", "bugreportd"); sp ds_binder(GetDumpstateService()); EXPECT_NE(ds_binder, nullptr); // Prepare arguments unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); EXPECT_NE(bugreport_fd.get(), -1); EXPECT_NE(screenshot_fd.get(), -1); sp listener1(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1); EXPECT_TRUE(status.isOk()); // try to make another call to startBugreport. This should fail. sp listener2(new DumpstateListener(dup(fileno(stdout)))); status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2); EXPECT_FALSE(status.isOk()); WaitTillExecutionComplete(listener2.get()); EXPECT_EQ(listener2->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); // Meanwhile the first call works as expected. Service should not die in this case. WaitTillExecutionComplete(listener1.get()); // Bugreport generation requires user consent, which we cannot get in a test set up, // so instead of getting is_finished_, we are more likely to get a consent error. EXPECT_TRUE( listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT || listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); } } // namespace dumpstate } // namespace os } // namespace android cmds/dumpstate/tests/dumpstate_test.cpp0100644 0000000 0000000 00000174372 13756501734 017505 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "dumpstate" #include #include "DumpstateInternal.h" #include "DumpstateService.h" #include "android/os/BnDumpstate.h" #include "dumpstate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace os { namespace dumpstate { using ::testing::EndsWith; using ::testing::HasSubstr; using ::testing::IsNull; using ::testing::IsEmpty; using ::testing::NotNull; using ::testing::StrEq; using ::testing::StartsWith; using ::testing::Test; using ::testing::internal::CaptureStderr; using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) class DumpstateListenerMock : public IDumpstateListener { public: MOCK_METHOD1(onProgress, binder::Status(int32_t progress)); MOCK_METHOD1(onError, binder::Status(int32_t error_code)); MOCK_METHOD0(onFinished, binder::Status()); MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status, int32_t size, int32_t durationMs)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; static int calls_; // Base class for all tests in this file class DumpstateBaseTest : public Test { public: virtual void SetUp() override { calls_++; SetDryRun(false); } void SetDryRun(bool dry_run) const { PropertiesHelper::dry_run_ = dry_run; } void SetBuildType(const std::string& build_type) const { PropertiesHelper::build_type_ = build_type; } void SetUnroot(bool unroot) const { PropertiesHelper::unroot_ = unroot; } bool IsStandalone() const { return calls_ == 1; } void DropRoot() const { DropRootUser(); uid_t uid = getuid(); ASSERT_EQ(2000, (int)uid); } protected: const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str()); const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/"; const std::string kTestDataPath = kFixturesPath + "tests/testdata/"; const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture"; const std::string kEchoCommand = "/system/bin/echo"; /* * Copies a text file fixture to a temporary file, returning it's path. * * Useful in cases where the test case changes the content of the tile. */ std::string CopyTextFileFixture(const std::string& relative_name) { std::string from = kTestDataPath + relative_name; // Not using TemporaryFile because it's deleted at the end, and it's useful to keep it // around for poking when the test fails. std::string to = kTestDataPath + relative_name + ".tmp"; ALOGD("CopyTextFileFixture: from %s to %s\n", from.c_str(), to.c_str()); android::base::RemoveFileIfExists(to); CopyTextFile(from, to); return to.c_str(); } // Need functions that returns void to use assertions - // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#assertion-placement void ReadFileToString(const std::string& path, std::string* content) { ASSERT_TRUE(android::base::ReadFileToString(path, content)) << "could not read contents from " << path; } void WriteStringToFile(const std::string& content, const std::string& path) { ASSERT_TRUE(android::base::WriteStringToFile(content, path)) << "could not write contents to " << path; } private: void CopyTextFile(const std::string& from, const std::string& to) { std::string content; ReadFileToString(from, &content); WriteStringToFile(content, to); } }; class DumpOptionsTest : public Test { public: virtual ~DumpOptionsTest() { } virtual void SetUp() { options_ = Dumpstate::DumpOptions(); } void TearDown() { // Reset the property property_set("dumpstate.options", ""); } Dumpstate::DumpOptions options_; }; TEST_F(DumpOptionsTest, InitializeNone) { // clang-format off char* argv[] = { const_cast("dumpstate") }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_FALSE(options_.do_add_date); EXPECT_FALSE(options_.do_zip_file); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.do_fb); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.do_broadcast); } TEST_F(DumpOptionsTest, InitializeAdbBugreport) { // clang-format off char* argv[] = { const_cast("dumpstatez"), const_cast("-S"), const_cast("-d"), const_cast("-z"), }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.use_control_socket); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_fb); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.do_broadcast); EXPECT_FALSE(options_.use_socket); } TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { // clang-format off char* argv[] = { const_cast("dumpstate"), const_cast("-s"), }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.use_socket); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.do_add_date); EXPECT_FALSE(options_.do_zip_file); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_fb); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.do_broadcast); } TEST_F(DumpOptionsTest, InitializeFullBugReport) { // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", "bugreportfull"); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_fb); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_broadcast); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.do_start_service); } TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", "bugreportplus"); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_start_service); EXPECT_FALSE(options_.do_fb); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", "bugreportremote"); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.is_remote_mode); EXPECT_FALSE(options_.do_vibrate); EXPECT_FALSE(options_.do_fb); // Other options retain default values EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.use_socket); } TEST_F(DumpOptionsTest, InitializeWearBugReport) { // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", "bugreportwear"); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_fb); EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_start_service); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", "bugreporttelephony"); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_FALSE(options_.do_fb); EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.telephony_only); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } TEST_F(DumpOptionsTest, InitializeWifiBugReport) { // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", "bugreportwifi"); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_FALSE(options_.do_fb); EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.wifi_only); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { // default: commandline options are not overridden // clang-format off char* argv[] = { const_cast("bugreport"), const_cast("-d"), const_cast("-p"), const_cast("-B"), const_cast("-z"), }; // clang-format on property_set("dumpstate.options", ""); Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_fb); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.do_broadcast); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.wifi_only); } TEST_F(DumpOptionsTest, InitializePartial1) { // clang-format off char* argv[] = { const_cast("dumpstate"), const_cast("-d"), const_cast("-z"), const_cast("-s"), const_cast("-S"), }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_add_date); EXPECT_TRUE(options_.do_zip_file); // TODO: Maybe we should trim the filename EXPECT_TRUE(options_.use_socket); EXPECT_TRUE(options_.use_control_socket); // Other options retain default values EXPECT_FALSE(options_.show_header_only); EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.do_fb); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.do_broadcast); } TEST_F(DumpOptionsTest, InitializePartial2) { // clang-format off char* argv[] = { const_cast("dumpstate"), const_cast("-v"), const_cast("-q"), const_cast("-p"), const_cast("-P"), const_cast("-R"), const_cast("-B"), }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.show_header_only); EXPECT_FALSE(options_.do_vibrate); EXPECT_TRUE(options_.do_fb); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.is_remote_mode); EXPECT_TRUE(options_.do_broadcast); // Other options retain default values EXPECT_FALSE(options_.do_add_date); EXPECT_FALSE(options_.do_zip_file); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.use_control_socket); } TEST_F(DumpOptionsTest, InitializeHelp) { // clang-format off char* argv[] = { const_cast("dumpstate"), const_cast("-h") }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); // -h is for help. EXPECT_EQ(status, Dumpstate::RunStatus::HELP); } TEST_F(DumpOptionsTest, InitializeUnknown) { // clang-format off char* argv[] = { const_cast("dumpstate"), const_cast("-u") // unknown flag }; // clang-format on Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); // -u is unknown. EXPECT_EQ(status, Dumpstate::RunStatus::INVALID_INPUT); } TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile1) { options_.do_zip_file = true; // Writing to socket = !writing to file. options_.use_socket = true; EXPECT_FALSE(options_.ValidateOptions()); options_.use_socket = false; EXPECT_TRUE(options_.ValidateOptions()); } TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) { options_.do_broadcast = true; // Writing to socket = !writing to file. options_.use_socket = true; EXPECT_FALSE(options_.ValidateOptions()); options_.use_socket = false; EXPECT_TRUE(options_.ValidateOptions()); } TEST_F(DumpOptionsTest, ValidateOptionsNeedZipfile) { options_.use_control_socket = true; EXPECT_FALSE(options_.ValidateOptions()); options_.do_zip_file = true; EXPECT_TRUE(options_.ValidateOptions()); } TEST_F(DumpOptionsTest, ValidateOptionsUpdateProgressNeedsBroadcast) { options_.do_progress_updates = true; EXPECT_FALSE(options_.ValidateOptions()); options_.do_broadcast = true; EXPECT_TRUE(options_.ValidateOptions()); } TEST_F(DumpOptionsTest, ValidateOptionsRemoteMode) { options_.is_remote_mode = true; EXPECT_FALSE(options_.ValidateOptions()); options_.do_broadcast = true; options_.do_zip_file = true; options_.do_add_date = true; EXPECT_TRUE(options_.ValidateOptions()); } class DumpstateTest : public DumpstateBaseTest { public: void SetUp() { DumpstateBaseTest::SetUp(); SetDryRun(false); SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)")); ds.progress_.reset(new Progress()); ds.update_progress_threshold_ = 0; ds.options_.reset(new Dumpstate::DumpOptions()); } // Runs a command and capture `stdout` and `stderr`. int RunCommand(const std::string& title, const std::vector& full_command, const CommandOptions& options = CommandOptions::DEFAULT) { CaptureStdout(); CaptureStderr(); int status = ds.RunCommand(title, full_command, options); out = GetCapturedStdout(); err = GetCapturedStderr(); return status; } // Dumps a file and capture `stdout` and `stderr`. int DumpFile(const std::string& title, const std::string& path) { CaptureStdout(); CaptureStderr(); int status = ds.DumpFile(title, path); out = GetCapturedStdout(); err = GetCapturedStderr(); return status; } void SetProgress(long progress, long initial_max, long threshold = 0) { ds.options_->do_progress_updates = true; ds.update_progress_threshold_ = threshold; ds.last_updated_progress_ = 0; ds.progress_.reset(new Progress(initial_max, progress, 1.2)); } std::string GetProgressMessage(const std::string& listener_name, int progress, int max, int old_max = 0, bool update_progress = true) { EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress"; EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max"; bool max_increased = old_max > 0; std::string message = ""; if (max_increased) { message = android::base::StringPrintf("Adjusting max progress from %d to %d\n", old_max, max); } if (update_progress) { message += android::base::StringPrintf("Setting progress (%s): %d/%d (%d%%)\n", listener_name.c_str(), progress, max, (100 * progress / max)); } return message; } // `stdout` and `stderr` from the last command ran. std::string out, err; Dumpstate& ds = Dumpstate::GetInstance(); }; TEST_F(DumpstateTest, RunCommandNoArgs) { EXPECT_EQ(-1, RunCommand("", {})); } TEST_F(DumpstateTest, RunCommandNoTitle) { EXPECT_EQ(0, RunCommand("", {kSimpleCommand})); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateTest, RunCommandWithTitle) { EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); EXPECT_THAT(err, StrEq("stderr\n")); // We don't know the exact duration, so we check the prefix and suffix EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n")); } TEST_F(DumpstateTest, RunCommandWithLoggingMessage) { EXPECT_EQ( 0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build())); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n")); } TEST_F(DumpstateTest, RunCommandRedirectStderr) { EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).RedirectStderr().Build())); EXPECT_THAT(out, IsEmpty()); EXPECT_THAT(err, StrEq("stdout\nstderr\n")); } TEST_F(DumpstateTest, RunCommandWithOneArg) { EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"})); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("one\n")); } TEST_F(DumpstateTest, RunCommandWithMultipleArgs) { EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"})); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("one is the loniest number\n")); } TEST_F(DumpstateTest, RunCommandDryRun) { SetDryRun(true); EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); // We don't know the exact duration, so we check the prefix and suffix EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\n\t(skipped on dry run)\n")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateTest, RunCommandDryRunNoTitle) { SetDryRun(true); EXPECT_EQ(0, RunCommand("", {kSimpleCommand})); EXPECT_THAT(out, IsEmpty()); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateTest, RunCommandDryRunAlways) { SetDryRun(true); EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build())); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateTest, RunCommandNotFound) { EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"})); EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code")); EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed")); } TEST_F(DumpstateTest, RunCommandFails) { EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"})); EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand + " --exit 42' failed: exit code 42\n")); EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand + " --exit 42' failed: exit code 42\n")); } TEST_F(DumpstateTest, RunCommandCrashes) { EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"})); // We don't know the exit code, so check just the prefix. EXPECT_THAT( out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code")); EXPECT_THAT( err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code")); } TEST_F(DumpstateTest, RunCommandTimesout) { EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"}, CommandOptions::WithTimeout(1).Build())); EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand + " --sleep 2' timed out after 1")); EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand + " --sleep 2' timed out after 1")); } TEST_F(DumpstateTest, RunCommandIsKilled) { CaptureStdout(); CaptureStderr(); std::thread t([=]() { EXPECT_EQ(SIGTERM, ds.RunCommand("", {kSimpleCommand, "--pid", "--sleep", "20"}, CommandOptions::WithTimeout(100).Always().Build())); }); // Capture pid and pre-sleep output. sleep(1); // Wait a little bit to make sure pid and 1st line were printed. std::string err = GetCapturedStderr(); EXPECT_THAT(err, StrEq("sleeping for 20s\n")); std::string out = GetCapturedStdout(); std::vector lines = android::base::Split(out, "\n"); ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out; int pid = atoi(lines[0].c_str()); EXPECT_THAT(lines[1], StrEq("stdout line1")); EXPECT_THAT(lines[2], IsEmpty()); // \n // Then kill the process. CaptureStdout(); CaptureStderr(); ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid; t.join(); // Finally, check output after murder. out = GetCapturedStdout(); err = GetCapturedStderr(); EXPECT_THAT(out, StrEq("*** command '" + kSimpleCommand + " --pid --sleep 20' failed: killed by signal 15\n")); EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand + " --pid --sleep 20' failed: killed by signal 15\n")); } TEST_F(DumpstateTest, RunCommandProgress) { sp listener(new DumpstateListenerMock()); ds.listener_ = listener; ds.listener_name_ = "FoxMulder"; SetProgress(0, 30); EXPECT_CALL(*listener, onProgressUpdated(20)); EXPECT_CALL(*listener, onProgress(66)); // 20/30 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build())); std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); EXPECT_CALL(*listener, onProgressUpdated(30)); EXPECT_CALL(*listener, onProgress(100)); // 35/35 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build())); progress_message = GetProgressMessage(ds.listener_name_, 30, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); // Run a command that will increase maximum timeout. EXPECT_CALL(*listener, onProgressUpdated(31)); EXPECT_CALL(*listener, onMaxProgressUpdated(37)); EXPECT_CALL(*listener, onProgress(83)); // 31/37 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30); // 20% increase EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); // Make sure command ran while in dry_run is counted. SetDryRun(true); EXPECT_CALL(*listener, onProgressUpdated(35)); EXPECT_CALL(*listener, onProgress(94)); // 35/37 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); progress_message = GetProgressMessage(ds.listener_name_, 35, 37); EXPECT_THAT(out, IsEmpty()); EXPECT_THAT(err, StrEq(progress_message)); ds.listener_.clear(); } TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) { sp listener(new DumpstateListenerMock()); ds.listener_ = listener; ds.listener_name_ = "FoxMulder"; SetProgress(0, 8, 5); // 8 max, 5 threshold // First update should always be sent. EXPECT_CALL(*listener, onProgressUpdated(1)); EXPECT_CALL(*listener, onProgress(12)); // 1/12 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5). EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); // Third update should be sent because it reaches threshold (6 - 1 = 5). EXPECT_CALL(*listener, onProgressUpdated(6)); EXPECT_CALL(*listener, onProgress(75)); // 6/8 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); progress_message = GetProgressMessage(ds.listener_name_, 6, 8); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5). // But max update should be sent. EXPECT_CALL(*listener, onMaxProgressUpdated(10)); // 9 * 120% = 10.8 = 10 EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build())); progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); ds.listener_.clear(); } TEST_F(DumpstateTest, RunCommandDropRoot) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateTest.RunCommandDropRoot() on test suite\n") return; } // First check root case - only available when running with 'adb root'. uid_t uid = getuid(); if (uid == 0) { EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"})); EXPECT_THAT(out, StrEq("0\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); return; } // Then run dropping root. EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).DropRoot().Build())); EXPECT_THAT(out, StrEq("2000\nstdout\n")); EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n")); } TEST_F(DumpstateTest, RunCommandAsRootUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateTest.RunCommandAsRootUserBuild() on test suite\n") return; } if (!PropertiesHelper::IsUserBuild()) { // Emulates user build if necessarily. SetBuildType("user"); } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build())); // We don't know the exact path of su, so we just check for the 'root ...' commands EXPECT_THAT(out, StartsWith("Skipping")); EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateTest.RunCommandAsRootNonUserBuild() on test suite\n") return; } if (PropertiesHelper::IsUserBuild()) { ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n"); return; } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRoot().Build())); EXPECT_THAT(out, StrEq("0\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild_withUnroot) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE( "Skipping DumpstateTest.RunCommandAsRootNonUserBuild_withUnroot() " "on test suite\n") return; } if (PropertiesHelper::IsUserBuild()) { ALOGI("Skipping RunCommandAsRootNonUserBuild_withUnroot on user builds\n"); return; } // Same test as above, but with unroot property set, which will override su availability. SetUnroot(true); DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRoot().Build())); // AsRoot is ineffective. EXPECT_THAT(out, StrEq("2000\nstdout\n")); EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n")); } TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n") return; } if (!PropertiesHelper::IsUserBuild()) { // Emulates user build if necessarily. SetBuildType("user"); } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); EXPECT_THAT(out, StrEq("2000\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n") return; } if (PropertiesHelper::IsUserBuild()) { ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n"); return; } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); EXPECT_THAT(out, StrEq("0\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild_withUnroot) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE( "Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild_withUnroot() " "on test suite\n") return; } if (PropertiesHelper::IsUserBuild()) { ALOGI("Skipping RunCommandAsRootIfAvailableOnDebugBuild_withUnroot on user builds\n"); return; } // Same test as above, but with unroot property set, which will override su availability. SetUnroot(true); DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); // It's a userdebug build, so "su root" should be available, but unroot=true overrides it. EXPECT_THAT(out, StrEq("2000\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) { EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist")); EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) { EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist")); EXPECT_THAT(err, IsEmpty()); // We don't know the exact duration, so we check the prefix and suffix EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No " "such file or directory\n")); } TEST_F(DumpstateTest, DumpFileSingleLine) { EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline } TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) { EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\n")); } TEST_F(DumpstateTest, DumpFileMultipleLines) { EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n")); } TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) { EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n")); } TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) { SetDryRun(true); EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, IsEmpty()); } TEST_F(DumpstateTest, DumpFileOnDryRun) { SetDryRun(true); EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT( out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:")); EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n")); } TEST_F(DumpstateTest, DumpFileUpdateProgress) { sp listener(new DumpstateListenerMock()); ds.listener_ = listener; ds.listener_name_ = "FoxMulder"; SetProgress(0, 30); EXPECT_CALL(*listener, onProgressUpdated(5)); EXPECT_CALL(*listener, onProgress(16)); // 5/30 % EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); std::string progress_message = GetProgressMessage(ds.listener_name_, 5, 30); // TODO: unhardcode WEIGHT_FILE (5)? EXPECT_THAT(err, StrEq(progress_message)); EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline ds.listener_.clear(); } class DumpstateServiceTest : public DumpstateBaseTest { public: DumpstateService dss; }; TEST_F(DumpstateServiceTest, SetListenerNoName) { sp listener(new DumpstateListenerMock()); sp token; EXPECT_TRUE(dss.setListener("", listener, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, IsNull()); } TEST_F(DumpstateServiceTest, SetListenerNoPointer) { sp token; EXPECT_TRUE( dss.setListener("whatever", nullptr, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, IsNull()); } TEST_F(DumpstateServiceTest, SetListenerTwice) { sp listener(new DumpstateListenerMock()); sp token; EXPECT_TRUE( dss.setListener("whatever", listener, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, NotNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); EXPECT_FALSE(Dumpstate::GetInstance().report_section_); token.clear(); EXPECT_TRUE( dss.setListener("whatsoever", listener, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, IsNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); EXPECT_FALSE(Dumpstate::GetInstance().report_section_); } TEST_F(DumpstateServiceTest, SetListenerWithSectionDetails) { sp listener(new DumpstateListenerMock()); sp token; Dumpstate::GetInstance().listener_ = nullptr; EXPECT_TRUE( dss.setListener("whatever", listener, /* getSectionDetails = */ true, &token).isOk()); ASSERT_THAT(token, NotNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); EXPECT_TRUE(Dumpstate::GetInstance().report_section_); } class ProgressTest : public DumpstateBaseTest { public: Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") { return Progress(max, growth_factor, path); } void AssertStats(const std::string& path, int32_t expected_runs, int32_t expected_average) { std::string expected_content = android::base::StringPrintf("%d %d\n", expected_runs, expected_average); std::string actual_content; ReadFileToString(path, &actual_content); ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path; } }; TEST_F(ProgressTest, SimpleTest) { Progress progress; EXPECT_EQ(0, progress.Get()); EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax()); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); bool max_increased = progress.Inc(1); EXPECT_EQ(1, progress.Get()); EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax()); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); EXPECT_FALSE(max_increased); // Ignore negative increase. max_increased = progress.Inc(-1); EXPECT_EQ(1, progress.Get()); EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax()); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); EXPECT_FALSE(max_increased); } TEST_F(ProgressTest, MaxGrowsInsideNewRange) { Progress progress = GetInstance(10, 1.2); // 20% growth factor EXPECT_EQ(0, progress.Get()); EXPECT_EQ(10, progress.GetInitialMax()); EXPECT_EQ(10, progress.GetMax()); // No increase bool max_increased = progress.Inc(10); EXPECT_EQ(10, progress.Get()); EXPECT_EQ(10, progress.GetMax()); EXPECT_FALSE(max_increased); // Increase, with new value < max*20% max_increased = progress.Inc(1); EXPECT_EQ(11, progress.Get()); EXPECT_EQ(13, progress.GetMax()); // 11 average * 20% growth = 13.2 = 13 EXPECT_TRUE(max_increased); } TEST_F(ProgressTest, MaxGrowsOutsideNewRange) { Progress progress = GetInstance(10, 1.2); // 20% growth factor EXPECT_EQ(0, progress.Get()); EXPECT_EQ(10, progress.GetInitialMax()); EXPECT_EQ(10, progress.GetMax()); // No increase bool max_increased = progress.Inc(10); EXPECT_EQ(10, progress.Get()); EXPECT_EQ(10, progress.GetMax()); EXPECT_FALSE(max_increased); // Increase, with new value > max*20% max_increased = progress.Inc(5); EXPECT_EQ(15, progress.Get()); EXPECT_EQ(18, progress.GetMax()); // 15 average * 20% growth = 18 EXPECT_TRUE(max_increased); } TEST_F(ProgressTest, InvalidPath) { Progress progress("/devil/null"); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, EmptyFile) { Progress progress(CopyTextFileFixture("empty-file.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLine1stEntryNAN) { Progress progress(CopyTextFileFixture("stats-invalid-1st-NAN.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLine2ndEntryNAN) { Progress progress(CopyTextFileFixture("stats-invalid-2nd-NAN.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLineBothNAN) { Progress progress(CopyTextFileFixture("stats-invalid-both-NAN.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLine1stEntryNegative) { Progress progress(CopyTextFileFixture("stats-invalid-1st-negative.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLine2ndEntryNegative) { Progress progress(CopyTextFileFixture("stats-invalid-2nd-negative.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLine1stEntryTooBig) { Progress progress(CopyTextFileFixture("stats-invalid-1st-too-big.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } TEST_F(ProgressTest, InvalidLine2ndEntryTooBig) { Progress progress(CopyTextFileFixture("stats-invalid-2nd-too-big.txt")); EXPECT_EQ(Progress::kDefaultMax, progress.GetMax()); } // Tests stats are properly saved when the file does not exists. TEST_F(ProgressTest, FirstTime) { if (!IsStandalone()) { // TODO: temporarily disabled because it's failing when running as suite MYLOGE("Skipping ProgressTest.FirstTime() on test suite\n") return; } std::string path = kTestDataPath + "FirstTime.txt"; android::base::RemoveFileIfExists(path); Progress run1(path); EXPECT_EQ(0, run1.Get()); EXPECT_EQ(Progress::kDefaultMax, run1.GetInitialMax()); EXPECT_EQ(Progress::kDefaultMax, run1.GetMax()); bool max_increased = run1.Inc(20); EXPECT_EQ(20, run1.Get()); EXPECT_EQ(Progress::kDefaultMax, run1.GetMax()); EXPECT_FALSE(max_increased); run1.Save(); AssertStats(path, 1, 20); } // Tests what happens when the persistent settings contains the average duration of 1 run. // Data on file is 1 run and 109 average. TEST_F(ProgressTest, SecondTime) { std::string path = CopyTextFileFixture("stats-one-run-no-newline.txt"); Progress run1 = GetInstance(-42, 1.2, path); EXPECT_EQ(0, run1.Get()); EXPECT_EQ(10, run1.GetInitialMax()); EXPECT_EQ(10, run1.GetMax()); bool max_increased = run1.Inc(20); EXPECT_EQ(20, run1.Get()); EXPECT_EQ(24, run1.GetMax()); EXPECT_TRUE(max_increased); // Average now is 2 runs and (10 + 20)/ 2 = 15 run1.Save(); AssertStats(path, 2, 15); Progress run2 = GetInstance(-42, 1.2, path); EXPECT_EQ(0, run2.Get()); EXPECT_EQ(15, run2.GetInitialMax()); EXPECT_EQ(15, run2.GetMax()); max_increased = run2.Inc(25); EXPECT_EQ(25, run2.Get()); EXPECT_EQ(30, run2.GetMax()); EXPECT_TRUE(max_increased); // Average now is 3 runs and (15 * 2 + 25)/ 3 = 18.33 = 18 run2.Save(); AssertStats(path, 3, 18); Progress run3 = GetInstance(-42, 1.2, path); EXPECT_EQ(0, run3.Get()); EXPECT_EQ(18, run3.GetInitialMax()); EXPECT_EQ(18, run3.GetMax()); // Make sure average decreases as well max_increased = run3.Inc(5); EXPECT_EQ(5, run3.Get()); EXPECT_EQ(18, run3.GetMax()); EXPECT_FALSE(max_increased); // Average now is 4 runs and (18 * 3 + 5)/ 4 = 14.75 = 14 run3.Save(); AssertStats(path, 4, 14); } // Tests what happens when the persistent settings contains the average duration of 2 runs. // Data on file is 2 runs and 15 average. TEST_F(ProgressTest, ThirdTime) { std::string path = CopyTextFileFixture("stats-two-runs.txt"); AssertStats(path, 2, 15); // Sanity check Progress run1 = GetInstance(-42, 1.2, path); EXPECT_EQ(0, run1.Get()); EXPECT_EQ(15, run1.GetInitialMax()); EXPECT_EQ(15, run1.GetMax()); bool max_increased = run1.Inc(20); EXPECT_EQ(20, run1.Get()); EXPECT_EQ(24, run1.GetMax()); EXPECT_TRUE(max_increased); // Average now is 3 runs and (15 * 2 + 20)/ 3 = 16.66 = 16 run1.Save(); AssertStats(path, 3, 16); } class DumpstateUtilTest : public DumpstateBaseTest { public: void SetUp() { DumpstateBaseTest::SetUp(); SetDryRun(false); } void CaptureFdOut() { ReadFileToString(path_, &out); } void CreateFd(const std::string& name) { path_ = kTestDataPath + name; MYLOGD("Creating fd for file %s\n", path_.c_str()); fd = TEMP_FAILURE_RETRY(open(path_.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); ASSERT_GE(fd, 0) << "could not create FD for path " << path_; } // Runs a command into the `fd` and capture `stderr`. int RunCommand(const std::string& title, const std::vector& full_command, const CommandOptions& options = CommandOptions::DEFAULT) { CaptureStderr(); int status = RunCommandToFd(fd, title, full_command, options); close(fd); CaptureFdOut(); err = GetCapturedStderr(); return status; } // Dumps a file and into the `fd` and `stderr`. int DumpFile(const std::string& title, const std::string& path) { CaptureStderr(); int status = DumpFileToFd(fd, title, path); close(fd); CaptureFdOut(); err = GetCapturedStderr(); return status; } // Find out the pid of the process_name int FindPidOfProcess(const std::string& process_name) { CaptureStderr(); int status = GetPidByName(process_name); err = GetCapturedStderr(); return status; } int fd; // 'fd` output and `stderr` from the last command ran. std::string out, err; private: std::string path_; }; TEST_F(DumpstateUtilTest, RunCommandNoArgs) { CreateFd("RunCommandNoArgs.txt"); EXPECT_EQ(-1, RunCommand("", {})); } TEST_F(DumpstateUtilTest, RunCommandNoTitle) { CreateFd("RunCommandWithNoArgs.txt"); EXPECT_EQ(0, RunCommand("", {kSimpleCommand})); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandWithTitle) { CreateFd("RunCommandWithNoArgs.txt"); EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); EXPECT_THAT(out, StrEq("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandWithOneArg) { CreateFd("RunCommandWithOneArg.txt"); EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"})); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("one\n")); } TEST_F(DumpstateUtilTest, RunCommandWithMultipleArgs) { CreateFd("RunCommandWithMultipleArgs.txt"); EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"})); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("one is the loniest number\n")); } TEST_F(DumpstateUtilTest, RunCommandWithLoggingMessage) { CreateFd("RunCommandWithLoggingMessage.txt"); EXPECT_EQ( 0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build())); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandRedirectStderr) { CreateFd("RunCommandRedirectStderr.txt"); EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).RedirectStderr().Build())); EXPECT_THAT(out, IsEmpty()); EXPECT_THAT(err, StrEq("stdout\nstderr\n")); } TEST_F(DumpstateUtilTest, RunCommandDryRun) { CreateFd("RunCommandDryRun.txt"); SetDryRun(true); EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); EXPECT_THAT(out, StrEq(android::base::StringPrintf( "------ I AM GROOT (%s) ------\n\t(skipped on dry run)\n", kSimpleCommand.c_str()))); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateUtilTest, RunCommandDryRunNoTitle) { CreateFd("RunCommandDryRun.txt"); SetDryRun(true); EXPECT_EQ(0, RunCommand("", {kSimpleCommand})); EXPECT_THAT( out, StrEq(android::base::StringPrintf("%s: skipped on dry run\n", kSimpleCommand.c_str()))); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateUtilTest, RunCommandDryRunAlways) { CreateFd("RunCommandDryRunAlways.txt"); SetDryRun(true); EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build())); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandNotFound) { CreateFd("RunCommandNotFound.txt"); EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"})); EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code")); EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed")); } TEST_F(DumpstateUtilTest, RunCommandFails) { CreateFd("RunCommandFails.txt"); EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"})); EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand + " --exit 42' failed: exit code 42\n")); EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand + " --exit 42' failed: exit code 42\n")); } TEST_F(DumpstateUtilTest, RunCommandCrashes) { CreateFd("RunCommandCrashes.txt"); EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"})); // We don't know the exit code, so check just the prefix. EXPECT_THAT( out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code")); EXPECT_THAT( err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code")); } TEST_F(DumpstateUtilTest, RunCommandTimesoutWithSec) { CreateFd("RunCommandTimesout.txt"); EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"}, CommandOptions::WithTimeout(1).Build())); EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand + " --sleep 2' timed out after 1")); EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand + " --sleep 2' timed out after 1")); } TEST_F(DumpstateUtilTest, RunCommandTimesoutWithMsec) { CreateFd("RunCommandTimesout.txt"); EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"}, CommandOptions::WithTimeoutInMs(1000).Build())); EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand + " --sleep 2' timed out after 1")); EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand + " --sleep 2' timed out after 1")); } TEST_F(DumpstateUtilTest, RunCommandIsKilled) { CreateFd("RunCommandIsKilled.txt"); CaptureStderr(); std::thread t([=]() { EXPECT_EQ(SIGTERM, RunCommandToFd(fd, "", {kSimpleCommand, "--pid", "--sleep", "20"}, CommandOptions::WithTimeout(100).Always().Build())); }); // Capture pid and pre-sleep output. sleep(1); // Wait a little bit to make sure pid and 1st line were printed. std::string err = GetCapturedStderr(); EXPECT_THAT(err, StrEq("sleeping for 20s\n")); CaptureFdOut(); std::vector lines = android::base::Split(out, "\n"); ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out; int pid = atoi(lines[0].c_str()); EXPECT_THAT(lines[1], StrEq("stdout line1")); EXPECT_THAT(lines[2], IsEmpty()); // \n // Then kill the process. CaptureFdOut(); CaptureStderr(); ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid; t.join(); // Finally, check output after murder. CaptureFdOut(); err = GetCapturedStderr(); // out starts with the pid, which is an unknown EXPECT_THAT(out, EndsWith("stdout line1\n*** command '" + kSimpleCommand + " --pid --sleep 20' failed: killed by signal 15\n")); EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand + " --pid --sleep 20' failed: killed by signal 15\n")); } TEST_F(DumpstateUtilTest, RunCommandAsRootUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootUserBuild() on test suite\n") return; } CreateFd("RunCommandAsRootUserBuild.txt"); if (!PropertiesHelper::IsUserBuild()) { // Emulates user build if necessarily. SetBuildType("user"); } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build())); // We don't know the exact path of su, so we just check for the 'root ...' commands EXPECT_THAT(out, StartsWith("Skipping")); EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateUtilTest, RunCommandAsRootNonUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootNonUserBuild() on test suite\n") return; } CreateFd("RunCommandAsRootNonUserBuild.txt"); if (PropertiesHelper::IsUserBuild()) { ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n"); return; } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRoot().Build())); EXPECT_THAT(out, StrEq("0\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n") return; } CreateFd("RunCommandAsRootIfAvailableOnUserBuild.txt"); if (!PropertiesHelper::IsUserBuild()) { // Emulates user build if necessarily. SetBuildType("user"); } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); EXPECT_THAT(out, StrEq("2000\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnDebugBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n") return; } CreateFd("RunCommandAsRootIfAvailableOnDebugBuild.txt"); if (PropertiesHelper::IsUserBuild()) { ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n"); return; } DropRoot(); EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); EXPECT_THAT(out, StrEq("0\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); } TEST_F(DumpstateUtilTest, RunCommandDropRoot) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping // to Shell - need to refactor tests to avoid this problem) MYLOGE("Skipping DumpstateUtilTest.RunCommandDropRoot() on test suite\n") return; } CreateFd("RunCommandDropRoot.txt"); // First check root case - only available when running with 'adb root'. uid_t uid = getuid(); if (uid == 0) { EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"})); EXPECT_THAT(out, StrEq("0\nstdout\n")); EXPECT_THAT(err, StrEq("stderr\n")); return; } // Then run dropping root. EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, CommandOptions::WithTimeout(1).DropRoot().Build())); EXPECT_THAT(out, StrEq("2000\nstdout\n")); EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n")); } TEST_F(DumpstateUtilTest, DumpFileNotFoundNoTitle) { CreateFd("DumpFileNotFound.txt"); EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist")); EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateUtilTest, DumpFileNotFoundWithTitle) { CreateFd("DumpFileNotFound.txt"); EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist")); EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No such " "file or directory\n")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateUtilTest, DumpFileSingleLine) { CreateFd("DumpFileSingleLine.txt"); EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline } TEST_F(DumpstateUtilTest, DumpFileSingleLineWithNewLine) { CreateFd("DumpFileSingleLineWithNewLine.txt"); EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\n")); } TEST_F(DumpstateUtilTest, DumpFileMultipleLines) { CreateFd("DumpFileMultipleLines.txt"); EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n")); } TEST_F(DumpstateUtilTest, DumpFileMultipleLinesWithNewLine) { CreateFd("DumpFileMultipleLinesWithNewLine.txt"); EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n")); } TEST_F(DumpstateUtilTest, DumpFileOnDryRunNoTitle) { CreateFd("DumpFileOnDryRun.txt"); SetDryRun(true); std::string path = kTestDataPath + "single-line.txt"; EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT(out, StrEq(path + ": skipped on dry run\n")); } TEST_F(DumpstateUtilTest, DumpFileOnDryRun) { CreateFd("DumpFileOnDryRun.txt"); SetDryRun(true); std::string path = kTestDataPath + "single-line.txt"; EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt")); EXPECT_THAT(err, IsEmpty()); EXPECT_THAT( out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:")); EXPECT_THAT(out, EndsWith("skipped on dry run\n")); } TEST_F(DumpstateUtilTest, FindingPidWithExistingProcess) { // init process always has pid 1. EXPECT_EQ(1, FindPidOfProcess("init")); EXPECT_THAT(err, IsEmpty()); } TEST_F(DumpstateUtilTest, FindingPidWithNotExistingProcess) { // find the process with abnormal name. EXPECT_EQ(-1, FindPidOfProcess("abcdef12345-543")); EXPECT_THAT(err, StrEq("can't find the pid\n")); } } // namespace dumpstate } // namespace os } // namespace android cmds/dumpstate/tests/dumpstate_test_fixture.cpp0100644 0000000 0000000 00000006007 13756501734 021240 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #define LOG_TAG "dumpstate" #include void PrintDefaultOutput() { fprintf(stdout, "stdout\n"); fflush(stdout); fprintf(stderr, "stderr\n"); fflush(stderr); } /* * Binary used to on RunCommand tests. * * Usage: * * - Unless stated otherwise this command: * * 1.Prints `stdout\n` on `stdout` and flushes it. * 2.Prints `stderr\n` on `stderr` and flushes it. * 3.Exit with status 0. * * - If 1st argument is '--pid', it first prints its pid on `stdout`. * * - If 1st argument is '--uid', it first prints its uid on `stdout`. * * - If 1st argument is '--crash', it uses ALOGF to crash and returns 666. * * - With argument '--exit' 'CODE', returns CODE; * * - With argument '--sleep 'TIME': * * 1.Prints `stdout line1\n` on `stdout` and `sleeping TIME s\n` on `stderr` * 2.Sleeps for TIME s * 3.Prints `stdout line2\n` on `stdout` and `woke up\n` on `stderr` */ int main(int argc, char* const argv[]) { if (argc == 2) { if (strcmp(argv[1], "--crash") == 0) { PrintDefaultOutput(); LOG_FATAL("D'OH\n"); return 666; } } if (argc == 3) { if (strcmp(argv[1], "--exit") == 0) { PrintDefaultOutput(); return atoi(argv[2]); } } if (argc > 1) { int index = 1; // First check arguments that can shift the index. if (strcmp(argv[1], "--pid") == 0) { index++; fprintf(stdout, "%d\n", getpid()); fflush(stdout); } else if (strcmp(argv[1], "--uid") == 0) { index++; fprintf(stdout, "%d\n", getuid()); fflush(stdout); } // Then the "common" arguments, if any. if (argc > index + 1) { if (strcmp(argv[index], "--sleep") == 0) { int napTime = atoi(argv[index + 1]); fprintf(stdout, "stdout line1\n"); fflush(stdout); fprintf(stderr, "sleeping for %ds\n", napTime); fflush(stderr); sleep(napTime); fprintf(stdout, "stdout line2\n"); fflush(stdout); fprintf(stderr, "woke up\n"); fflush(stderr); return 0; } } } PrintDefaultOutput(); return 0; } cmds/dumpstate/tests/testdata/0040755 0000000 0000000 00000000000 13756501734 015532 5ustar000000000 0000000 cmds/dumpstate/tests/testdata/empty-file.txt0100644 0000000 0000000 00000000000 13756501734 020331 0ustar000000000 0000000 cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt0100644 0000000 0000000 00000000041 13756501734 023636 0ustar000000000 0000000 I AM LINE1 I AM LINE2 I AM LINE3 cmds/dumpstate/tests/testdata/multiple-lines.txt0100644 0000000 0000000 00000000040 13756501734 021225 0ustar000000000 0000000 I AM LINE1 I AM LINE2 I AM LINE3cmds/dumpstate/tests/testdata/single-line-with-newline.txt0100644 0000000 0000000 00000000013 13756501734 023100 0ustar000000000 0000000 I AM LINE1 cmds/dumpstate/tests/testdata/single-line.txt0100644 0000000 0000000 00000000012 13756501734 020467 0ustar000000000 0000000 I AM LINE1cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt0100644 0000000 0000000 00000000017 13756501734 022167 0ustar000000000 0000000 SIX_SIX_SIX 42 cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt0100644 0000000 0000000 00000000010 13756501734 023346 0ustar000000000 0000000 -666 42 cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt0100644 0000000 0000000 00000000016 13756501734 023112 0ustar000000000 0000000 4815162342 42 cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt0100644 0000000 0000000 00000000016 13756501734 022142 0ustar000000000 0000000 666 FORTY_TWO cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt0100644 0000000 0000000 00000000010 13756501734 023322 0ustar000000000 0000000 666 -42 cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt0100644 0000000 0000000 00000000017 13756501734 023067 0ustar000000000 0000000 666 4815162342 cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt0100644 0000000 0000000 00000000016 13756501734 022413 0ustar000000000 0000000 N_RUNS AVERAGEcmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt0100644 0000000 0000000 00000000004 13756501734 023052 0ustar000000000 0000000 1 10cmds/dumpstate/tests/testdata/stats-two-runs.txt0100644 0000000 0000000 00000000005 13756501734 021215 0ustar000000000 0000000 2 15 cmds/dumpstate/utils.cpp0100644 0000000 0000000 00000077142 13756501734 014433 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #define LOG_TAG "dumpstate" #include "dumpstate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DumpstateInternal.h" // TODO: remove once moved to namespace using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::PropertiesHelper; // Keep in sync with // frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds /* Most simple commands have 10 as timeout, so 5 is a good estimate */ static const int32_t WEIGHT_FILE = 5; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); static int RunCommand(const std::string& title, const std::vector& full_command, const CommandOptions& options = CommandOptions::DEFAULT) { return ds.RunCommand(title, full_command, options); } // Reasonable value for max stats. static const int STATS_MAX_N_RUNS = 1000; static const long STATS_MAX_AVERAGE = 100000; CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build(); // TODO(111441001): Default DumpOptions to sensible values. Dumpstate::Dumpstate(const std::string& version) : pid_(getpid()), options_(new Dumpstate::DumpOptions()), version_(version), now_(time(nullptr)) { } Dumpstate& Dumpstate::GetInstance() { static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT)); return singleton_; } DurationReporter::DurationReporter(const std::string& title, bool logcat_only) : title_(title), logcat_only_(logcat_only) { if (!title_.empty()) { started_ = Nanotime(); } } DurationReporter::~DurationReporter() { if (!title_.empty()) { float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; if (elapsed < .5f) { return; } MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); if (logcat_only_) { return; } // Use "Yoda grammar" to make it easier to grep|sort sections. printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } } const int32_t Progress::kDefaultMax = 5000; Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) { } Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor) : Progress(initial_max, growth_factor, "") { progress_ = progress; } Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path) : initial_max_(initial_max), progress_(0), max_(initial_max), growth_factor_(growth_factor), n_runs_(0), average_max_(0), path_(path) { if (!path_.empty()) { Load(); } } void Progress::Load() { MYLOGD("Loading stats from %s\n", path_.c_str()); std::string content; if (!android::base::ReadFileToString(path_, &content)) { MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_); return; } if (content.empty()) { MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_); return; } std::vector lines = android::base::Split(content, "\n"); if (lines.size() < 1) { MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(), (int)lines.size(), max_); return; } char* ptr; n_runs_ = strtol(lines[0].c_str(), &ptr, 10); average_max_ = strtol(ptr, nullptr, 10); if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS || average_max_ > STATS_MAX_AVERAGE) { MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str()); initial_max_ = Progress::kDefaultMax; } else { initial_max_ = average_max_; } max_ = initial_max_; MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_); } void Progress::Save() { int32_t total = n_runs_ * average_max_ + progress_; int32_t runs = n_runs_ + 1; int32_t average = floor(((float)total) / runs); MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average, path_.c_str()); if (path_.empty()) { return; } std::string content = android::base::StringPrintf("%d %d\n", runs, average); if (!android::base::WriteStringToFile(content, path_)) { MYLOGE("Could not save stats on %s\n", path_.c_str()); } } int32_t Progress::Get() const { return progress_; } bool Progress::Inc(int32_t delta_sec) { bool changed = false; if (delta_sec >= 0) { progress_ += delta_sec; if (progress_ > max_) { int32_t old_max = max_; max_ = floor((float)progress_ * growth_factor_); MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_); changed = true; } } return changed; } int32_t Progress::GetMax() const { return max_; } int32_t Progress::GetInitialMax() const { return initial_max_; } void Progress::Dump(int fd, const std::string& prefix) const { const char* pr = prefix.c_str(); dprintf(fd, "%sprogress: %d\n", pr, progress_); dprintf(fd, "%smax: %d\n", pr, max_); dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_); dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_); dprintf(fd, "%spath: %s\n", pr, path_.c_str()); dprintf(fd, "%sn_runs: %d\n", pr, n_runs_); dprintf(fd, "%saverage_max: %d\n", pr, average_max_); } bool Dumpstate::IsZipping() const { return zip_writer_ != nullptr; } std::string Dumpstate::GetPath(const std::string& suffix) const { return GetPath(bugreport_internal_dir_, suffix); } std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), name_.c_str(), suffix.c_str()); } void Dumpstate::SetProgress(std::unique_ptr progress) { progress_ = std::move(progress); } void for_each_userid(void (*func)(int), const char *header) { std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf( "for_each_userid(%s)", header); DurationReporter duration_reporter(title); if (PropertiesHelper::IsDryRun()) return; DIR *d; struct dirent *de; if (header) printf("\n------ %s ------\n", header); func(0); if (!(d = opendir("/data/system/users"))) { printf("Failed to open /data/system/users (%s)\n", strerror(errno)); return; } while ((de = readdir(d))) { int userid; if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { continue; } func(userid); } closedir(d); } static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { DIR *d; struct dirent *de; if (!(d = opendir("/proc"))) { printf("Failed to open /proc (%s)\n", strerror(errno)); return; } if (header) printf("\n------ %s ------\n", header); while ((de = readdir(d))) { if (ds.IsUserConsentDenied()) { MYLOGE( "Returning early because user denied consent to share bugreport with calling app."); closedir(d); return; } int pid; int fd; char cmdpath[255]; char cmdline[255]; if (!(pid = atoi(de->d_name))) { continue; } memset(cmdline, 0, sizeof(cmdline)); snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); close(fd); if (cmdline[0]) { helper(pid, cmdline, arg); continue; } } // if no cmdline, a kernel thread has comm snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); close(fd); if (cmdline[1]) { cmdline[0] = '['; size_t len = strcspn(cmdline, "\f\b\r\n"); cmdline[len] = ']'; cmdline[len+1] = '\0'; } } if (!cmdline[0]) { strcpy(cmdline, "N/A"); } helper(pid, cmdline, arg); } closedir(d); } static void for_each_pid_helper(int pid, const char *cmdline, void *arg) { for_each_pid_func *func = (for_each_pid_func*) arg; func(pid, cmdline); } void for_each_pid(for_each_pid_func func, const char *header) { std::string title = header == nullptr ? "for_each_pid" : android::base::StringPrintf("for_each_pid(%s)", header); DurationReporter duration_reporter(title); if (PropertiesHelper::IsDryRun()) return; __for_each_pid(for_each_pid_helper, header, (void *) func); } static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { DIR *d; struct dirent *de; char taskpath[255]; for_each_tid_func *func = (for_each_tid_func *) arg; snprintf(taskpath, sizeof(taskpath), "/proc/%d/task", pid); if (!(d = opendir(taskpath))) { printf("Failed to open %s (%s)\n", taskpath, strerror(errno)); return; } func(pid, pid, cmdline); while ((de = readdir(d))) { if (ds.IsUserConsentDenied()) { MYLOGE( "Returning early because user denied consent to share bugreport with calling app."); closedir(d); return; } int tid; int fd; char commpath[255]; char comm[255]; if (!(tid = atoi(de->d_name))) { continue; } if (tid == pid) continue; snprintf(commpath, sizeof(commpath), "/proc/%d/comm", tid); memset(comm, 0, sizeof(comm)); if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY | O_CLOEXEC))) < 0) { strcpy(comm, "N/A"); } else { char *c; TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); close(fd); c = strrchr(comm, '\n'); if (c) { *c = '\0'; } } func(pid, tid, comm); } closedir(d); } void for_each_tid(for_each_tid_func func, const char *header) { std::string title = header == nullptr ? "for_each_tid" : android::base::StringPrintf("for_each_tid(%s)", header); DurationReporter duration_reporter(title); if (PropertiesHelper::IsDryRun()) return; __for_each_pid(for_each_tid_helper, header, (void *) func); } void show_wchan(int pid, int tid, const char *name) { if (PropertiesHelper::IsDryRun()) return; char path[255]; char buffer[255]; int fd, ret, save_errno; char name_buffer[255]; memset(buffer, 0, sizeof(buffer)); snprintf(path, sizeof(path), "/proc/%d/wchan", tid); if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { printf("Failed to open '%s' (%s)\n", path, strerror(errno)); return; } ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); save_errno = errno; close(fd); if (ret < 0) { printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); return; } snprintf(name_buffer, sizeof(name_buffer), "%*s%s", pid == tid ? 0 : 3, "", name); printf("%-7d %-32s %s\n", tid, name_buffer, buffer); return; } // print time in centiseconds static void snprcent(char *buffer, size_t len, size_t spc, unsigned long long time) { static long hz; // cache discovered hz if (hz <= 0) { hz = sysconf(_SC_CLK_TCK); if (hz <= 0) { hz = 1000; } } // convert to centiseconds time = (time * 100 + (hz / 2)) / hz; char str[16]; snprintf(str, sizeof(str), " %llu.%02u", time / 100, (unsigned)(time % 100)); size_t offset = strlen(buffer); snprintf(buffer + offset, (len > offset) ? len - offset : 0, "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); } // print permille as a percent static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { char str[16]; snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); size_t offset = strlen(buffer); snprintf(buffer + offset, (len > offset) ? len - offset : 0, "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); } void show_showtime(int pid, const char *name) { if (PropertiesHelper::IsDryRun()) return; char path[255]; char buffer[1023]; int fd, ret, save_errno; memset(buffer, 0, sizeof(buffer)); snprintf(path, sizeof(path), "/proc/%d/stat", pid); if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { printf("Failed to open '%s' (%s)\n", path, strerror(errno)); return; } ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); save_errno = errno; close(fd); if (ret < 0) { printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); return; } // field 14 is utime // field 15 is stime // field 42 is iotime unsigned long long utime = 0, stime = 0, iotime = 0; if (sscanf(buffer, "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d " "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ", &utime, &stime, &iotime) != 3) { return; } unsigned long long total = utime + stime; if (!total) { return; } unsigned permille = (iotime * 1000 + (total / 2)) / total; if (permille > 1000) { permille = 1000; } // try to beautify and stabilize columns at <80 characters snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); if ((name[0] != '[') || utime) { snprcent(buffer, sizeof(buffer), 57, utime); } snprcent(buffer, sizeof(buffer), 65, stime); if ((name[0] != '[') || iotime) { snprcent(buffer, sizeof(buffer), 73, iotime); } if (iotime) { snprdec(buffer, sizeof(buffer), 79, permille); } puts(buffer); // adds a trailing newline return; } void do_dmesg() { const char *title = "KERNEL LOG (dmesg)"; DurationReporter duration_reporter(title); printf("------ %s ------\n", title); if (PropertiesHelper::IsDryRun()) return; /* Get size of kernel buffer */ int size = klogctl(KLOG_SIZE_BUFFER, nullptr, 0); if (size <= 0) { printf("Unexpected klogctl return value: %d\n\n", size); return; } char *buf = (char *) malloc(size + 1); if (buf == nullptr) { printf("memory allocation failed\n\n"); return; } int retval = klogctl(KLOG_READ_ALL, buf, size); if (retval < 0) { printf("klogctl failure\n\n"); free(buf); return; } buf[retval] = '\0'; printf("%s\n\n", buf); free(buf); return; } void do_showmap(int pid, const char *name) { char title[255]; char arg[255]; snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name); snprintf(arg, sizeof(arg), "%d", pid); RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT); } int Dumpstate::DumpFile(const std::string& title, const std::string& path) { DurationReporter duration_reporter(title); int status = DumpFileToFd(STDOUT_FILENO, title, path); UpdateProgress(WEIGHT_FILE); return status; } int read_file_as_long(const char *path, long int *output) { int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); if (fd < 0) { int err = errno; MYLOGE("Error opening file descriptor for %s: %s\n", path, strerror(err)); return -1; } char buffer[50]; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); if (bytes_read == -1) { MYLOGE("Error reading file %s: %s\n", path, strerror(errno)); return -2; } if (bytes_read == 0) { MYLOGE("File %s is empty\n", path); return -3; } *output = atoi(buffer); return 0; } /* calls skip to gate calling dump_from_fd recursively * in the specified directory. dump_from_fd defaults to * dump_file_from_fd above when set to NULL. skip defaults * to false when set to NULL. dump_from_fd will always be * called with title NULL. */ int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), int (*dump_from_fd)(const char* title, const char* path, int fd)) { DurationReporter duration_reporter(title); DIR *dirp; struct dirent *d; char *newpath = nullptr; const char *slash = "/"; int retval = 0; if (!title.empty()) { printf("------ %s (%s) ------\n", title.c_str(), dir); } if (PropertiesHelper::IsDryRun()) return 0; if (dir[strlen(dir) - 1] == '/') { ++slash; } dirp = opendir(dir); if (dirp == nullptr) { retval = -errno; MYLOGE("%s: %s\n", dir, strerror(errno)); return retval; } if (!dump_from_fd) { dump_from_fd = dump_file_from_fd; } for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) { if ((d->d_name[0] == '.') && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) || (d->d_name[1] == '\0'))) { continue; } asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name, (d->d_type == DT_DIR) ? "/" : ""); if (!newpath) { retval = -errno; continue; } if (skip && (*skip)(newpath)) { continue; } if (d->d_type == DT_DIR) { int ret = dump_files("", newpath, skip, dump_from_fd); if (ret < 0) { retval = ret; } continue; } android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC))); if (fd.get() < 0) { retval = -1; printf("*** %s: %s\n", newpath, strerror(errno)); continue; } (*dump_from_fd)(nullptr, newpath, fd.get()); } closedir(dirp); if (!title.empty()) { printf("\n"); } return retval; } /* fd must have been opened with the flag O_NONBLOCK. With this flag set, * it's possible to avoid issues where opening the file itself can get * stuck. */ int dump_file_from_fd(const char *title, const char *path, int fd) { if (PropertiesHelper::IsDryRun()) return 0; int flags = fcntl(fd, F_GETFL); if (flags == -1) { printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno)); return -1; } else if (!(flags & O_NONBLOCK)) { printf("*** %s: fd must have O_NONBLOCK set.\n", path); return -1; } return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun()); } int Dumpstate::RunCommand(const std::string& title, const std::vector& full_command, const CommandOptions& options) { DurationReporter duration_reporter(title); int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options); /* TODO: for now we're simplifying the progress calculation by using the * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys, * where its weight should be much higher proportionally to its timeout. * Ideally, it should use a options.EstimatedDuration() instead...*/ UpdateProgress(options.Timeout()); return status; } void Dumpstate::RunDumpsys(const std::string& title, const std::vector& dumpsys_args, const CommandOptions& options, long dumpsysTimeoutMs) { long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); std::vector dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); RunCommand(title, dumpsys, options); } int open_socket(const char *service) { int s = android_get_control_socket(service); if (s < 0) { MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); return -1; } fcntl(s, F_SETFD, FD_CLOEXEC); if (listen(s, 4) < 0) { MYLOGE("listen(control socket): %s\n", strerror(errno)); return -1; } struct sockaddr addr; socklen_t alen = sizeof(addr); int fd = accept(s, &addr, &alen); if (fd < 0) { MYLOGE("accept(control socket): %s\n", strerror(errno)); return -1; } return fd; } /* redirect output to a service control socket */ bool redirect_to_socket(FILE* redirect, const char* service) { int fd = open_socket(service); if (fd == -1) { return false; } fflush(redirect); // TODO: handle dup2 failure TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); return true; } // TODO: should call is_valid_output_file and/or be merged into it. void create_parent_dirs(const char *path) { char *chp = const_cast (path); /* skip initial slash */ if (chp[0] == '/') chp++; /* create leading directories, if necessary */ struct stat dir_stat; while (chp && chp[0]) { chp = strchr(chp, '/'); if (chp) { *chp = 0; if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) { MYLOGI("Creating directory %s\n", path); if (mkdir(path, 0770)) { /* drwxrwx--- */ MYLOGE("Unable to create directory %s: %s\n", path, strerror(errno)); } else if (chown(path, AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno)); } } *chp++ = '/'; } } } bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { create_parent_dirs(path); int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); if (fd < 0) { MYLOGE("%s: %s\n", path, strerror(errno)); return false; } TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); return true; } bool redirect_to_file(FILE* redirect, char* path) { return _redirect_to_file(redirect, path, O_TRUNC); } bool redirect_to_existing_file(FILE* redirect, char* path) { return _redirect_to_file(redirect, path, O_APPEND); } void dump_route_tables() { DurationReporter duration_reporter("DUMP ROUTE TABLES"); if (PropertiesHelper::IsDryRun()) return; const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables"; ds.DumpFile("RT_TABLES", RT_TABLES_PATH); FILE* fp = fopen(RT_TABLES_PATH, "re"); if (!fp) { printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno)); return; } char table[16]; // Each line has an integer (the table number), a space, and a string (the table name). We only // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name. // Add a fixed max limit so this doesn't go awry. for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) { RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table}); RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table}); } fclose(fp); } // TODO: make this function thread safe if sections are generated in parallel. void Dumpstate::UpdateProgress(int32_t delta_sec) { if (progress_ == nullptr) { MYLOGE("UpdateProgress: progress_ not set\n"); return; } // Always update progess so stats can be tuned... bool max_changed = progress_->Inc(delta_sec); // ...but only notifiy listeners when necessary. if (!options_->do_progress_updates) return; int progress = progress_->Get(); int max = progress_->GetMax(); // adjusts max on the fly if (max_changed && listener_ != nullptr) { listener_->onMaxProgressUpdated(max); } int32_t last_update_delta = progress - last_updated_progress_; if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) { return; } last_updated_progress_ = progress; if (control_socket_fd_ >= 0) { dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max); fsync(control_socket_fd_); } int percent = 100 * progress / max; if (listener_ != nullptr) { if (percent % 5 == 0) { // We don't want to spam logcat, so only log multiples of 5. MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, percent); } else { // stderr is ignored on normal invocations, but useful when calling // /system/bin/dumpstate directly for debuggging. fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, percent); } // TODO(b/111441001): Remove in favor of onProgress listener_->onProgressUpdated(progress); listener_->onProgress(percent); } } void Dumpstate::TakeScreenshot(const std::string& path) { const std::string& real_path = path.empty() ? screenshot_path_ : path; int status = RunCommand("", {"/system/bin/screencap", "-p", real_path}, CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); if (status == 0) { MYLOGD("Screenshot saved on %s\n", real_path.c_str()); } else { MYLOGE("Failed to take screenshot on %s\n", real_path.c_str()); } } bool is_dir(const char* pathname) { struct stat info; if (stat(pathname, &info) == -1) { return false; } return S_ISDIR(info.st_mode); } time_t get_mtime(int fd, time_t default_mtime) { struct stat info; if (fstat(fd, &info) == -1) { return default_mtime; } return info.st_mtime; } void dump_emmc_ecsd(const char *ext_csd_path) { // List of interesting offsets struct hex { char str[2]; }; static const size_t EXT_CSD_REV = 192 * sizeof(hex); static const size_t EXT_PRE_EOL_INFO = 267 * sizeof(hex); static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268 * sizeof(hex); static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269 * sizeof(hex); std::string buffer; if (!android::base::ReadFileToString(ext_csd_path, &buffer)) { return; } printf("------ %s Extended CSD ------\n", ext_csd_path); if (buffer.length() < (EXT_CSD_REV + sizeof(hex))) { printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); return; } int ext_csd_rev = 0; std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex)); if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) { printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); return; } static const char *ver_str[] = { "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" }; printf("rev 1.%d (MMC %s)\n", ext_csd_rev, (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev] : "Unknown"); if (ext_csd_rev < 7) { printf("\n"); return; } if (buffer.length() < (EXT_PRE_EOL_INFO + sizeof(hex))) { printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); return; } int ext_pre_eol_info = 0; sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex)); if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) { printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); return; } static const char *eol_str[] = { "Undefined", "Normal", "Warning (consumed 80% of reserve)", "Urgent (consumed 90% of reserve)" }; printf( "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info, eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info : 0]); for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A; lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B; lifetime += sizeof(hex)) { int ext_device_life_time_est; static const char *est_str[] = { "Undefined", "0-10% of device lifetime used", "10-20% of device lifetime used", "20-30% of device lifetime used", "30-40% of device lifetime used", "40-50% of device lifetime used", "50-60% of device lifetime used", "60-70% of device lifetime used", "70-80% of device lifetime used", "80-90% of device lifetime used", "90-100% of device lifetime used", "Exceeded the maximum estimated device lifetime", }; if (buffer.length() < (lifetime + sizeof(hex))) { printf("*** %s: truncated content %zu\n", ext_csd_path, buffer.length()); break; } ext_device_life_time_est = 0; sub = buffer.substr(lifetime, sizeof(hex)); if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) { printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path, (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', sub.c_str()); continue; } printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n", (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', ext_device_life_time_est, est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0]))) ? ext_device_life_time_est : 0]); } printf("\n"); } cmds/dumpsys/0040755 0000000 0000000 00000000000 13756501734 012255 5ustar000000000 0000000 cmds/dumpsys/.clang-format0100644 0000000 0000000 00000000440 13756501734 014623 0ustar000000000 0000000 BasedOnStyle: Google AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: false AccessModifierOffset: -2 ColumnLimit: 100 CommentPragmas: NOLINT:.* DerivePointerAlignment: false IndentWidth: 4 PointerAlignment: Left TabWidth: 4 UseTab: Never PenaltyExcessCharacter: 32 cmds/dumpsys/Android.bp0100644 0000000 0000000 00000001405 13756501734 014155 0ustar000000000 0000000 cc_defaults { name: "dumpsys_defaults", cflags: [ "-Wall", "-Werror", ], srcs: [ "dumpsys.cpp", ], shared_libs: [ "libbase", "libutils", "liblog", "libbinder", ], static_libs: [ "libserviceutils", ], } // // Static library used in testing and executable // cc_library_static { name: "libdumpsys", defaults: ["dumpsys_defaults"], export_include_dirs: ["."], } // // Executable // cc_binary { name: "dumpsys", defaults: ["dumpsys_defaults"], srcs: [ "main.cpp", ], } cc_binary { name: "dumpsys_vendor", stem: "dumpsys", vendor: true, defaults: ["dumpsys_defaults"], srcs: [ "main.cpp", ], } cmds/dumpsys/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501734 015375 0ustar000000000 0000000 cmds/dumpsys/NOTICE0100644 0000000 0000000 00000024707 13756501734 013170 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS cmds/dumpsys/OWNERS0100644 0000000 0000000 00000000131 13756501734 013205 0ustar000000000 0000000 set noparent felipeal@google.com nandana@google.com jsharkey@android.com enh@google.com cmds/dumpsys/dumpsys.cpp0100644 0000000 0000000 00000037331 13756501734 014471 0ustar000000000 0000000 /* * Copyright (C) 2009 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dumpsys.h" using namespace android; using ::android::base::StringAppendF; using ::android::base::StringPrintf; using ::android::base::unique_fd; using ::android::base::WriteFully; using ::android::base::WriteStringToFd; static int sort_func(const String16* lhs, const String16* rhs) { return lhs->compare(*rhs); } static void usage() { fprintf(stderr, "usage: dumpsys\n" " To dump all services.\n" "or:\n" " dumpsys [-t TIMEOUT] [--priority LEVEL] [--help | -l | --skip SERVICES | " "SERVICE [ARGS]]\n" " --help: shows this help\n" " -l: only list services, do not dump them\n" " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" " --proto: filter services that support dumping data in proto format. Dumps\n" " will be in proto format.\n" " --priority LEVEL: filter services based on specified priority\n" " LEVEL must be one of CRITICAL | HIGH | NORMAL\n" " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n" " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n"); } static bool IsSkipped(const Vector& skipped, const String16& service) { for (const auto& candidate : skipped) { if (candidate == service) { return true; } } return false; } static bool ConvertPriorityTypeToBitmask(const String16& type, int& bitmask) { if (type == PriorityDumper::PRIORITY_ARG_CRITICAL) { bitmask = IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL; return true; } if (type == PriorityDumper::PRIORITY_ARG_HIGH) { bitmask = IServiceManager::DUMP_FLAG_PRIORITY_HIGH; return true; } if (type == PriorityDumper::PRIORITY_ARG_NORMAL) { bitmask = IServiceManager::DUMP_FLAG_PRIORITY_NORMAL; return true; } return false; } String16 ConvertBitmaskToPriorityType(int bitmask) { if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { return String16(PriorityDumper::PRIORITY_ARG_CRITICAL); } if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) { return String16(PriorityDumper::PRIORITY_ARG_HIGH); } if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) { return String16(PriorityDumper::PRIORITY_ARG_NORMAL); } return String16(""); } int Dumpsys::main(int argc, char* const argv[]) { Vector services; Vector args; String16 priorityType; Vector skippedServices; Vector protoServices; bool showListOnly = false; bool skipServices = false; bool asProto = false; int timeoutArgMs = 10000; int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL; static struct option longOptions[] = {{"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0}, {"skip", no_argument, 0, 0}, {"help", no_argument, 0, 0}, {0, 0, 0, 0}}; // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but // happens on test cases). optind = 1; while (1) { int c; int optionIndex = 0; c = getopt_long(argc, argv, "+t:T:l", longOptions, &optionIndex); if (c == -1) { break; } switch (c) { case 0: if (!strcmp(longOptions[optionIndex].name, "skip")) { skipServices = true; } else if (!strcmp(longOptions[optionIndex].name, "proto")) { asProto = true; } else if (!strcmp(longOptions[optionIndex].name, "help")) { usage(); return 0; } else if (!strcmp(longOptions[optionIndex].name, "priority")) { priorityType = String16(String8(optarg)); if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) { fprintf(stderr, "\n"); usage(); return -1; } } break; case 't': { char* endptr; timeoutArgMs = strtol(optarg, &endptr, 10); timeoutArgMs = timeoutArgMs * 1000; if (*endptr != '\0' || timeoutArgMs <= 0) { fprintf(stderr, "Error: invalid timeout(seconds) number: '%s'\n", optarg); return -1; } } break; case 'T': { char* endptr; timeoutArgMs = strtol(optarg, &endptr, 10); if (*endptr != '\0' || timeoutArgMs <= 0) { fprintf(stderr, "Error: invalid timeout(milliseconds) number: '%s'\n", optarg); return -1; } } break; case 'l': showListOnly = true; break; default: fprintf(stderr, "\n"); usage(); return -1; } } for (int i = optind; i < argc; i++) { if (skipServices) { skippedServices.add(String16(argv[i])); } else { if (i == optind) { services.add(String16(argv[i])); } else { args.add(String16(argv[i])); } } } if ((skipServices && skippedServices.empty()) || (showListOnly && (!services.empty() || !skippedServices.empty()))) { usage(); return -1; } if (services.empty() || showListOnly) { services = listServices(priorityFlags, asProto); setServiceArgs(args, asProto, priorityFlags); } const size_t N = services.size(); if (N > 1) { // first print a list of the current services aout << "Currently running services:" << endl; for (size_t i=0; i service = sm_->checkService(services[i]); if (service != nullptr) { bool skipped = IsSkipped(skippedServices, services[i]); aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl; } } } if (showListOnly) { return 0; } for (size_t i = 0; i < N; i++) { const String16& serviceName = services[i]; if (IsSkipped(skippedServices, serviceName)) continue; if (startDumpThread(serviceName, args) == OK) { bool addSeparator = (N > 1); if (addSeparator) { writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags); } std::chrono::duration elapsedDuration; size_t bytesWritten = 0; status_t status = writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs), asProto, elapsedDuration, bytesWritten); if (status == TIMED_OUT) { aout << endl << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs << "ms) EXPIRED ***" << endl << endl; } if (addSeparator) { writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration); } bool dumpComplete = (status == OK); stopDumpThread(dumpComplete); } } return 0; } Vector Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const { Vector services = sm_->listServices(priorityFilterFlags); services.sort(sort_func); if (filterByProto) { Vector protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO); protoServices.sort(sort_func); Vector intersection; std::set_intersection(services.begin(), services.end(), protoServices.begin(), protoServices.end(), std::back_inserter(intersection)); services = std::move(intersection); } return services; } void Dumpsys::setServiceArgs(Vector& args, bool asProto, int priorityFlags) { // Add proto flag if dumping service as proto. if (asProto) { args.insertAt(String16(PriorityDumper::PROTO_ARG), 0); } // Add -a (dump all) flag if dumping all services, dumping normal services or // services not explicitly registered to a priority bucket (default services). if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) || (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) || (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT)) { args.insertAt(String16("-a"), 0); } // Add priority flags when dumping services registered to a specific priority bucket. if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) || (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) || (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) { String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags); args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0); args.insertAt(priorityType, 1); } } status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector& args) { sp service = sm_->checkService(serviceName); if (service == nullptr) { aerr << "Can't find service: " << serviceName << endl; return NAME_NOT_FOUND; } int sfd[2]; if (pipe(sfd) != 0) { aerr << "Failed to create pipe to dump service info for " << serviceName << ": " << strerror(errno) << endl; return -errno; } redirectFd_ = unique_fd(sfd[0]); unique_fd remote_end(sfd[1]); sfd[0] = sfd[1] = -1; // dump blocks until completion, so spawn a thread.. activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable { int err = service->dump(remote_end.get(), args); // It'd be nice to be able to close the remote end of the socketpair before the dump // call returns, to terminate our reads if the other end closes their copy of the // file descriptor, but then hangs for some reason. There doesn't seem to be a good // way to do this, though. remote_end.reset(); if (err != 0) { aerr << "Error dumping service info: (" << strerror(err) << ") " << serviceName << endl; } }); return OK; } void Dumpsys::stopDumpThread(bool dumpComplete) { if (dumpComplete) { activeThread_.join(); } else { activeThread_.detach(); } /* close read end of the dump output redirection pipe */ redirectFd_.reset(); } void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const { std::string msg( "----------------------------------------" "---------------------------------------\n"); if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL || priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL || priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) { StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str()); } else { String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags); StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(), String8(serviceName).c_str()); } WriteStringToFd(msg, fd); } status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout, bool asProto, std::chrono::duration& elapsedDuration, size_t& bytesWritten) const { status_t status = OK; size_t totalBytes = 0; auto start = std::chrono::steady_clock::now(); auto end = start + timeout; int serviceDumpFd = redirectFd_.get(); if (serviceDumpFd == -1) { return INVALID_OPERATION; } struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN}; while (true) { // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout. auto time_left_ms = [end]() { auto now = std::chrono::steady_clock::now(); auto diff = std::chrono::duration_cast(end - now); return std::max(diff.count(), 0LL); }; int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { aerr << "Error in poll while dumping service " << serviceName << " : " << strerror(errno) << endl; status = -errno; break; } else if (rc == 0) { status = TIMED_OUT; break; } char buf[4096]; rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf))); if (rc < 0) { aerr << "Failed to read while dumping service " << serviceName << ": " << strerror(errno) << endl; status = -errno; break; } else if (rc == 0) { // EOF. break; } if (!WriteFully(fd, buf, rc)) { aerr << "Failed to write while dumping service " << serviceName << ": " << strerror(errno) << endl; status = -errno; break; } totalBytes += rc; } if ((status == TIMED_OUT) && (!asProto)) { std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n", String8(serviceName).string(), timeout.count()); WriteStringToFd(msg, fd); } elapsedDuration = std::chrono::steady_clock::now() - start; bytesWritten = totalBytes; return status; } void Dumpsys::writeDumpFooter(int fd, const String16& serviceName, const std::chrono::duration& elapsedDuration) const { using std::chrono::system_clock; const auto finish = system_clock::to_time_t(system_clock::now()); std::tm finish_tm; localtime_r(&finish, &finish_tm); std::stringstream oss; oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S"); std::string msg = StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n", elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str()); WriteStringToFd(msg, fd); } cmds/dumpsys/dumpsys.h0100644 0000000 0000000 00000011100 13756501734 014120 0ustar000000000 0000000 /* * 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. */ #ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_ #define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_ #include #include #include namespace android { class Dumpsys { public: explicit Dumpsys(android::IServiceManager* sm) : sm_(sm) { } /** * Main entry point into dumpsys. */ int main(int argc, char* const argv[]); /** * Returns a list of services. * @param priorityFlags filter services by specified priorities * @param supportsProto filter services that support proto dumps * @return list of services */ Vector listServices(int priorityFlags, bool supportsProto) const; /** * Modifies @{code args} to add additional arguments to indicate if the service * must dump as proto or dump to a certian priority bucket. * @param args initial list of arguments to pass to service dump method. * @param asProto dump service as proto by passing an additional --proto arg * @param priorityFlags indicates priority of dump by passing additional priority args * to the service */ static void setServiceArgs(Vector& args, bool asProto, int priorityFlags); /** * Starts a thread to connect to a service and get its dump output. The thread redirects * the output to a pipe. Thread must be stopped by a subsequent callto {@code * stopDumpThread}. * @param serviceName * @param args list of arguments to pass to service dump method. * @return {@code OK} thread is started successfully. * {@code NAME_NOT_FOUND} service could not be found. * {@code != OK} error */ status_t startDumpThread(const String16& serviceName, const Vector& args); /** * Writes a section header to a file descriptor. * @param fd file descriptor to write data * @param serviceName * @param priorityFlags dump priority specified */ void writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const; /** * Redirects service dump to a file descriptor. This requires * {@code startDumpThread} to be called successfully otherwise the function will * return {@code INVALID_OPERATION}. * @param fd file descriptor to write data * @param serviceName * @param timeout timeout to terminate the dump if not completed * @param asProto used to supresses additional output to the fd such as timeout * error messages * @param elapsedDuration returns elapsed time in seconds * @param bytesWritten returns number of bytes written * @return {@code OK} if successful * {@code TIMED_OUT} dump timed out * {@code INVALID_OPERATION} invalid state * {@code != OK} error */ status_t writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout, bool asProto, std::chrono::duration& elapsedDuration, size_t& bytesWritten) const; /** * Writes a section footer to a file descriptor with duration info. * @param fd file descriptor to write data * @param serviceName * @param elapsedDuration duration of dump */ void writeDumpFooter(int fd, const String16& serviceName, const std::chrono::duration& elapsedDuration) const; /** * Terminates dump thread. * @param dumpComplete If {@code true}, indicates the dump was successfully completed and * tries to join the thread. Otherwise thread is detached. */ void stopDumpThread(bool dumpComplete); /** * Returns file descriptor of the pipe used to dump service data. This assumes * {@code startDumpThread} was called successfully. */ int getDumpFd() const { return redirectFd_.get(); } private: android::IServiceManager* sm_; std::thread activeThread_; mutable android::base::unique_fd redirectFd_; }; } #endif // FRAMEWORK_NATIVE_CMD_DUMPSYS_H_ cmds/dumpsys/main.cpp0100644 0000000 0000000 00000002332 13756501734 013702 0ustar000000000 0000000 /* * Copyright (C) 2009 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. */ /* * Command that dumps interesting system state to the log. */ #include "dumpsys.h" #include #include #include #include using namespace android; int main(int argc, char* const argv[]) { signal(SIGPIPE, SIG_IGN); sp sm = defaultServiceManager(); fflush(stdout); if (sm == nullptr) { ALOGE("Unable to get default service manager!"); aerr << "dumpsys: Unable to get default service manager!" << endl; return 20; } Dumpsys dumpsys(sm.get()); return dumpsys.main(argc, argv); } cmds/dumpsys/tests/0040755 0000000 0000000 00000000000 13756501734 013417 5ustar000000000 0000000 cmds/dumpsys/tests/Android.bp0100644 0000000 0000000 00000000571 13756501734 015322 0ustar000000000 0000000 // Build the unit tests for dumpsys cc_test { name: "dumpsys_test", test_suites: ["device-tests"], srcs: ["dumpsys_test.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", "libutils", ], static_libs: [ "libdumpsys", "libgmock", "libserviceutils", ], clang: true, } cmds/dumpsys/tests/AndroidTest.xml0100644 0000000 0000000 00000002306 13756501734 016357 0ustar000000000 0000000 cmds/dumpsys/tests/dumpsys_test.cpp0100644 0000000 0000000 00000050315 13756501734 016667 0ustar000000000 0000000 /* * 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. */ #include "../dumpsys.h" #include #include #include #include #include #include #include #include using namespace android; using ::testing::_; using ::testing::Action; using ::testing::ActionInterface; using ::testing::DoAll; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::MakeAction; using ::testing::Mock; using ::testing::Not; using ::testing::Return; using ::testing::StrEq; using ::testing::Test; using ::testing::WithArg; using ::testing::internal::CaptureStderr; using ::testing::internal::CaptureStdout; using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; class ServiceManagerMock : public IServiceManager { public: MOCK_CONST_METHOD1(getService, sp(const String16&)); MOCK_CONST_METHOD1(checkService, sp(const String16&)); MOCK_METHOD4(addService, status_t(const String16&, const sp&, bool, int)); MOCK_METHOD1(listServices, Vector(int)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; class BinderMock : public BBinder { public: BinderMock() { } MOCK_METHOD2(dump, status_t(int, const Vector&)); }; // gmock black magic to provide a WithArg<0>(WriteOnFd(output)) matcher typedef void WriteOnFdFunction(int); class WriteOnFdAction : public ActionInterface { public: explicit WriteOnFdAction(const std::string& output) : output_(output) { } virtual Result Perform(const ArgumentTuple& args) { int fd = ::testing::get<0>(args); android::base::WriteStringToFd(output_, fd); } private: std::string output_; }; // Matcher used to emulate dump() by writing on its file descriptor. Action WriteOnFd(const std::string& output) { return MakeAction(new WriteOnFdAction(output)); } // Matcher for args using Android's Vector format // TODO: move it to some common testing library MATCHER_P(AndroidElementsAre, expected, "") { std::ostringstream errors; if (arg.size() != expected.size()) { errors << " sizes do not match (expected " << expected.size() << ", got " << arg.size() << ")\n"; } int i = 0; std::ostringstream actual_stream, expected_stream; for (const String16& actual : arg) { std::string actual_str = String8(actual).c_str(); std::string expected_str = expected[i]; actual_stream << "'" << actual_str << "' "; expected_stream << "'" << expected_str << "' "; if (actual_str != expected_str) { errors << " element mismatch at index " << i << "\n"; } i++; } if (!errors.str().empty()) { errors << "\nExpected args: " << expected_stream.str() << "\nActual args: " << actual_stream.str(); *result_listener << errors.str(); return false; } return true; } // Custom action to sleep for timeout seconds ACTION_P(Sleep, timeout) { sleep(timeout); } class DumpsysTest : public Test { public: DumpsysTest() : sm_(), dump_(&sm_), stdout_(), stderr_() { } void ExpectListServices(std::vector services) { Vector services16; for (auto& service : services) { services16.add(String16(service.c_str())); } EXPECT_CALL(sm_, listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL)) .WillRepeatedly(Return(services16)); } void ExpectListServicesWithPriority(std::vector services, int dumpFlags) { Vector services16; for (auto& service : services) { services16.add(String16(service.c_str())); } EXPECT_CALL(sm_, listServices(dumpFlags)).WillRepeatedly(Return(services16)); } sp ExpectCheckService(const char* name, bool running = true) { sp binder_mock; if (running) { binder_mock = new BinderMock; } EXPECT_CALL(sm_, checkService(String16(name))).WillRepeatedly(Return(binder_mock)); return binder_mock; } void ExpectDump(const char* name, const std::string& output) { sp binder_mock = ExpectCheckService(name); EXPECT_CALL(*binder_mock, dump(_, _)) .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0))); } void ExpectDumpWithArgs(const char* name, std::vector args, const std::string& output) { sp binder_mock = ExpectCheckService(name); EXPECT_CALL(*binder_mock, dump(_, AndroidElementsAre(args))) .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0))); } sp ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) { sp binder_mock = ExpectCheckService(name); EXPECT_CALL(*binder_mock, dump(_, _)) .WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0))); return binder_mock; } void CallMain(const std::vector& args) { const char* argv[1024] = {"/some/virtual/dir/dumpsys"}; int argc = (int)args.size() + 1; int i = 1; for (const std::string& arg : args) { argv[i++] = arg.c_str(); } CaptureStdout(); CaptureStderr(); int status = dump_.main(argc, const_cast(argv)); stdout_ = GetCapturedStdout(); stderr_ = GetCapturedStderr(); EXPECT_THAT(status, Eq(0)); } void CallSingleService(const String16& serviceName, Vector& args, int priorityFlags, bool supportsProto, std::chrono::duration& elapsedDuration, size_t& bytesWritten) { CaptureStdout(); CaptureStderr(); dump_.setServiceArgs(args, supportsProto, priorityFlags); status_t status = dump_.startDumpThread(serviceName, args); EXPECT_THAT(status, Eq(0)); status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false, elapsedDuration, bytesWritten); EXPECT_THAT(status, Eq(0)); dump_.stopDumpThread(/* dumpCompleted = */ true); stdout_ = GetCapturedStdout(); stderr_ = GetCapturedStderr(); } void AssertRunningServices(const std::vector& services) { std::string expected; if (services.size() > 1) { expected.append("Currently running services:\n"); } for (const std::string& service : services) { expected.append(" ").append(service).append("\n"); } EXPECT_THAT(stdout_, HasSubstr(expected)); } void AssertOutput(const std::string& expected) { EXPECT_THAT(stdout_, StrEq(expected)); } void AssertOutputContains(const std::string& expected) { EXPECT_THAT(stdout_, HasSubstr(expected)); } void AssertDumped(const std::string& service, const std::string& dump) { EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump)); EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: ")); } void AssertDumpedWithPriority(const std::string& service, const std::string& dump, const char16_t* priorityType) { std::string priority = String8(priorityType).c_str(); EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + priority + " " + service + ":\n" + dump)); EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: ")); } void AssertNotDumped(const std::string& dump) { EXPECT_THAT(stdout_, Not(HasSubstr(dump))); } void AssertStopped(const std::string& service) { EXPECT_THAT(stderr_, HasSubstr("Can't find service: " + service + "\n")); } ServiceManagerMock sm_; Dumpsys dump_; private: std::string stdout_, stderr_; }; // Tests 'dumpsys -l' when all services are running TEST_F(DumpsysTest, ListAllServices) { ExpectListServices({"Locksmith", "Valet"}); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); CallMain({"-l"}); AssertRunningServices({"Locksmith", "Valet"}); } // Tests 'dumpsys -l' when a service is not running TEST_F(DumpsysTest, ListRunningServices) { ExpectListServices({"Locksmith", "Valet"}); ExpectCheckService("Locksmith"); ExpectCheckService("Valet", false); CallMain({"-l"}); AssertRunningServices({"Locksmith"}); AssertNotDumped({"Valet"}); } // Tests 'dumpsys -l --priority HIGH' TEST_F(DumpsysTest, ListAllServicesWithPriority) { ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); CallMain({"-l", "--priority", "HIGH"}); AssertRunningServices({"Locksmith", "Valet"}); } // Tests 'dumpsys -l --priority HIGH' with and empty list TEST_F(DumpsysTest, ListEmptyServicesWithPriority) { ExpectListServicesWithPriority({}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); CallMain({"-l", "--priority", "HIGH"}); AssertRunningServices({}); } // Tests 'dumpsys -l --proto' TEST_F(DumpsysTest, ListAllServicesWithProto) { ExpectListServicesWithPriority({"Locksmith", "Valet", "Car"}, IServiceManager::DUMP_FLAG_PRIORITY_ALL); ExpectListServicesWithPriority({"Valet", "Car"}, IServiceManager::DUMP_FLAG_PROTO); ExpectCheckService("Car"); ExpectCheckService("Valet"); CallMain({"-l", "--proto"}); AssertRunningServices({"Car", "Valet"}); } // Tests 'dumpsys service_name' on a service is running TEST_F(DumpsysTest, DumpRunningService) { ExpectDump("Valet", "Here's your car"); CallMain({"Valet"}); AssertOutput("Here's your car"); } // Tests 'dumpsys -t 1 service_name' on a service that times out after 2s TEST_F(DumpsysTest, DumpRunningServiceTimeoutInSec) { sp binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car"); CallMain({"-t", "1", "Valet"}); AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1000ms) EXPIRED"); AssertNotDumped("Here's your car"); // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp Mock::AllowLeak(binder_mock.get()); } // Tests 'dumpsys -T 500 service_name' on a service that times out after 2s TEST_F(DumpsysTest, DumpRunningServiceTimeoutInMs) { sp binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car"); CallMain({"-T", "500", "Valet"}); AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (500ms) EXPIRED"); AssertNotDumped("Here's your car"); // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp Mock::AllowLeak(binder_mock.get()); } // Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running TEST_F(DumpsysTest, DumpWithArgsRunningService) { ExpectDumpWithArgs("SERVICE", {"Y", "U", "NO", "HANDLE", "ARGS"}, "I DO!"); CallMain({"SERVICE", "Y", "U", "NO", "HANDLE", "ARGS"}); AssertOutput("I DO!"); } // Tests dumpsys passes the -a flag when called on all services TEST_F(DumpsysTest, PassAllFlagsToServices) { ExpectListServices({"Locksmith", "Valet"}); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); ExpectDumpWithArgs("Locksmith", {"-a"}, "dumped1"); ExpectDumpWithArgs("Valet", {"-a"}, "dumped2"); CallMain({"-T", "500"}); AssertDumped("Locksmith", "dumped1"); AssertDumped("Valet", "dumped2"); } // Tests dumpsys passes the -a flag when called on NORMAL priority services TEST_F(DumpsysTest, PassAllFlagsToNormalServices) { ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); ExpectDumpWithArgs("Locksmith", {"--dump-priority", "NORMAL", "-a"}, "dump1"); ExpectDumpWithArgs("Valet", {"--dump-priority", "NORMAL", "-a"}, "dump2"); CallMain({"--priority", "NORMAL"}); AssertDumped("Locksmith", "dump1"); AssertDumped("Valet", "dump2"); } // Tests dumpsys passes only priority flags when called on CRITICAL priority services TEST_F(DumpsysTest, PassPriorityFlagsToCriticalServices) { ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); ExpectDumpWithArgs("Locksmith", {"--dump-priority", "CRITICAL"}, "dump1"); ExpectDumpWithArgs("Valet", {"--dump-priority", "CRITICAL"}, "dump2"); CallMain({"--priority", "CRITICAL"}); AssertDumpedWithPriority("Locksmith", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL); AssertDumpedWithPriority("Valet", "dump2", PriorityDumper::PRIORITY_ARG_CRITICAL); } // Tests dumpsys passes only priority flags when called on HIGH priority services TEST_F(DumpsysTest, PassPriorityFlagsToHighServices) { ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); ExpectDumpWithArgs("Locksmith", {"--dump-priority", "HIGH"}, "dump1"); ExpectDumpWithArgs("Valet", {"--dump-priority", "HIGH"}, "dump2"); CallMain({"--priority", "HIGH"}); AssertDumpedWithPriority("Locksmith", "dump1", PriorityDumper::PRIORITY_ARG_HIGH); AssertDumpedWithPriority("Valet", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); } // Tests 'dumpsys' with no arguments TEST_F(DumpsysTest, DumpMultipleServices) { ExpectListServices({"running1", "stopped2", "running3"}); ExpectDump("running1", "dump1"); ExpectCheckService("stopped2", false); ExpectDump("running3", "dump3"); CallMain({}); AssertRunningServices({"running1", "running3"}); AssertDumped("running1", "dump1"); AssertStopped("stopped2"); AssertDumped("running3", "dump3"); } // Tests 'dumpsys --skip skipped3 skipped5', which should skip these services TEST_F(DumpsysTest, DumpWithSkip) { ExpectListServices({"running1", "stopped2", "skipped3", "running4", "skipped5"}); ExpectDump("running1", "dump1"); ExpectCheckService("stopped2", false); ExpectDump("skipped3", "dump3"); ExpectDump("running4", "dump4"); ExpectDump("skipped5", "dump5"); CallMain({"--skip", "skipped3", "skipped5"}); AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"}); AssertDumped("running1", "dump1"); AssertDumped("running4", "dump4"); AssertStopped("stopped2"); AssertNotDumped("dump3"); AssertNotDumped("dump5"); } // Tests 'dumpsys --skip skipped3 skipped5 --priority CRITICAL', which should skip these services TEST_F(DumpsysTest, DumpWithSkipAndPriority) { ExpectListServicesWithPriority({"running1", "stopped2", "skipped3", "running4", "skipped5"}, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); ExpectDump("running1", "dump1"); ExpectCheckService("stopped2", false); ExpectDump("skipped3", "dump3"); ExpectDump("running4", "dump4"); ExpectDump("skipped5", "dump5"); CallMain({"--priority", "CRITICAL", "--skip", "skipped3", "skipped5"}); AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"}); AssertDumpedWithPriority("running1", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL); AssertDumpedWithPriority("running4", "dump4", PriorityDumper::PRIORITY_ARG_CRITICAL); AssertStopped("stopped2"); AssertNotDumped("dump3"); AssertNotDumped("dump5"); } // Tests 'dumpsys --priority CRITICAL' TEST_F(DumpsysTest, DumpWithPriorityCritical) { ExpectListServicesWithPriority({"runningcritical1", "runningcritical2"}, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); ExpectDump("runningcritical1", "dump1"); ExpectDump("runningcritical2", "dump2"); CallMain({"--priority", "CRITICAL"}); AssertRunningServices({"runningcritical1", "runningcritical2"}); AssertDumpedWithPriority("runningcritical1", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL); AssertDumpedWithPriority("runningcritical2", "dump2", PriorityDumper::PRIORITY_ARG_CRITICAL); } // Tests 'dumpsys --priority HIGH' TEST_F(DumpsysTest, DumpWithPriorityHigh) { ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); ExpectDump("runninghigh1", "dump1"); ExpectDump("runninghigh2", "dump2"); CallMain({"--priority", "HIGH"}); AssertRunningServices({"runninghigh1", "runninghigh2"}); AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH); AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); } // Tests 'dumpsys --priority NORMAL' TEST_F(DumpsysTest, DumpWithPriorityNormal) { ExpectListServicesWithPriority({"runningnormal1", "runningnormal2"}, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); ExpectDump("runningnormal1", "dump1"); ExpectDump("runningnormal2", "dump2"); CallMain({"--priority", "NORMAL"}); AssertRunningServices({"runningnormal1", "runningnormal2"}); AssertDumped("runningnormal1", "dump1"); AssertDumped("runningnormal2", "dump2"); } // Tests 'dumpsys --proto' TEST_F(DumpsysTest, DumpWithProto) { ExpectListServicesWithPriority({"run8", "run1", "run2", "run5"}, IServiceManager::DUMP_FLAG_PRIORITY_ALL); ExpectListServicesWithPriority({"run3", "run2", "run4", "run8"}, IServiceManager::DUMP_FLAG_PROTO); ExpectDump("run2", "dump1"); ExpectDump("run8", "dump2"); CallMain({"--proto"}); AssertRunningServices({"run2", "run8"}); AssertDumped("run2", "dump1"); AssertDumped("run8", "dump2"); } // Tests 'dumpsys --priority HIGH --proto' TEST_F(DumpsysTest, DumpWithPriorityHighAndProto) { ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); ExpectListServicesWithPriority({"runninghigh1", "runninghigh2", "runninghigh3"}, IServiceManager::DUMP_FLAG_PROTO); ExpectDump("runninghigh1", "dump1"); ExpectDump("runninghigh2", "dump2"); CallMain({"--priority", "HIGH", "--proto"}); AssertRunningServices({"runninghigh1", "runninghigh2"}); AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH); AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); } TEST_F(DumpsysTest, GetBytesWritten) { const char* serviceName = "service2"; const char* dumpContents = "dump1"; ExpectDump(serviceName, dumpContents); String16 service(serviceName); Vector args; std::chrono::duration elapsedDuration; size_t bytesWritten; CallSingleService(service, args, IServiceManager::DUMP_FLAG_PRIORITY_ALL, /* as_proto = */ false, elapsedDuration, bytesWritten); AssertOutput(dumpContents); EXPECT_THAT(bytesWritten, Eq(strlen(dumpContents))); } TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) { std::chrono::duration elapsedDuration; size_t bytesWritten; status_t status = dump_.writeDump(STDOUT_FILENO, String16("service"), std::chrono::milliseconds(500), /* as_proto = */ false, elapsedDuration, bytesWritten); EXPECT_THAT(status, Eq(INVALID_OPERATION)); }cmds/flatland/0040755 0000000 0000000 00000000000 13756501734 012336 5ustar000000000 0000000 cmds/flatland/Android.mk0100644 0000000 0000000 00000001120 13756501734 014236 0ustar000000000 0000000 local_target_dir := $(TARGET_OUT_DATA)/local/tmp LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ Composers.cpp \ GLHelper.cpp \ Renderers.cpp \ Main.cpp \ LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE:= flatland LOCAL_MODULE_TAGS := tests LOCAL_MODULE_PATH := $(local_target_dir) LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := flatland LOCAL_MODULE_STEM_64 := flatland64 LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv2 \ libcutils \ libgui \ libui \ libutils \ include $(BUILD_EXECUTABLE) cmds/flatland/Composers.cpp0100644 0000000 0000000 00000017266 13756501734 015025 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #include #include #include "Flatland.h" #include "GLHelper.h" namespace android { class Blitter { public: bool setUp(GLHelper* helper) { bool result; result = helper->getShaderProgram("Blit", &mBlitPgm); if (!result) { return false; } mPosAttribLoc = glGetAttribLocation(mBlitPgm, "position"); mUVAttribLoc = glGetAttribLocation(mBlitPgm, "uv"); mUVToTexUniformLoc = glGetUniformLocation(mBlitPgm, "uvToTex"); mObjToNdcUniformLoc = glGetUniformLocation(mBlitPgm, "objToNdc"); mBlitSrcSamplerLoc = glGetUniformLocation(mBlitPgm, "blitSrc"); mModColorUniformLoc = glGetUniformLocation(mBlitPgm, "modColor"); return true; } bool blit(GLuint texName, const float* texMatrix, int32_t x, int32_t y, uint32_t w, uint32_t h) { float modColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; return modBlit(texName, texMatrix, modColor, x, y, w, h); } bool modBlit(GLuint texName, const float* texMatrix, float* modColor, int32_t x, int32_t y, uint32_t w, uint32_t h) { glUseProgram(mBlitPgm); GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp); float screenToNdc[16] = { 2.0f/float(vp[2]), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f/float(vp[3]), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, }; const float pos[] = { float(x), float(y), float(x+w), float(y), float(x), float(y+h), float(x+w), float(y+h), }; const float uv[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, }; glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos); glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv); glEnableVertexAttribArray(mPosAttribLoc); glEnableVertexAttribArray(mUVAttribLoc); glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, screenToNdc); glUniformMatrix4fv(mUVToTexUniformLoc, 1, GL_FALSE, texMatrix); glUniform4fv(mModColorUniformLoc, 1, modColor); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName); glUniform1i(mBlitSrcSamplerLoc, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(mPosAttribLoc); glDisableVertexAttribArray(mUVAttribLoc); if (glGetError() != GL_NO_ERROR) { fprintf(stderr, "GL error!\n"); } return true; } private: GLuint mBlitPgm; GLint mPosAttribLoc; GLint mUVAttribLoc; GLint mUVToTexUniformLoc; GLint mObjToNdcUniformLoc; GLint mBlitSrcSamplerLoc; GLint mModColorUniformLoc; }; class ComposerBase : public Composer { public: virtual ~ComposerBase() {} virtual bool setUp(const LayerDesc& desc, GLHelper* helper) { mLayerDesc = desc; return setUp(helper); } virtual void tearDown() { } virtual bool compose(GLuint /*texName*/, const sp& /*glc*/) { return true; } protected: virtual bool setUp(GLHelper* /*helper*/) { return true; } LayerDesc mLayerDesc; }; Composer* nocomp() { class NoComp : public ComposerBase { }; return new NoComp(); } Composer* opaque() { class OpaqueComp : public ComposerBase { virtual bool setUp(GLHelper* helper) { return mBlitter.setUp(helper); } virtual bool compose(GLuint texName, const sp& glc) { float texMatrix[16]; glc->getTransformMatrix(texMatrix); int32_t x = mLayerDesc.x; int32_t y = mLayerDesc.y; int32_t w = mLayerDesc.width; int32_t h = mLayerDesc.height; return mBlitter.blit(texName, texMatrix, x, y, w, h); } Blitter mBlitter; }; return new OpaqueComp(); } Composer* opaqueShrink() { class OpaqueComp : public ComposerBase { virtual bool setUp(GLHelper* helper) { mParity = false; return mBlitter.setUp(helper); } virtual bool compose(GLuint texName, const sp& glc) { float texMatrix[16]; glc->getTransformMatrix(texMatrix); int32_t x = mLayerDesc.x; int32_t y = mLayerDesc.y; int32_t w = mLayerDesc.width; int32_t h = mLayerDesc.height; mParity = !mParity; if (mParity) { x += w / 128; y += h / 128; w -= w / 64; h -= h / 64; } return mBlitter.blit(texName, texMatrix, x, y, w, h); } Blitter mBlitter; bool mParity; }; return new OpaqueComp(); } Composer* blend() { class BlendComp : public ComposerBase { virtual bool setUp(GLHelper* helper) { return mBlitter.setUp(helper); } virtual bool compose(GLuint texName, const sp& glc) { bool result; float texMatrix[16]; glc->getTransformMatrix(texMatrix); float modColor[4] = { .75f, .75f, .75f, .75f }; int32_t x = mLayerDesc.x; int32_t y = mLayerDesc.y; int32_t w = mLayerDesc.width; int32_t h = mLayerDesc.height; glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); result = mBlitter.modBlit(texName, texMatrix, modColor, x, y, w, h); if (!result) { return false; } glDisable(GL_BLEND); return true; } Blitter mBlitter; }; return new BlendComp(); } Composer* blendShrink() { class BlendShrinkComp : public ComposerBase { virtual bool setUp(GLHelper* helper) { mParity = false; return mBlitter.setUp(helper); } virtual bool compose(GLuint texName, const sp& glc) { bool result; float texMatrix[16]; glc->getTransformMatrix(texMatrix); float modColor[4] = { .75f, .75f, .75f, .75f }; int32_t x = mLayerDesc.x; int32_t y = mLayerDesc.y; int32_t w = mLayerDesc.width; int32_t h = mLayerDesc.height; mParity = !mParity; if (mParity) { x += w / 128; y += h / 128; w -= w / 64; h -= h / 64; } glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); result = mBlitter.modBlit(texName, texMatrix, modColor, x, y, w, h); if (!result) { return false; } glDisable(GL_BLEND); return true; } Blitter mBlitter; bool mParity; }; return new BlendShrinkComp(); } } // namespace android cmds/flatland/Flatland.h0100644 0000000 0000000 00000003237 13756501734 014236 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #include #include #include #include namespace android { #define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0]))) enum { MAX_NUM_LAYERS = 16 }; enum { MAX_TEST_RUNS = 16 }; class Composer; class Renderer; class GLHelper; struct LayerDesc { uint32_t flags; Renderer* (*rendererFactory)(); Composer* (*composerFactory)(); int32_t x; int32_t y; uint32_t width; uint32_t height; }; void resetColorGenerator(); class Composer { public: virtual ~Composer() {} virtual bool setUp(const LayerDesc& desc, GLHelper* helper) = 0; virtual void tearDown() = 0; virtual bool compose(GLuint texName, const sp& glc) = 0; }; Composer* nocomp(); Composer* opaque(); Composer* opaqueShrink(); Composer* blend(); Composer* blendShrink(); class Renderer { public: virtual ~Renderer() {} virtual bool setUp(GLHelper* helper) = 0; virtual void tearDown() = 0; virtual bool render(EGLSurface surface) = 0; }; Renderer* staticGradient(); } // namespace android cmds/flatland/GLHelper.cpp0100644 0000000 0000000 00000031011 13756501734 014475 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #include #include #include #include #include "GLHelper.h" namespace android { GLHelper::GLHelper() : mDisplay(EGL_NO_DISPLAY), mContext(EGL_NO_CONTEXT), mDummySurface(EGL_NO_SURFACE), mConfig(0), mShaderPrograms(nullptr), mDitherTexture(0) { } GLHelper::~GLHelper() { } bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) { bool result; mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (mDisplay == EGL_NO_DISPLAY) { fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError()); return false; } EGLint majorVersion; EGLint minorVersion; result = eglInitialize(mDisplay, &majorVersion, &minorVersion); if (result != EGL_TRUE) { fprintf(stderr, "eglInitialize error: %#x\n", eglGetError()); return false; } EGLint numConfigs = 0; EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; result = eglChooseConfig(mDisplay, configAttribs, &mConfig, 1, &numConfigs); if (result != EGL_TRUE) { fprintf(stderr, "eglChooseConfig error: %#x\n", eglGetError()); return false; } EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs); if (mContext == EGL_NO_CONTEXT) { fprintf(stderr, "eglCreateContext error: %#x\n", eglGetError()); return false; } bool resultb = createNamedSurfaceTexture(0, 1, 1, &mDummyGLConsumer, &mDummySurface); if (!resultb) { return false; } resultb = makeCurrent(mDummySurface); if (!resultb) { return false; } resultb = setUpShaders(shaderDescs, numShaders); if (!resultb) { return false; } return true; } void GLHelper::tearDown() { if (mShaderPrograms != nullptr) { delete[] mShaderPrograms; mShaderPrograms = nullptr; } if (mSurfaceComposerClient != nullptr) { mSurfaceComposerClient->dispose(); mSurfaceComposerClient.clear(); } if (mDisplay != EGL_NO_DISPLAY) { eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } if (mContext != EGL_NO_CONTEXT) { eglDestroyContext(mDisplay, mContext); } if (mDummySurface != EGL_NO_SURFACE) { eglDestroySurface(mDisplay, mDummySurface); } mDisplay = EGL_NO_DISPLAY; mContext = EGL_NO_CONTEXT; mDummySurface = EGL_NO_SURFACE; mDummyGLConsumer.clear(); mConfig = 0; } bool GLHelper::makeCurrent(EGLSurface surface) { EGLint result; result = eglMakeCurrent(mDisplay, surface, surface, mContext); if (result != EGL_TRUE) { fprintf(stderr, "eglMakeCurrent error: %#x\n", eglGetError()); return false; } EGLint w, h; eglQuerySurface(mDisplay, surface, EGL_WIDTH, &w); eglQuerySurface(mDisplay, surface, EGL_HEIGHT, &h); glViewport(0, 0, w, h); return true; } bool GLHelper::createSurfaceTexture(uint32_t w, uint32_t h, sp* glConsumer, EGLSurface* surface, GLuint* name) { if (!makeCurrent(mDummySurface)) { return false; } *name = 0; glGenTextures(1, name); if (*name == 0) { fprintf(stderr, "glGenTextures error: %#x\n", glGetError()); return false; } return createNamedSurfaceTexture(*name, w, h, glConsumer, surface); } void GLHelper::destroySurface(EGLSurface* surface) { if (eglGetCurrentSurface(EGL_READ) == *surface || eglGetCurrentSurface(EGL_DRAW) == *surface) { eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } eglDestroySurface(mDisplay, *surface); *surface = EGL_NO_SURFACE; } bool GLHelper::swapBuffers(EGLSurface surface) { EGLint result; result = eglSwapBuffers(mDisplay, surface); if (result != EGL_TRUE) { fprintf(stderr, "eglSwapBuffers error: %#x\n", eglGetError()); return false; } return true; } bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) { for (size_t i = 0; i < mNumShaders; i++) { if (strcmp(mShaderDescs[i].name, name) == 0) { *outPgm = mShaderPrograms[i]; return true; } } fprintf(stderr, "unknown shader name: \"%s\"\n", name); return false; } bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, sp* glConsumer, EGLSurface* surface) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp glc = new GLConsumer(consumer, name, GL_TEXTURE_EXTERNAL_OES, false, true); glc->setDefaultBufferSize(w, h); producer->setMaxDequeuedBufferCount(2); glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); sp anw = new Surface(producer); EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); return false; } *glConsumer = glc; *surface = s; return true; } bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) { const sp dpy = mSurfaceComposerClient->getInternalDisplayToken(); if (dpy == nullptr) { fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n"); return false; } DisplayInfo info; status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err); return false; } float scaleX = float(info.w) / float(w); float scaleY = float(info.h) / float(h); *scale = scaleX < scaleY ? scaleX : scaleY; return true; } bool GLHelper::createWindowSurface(uint32_t w, uint32_t h, sp* surfaceControl, EGLSurface* surface) { bool result; status_t err; if (mSurfaceComposerClient == nullptr) { mSurfaceComposerClient = new SurfaceComposerClient; } err = mSurfaceComposerClient->initCheck(); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err); return false; } sp sc = mSurfaceComposerClient->createSurface( String8("Benchmark"), w, h, PIXEL_FORMAT_RGBA_8888, 0); if (sc == nullptr || !sc->isValid()) { fprintf(stderr, "Failed to create SurfaceControl.\n"); return false; } float scale; result = computeWindowScale(w, h, &scale); if (!result) { return false; } SurfaceComposerClient::Transaction{}.setLayer(sc, 0x7FFFFFFF) .setMatrix(sc, scale, 0.0f, 0.0f, scale) .show(sc) .apply(); sp anw = sc->getSurface(); EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); return false; } *surfaceControl = sc; *surface = s; return true; } static bool compileShader(GLenum shaderType, const char* src, GLuint* outShader) { GLuint shader = glCreateShader(shaderType); if (shader == 0) { fprintf(stderr, "glCreateShader error: %#x\n", glGetError()); return false; } glShaderSource(shader, 1, &src, nullptr); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = new char[infoLen]; if (buf) { glGetShaderInfoLog(shader, infoLen, nullptr, buf); fprintf(stderr, "Shader compile log:\n%s\n", buf); delete[] buf; } } glDeleteShader(shader); return false; } *outShader = shader; return true; } static void printShaderSource(const char* const* src) { for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != nullptr; i++) { fprintf(stderr, "%3zu: %s\n", i+1, src[i]); } } static const char* makeShaderString(const char* const* src) { size_t len = 0; for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != nullptr; i++) { // The +1 is for the '\n' that will be added. len += strlen(src[i]) + 1; } char* result = new char[len+1]; char* end = result; for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != nullptr; i++) { strcpy(end, src[i]); end += strlen(src[i]); *end = '\n'; end++; } *end = '\0'; return result; } static bool compileShaderLines(GLenum shaderType, const char* const* lines, GLuint* outShader) { const char* src = makeShaderString(lines); bool result = compileShader(shaderType, src, outShader); if (!result) { fprintf(stderr, "Shader source:\n"); printShaderSource(lines); delete[] src; return false; } delete[] src; return true; } static bool linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) { GLuint program = glCreateProgram(); if (program == 0) { fprintf(stderr, "glCreateProgram error: %#x\n", glGetError()); return false; } glAttachShader(program, vs); glAttachShader(program, fs); glLinkProgram(program); GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLint bufLength = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); if (bufLength) { char* buf = new char[bufLength]; if (buf) { glGetProgramInfoLog(program, bufLength, nullptr, buf); fprintf(stderr, "Program link log:\n%s\n", buf); delete[] buf; } } glDeleteProgram(program); program = 0; } *outPgm = program; return program != 0; } bool GLHelper::setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders) { mShaderPrograms = new GLuint[numShaders]; bool result = true; for (size_t i = 0; i < numShaders && result; i++) { GLuint vs, fs; result = compileShaderLines(GL_VERTEX_SHADER, shaderDescs[i].vertexShader, &vs); if (!result) { return false; } result = compileShaderLines(GL_FRAGMENT_SHADER, shaderDescs[i].fragmentShader, &fs); if (!result) { glDeleteShader(vs); return false; } result = linkShaderProgram(vs, fs, &mShaderPrograms[i]); glDeleteShader(vs); glDeleteShader(fs); } mNumShaders = numShaders; mShaderDescs = shaderDescs; return result; } bool GLHelper::getDitherTexture(GLuint* outTexName) { if (mDitherTexture == 0) { const uint8_t pattern[] = { 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5 }; glGenTextures(1, &mDitherTexture); glBindTexture(GL_TEXTURE_2D, mDitherTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); } *outTexName = mDitherTexture; return true; } } cmds/flatland/GLHelper.h0100644 0000000 0000000 00000004441 13756501734 014151 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #include #include #include #include #include namespace android { class SurfaceComposerClient; class SurfaceControl; enum { MAX_SHADER_LINES = 128 }; struct ShaderDesc { const char* name; const char* vertexShader[MAX_SHADER_LINES]; const char* fragmentShader[MAX_SHADER_LINES]; }; class GLHelper { public: enum { DITHER_KERNEL_SIZE = 4 }; GLHelper(); ~GLHelper(); bool setUp(const ShaderDesc* shaderDescs, size_t numShaders); void tearDown(); bool makeCurrent(EGLSurface surface); bool createSurfaceTexture(uint32_t w, uint32_t h, sp* surfaceTexture, EGLSurface* surface, GLuint* name); bool createWindowSurface(uint32_t w, uint32_t h, sp* surfaceControl, EGLSurface* surface); void destroySurface(EGLSurface* surface); bool swapBuffers(EGLSurface surface); bool getShaderProgram(const char* name, GLuint* outPgm); bool getDitherTexture(GLuint* outTexName); private: bool createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h, sp* surfaceTexture, EGLSurface* surface); bool computeWindowScale(uint32_t w, uint32_t h, float* scale); bool setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders); EGLDisplay mDisplay; EGLContext mContext; EGLSurface mDummySurface; sp mDummyGLConsumer; EGLConfig mConfig; sp mSurfaceComposerClient; GLuint* mShaderPrograms; const ShaderDesc* mShaderDescs; size_t mNumShaders; GLuint mDitherTexture; }; } // namespace android cmds/flatland/Main.cpp0100644 0000000 0000000 00000051026 13756501734 013727 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #define ATRACE_TAG ATRACE_TAG_ALWAYS #include #include #include #include #include #include #include #include #include #include #include "Flatland.h" #include "GLHelper.h" using namespace ::android; static uint32_t g_SleepBetweenSamplesMs = 0; static bool g_PresentToWindow = false; static size_t g_BenchmarkNameLen = 0; struct BenchmarkDesc { // The name of the test. const char* name; // The dimensions of the space in which window layers are specified. uint32_t width; uint32_t height; // The screen heights at which to run the test. uint32_t runHeights[MAX_TEST_RUNS]; // The list of window layers. LayerDesc layers[MAX_NUM_LAYERS]; }; static const BenchmarkDesc benchmarks[] = { { "16:10 Single Static Window", 2560, 1600, { 800, 1200, 1600, 2400 }, { { // Window 0, staticGradient, opaque, 0, 50, 2560, 1454, }, { // Status bar 0, staticGradient, opaque, 0, 0, 2560, 50, }, { // Navigation bar 0, staticGradient, opaque, 0, 1504, 2560, 96, }, }, }, { "4:3 Single Static Window", 2048, 1536, { 1536 }, { { // Window 0, staticGradient, opaque, 0, 50, 2048, 1440, }, { // Status bar 0, staticGradient, opaque, 0, 0, 2048, 50, }, { // Navigation bar 0, staticGradient, opaque, 0, 1440, 2048, 96, }, }, }, { "16:10 App -> Home Transition", 2560, 1600, { 800, 1200, 1600, 2400 }, { { // Wallpaper 0, staticGradient, opaque, 0, 50, 2560, 1454, }, { // Launcher 0, staticGradient, blend, 0, 50, 2560, 1454, }, { // Outgoing activity 0, staticGradient, blendShrink, 20, 70, 2520, 1414, }, { // Status bar 0, staticGradient, opaque, 0, 0, 2560, 50, }, { // Navigation bar 0, staticGradient, opaque, 0, 1504, 2560, 96, }, }, }, { "4:3 App -> Home Transition", 2048, 1536, { 1536 }, { { // Wallpaper 0, staticGradient, opaque, 0, 50, 2048, 1440, }, { // Launcher 0, staticGradient, blend, 0, 50, 2048, 1440, }, { // Outgoing activity 0, staticGradient, blendShrink, 20, 70, 2048, 1400, }, { // Status bar 0, staticGradient, opaque, 0, 0, 2048, 50, }, { // Navigation bar 0, staticGradient, opaque, 0, 1440, 2048, 96, }, }, }, { "16:10 SurfaceView -> Home Transition", 2560, 1600, { 800, 1200, 1600, 2400 }, { { // Wallpaper 0, staticGradient, opaque, 0, 50, 2560, 1454, }, { // Launcher 0, staticGradient, blend, 0, 50, 2560, 1454, }, { // Outgoing SurfaceView 0, staticGradient, blendShrink, 20, 70, 2520, 1414, }, { // Outgoing activity 0, staticGradient, blendShrink, 20, 70, 2520, 1414, }, { // Status bar 0, staticGradient, opaque, 0, 0, 2560, 50, }, { // Navigation bar 0, staticGradient, opaque, 0, 1504, 2560, 96, }, }, }, { "4:3 SurfaceView -> Home Transition", 2048, 1536, { 1536 }, { { // Wallpaper 0, staticGradient, opaque, 0, 50, 2048, 1440, }, { // Launcher 0, staticGradient, blend, 0, 50, 2048, 1440, }, { // Outgoing SurfaceView 0, staticGradient, blendShrink, 20, 70, 2048, 1400, }, { // Outgoing activity 0, staticGradient, blendShrink, 20, 70, 2048, 1400, }, { // Status bar 0, staticGradient, opaque, 0, 0, 2048, 50, }, { // Navigation bar 0, staticGradient, opaque, 0, 1440, 2048, 96, }, }, }, }; static const ShaderDesc shaders[] = { { .name="Blit", .vertexShader={ "precision mediump float;", "", "attribute vec4 position;", "attribute vec4 uv;", "", "varying vec4 texCoords;", "", "uniform mat4 objToNdc;", "uniform mat4 uvToTex;", "", "void main() {", " gl_Position = objToNdc * position;", " texCoords = uvToTex * uv;", "}", }, .fragmentShader={ "#extension GL_OES_EGL_image_external : require", "precision mediump float;", "", "varying vec4 texCoords;", "", "uniform samplerExternalOES blitSrc;", "uniform vec4 modColor;", "", "void main() {", " gl_FragColor = texture2D(blitSrc, texCoords.xy);", " gl_FragColor *= modColor;", "}", }, }, { .name="Gradient", .vertexShader={ "precision mediump float;", "", "attribute vec4 position;", "attribute vec4 uv;", "", "varying float interp;", "", "uniform mat4 objToNdc;", "uniform mat4 uvToInterp;", "", "void main() {", " gl_Position = objToNdc * position;", " interp = (uvToInterp * uv).x;", "}", }, .fragmentShader={ "precision mediump float;", "", "varying float interp;", "", "uniform vec4 color0;", "uniform vec4 color1;", "", "uniform sampler2D ditherKernel;", "uniform float invDitherKernelSize;", "uniform float invDitherKernelSizeSq;", "", "void main() {", " float dither = texture2D(ditherKernel,", " gl_FragCoord.xy * invDitherKernelSize).a;", " dither *= invDitherKernelSizeSq;", " vec4 color = mix(color0, color1, clamp(interp, 0.0, 1.0));", " gl_FragColor = color + vec4(dither, dither, dither, 0.0);", "}", }, }, }; class Layer { public: Layer() : mGLHelper(nullptr), mSurface(EGL_NO_SURFACE) { } bool setUp(const LayerDesc& desc, GLHelper* helper) { bool result; mDesc = desc; mGLHelper = helper; result = mGLHelper->createSurfaceTexture(mDesc.width, mDesc.height, &mGLConsumer, &mSurface, &mTexName); if (!result) { return false; } mRenderer = desc.rendererFactory(); result = mRenderer->setUp(helper); if (!result) { return false; } mComposer = desc.composerFactory(); result = mComposer->setUp(desc, helper); if (!result) { return false; } return true; } void tearDown() { if (mComposer != nullptr) { mComposer->tearDown(); delete mComposer; mComposer = nullptr; } if (mRenderer != nullptr) { mRenderer->tearDown(); delete mRenderer; mRenderer = nullptr; } if (mSurface != EGL_NO_SURFACE) { mGLHelper->destroySurface(&mSurface); mGLConsumer->abandon(); } mGLHelper = nullptr; mGLConsumer.clear(); } bool render() { return mRenderer->render(mSurface); } bool prepareComposition() { status_t err; err = mGLConsumer->updateTexImage(); if (err < 0) { fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err); return false; } return true; } bool compose() { return mComposer->compose(mTexName, mGLConsumer); } private: LayerDesc mDesc; GLHelper* mGLHelper; GLuint mTexName; sp mGLConsumer; EGLSurface mSurface; Renderer* mRenderer; Composer* mComposer; }; class BenchmarkRunner { public: BenchmarkRunner(const BenchmarkDesc& desc, size_t instance) : mDesc(desc), mInstance(instance), mNumLayers(countLayers(desc)), mGLHelper(nullptr), mSurface(EGL_NO_SURFACE), mWindowSurface(EGL_NO_SURFACE) { } bool setUp() { ATRACE_CALL(); bool result; float scaleFactor = float(mDesc.runHeights[mInstance]) / float(mDesc.height); uint32_t w = uint32_t(scaleFactor * float(mDesc.width)); uint32_t h = mDesc.runHeights[mInstance]; mGLHelper = new GLHelper(); result = mGLHelper->setUp(shaders, NELEMS(shaders)); if (!result) { return false; } GLuint texName; result = mGLHelper->createSurfaceTexture(w, h, &mGLConsumer, &mSurface, &texName); if (!result) { return false; } for (size_t i = 0; i < mNumLayers; i++) { // Scale the layer to match the current screen size. LayerDesc ld = mDesc.layers[i]; ld.x = int32_t(scaleFactor * float(ld.x)); ld.y = int32_t(scaleFactor * float(ld.y)); ld.width = uint32_t(scaleFactor * float(ld.width)); ld.height = uint32_t(scaleFactor * float(ld.height)); // Set up the layer. result = mLayers[i].setUp(ld, mGLHelper); if (!result) { return false; } } if (g_PresentToWindow) { result = mGLHelper->createWindowSurface(w, h, &mSurfaceControl, &mWindowSurface); if (!result) { return false; } result = doFrame(mWindowSurface); if (!result) { return false; } } return true; } void tearDown() { ATRACE_CALL(); for (size_t i = 0; i < mNumLayers; i++) { mLayers[i].tearDown(); } if (mGLHelper != nullptr) { if (mWindowSurface != EGL_NO_SURFACE) { mGLHelper->destroySurface(&mWindowSurface); } mGLHelper->destroySurface(&mSurface); mGLConsumer->abandon(); mGLConsumer.clear(); mSurfaceControl.clear(); mGLHelper->tearDown(); delete mGLHelper; mGLHelper = nullptr; } } nsecs_t run(uint32_t warmUpFrames, uint32_t totalFrames) { ATRACE_CALL(); bool result; resetColorGenerator(); // Do the warm-up frames. for (uint32_t i = 0; i < warmUpFrames; i++) { result = doFrame(mSurface); if (!result) { return -1; } } // Grab the fence for the start timestamp. sp startFence = mGLConsumer->getCurrentFence(); // the timed frames. for (uint32_t i = warmUpFrames; i < totalFrames; i++) { result = doFrame(mSurface); if (!result) { return -1; } } // Grab the fence for the end timestamp. sp endFence = mGLConsumer->getCurrentFence(); // Keep doing frames until the end fence has signaled. while (endFence->wait(0) == -ETIME) { result = doFrame(mSurface); if (!result) { return -1; } } // Compute the time delta. nsecs_t startTime = startFence->getSignalTime(); nsecs_t endTime = endFence->getSignalTime(); return endTime - startTime; } private: bool doFrame(EGLSurface surface) { bool result; status_t err; for (size_t i = 0; i < mNumLayers; i++) { result = mLayers[i].render(); if (!result) { return false; } } for (size_t i = 0; i < mNumLayers; i++) { result = mLayers[i].prepareComposition(); if (!result) { return false; } } result = mGLHelper->makeCurrent(surface); if (!result) { return false; } glClearColor(1.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); for (size_t i = 0; i < mNumLayers; i++) { result = mLayers[i].compose(); if (!result) { return false; } } result = mGLHelper->swapBuffers(surface); if (!result) { return false; } err = mGLConsumer->updateTexImage(); if (err < 0) { fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err); return false; } return true; } static size_t countLayers(const BenchmarkDesc& desc) { size_t i; for (i = 0; i < MAX_NUM_LAYERS; i++) { if (desc.layers[i].rendererFactory == nullptr) { break; } } return i; } const BenchmarkDesc& mDesc; const size_t mInstance; const size_t mNumLayers; GLHelper* mGLHelper; // The surface into which layers are composited sp mGLConsumer; EGLSurface mSurface; // Used for displaying the surface to a window. EGLSurface mWindowSurface; sp mSurfaceControl; Layer mLayers[MAX_NUM_LAYERS]; }; static int cmpDouble(const double* lhs, const double* rhs) { if (*lhs < *rhs) { return -1; } else if (*rhs < *lhs) { return 1; } return 0; } // Run a single benchmark and print the result. static bool runTest(const BenchmarkDesc b, size_t run) { bool success = true; double prevResult = 0.0, result = 0.0; Vector samples; uint32_t runHeight = b.runHeights[run]; uint32_t runWidth = b.width * runHeight / b.height; printf(" %-*s | %4d x %4d | ", static_cast(g_BenchmarkNameLen), b.name, runWidth, runHeight); fflush(stdout); BenchmarkRunner r(b, run); if (!r.setUp()) { fprintf(stderr, "error initializing runner.\n"); return false; } // The slowest 1/outlierFraction sample results are ignored as potential // outliers. const uint32_t outlierFraction = 16; const double threshold = .0025; uint32_t warmUpFrames = 1; uint32_t totalFrames = 5; // Find the number of frames needed to run for over 100ms. double runTime = 0.0; while (true) { runTime = double(r.run(warmUpFrames, totalFrames)); if (runTime < 50e6) { warmUpFrames *= 2; totalFrames *= 2; } else { break; } } if (totalFrames - warmUpFrames > 16) { // The test runs too fast to get a stable result. Skip it. printf(" fast"); goto done; } else if (totalFrames == 5 && runTime > 200e6) { // The test runs too slow to be very useful. Skip it. printf(" slow"); goto done; } do { size_t newSamples = samples.size(); if (newSamples == 0) { newSamples = 4*outlierFraction; } if (newSamples > 512) { printf("varies"); goto done; } for (size_t i = 0; i < newSamples; i++) { double sample = double(r.run(warmUpFrames, totalFrames)); if (g_SleepBetweenSamplesMs > 0) { usleep(g_SleepBetweenSamplesMs * 1000); } if (sample < 0.0) { success = false; goto done; } samples.add(sample); } samples.sort(cmpDouble); prevResult = result; size_t elem = (samples.size() * (outlierFraction-1) / outlierFraction); result = (samples[elem-1] + samples[elem]) * 0.5; } while (fabs(result - prevResult) > threshold * result); printf("%6.3f", result / double(totalFrames - warmUpFrames) / 1e6); done: printf("\n"); fflush(stdout); r.tearDown(); return success; } static void printResultsTableHeader() { const char* scenario = "Scenario"; size_t len = strlen(scenario); size_t leftPad = (g_BenchmarkNameLen - len) / 2; size_t rightPad = g_BenchmarkNameLen - len - leftPad; printf(" %*s%s%*s | Resolution | Time (ms)\n", static_cast(leftPad), "", "Scenario", static_cast(rightPad), ""); } // Run ALL the benchmarks! static bool runTests() { printResultsTableHeader(); for (size_t i = 0; i < NELEMS(benchmarks); i++) { const BenchmarkDesc& b = benchmarks[i]; for (size_t j = 0; j < MAX_TEST_RUNS && b.runHeights[j]; j++) { if (!runTest(b, j)) { return false; } } } return true; } // Return the length longest benchmark name. static size_t maxBenchmarkNameLen() { size_t maxLen = 0; for (size_t i = 0; i < NELEMS(benchmarks); i++) { const BenchmarkDesc& b = benchmarks[i]; size_t len = strlen(b.name); if (len > maxLen) { maxLen = len; } } return maxLen; } // Print the command usage help to stderr. static void showHelp(const char *cmd) { fprintf(stderr, "usage: %s [options]\n", cmd); fprintf(stderr, "options include:\n" " -s N sleep for N ms between samples\n" " -d display the test frame to a window\n" " --help print this helpful message and exit\n" ); } int main(int argc, char** argv) { if (argc == 2 && 0 == strcmp(argv[1], "--help")) { showHelp(argv[0]); exit(0); } for (;;) { int ret; int option_index = 0; static struct option long_options[] = { {"help", no_argument, 0, 0 }, { 0, 0, 0, 0 } }; ret = getopt_long(argc, argv, "ds:", long_options, &option_index); if (ret < 0) { break; } switch(ret) { case 'd': g_PresentToWindow = true; break; case 's': g_SleepBetweenSamplesMs = atoi(optarg); break; case 0: if (strcmp(long_options[option_index].name, "help")) { showHelp(argv[0]); exit(0); } break; default: showHelp(argv[0]); exit(2); } } g_BenchmarkNameLen = maxBenchmarkNameLen(); printf(" cmdline:"); for (int i = 0; i < argc; i++) { printf(" %s", argv[i]); } printf("\n"); if (!runTests()) { fprintf(stderr, "exiting due to error.\n"); return 1; } } cmds/flatland/README.txt0100644 0000000 0000000 00000007450 13756501734 014037 0ustar000000000 0000000 Flatland is a benchmark for measuring GPU performance in various 2D UI rendering and window compositing scenarios. It is designed to be used early in the device development process to evaluate GPU hardware (e.g. for SoC selection). It uses OpenGL ES 2.0, gralloc, and the Android explicit synchronization framework, so it can only be run on devices with drivers supporting those HALs. Preparing a Device Because it's measuring hardware performance, flatland should be run in as consistent and static an environment as possible. The display should be turned off and background services should be stopped before running the benchmark. Running 'adb shell stop' after turning off the display is probably sufficient for this, but if there are device- specific background services that consume much CPU cycles, memory bandwidth, or might otherwise interfere with GPU rendering, those should be stopped as well (and ideally they'd be fixed or eliminated for production devices). Additionally, all relevant hardware clocks should be locked at a particular frequency when running flatland. At a minimum this includes the CPU, GPU, and memory bus clocks. Running flatland with dynamic clocking essentially measures the behavior of the dynamic clocking algorithm under a fairly unrealistic workload, and will likely result in unstable and useless results. If running the benchmark with the clocks locked causes thermal issues, the -s command line option can be used to insert a sleep (specified in milliseconds) in between each benchmark sample run. Regardless of the scenario being measured, each sample measurement runs for between 50 and 200 ms, so a sleep time between 10 and 50 ms should address most thermal problems. Interpreting the Output The output of flatland should look something like this: cmdline: flatland Scenario | Resolution | Time (ms) 16:10 Single Static Window | 1280 x 800 | fast 16:10 Single Static Window | 2560 x 1600 | 5.368 16:10 Single Static Window | 3840 x 2400 | 11.979 16:10 App -> Home Transition | 1280 x 800 | 4.069 16:10 App -> Home Transition | 2560 x 1600 | 15.911 16:10 App -> Home Transition | 3840 x 2400 | 38.795 16:10 SurfaceView -> Home Transition | 1280 x 800 | 5.387 16:10 SurfaceView -> Home Transition | 2560 x 1600 | 21.147 16:10 SurfaceView -> Home Transition | 3840 x 2400 | slow The first column is simply a description of the scenario that's being simulated. The second column indicates the resolution at which the scenario was measured. The third column is the measured benchmark result. It indicates the expected time in milliseconds that a single frame of the scenario takes to complete. The third column may also contain one of three other values: fast - This indicates that frames of the scenario completed too fast to be reliably benchmarked. This corresponds to a frame time less than 3 ms. Rather than spending time trying (and likely failing) to get a stable result, the scenario was skipped. slow - This indicates that frames of the scenario took too long to complete. This corresponds to a frame time over 50 ms. Rather than simulating a scenario that is obviously impractical on this device, the scenario was skipped. varies - This indicates that the scenario was measured, but it did not yield a stable result. Occasionally this happens with an otherwise stable scenario. In this case, simply rerunning flatland should yield a valid result. If a scenario repeatedly results in a 'varies' output, that probably indicates that something is wrong with the environment in which flatland is being run. Check that the hardware clock frequencies are locked and that no heavy-weight services / daemons are running in the background. cmds/flatland/Renderers.cpp0100644 0000000 0000000 00000013227 13756501734 014775 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #include "Flatland.h" #include "GLHelper.h" namespace android { static float colors[][4] = { { .85f, .14f, .44f, 1.0f }, { .91f, .72f, .10f, 1.0f }, { .04f, .66f, .42f, 1.0f }, { .84f, .39f, .68f, 1.0f }, { .38f, .53f, .78f, 1.0f }, }; static size_t g_colorIndex; const float* genColor() { float* color = colors[g_colorIndex]; g_colorIndex = (g_colorIndex + 1) % NELEMS(colors); return color; } void resetColorGenerator() { g_colorIndex = 0; } class GradientRenderer { public: bool setUp(GLHelper* helper) { bool result; result = helper->getShaderProgram("Gradient", &mGradPgm); if (!result) { return false; } result = helper->getDitherTexture(&mDitherTexName); if (!result) { return false; } mPosAttribLoc = glGetAttribLocation(mGradPgm, "position"); mUVAttribLoc = glGetAttribLocation(mGradPgm, "uv"); mUVToInterpUniformLoc = glGetUniformLocation(mGradPgm, "uvToInterp"); mObjToNdcUniformLoc = glGetUniformLocation(mGradPgm, "objToNdc"); mDitherKernelSamplerLoc = glGetUniformLocation(mGradPgm, "ditherKernel"); mInvDitherKernelSizeUniformLoc = glGetUniformLocation(mGradPgm, "invDitherKernelSize"); mInvDitherKernelSizeSqUniformLoc = glGetUniformLocation(mGradPgm, "invDitherKernelSizeSq"); mColor0UniformLoc = glGetUniformLocation(mGradPgm, "color0"); mColor1UniformLoc = glGetUniformLocation(mGradPgm, "color1"); return true; } void tearDown() { } bool drawGradient() { float identity[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; const float pos[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; const float uv[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, }; const float* color0 = genColor(); const float* color1 = genColor(); glUseProgram(mGradPgm); glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos); glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv); glEnableVertexAttribArray(mPosAttribLoc); glEnableVertexAttribArray(mUVAttribLoc); float invDitherKernelSize = 1.0f / float(GLHelper::DITHER_KERNEL_SIZE); float invDitherKernelSizeSq = invDitherKernelSize * invDitherKernelSize; glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, identity); glUniformMatrix4fv(mUVToInterpUniformLoc, 1, GL_FALSE, identity); glUniform1f(mInvDitherKernelSizeUniformLoc, invDitherKernelSize); glUniform1f(mInvDitherKernelSizeSqUniformLoc, invDitherKernelSizeSq); glUniform4fv(mColor0UniformLoc, 1, color0); glUniform4fv(mColor1UniformLoc, 1, color1); if (glGetError() != GL_NO_ERROR) { fprintf(stderr, "GL error! 0\n"); } glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mDitherTexName); if (glGetError() != GL_NO_ERROR) { fprintf(stderr, "GL error! 1\n"); } glUniform1i(mDitherKernelSamplerLoc, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(mPosAttribLoc); glDisableVertexAttribArray(mUVAttribLoc); if (glGetError() != GL_NO_ERROR) { fprintf(stderr, "GL error! 2\n"); } return true; } GLuint mGradPgm; GLuint mDitherTexName; GLuint mPosAttribLoc; GLuint mUVAttribLoc; GLuint mObjToNdcUniformLoc; GLuint mUVToInterpUniformLoc; GLuint mDitherKernelSamplerLoc; GLuint mInvDitherKernelSizeUniformLoc; GLuint mInvDitherKernelSizeSqUniformLoc; GLuint mColor0UniformLoc; GLuint mColor1UniformLoc; }; Renderer* staticGradient() { class NoRenderer : public Renderer { virtual bool setUp(GLHelper* helper) { mIsFirstFrame = true; mGLHelper = helper; return mGradientRenderer.setUp(helper); } virtual void tearDown() { mGradientRenderer.tearDown(); } virtual bool render(EGLSurface surface) { if (mIsFirstFrame) { bool result; mIsFirstFrame = false; result = mGLHelper->makeCurrent(surface); if (!result) { return false; } result = mGradientRenderer.drawGradient(); if (!result) { return false; } result = mGLHelper->swapBuffers(surface); if (!result) { return false; } } return true; } bool mIsFirstFrame; GLHelper* mGLHelper; GradientRenderer mGradientRenderer; }; return new NoRenderer; } } // namespace android cmds/installd/0040755 0000000 0000000 00000000000 13756501734 012363 5ustar000000000 0000000 cmds/installd/Android.bp0100644 0000000 0000000 00000011566 13756501734 014274 0ustar000000000 0000000 cc_defaults { name: "installd_defaults", cflags: [ "-Wall", "-Werror", "-Wextra", "-Wunreachable-code", "-Wunreachable-code-break", "-Wunreachable-code-return", ], srcs: [ "CacheItem.cpp", "CacheTracker.cpp", "InstalldNativeService.cpp", "QuotaUtils.cpp", "dexopt.cpp", "globals.cpp", "utils.cpp", "utils_default.cpp", "view_compiler.cpp", ":installd_aidl", ], header_libs: [ "dex2oat_headers", ], shared_libs: [ "libbase", "libbinder", "libcrypto", "libcutils", "liblog", "liblogwrap", "libprocessgroup", "libselinux", "libutils", "server_configurable_flags", ], product_variables: { arc: { exclude_srcs: [ "QuotaUtils.cpp", ], static_libs: [ "libarcdiskquota", "arc_services_aidl", ], cflags: [ "-DUSE_ARC", ], }, }, clang: true, tidy: true, tidy_checks: [ "-*", "clang-analyzer-security*", "cert-*", "-cert-err58-cpp", ], tidy_flags: [ "-warnings-as-errors=clang-analyzer-security*,cert-*" ], } // // Static library used in testing and executable // cc_library_static { name: "libinstalld", defaults: ["installd_defaults"], export_include_dirs: ["."], aidl: { export_aidl_headers: true, }, product_variables: { arc: { exclude_srcs: [ "QuotaUtils.cpp", ], static_libs: [ "libarcdiskquota", "arc_services_aidl", ], cflags: [ "-DUSE_ARC", ], }, }, } cc_library_headers { name: "libinstalld_headers", export_include_dirs: ["."], } // // Executable // cc_binary { name: "installd", defaults: ["installd_defaults"], srcs: ["installd.cpp"], static_libs: ["libdiskusage"], init_rc: ["installd.rc"], product_variables: { arc: { exclude_srcs: [ "QuotaUtils.cpp", ], static_libs: [ "libarcdiskquota", "arc_services_aidl", ], cflags: [ "-DUSE_ARC", ], }, }, // Needs to be wherever installd is as it's execed by // installd. required: [ "migrate_legacy_obb_data.sh" ], } // OTA chroot tool cc_binary { name: "otapreopt_chroot", cflags: [ "-Wall", "-Werror", ], clang: true, srcs: [ "otapreopt_chroot.cpp", "otapreopt_utils.cpp", ], shared_libs: [ "libbase", "libbinder", "liblog", "libprotobuf-cpp-full", "libselinux", "libutils", "libziparchive", ], static_libs: [ "libapex", "libapexd", "lib_apex_manifest_proto", "libavb", "libdm", "libvold_binder", ], } filegroup { name: "installd_aidl", srcs: [ "binder/android/os/IInstalld.aidl", ], } // // Static library for otapreopt used in testing // cc_library_static { name: "libotapreoptparameters", cflags: [ "-Wall", "-Werror" ], srcs: ["otapreopt_parameters.cpp"], export_include_dirs: ["."], shared_libs: [ "libbase", "libcutils", "liblog", "libprocessgroup", "libutils", ], } // // OTA Executable // cc_binary { name: "otapreopt", cflags: [ "-Wall", "-Werror" ], srcs: [ "dexopt.cpp", "globals.cpp", "otapreopt.cpp", "otapreopt_utils.cpp", "utils.cpp", "utils_default.cpp", "view_compiler.cpp", ], header_libs: ["dex2oat_headers"], static_libs: [ "libartimagevalues", "libdiskusage", "libotapreoptparameters", ], shared_libs: [ "libbase", "libcrypto", "libcutils", "liblog", "liblogwrap", "libprocessgroup", "libselinux", "libutils", "server_configurable_flags", ], } // OTA slot script sh_binary { name: "otapreopt_slot", src: "otapreopt_slot.sh", init_rc: ["otapreopt.rc"], } // OTA postinstall script sh_binary { name: "otapreopt_script", src: "otapreopt_script.sh", // Let this depend on otapreopt, the chroot tool and the slot script, // so we just have to mention one in a configuration. required: [ "otapreopt", "otapreopt_chroot", "otapreopt_slot", ], } // Script to migrate legacy obb data. sh_binary { name: "migrate_legacy_obb_data.sh", src: "migrate_legacy_obb_data.sh" } cmds/installd/CacheItem.cpp0100644 0000000 0000000 00000007671 13756501734 014721 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "CacheItem.h" #include #include #include #include #include #include "utils.h" using android::base::StringPrintf; namespace android { namespace installd { CacheItem::CacheItem(FTSENT* p) { level = p->fts_level; directory = S_ISDIR(p->fts_statp->st_mode); size = p->fts_statp->st_blocks * 512; modified = p->fts_statp->st_mtime; mParent = static_cast(p->fts_parent->fts_pointer); if (mParent) { group = mParent->group; tombstone = mParent->tombstone; mName = p->fts_name; mName.insert(0, "/"); } else { group = false; tombstone = false; mName = p->fts_path; } } CacheItem::~CacheItem() { } std::string CacheItem::toString() { return StringPrintf("%s size=%" PRId64 " mod=%ld", buildPath().c_str(), size, modified); } std::string CacheItem::buildPath() { std::string res = mName; CacheItem* parent = mParent; while (parent) { res.insert(0, parent->mName); parent = parent->mParent; } return res; } int CacheItem::purge() { int res = 0; auto path = buildPath(); if (directory) { FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(WARNING) << "Failed to fts_open " << path; return -1; } while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_D: if (p->fts_level == 0) { p->fts_number = tombstone; } else { p->fts_number = p->fts_parent->fts_number | (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); } break; case FTS_F: if (p->fts_parent->fts_number) { if (truncate(p->fts_path, 0) != 0) { PLOG(WARNING) << "Failed to truncate " << p->fts_path; res = -1; } } else { if (unlink(p->fts_path) != 0) { PLOG(WARNING) << "Failed to unlink " << p->fts_path; res = -1; } } break; case FTS_DEFAULT: case FTS_SL: case FTS_SLNONE: if (unlink(p->fts_path) != 0) { PLOG(WARNING) << "Failed to unlink " << p->fts_path; res = -1; } break; case FTS_DP: if (rmdir(p->fts_path) != 0) { PLOG(WARNING) << "Failed to rmdir " << p->fts_path; res = -1; } break; } } } else { if (tombstone) { if (truncate(path.c_str(), 0) != 0) { PLOG(WARNING) << "Failed to truncate " << path; res = -1; } } else { if (unlink(path.c_str()) != 0) { PLOG(WARNING) << "Failed to unlink " << path; res = -1; } } } return res; } } // namespace installd } // namespace android cmds/installd/CacheItem.h0100644 0000000 0000000 00000002724 13756501734 014360 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_INSTALLD_CACHE_ITEM_H #define ANDROID_INSTALLD_CACHE_ITEM_H #include #include #include #include #include #include namespace android { namespace installd { /** * Single cache item that can be purged to free up space. This may be an * isolated file, or an entire directory tree that should be deleted as a * group. */ class CacheItem { public: CacheItem(FTSENT* p); ~CacheItem(); std::string toString(); std::string buildPath(); int purge(); short level; bool directory; bool group; bool tombstone; int64_t size; time_t modified; private: CacheItem* mParent; std::string mName; DISALLOW_COPY_AND_ASSIGN(CacheItem); }; } // namespace installd } // namespace android #endif // ANDROID_INSTALLD_CACHE_ITEM_H cmds/installd/CacheTracker.cpp0100644 0000000 0000000 00000014110 13756501734 015400 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER #include "CacheTracker.h" #include #include #include #include #include #include "QuotaUtils.h" #include "utils.h" using android::base::StringPrintf; namespace android { namespace installd { CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& uuid) : cacheUsed(0), cacheQuota(0), mUserId(userId), mAppId(appId), mItemsLoaded(false), mUuid(uuid) { } CacheTracker::~CacheTracker() { } std::string CacheTracker::toString() { return StringPrintf("UID=%d used=%" PRId64 " quota=%" PRId64 " ratio=%d", multiuser_get_uid(mUserId, mAppId), cacheUsed, cacheQuota, getCacheRatio()); } void CacheTracker::addDataPath(const std::string& dataPath) { mDataPaths.push_back(dataPath); } void CacheTracker::loadStats() { ATRACE_BEGIN("loadStats quota"); cacheUsed = 0; if (loadQuotaStats()) { return; } ATRACE_END(); ATRACE_BEGIN("loadStats tree"); cacheUsed = 0; for (const auto& path : mDataPaths) { auto cachePath = read_path_inode(path, "cache", kXattrInodeCache); auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache); calculate_tree_size(cachePath, &cacheUsed); calculate_tree_size(codeCachePath, &cacheUsed); } ATRACE_END(); } bool CacheTracker::loadQuotaStats() { int cacheGid = multiuser_get_cache_gid(mUserId, mAppId); int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId); if (IsQuotaSupported(mUuid) && cacheGid != -1 && extCacheGid != -1) { int64_t space; if ((space = GetOccupiedSpaceForGid(mUuid, cacheGid)) != -1) { cacheUsed += space; } else { return false; } if ((space = GetOccupiedSpaceForGid(mUuid, extCacheGid)) != -1) { cacheUsed += space; } else { return false; } return true; } else { return false; } } void CacheTracker::loadItemsFrom(const std::string& path) { FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(WARNING) << "Failed to fts_open " << path; return; } while ((p = fts_read(fts)) != nullptr) { if (p->fts_level == 0) continue; // Create tracking nodes for everything we encounter switch (p->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: { auto item = std::shared_ptr(new CacheItem(p)); p->fts_pointer = static_cast(item.get()); items.push_back(item); } } switch (p->fts_info) { case FTS_D: { auto item = static_cast(p->fts_pointer); item->group |= (getxattr(p->fts_path, kXattrCacheGroup, nullptr, 0) >= 0); item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0); // When group, immediately collect all files under tree if (item->group) { while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_DP && p->fts_level == item->level) break; switch (p->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: item->size += p->fts_statp->st_blocks * 512; item->modified = std::max(item->modified, p->fts_statp->st_mtime); } } } } } // Bubble up modified time to parent CHECK(p != nullptr); switch (p->fts_info) { case FTS_DP: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: { auto item = static_cast(p->fts_pointer); auto parent = static_cast(p->fts_parent->fts_pointer); if (parent) { parent->modified = std::max(parent->modified, item->modified); } } } } fts_close(fts); } void CacheTracker::loadItems() { items.clear(); ATRACE_BEGIN("loadItems"); for (const auto& path : mDataPaths) { loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache)); loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache)); } ATRACE_END(); ATRACE_BEGIN("sortItems"); auto cmp = [](std::shared_ptr left, std::shared_ptr right) { // TODO: sort dotfiles last // TODO: sort code_cache last if (left->modified != right->modified) { return (left->modified > right->modified); } if (left->level != right->level) { return (left->level < right->level); } return left->directory; }; std::stable_sort(items.begin(), items.end(), cmp); ATRACE_END(); } void CacheTracker::ensureItems() { if (mItemsLoaded) { return; } else { loadItems(); mItemsLoaded = true; } } int CacheTracker::getCacheRatio() { if (cacheQuota == 0) { return 0; } else { return (cacheUsed * 10000) / cacheQuota; } } } // namespace installd } // namespace android cmds/installd/CacheTracker.h0100644 0000000 0000000 00000003562 13756501734 015056 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_INSTALLD_CACHE_TRACKER_H #define ANDROID_INSTALLD_CACHE_TRACKER_H #include #include #include #include #include #include #include #include "CacheItem.h" namespace android { namespace installd { /** * Cache tracker for a single UID. Each tracker is used in two modes: first * for loading lightweight "stats", and then by loading detailed "items" * which can then be purged to free up space. */ class CacheTracker { public: CacheTracker(userid_t userId, appid_t appId, const std::string& uuid); ~CacheTracker(); std::string toString(); void addDataPath(const std::string& dataPath); void loadStats(); void loadItems(); void ensureItems(); int getCacheRatio(); int64_t cacheUsed; int64_t cacheQuota; std::vector> items; private: userid_t mUserId; appid_t mAppId; bool mItemsLoaded; const std::string& mUuid; std::vector mDataPaths; bool loadQuotaStats(); void loadItemsFrom(const std::string& path); DISALLOW_COPY_AND_ASSIGN(CacheTracker); }; } // namespace installd } // namespace android #endif // ANDROID_INSTALLD_CACHE_TRACKER_H cmds/installd/InstalldNativeService.cpp0100644 0000000 0000000 00000320243 13756501734 017332 0ustar000000000 0000000 /* ** Copyright 2008, 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. */ #include "InstalldNativeService.h" #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO: Move everything to base/logging. #include #include #include #include #include #include "dexopt.h" #include "globals.h" #include "installd_deps.h" #include "otapreopt_utils.h" #include "utils.h" #include "view_compiler.h" #include "CacheTracker.h" #include "MatchExtensionGen.h" #include "QuotaUtils.h" #ifndef LOG_TAG #define LOG_TAG "installd" #endif using android::base::StringPrintf; using std::endl; namespace android { namespace installd { // An uuid used in unit tests. static constexpr const char* kTestUuid = "TEST"; static constexpr const mode_t kRollbackFolderMode = 0700; static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M static constexpr const char* PKG_LIB_POSTFIX = "/lib"; static constexpr const char* CACHE_DIR_POSTFIX = "/cache"; static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; static constexpr const char *kIdMapPath = "/system/bin/idmap"; static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; static constexpr const char* IDMAP_SUFFIX = "@idmap"; // fsverity assumes the page size is always 4096. If not, the feature can not be // enabled. static constexpr int kVerityPageSize = 4096; static constexpr size_t kSha256Size = 32; static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; namespace { constexpr const char* kDump = "android.permission.DUMP"; static binder::Status ok() { return binder::Status::ok(); } static binder::Status exception(uint32_t code, const std::string& msg) { LOG(ERROR) << msg << " (" << code << ")"; return binder::Status::fromExceptionCode(code, String8(msg.c_str())); } static binder::Status error() { return binder::Status::fromServiceSpecificError(errno); } static binder::Status error(const std::string& msg) { PLOG(ERROR) << msg; return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str())); } static binder::Status error(uint32_t code, const std::string& msg) { LOG(ERROR) << msg << " (" << code << ")"; return binder::Status::fromServiceSpecificError(code, String8(msg.c_str())); } binder::Status checkPermission(const char* permission) { pid_t pid; uid_t uid; if (checkCallingPermission(String16(permission), reinterpret_cast(&pid), reinterpret_cast(&uid))) { return ok(); } else { return exception(binder::Status::EX_SECURITY, StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission)); } } binder::Status checkUid(uid_t expectedUid) { uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid == expectedUid || uid == AID_ROOT) { return ok(); } else { return exception(binder::Status::EX_SECURITY, StringPrintf("UID %d is not expected UID %d", uid, expectedUid)); } } binder::Status checkArgumentUuid(const std::unique_ptr& uuid) { if (!uuid || is_valid_filename(*uuid)) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("UUID %s is malformed", uuid->c_str())); } } binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr& uuid) { if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str())); } } binder::Status checkArgumentPackageName(const std::string& packageName) { if (is_valid_package_name(packageName)) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("Package name %s is malformed", packageName.c_str())); } } binder::Status checkArgumentPath(const std::string& path) { if (path.empty()) { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing path"); } if (path[0] != '/') { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("Path %s is relative", path.c_str())); } if ((path + '/').find("/../") != std::string::npos) { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("Path %s is shady", path.c_str())); } for (const char& c : path) { if (c == '\0' || c == '\n') { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("Path %s is malformed", path.c_str())); } } return ok(); } binder::Status checkArgumentPath(const std::unique_ptr& path) { if (path) { return checkArgumentPath(*path); } else { return ok(); } } #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ return status; \ } \ } #define CHECK_ARGUMENT_UUID(uuid) { \ binder::Status status = checkArgumentUuid((uuid)); \ if (!status.isOk()) { \ return status; \ } \ } #define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) { \ auto status = checkArgumentUuidTestOrNull(uuid); \ if (!status.isOk()) { \ return status; \ } \ } \ #define CHECK_ARGUMENT_PACKAGE_NAME(packageName) { \ binder::Status status = \ checkArgumentPackageName((packageName)); \ if (!status.isOk()) { \ return status; \ } \ } #define CHECK_ARGUMENT_PATH(path) { \ binder::Status status = checkArgumentPath((path)); \ if (!status.isOk()) { \ return status; \ } \ } #define ASSERT_PAGE_SIZE_4K() { \ if (getpagesize() != kVerityPageSize) { \ return error("FSVerity only supports 4K pages"); \ } \ } } // namespace status_t InstalldNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService::publish(); if (ret != android::OK) { return ret; } sp ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); return android::OK; } status_t InstalldNativeService::dump(int fd, const Vector & /* args */) { auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd)); const binder::Status dump_permission = checkPermission(kDump); if (!dump_permission.isOk()) { out << dump_permission.toString8() << endl; return PERMISSION_DENIED; } std::lock_guard lock(mLock); out << "installd is happy!" << endl; { std::lock_guard lock(mMountsLock); out << endl << "Storage mounts:" << endl; for (const auto& n : mStorageMounts) { out << " " << n.first << " = " << n.second << endl; } } { std::lock_guard lock(mQuotasLock); out << endl << "Per-UID cache quotas:" << endl; for (const auto& n : mCacheQuotas) { out << " " << n.first << " = " << n.second << endl; } } out << endl; out.flush(); return NO_ERROR; } /** * Perform restorecon of the given path, but only perform recursive restorecon * if the label of that top-level file actually changed. This can save us * significant time by avoiding no-op traversals of large filesystem trees. */ static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid, bool existing) { int res = 0; char* before = nullptr; char* after = nullptr; // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by // libselinux. Not needed here. if (lgetfilecon(path.c_str(), &before) < 0) { PLOG(ERROR) << "Failed before getfilecon for " << path; goto fail; } if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) { PLOG(ERROR) << "Failed top-level restorecon for " << path; goto fail; } if (lgetfilecon(path.c_str(), &after) < 0) { PLOG(ERROR) << "Failed after getfilecon for " << path; goto fail; } // If the initial top-level restorecon above changed the label, then go // back and restorecon everything recursively if (strcmp(before, after)) { if (existing) { LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path << "; running recursive restorecon"; } if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { PLOG(ERROR) << "Failed recursive restorecon for " << path; goto fail; } } goto done; fail: res = -1; done: free(before); free(after); return res; } static int restorecon_app_data_lazy(const std::string& parent, const char* name, const std::string& seInfo, uid_t uid, bool existing) { return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid, existing); } static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) { if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) { PLOG(ERROR) << "Failed to prepare " << path; return -1; } return 0; } static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { return true; } int32_t uid = multiuser_get_uid(userId, appId); int shared_app_gid = multiuser_get_shared_gid(userId, appId); if (shared_app_gid == -1) { // TODO(calin): this should no longer be possible but do not continue if we don't get // a valid shared gid. PLOG(WARNING) << "Invalid shared_app_gid for " << packageName; return true; } const std::string profile_dir = create_primary_current_profile_package_dir_path(userId, packageName); // read-write-execute only for the app user. if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) { PLOG(ERROR) << "Failed to prepare " << profile_dir; return false; } const std::string ref_profile_path = create_primary_reference_profile_package_dir_path(packageName); // Prepare the reference profile directory. Note that we use the non strict version of // fs_prepare_dir. This will fix the permission and the ownership to the correct values. // This is particularly important given that in O there were some fixes for how the // shared_app_gid is computed. // // Note that by the time we get here we know that we are using a correct uid (otherwise // prepare_app_dir and the above fs_prepare_file_strict which check the uid). So we // are sure that the gid being used belongs to the owning app and not someone else. // // dex2oat/profman runs under the shared app gid and it needs to read/write reference profiles. if (fs_prepare_dir(ref_profile_path.c_str(), 0770, AID_SYSTEM, shared_app_gid) != 0) { PLOG(ERROR) << "Failed to prepare " << ref_profile_path; return false; } return true; } binder::Status InstalldNativeService::createAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); // Assume invalid inode unless filled in below if (_aidl_return != nullptr) *_aidl_return = -1; int32_t uid = multiuser_get_uid(userId, appId); int32_t cacheGid = multiuser_get_cache_gid(userId, appId); mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; // If UID doesn't have a specific cache GID, use UID value if (cacheGid == -1) { cacheGid = uid; } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); bool existing = (access(path.c_str(), F_OK) == 0); if (prepare_app_dir(path, targetMode, uid) || prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { return error("Failed to prepare " + path); } // Consider restorecon over contents if label changed if (restorecon_app_data_lazy(path, seInfo, uid, existing) || restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { return error("Failed to restorecon " + path); } // Remember inode numbers of cache directories so that we can clear // contents while CE storage is locked if (write_path_inode(path, "cache", kXattrInodeCache) || write_path_inode(path, "code_cache", kXattrInodeCodeCache)) { return error("Failed to write_path_inode for " + path); } // And return the CE inode of the top-level data directory so we can // clear contents while CE storage is locked if ((_aidl_return != nullptr) && get_path_inode(path, reinterpret_cast(_aidl_return)) != 0) { return error("Failed to get_path_inode for " + path); } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); bool existing = (access(path.c_str(), F_OK) == 0); if (prepare_app_dir(path, targetMode, uid) || prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { return error("Failed to prepare " + path); } // Consider restorecon over contents if label changed if (restorecon_app_data_lazy(path, seInfo, uid, existing) || restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) || restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) { return error("Failed to restorecon " + path); } if (!prepare_app_profile_dir(packageName, appId, userId)) { return error("Failed to prepare profiles for " + packageName); } } return ok(); } binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); // This method only exists to upgrade system apps that have requested // forceDeviceEncrypted, so their default storage always lives in a // consistent location. This only works on non-FBE devices, since we // never want to risk exposing data on a device with real CE/DE storage. auto ce_path = create_data_user_ce_package_path(uuid_, userId, pkgname); auto de_path = create_data_user_de_package_path(uuid_, userId, pkgname); // If neither directory is marked as default, assume CE is default if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1 && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) { if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) { return error("Failed to mark default storage " + ce_path); } } // Migrate default data location if needed auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path; auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path; if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) { LOG(WARNING) << "Requested default storage " << target << " is not active; migrating from " << source; if (delete_dir_contents_and_dir(target) != 0) { return error("Failed to delete " + target); } if (rename(source.c_str(), target.c_str()) != 0) { return error("Failed to rename " + source + " to " + target); } } return ok(); } binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName, const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); binder::Status res = ok(); if (!clear_primary_reference_profile(packageName, profileName)) { res = error("Failed to clear reference profile for " + packageName); } if (!clear_primary_current_profiles(packageName, profileName)) { res = error("Failed to clear current profiles for " + packageName); } return res; } binder::Status InstalldNativeService::clearAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); binder::Status res = ok(); if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode); if (flags & FLAG_CLEAR_CACHE_ONLY) { path = read_path_inode(path, "cache", kXattrInodeCache); } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { path = read_path_inode(path, "code_cache", kXattrInodeCodeCache); } if (access(path.c_str(), F_OK) == 0) { if (delete_dir_contents(path) != 0) { res = error("Failed to delete contents of " + path); } else if ((flags & (FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE_ONLY)) == 0) { remove_path_xattr(path, kXattrInodeCache); remove_path_xattr(path, kXattrInodeCodeCache); } } } if (flags & FLAG_STORAGE_DE) { std::string suffix = ""; bool only_cache = false; if (flags & FLAG_CLEAR_CACHE_ONLY) { suffix = CACHE_DIR_POSTFIX; only_cache = true; } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { suffix = CODE_CACHE_DIR_POSTFIX; only_cache = true; } auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix; if (access(path.c_str(), F_OK) == 0) { if (delete_dir_contents(path) != 0) { res = error("Failed to delete contents of " + path); } } } if (flags & FLAG_STORAGE_EXTERNAL) { std::lock_guard lock(mMountsLock); for (const auto& n : mStorageMounts) { auto extPath = n.second; if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { extPath += StringPrintf("/%d", userId); } else if (userId != 0) { // TODO: support devices mounted under secondary users continue; } if (flags & FLAG_CLEAR_CACHE_ONLY) { // Clear only cached data from shared storage auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname); if (delete_dir_contents(path, true) != 0) { res = error("Failed to delete contents of " + path); } } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { // No code cache on shared storage } else { // Clear everything on shared storage auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); if (delete_dir_contents(path, true) != 0) { res = error("Failed to delete contents of " + path); } path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); if (delete_dir_contents(path, true) != 0) { res = error("Failed to delete contents of " + path); } path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); if (delete_dir_contents(path, true) != 0) { res = error("Failed to delete contents of " + path); } } } } return res; } static int destroy_app_reference_profile(const std::string& pkgname) { return delete_dir_contents_and_dir( create_primary_reference_profile_package_dir_path(pkgname), /*ignore_if_missing*/ true); } static int destroy_app_current_profiles(const std::string& pkgname, userid_t userid) { return delete_dir_contents_and_dir( create_primary_current_profile_package_dir_path(userid, pkgname), /*ignore_if_missing*/ true); } binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); binder::Status res = ok(); std::vector users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { if (destroy_app_current_profiles(packageName, user) != 0) { res = error("Failed to destroy current profiles for " + packageName); } } if (destroy_app_reference_profile(packageName) != 0) { res = error("Failed to destroy reference profile for " + packageName); } return res; } binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); binder::Status res = ok(); if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode); if (delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); if (delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } destroy_app_current_profiles(packageName, userId); // TODO(calin): If the package is still installed by other users it's probably // beneficial to keep the reference profile around. // Verify if it's ok to do that. destroy_app_reference_profile(packageName); } if (flags & FLAG_STORAGE_EXTERNAL) { std::lock_guard lock(mMountsLock); for (const auto& n : mStorageMounts) { auto extPath = n.second; if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { extPath += StringPrintf("/%d", userId); } else if (userId != 0) { // TODO: support devices mounted under secondary users continue; } auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } } } return res; } static gid_t get_cache_gid(uid_t uid) { int32_t gid = multiuser_get_cache_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid)); return (gid != -1) ? gid : uid; } binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr& uuid, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; for (auto user : get_known_users(uuid_)) { ATRACE_BEGIN("fixup user"); FTS* fts; FTSENT* p; auto ce_path = create_data_user_ce_path(uuid_, user); auto de_path = create_data_user_de_path(uuid_, user); char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); } while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_D && p->fts_level == 1) { // Track down inodes of cache directories uint64_t raw = 0; ino_t inode_cache = 0; ino_t inode_code_cache = 0; if (getxattr(p->fts_path, kXattrInodeCache, &raw, sizeof(raw)) == sizeof(raw)) { inode_cache = raw; } if (getxattr(p->fts_path, kXattrInodeCodeCache, &raw, sizeof(raw)) == sizeof(raw)) { inode_code_cache = raw; } // Figure out expected GID of each child FTSENT* child = fts_children(fts, 0); while (child != nullptr) { if ((child->fts_statp->st_ino == inode_cache) || (child->fts_statp->st_ino == inode_code_cache) || !strcmp(child->fts_name, "cache") || !strcmp(child->fts_name, "code_cache")) { child->fts_number = get_cache_gid(p->fts_statp->st_uid); } else { child->fts_number = p->fts_statp->st_uid; } child = child->fts_link; } } else if (p->fts_level >= 2) { if (p->fts_level > 2) { // Inherit GID from parent once we're deeper into tree p->fts_number = p->fts_parent->fts_number; } uid_t uid = p->fts_parent->fts_statp->st_uid; gid_t cache_gid = get_cache_gid(uid); gid_t expected = p->fts_number; gid_t actual = p->fts_statp->st_gid; if (actual == expected) { #if FIXUP_DEBUG LOG(DEBUG) << "Ignoring " << p->fts_path << " with expected GID " << expected; #endif if (!(flags & FLAG_FORCE)) { fts_set(fts, p, FTS_SKIP); } } else if ((actual == uid) || (actual == cache_gid)) { // Only consider fixing up when current GID belongs to app if (p->fts_info != FTS_D) { LOG(INFO) << "Fixing " << p->fts_path << " with unexpected GID " << actual << " instead of " << expected; } switch (p->fts_info) { case FTS_DP: // If we're moving towards cache GID, we need to set S_ISGID if (expected == cache_gid) { if (chmod(p->fts_path, 02771) != 0) { PLOG(WARNING) << "Failed to chmod " << p->fts_path; } } [[fallthrough]]; // also set GID case FTS_F: if (chown(p->fts_path, -1, expected) != 0) { PLOG(WARNING) << "Failed to chown " << p->fts_path; } break; case FTS_SL: case FTS_SLNONE: if (lchown(p->fts_path, -1, expected) != 0) { PLOG(WARNING) << "Failed to chown " << p->fts_path; } break; } } else { // Ignore all other GID transitions, since they're kinda shady LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected GID " << actual << " instead of " << expected; if (!(flags & FLAG_FORCE)) { fts_set(fts, p, FTS_SKIP); } } } } fts_close(fts); ATRACE_END(); } return ok(); } static int32_t copy_directory_recursive(const char* from, const char* to) { char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ (char*) from, (char*) to }; LOG(DEBUG) << "Copying " << from << " to " << to; return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); } binder::Status InstalldNativeService::snapshotAppData( const std::unique_ptr& volumeUuid, const std::string& packageName, int32_t user, int32_t snapshotId, int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); binder::Status res = ok(); // Default result to 0, it will be populated with inode of ce data snapshot // if FLAG_STORAGE_CE has been passed. if (_aidl_return != nullptr) *_aidl_return = 0; bool clear_ce_on_exit = false; bool clear_de_on_exit = false; auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name, &snapshotId] { if (clear_de_on_exit) { auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } }; auto scope_guard = android::base::make_scope_guard(deleter); // The app may not have any data at all, in which case it's OK to skip here. auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; return ok(); } // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set. binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user, storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); if (!clear_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear cache. LOG(WARNING) << "Failed to clear cache of app " << packageName; } // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set. binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user, storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0); if (!clear_code_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear code_cache. LOG(WARNING) << "Failed to clear code_cache of app " << packageName; } if (storageFlags & FLAG_STORAGE_DE) { auto from = create_data_user_de_package_path(volume_uuid, user, package_name); auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId); auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { return error(rc, "Failed to create folder " + to); } rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */); if (rc != 0) { return error(rc, "Failed clearing existing snapshot " + rollback_package_path); } rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_de_on_exit = true; return res; } } if (storageFlags & FLAG_STORAGE_CE) { auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId); auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { return error(rc, "Failed to create folder " + to); } rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */); if (rc != 0) { return error(rc, "Failed clearing existing snapshot " + rollback_package_path); } rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_ce_on_exit = true; return res; } if (_aidl_return != nullptr) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); clear_ce_on_exit = true; return res; } } } return res; } binder::Status InstalldNativeService::restoreAppDataSnapshot( const std::unique_ptr& volumeUuid, const std::string& packageName, const int32_t appId, const std::string& seInfo, const int32_t user, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, package_name); auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) && (access(from_de.c_str(), F_OK) == 0); if (!needs_ce_rollback && !needs_de_rollback) { return ok(); } // We know we're going to rollback one of the CE or DE data, so we clear // application data first. Note that it's possible that we're asked to // restore both CE & DE data but that one of the restores fail. Leaving the // app with no data in those cases is arguably better than leaving the app // with mismatched / stale data. LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot."; // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot // can only be called when user unlocks the phone, meaning that CE user data // is decrypted. binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, 0 /* ceDataInode */); if (!res.isOk()) { return res; } if (needs_ce_rollback) { auto to_ce = create_data_user_ce_path(volume_uuid, user); int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from_ce + " to " + to_ce); return res; } } if (needs_de_rollback) { auto to_de = create_data_user_de_path(volume_uuid, user); int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); if (rc != 0) { if (needs_ce_rollback) { auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name); LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data; if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data; } } res = error(rc, "Failed copying " + from_de + " to " + to_de); return res; } } // Finally, restore the SELinux label on the app data. return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); } binder::Status InstalldNativeService::destroyAppDataSnapshot( const std::unique_ptr &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (storageFlags & FLAG_STORAGE_DE) { auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + de_snapshot_path); } } if (storageFlags & FLAG_STORAGE_CE) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); } } return ok(); } binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr& fromUuid, const std::unique_ptr& toUuid, const std::string& packageName, const std::string& dataAppName, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(fromUuid); CHECK_ARGUMENT_UUID(toUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr; const char* to_uuid = toUuid ? toUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); const char* data_app_name = dataAppName.c_str(); binder::Status res = ok(); std::vector users = get_known_users(from_uuid); // Copy app { auto from = create_data_app_package_path(from_uuid, data_app_name); auto to = create_data_app_package_path(to_uuid, data_app_name); auto to_parent = create_data_app_path(to_uuid); int rc = copy_directory_recursive(from.c_str(), to_parent.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; } if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { res = error("Failed to restorecon " + to); goto fail; } } // Copy private data for all known users for (auto user : users) { // Data source may not exist for all users; that's okay auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; continue; } if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, seInfo, targetSdkVersion, nullptr).isOk()) { res = error("Failed to create package target"); goto fail; } { auto from = create_data_user_de_package_path(from_uuid, user, package_name); auto to = create_data_user_de_path(to_uuid, user); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; } } { auto from = create_data_user_ce_package_path(from_uuid, user, package_name); auto to = create_data_user_ce_path(to_uuid, user); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; } } if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, seInfo).isOk()) { res = error("Failed to restorecon"); goto fail; } } // We let the framework scan the new location and persist that before // deleting the data in the old location; this ordering ensures that // we can recover from things like battery pulls. return ok(); fail: // Nuke everything we might have already copied { auto to = create_data_app_package_path(to_uuid, data_app_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } for (auto user : users) { { auto to = create_data_user_de_package_path(to_uuid, user, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } { auto to = create_data_user_ce_package_path(to_uuid, user, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } } return res; } binder::Status InstalldNativeService::createUserData(const std::unique_ptr& uuid, int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_DE) { if (uuid_ == nullptr) { if (ensure_config_user_dirs(userId) != 0) { return error(StringPrintf("Failed to ensure dirs for %d", userId)); } } } return ok(); } binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr& uuid, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; binder::Status res = ok(); if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_path(uuid_, userId); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } if (uuid_ == nullptr) { path = create_data_misc_legacy_path(userId); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } path = create_primary_cur_profile_dir_path(userId); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } } } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_path(uuid_, userId); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } path = findDataMediaPath(uuid, userId); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } } return res; } binder::Status InstalldNativeService::freeCache(const std::unique_ptr& uuid, int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard lock(mLock); auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto data_path = create_data_path(uuid_); auto noop = (flags & FLAG_FREE_CACHE_NOOP); int64_t free = data_disk_free(data_path); if (free < 0) { return error("Failed to determine free space for " + data_path); } int64_t cleared = 0; int64_t needed = targetFreeBytes - free; LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested " << targetFreeBytes << "; needed " << needed; if (free >= targetFreeBytes) { return ok(); } if (flags & FLAG_FREE_CACHE_V2) { // This new cache strategy fairly removes files from UIDs by deleting // files from the UIDs which are most over their allocated quota // 1. Create trackers for every known UID ATRACE_BEGIN("create"); std::unordered_map> trackers; for (auto user : get_known_users(uuid_)) { FTS *fts; FTSENT *p; auto ce_path = create_data_user_ce_path(uuid_, user); auto de_path = create_data_user_de_path(uuid_, user); auto media_path = findDataMediaPath(uuid, user) + "/Android/data/"; char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), (char*) media_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); } while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_D && p->fts_level == 1) { uid_t uid = p->fts_statp->st_uid; if (multiuser_get_app_id(uid) == AID_MEDIA_RW) { uid = (multiuser_get_app_id(p->fts_statp->st_gid) - AID_EXT_GID_START) + AID_APP_START; } auto search = trackers.find(uid); if (search != trackers.end()) { search->second->addDataPath(p->fts_path); } else { auto tracker = std::shared_ptr(new CacheTracker( multiuser_get_user_id(uid), multiuser_get_app_id(uid), uuidString)); tracker->addDataPath(p->fts_path); { std::lock_guard lock(mQuotasLock); tracker->cacheQuota = mCacheQuotas[uid]; } if (tracker->cacheQuota == 0) { #if MEASURE_DEBUG LOG(WARNING) << "UID " << uid << " has no cache quota; assuming 64MB"; #endif tracker->cacheQuota = 67108864; } trackers[uid] = tracker; } fts_set(fts, p, FTS_SKIP); } } fts_close(fts); } ATRACE_END(); // 2. Populate tracker stats and insert into priority queue ATRACE_BEGIN("populate"); int64_t cacheTotal = 0; auto cmp = [](std::shared_ptr left, std::shared_ptr right) { return (left->getCacheRatio() < right->getCacheRatio()); }; std::priority_queue, std::vector>, decltype(cmp)> queue(cmp); for (const auto& it : trackers) { it.second->loadStats(); queue.push(it.second); cacheTotal += it.second->cacheUsed; } ATRACE_END(); // 3. Bounce across the queue, freeing items from whichever tracker is // the most over their assigned quota ATRACE_BEGIN("bounce"); std::shared_ptr active; while (active || !queue.empty()) { // Only look at apps under quota when explicitly requested if (active && (active->getCacheRatio() < 10000) && !(flags & FLAG_FREE_CACHE_V2_DEFY_QUOTA)) { LOG(DEBUG) << "Active ratio " << active->getCacheRatio() << " isn't over quota, and defy not requested"; break; } // Only keep clearing when we haven't pushed into reserved area if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) { LOG(DEBUG) << "Refusing to clear cached data in reserved space"; break; } // Find the best tracker to work with; this might involve swapping // if the active tracker is no longer the most over quota bool nextBetter = active && !queue.empty() && active->getCacheRatio() < queue.top()->getCacheRatio(); if (!active || nextBetter) { if (active) { // Current tracker still has items, so we'll consider it // again later once it bubbles up to surface queue.push(active); } active = queue.top(); queue.pop(); active->ensureItems(); continue; } // If no items remain, go find another tracker if (active->items.empty()) { active = nullptr; continue; } else { auto item = active->items.back(); active->items.pop_back(); LOG(DEBUG) << "Purging " << item->toString() << " from " << active->toString(); if (!noop) { item->purge(); } active->cacheUsed -= item->size; needed -= item->size; cleared += item->size; } // Verify that we're actually done before bailing, since sneaky // apps might be using hardlinks if (needed <= 0) { free = data_disk_free(data_path); needed = targetFreeBytes - free; if (needed <= 0) { break; } else { LOG(WARNING) << "Expected to be done but still need " << needed; } } } ATRACE_END(); } else { return error("Legacy cache logic no longer supported"); } free = data_disk_free(data_path); if (free >= targetFreeBytes) { return ok(); } else { return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64, targetFreeBytes, data_path.c_str(), free)); } } binder::Status InstalldNativeService::rmdex(const std::string& codePath, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(codePath); std::lock_guard lock(mLock); char dex_path[PKG_PATH_MAX]; const char* path = codePath.c_str(); const char* instruction_set = instructionSet.c_str(); if (validate_apk_path(path) && validate_system_app_path(path)) { return error("Invalid path " + codePath); } if (!create_cache_path(dex_path, path, instruction_set)) { return error("Failed to create cache path for " + codePath); } ALOGV("unlink %s\n", dex_path); if (unlink(dex_path) < 0) { // It's ok if we don't have a dalvik cache path. Report error only when the path exists // but could not be unlinked. if (errno != ENOENT) { return error(StringPrintf("Failed to unlink %s", dex_path)); } } return ok(); } struct stats { int64_t codeSize; int64_t dataSize; int64_t cacheSize; }; #if MEASURE_DEBUG static std::string toString(std::vector values) { std::stringstream res; res << "["; for (size_t i = 0; i < values.size(); i++) { res << values[i]; if (i < values.size() - 1) { res << ","; } } res << "]"; return res.str(); } #endif static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { int64_t space; if (stats != nullptr) { uid_t uid = multiuser_get_uid(userId, appId); if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { stats->dataSize += space; } int cacheGid = multiuser_get_cache_gid(userId, appId); if (cacheGid != -1) { if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { stats->cacheSize += space; } } int sharedGid = multiuser_get_shared_gid(0, appId); if (sharedGid != -1) { if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { stats->codeSize += space; } } } if (extStats != nullptr) { int extGid = multiuser_get_ext_gid(userId, appId); if (extGid != -1) { if ((space = GetOccupiedSpaceForGid(uuid, extGid)) != -1) { extStats->dataSize += space; } } int extCacheGid = multiuser_get_ext_cache_gid(userId, appId); if (extCacheGid != -1) { if ((space = GetOccupiedSpaceForGid(uuid, extCacheGid)) != -1) { extStats->dataSize += space; extStats->cacheSize += space; } } } } static void collectManualStats(const std::string& path, struct stats* stats) { DIR *d; int dfd; struct dirent *de; struct stat s; d = opendir(path.c_str()); if (d == nullptr) { if (errno != ENOENT) { PLOG(WARNING) << "Failed to open " << path; } return; } dfd = dirfd(d); while ((de = readdir(d))) { const char *name = de->d_name; int64_t size = 0; if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { size = s.st_blocks * 512; } if (de->d_type == DT_DIR) { if (!strcmp(name, ".")) { // Don't recurse, but still count node size } else if (!strcmp(name, "..")) { // Don't recurse or count node size continue; } else { // Measure all children nodes size = 0; calculate_tree_size(StringPrintf("%s/%s", path.c_str(), name), &size); } if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) { stats->cacheSize += size; } } // Legacy symlink isn't owned by app if (de->d_type == DT_LNK && !strcmp(name, "lib")) { continue; } // Everything found inside is considered data stats->dataSize += size; } closedir(d); } static void collectManualStatsForUser(const std::string& path, struct stats* stats, bool exclude_apps = false) { DIR *d; int dfd; struct dirent *de; struct stat s; d = opendir(path.c_str()); if (d == nullptr) { if (errno != ENOENT) { PLOG(WARNING) << "Failed to open " << path; } return; } dfd = dirfd(d); while ((de = readdir(d))) { if (de->d_type == DT_DIR) { const char *name = de->d_name; if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) != 0) { continue; } int32_t user_uid = multiuser_get_app_id(s.st_uid); if (!strcmp(name, ".") || !strcmp(name, "..")) { continue; } else if (exclude_apps && (user_uid >= AID_APP_START && user_uid <= AID_APP_END)) { continue; } else { collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats); } } } closedir(d); } static void collectManualExternalStatsForUser(const std::string& path, struct stats* stats) { FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(ERROR) << "Failed to fts_open " << path; return; } while ((p = fts_read(fts)) != nullptr) { p->fts_number = p->fts_parent->fts_number; switch (p->fts_info) { case FTS_D: if (p->fts_level == 4 && !strcmp(p->fts_name, "cache") && !strcmp(p->fts_parent->fts_parent->fts_name, "data") && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) { p->fts_number = 1; } [[fallthrough]]; // to count the directory case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: int64_t size = (p->fts_statp->st_blocks * 512); if (p->fts_number == 1) { stats->cacheSize += size; } stats->dataSize += size; break; } } fts_close(fts); } binder::Status InstalldNativeService::getAppSize(const std::unique_ptr& uuid, const std::vector& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector& ceDataInodes, const std::vector& codePaths, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); for (const auto& packageName : packageNames) { CHECK_ARGUMENT_PACKAGE_NAME(packageName); } for (const auto& codePath : codePaths) { CHECK_ARGUMENT_PATH(codePath); } // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. // When modifying this logic, always verify using tests: // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetAppSize #if MEASURE_DEBUG LOG(INFO) << "Measuring user " << userId << " app " << appId; #endif // Here's a summary of the common storage locations across the platform, // and how they're each tagged: // // /data/app/com.example UID system // /data/app/com.example/oat UID system // /data/user/0/com.example UID u0_a10 GID u0_a10 // /data/user/0/com.example/cache UID u0_a10 GID u0_a10_cache // /data/media/0/foo.txt UID u0_media_rw // /data/media/0/bar.jpg UID u0_media_rw GID u0_media_image // /data/media/0/Android/data/com.example UID u0_media_rw GID u0_a10_ext // /data/media/0/Android/data/com.example/cache UID u0_media_rw GID u0_a10_ext_cache // /data/media/obb/com.example UID system struct stats stats; struct stats extStats; memset(&stats, 0, sizeof(stats)); memset(&extStats, 0, sizeof(extStats)); auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (!IsQuotaSupported(uuidString)) { flags &= ~FLAG_USE_QUOTA; } ATRACE_BEGIN("obb"); for (const auto& packageName : packageNames) { auto obbCodePath = create_data_media_package_path(uuid_, userId, "obb", packageName.c_str()); calculate_tree_size(obbCodePath, &extStats.codeSize); } ATRACE_END(); if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) { ATRACE_BEGIN("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, multiuser_get_shared_gid(0, appId)); } ATRACE_END(); ATRACE_BEGIN("quota"); collectQuotaStats(uuidString, userId, appId, &stats, &extStats); ATRACE_END(); } else { ATRACE_BEGIN("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize); } ATRACE_END(); for (size_t i = 0; i < packageNames.size(); i++) { const char* pkgname = packageNames[i].c_str(); ATRACE_BEGIN("data"); auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]); collectManualStats(cePath, &stats); auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname); collectManualStats(dePath, &stats); ATRACE_END(); if (!uuid) { ATRACE_BEGIN("profiles"); calculate_tree_size( create_primary_current_profile_package_dir_path(userId, pkgname), &stats.dataSize); calculate_tree_size( create_primary_reference_profile_package_dir_path(pkgname), &stats.codeSize); ATRACE_END(); } ATRACE_BEGIN("external"); auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname); collectManualStats(extPath, &extStats); auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname); calculate_tree_size(mediaPath, &extStats.dataSize); ATRACE_END(); } if (!uuid) { ATRACE_BEGIN("dalvik"); int32_t sharedGid = multiuser_get_shared_gid(0, appId); if (sharedGid != -1) { calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, sharedGid, -1); } ATRACE_END(); } } std::vector ret; ret.push_back(stats.codeSize); ret.push_back(stats.dataSize); ret.push_back(stats.cacheSize); ret.push_back(extStats.codeSize); ret.push_back(extStats.dataSize); ret.push_back(extStats.cacheSize); #if MEASURE_DEBUG LOG(DEBUG) << "Final result " << toString(ret); #endif *_aidl_return = ret; return ok(); } binder::Status InstalldNativeService::getUserSize(const std::unique_ptr& uuid, int32_t userId, int32_t flags, const std::vector& appIds, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. // When modifying this logic, always verify using tests: // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetUserSize #if MEASURE_DEBUG LOG(INFO) << "Measuring user " << userId; #endif struct stats stats; struct stats extStats; memset(&stats, 0, sizeof(stats)); memset(&extStats, 0, sizeof(extStats)); auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (!IsQuotaSupported(uuidString)) { flags &= ~FLAG_USE_QUOTA; } if (flags & FLAG_USE_QUOTA) { int64_t space; ATRACE_BEGIN("obb"); if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) { extStats.codeSize += space; } ATRACE_END(); ATRACE_BEGIN("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true); ATRACE_END(); ATRACE_BEGIN("data"); auto cePath = create_data_user_ce_path(uuid_, userId); collectManualStatsForUser(cePath, &stats, true); auto dePath = create_data_user_de_path(uuid_, userId); collectManualStatsForUser(dePath, &stats, true); ATRACE_END(); if (!uuid) { ATRACE_BEGIN("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true); auto refProfilePath = create_primary_ref_profile_dir_path(); calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true); ATRACE_END(); } ATRACE_BEGIN("external"); uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) { extStats.dataSize += space; } ATRACE_END(); if (!uuid) { ATRACE_BEGIN("dalvik"); calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, -1, -1, true); calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize, -1, -1, true); ATRACE_END(); } ATRACE_BEGIN("quota"); int64_t dataSize = extStats.dataSize; for (auto appId : appIds) { if (appId >= AID_APP_START) { collectQuotaStats(uuidString, userId, appId, &stats, &extStats); #if MEASURE_DEBUG // Sleep to make sure we don't lose logs usleep(1); #endif } } extStats.dataSize = dataSize; ATRACE_END(); } else { ATRACE_BEGIN("obb"); auto obbPath = create_data_path(uuid_) + "/media/obb"; calculate_tree_size(obbPath, &extStats.codeSize); ATRACE_END(); ATRACE_BEGIN("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize); ATRACE_END(); ATRACE_BEGIN("data"); auto cePath = create_data_user_ce_path(uuid_, userId); collectManualStatsForUser(cePath, &stats); auto dePath = create_data_user_de_path(uuid_, userId); collectManualStatsForUser(dePath, &stats); ATRACE_END(); if (!uuid) { ATRACE_BEGIN("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); calculate_tree_size(userProfilePath, &stats.dataSize); auto refProfilePath = create_primary_ref_profile_dir_path(); calculate_tree_size(refProfilePath, &stats.codeSize); ATRACE_END(); } ATRACE_BEGIN("external"); auto dataMediaPath = create_data_media_path(uuid_, userId); collectManualExternalStatsForUser(dataMediaPath, &extStats); #if MEASURE_DEBUG LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache " << extStats.cacheSize; #endif ATRACE_END(); if (!uuid) { ATRACE_BEGIN("dalvik"); calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize); calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize); ATRACE_END(); } } std::vector ret; ret.push_back(stats.codeSize); ret.push_back(stats.dataSize); ret.push_back(stats.cacheSize); ret.push_back(extStats.codeSize); ret.push_back(extStats.dataSize); ret.push_back(extStats.cacheSize); #if MEASURE_DEBUG LOG(DEBUG) << "Final result " << toString(ret); #endif *_aidl_return = ret; return ok(); } binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr& uuid, int32_t userId, int32_t flags, const std::vector& appIds, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. // When modifying this logic, always verify using tests: // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetExternalSize #if MEASURE_DEBUG LOG(INFO) << "Measuring external " << userId; #endif auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; int64_t totalSize = 0; int64_t audioSize = 0; int64_t videoSize = 0; int64_t imageSize = 0; int64_t appSize = 0; int64_t obbSize = 0; if (!IsQuotaSupported(uuidString)) { flags &= ~FLAG_USE_QUOTA; } if (flags & FLAG_USE_QUOTA) { int64_t space; ATRACE_BEGIN("quota"); uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) { totalSize = space; } gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO); if ((space = GetOccupiedSpaceForGid(uuidString, audioGid)) != -1) { audioSize = space; } gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO); if ((space = GetOccupiedSpaceForGid(uuidString, videoGid)) != -1) { videoSize = space; } gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE); if ((space = GetOccupiedSpaceForGid(uuidString, imageGid)) != -1) { imageSize = space; } if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) { obbSize = space; } ATRACE_END(); ATRACE_BEGIN("apps"); struct stats extStats; memset(&extStats, 0, sizeof(extStats)); for (auto appId : appIds) { if (appId >= AID_APP_START) { collectQuotaStats(uuidString, userId, appId, nullptr, &extStats); } } appSize = extStats.dataSize; ATRACE_END(); } else { ATRACE_BEGIN("manual"); FTS *fts; FTSENT *p; auto path = create_data_media_path(uuid_, userId); char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open " + path); } while ((p = fts_read(fts)) != nullptr) { char* ext; int64_t size = (p->fts_statp->st_blocks * 512); switch (p->fts_info) { case FTS_F: // Only categorize files not belonging to apps if (p->fts_parent->fts_number == 0) { ext = strrchr(p->fts_name, '.'); if (ext != nullptr) { switch (MatchExtension(++ext)) { case AID_MEDIA_AUDIO: audioSize += size; break; case AID_MEDIA_VIDEO: videoSize += size; break; case AID_MEDIA_IMAGE: imageSize += size; break; } } } [[fallthrough]]; // always count against total case FTS_D: // Ignore data belonging to specific apps p->fts_number = p->fts_parent->fts_number; if (p->fts_level == 1 && !strcmp(p->fts_name, "Android")) { p->fts_number = 1; } [[fallthrough]]; // always count against total case FTS_DEFAULT: case FTS_SL: case FTS_SLNONE: if (p->fts_parent->fts_number == 1) { appSize += size; } totalSize += size; break; } } fts_close(fts); ATRACE_END(); ATRACE_BEGIN("obb"); auto obbPath = StringPrintf("%s/Android/obb", create_data_media_path(uuid_, userId).c_str()); calculate_tree_size(obbPath, &obbSize); ATRACE_END(); } std::vector ret; ret.push_back(totalSize); ret.push_back(audioSize); ret.push_back(videoSize); ret.push_back(imageSize); ret.push_back(appSize); ret.push_back(obbSize); #if MEASURE_DEBUG LOG(DEBUG) << "Final result " << toString(ret); #endif *_aidl_return = ret; return ok(); } binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr& uuid, int32_t userId, int32_t appId, int64_t cacheQuota) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard lock(mQuotasLock); int32_t uid = multiuser_get_uid(userId, appId); mCacheQuotas[uid] = cacheQuota; return ok(); } // Dumps the contents of a profile file, using pkgname's dex files for pretty // printing the result. binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName, const std::string& profileName, const std::string& codePath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); std::lock_guard lock(mLock); *_aidl_return = dump_profiles(uid, packageName, profileName, codePath); return ok(); } // Copy the contents of a system profile over the data profile. binder::Status InstalldNativeService::copySystemProfile(const std::string& systemProfile, int32_t packageUid, const std::string& packageName, const std::string& profileName, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); } // TODO: Consider returning error codes. binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName, const std::string& profileName, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); *_aidl_return = analyze_primary_profiles(uid, packageName, profileName); return ok(); } binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId, const std::string& packageName, const std::string& profileName, const std::string& classpath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath); return ok(); } binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& packageName, const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); std::string snapshot = create_snapshot_profile_path(packageName, profileName); if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) { return error("Failed to destroy profile snapshot for " + packageName + ":" + profileName); } return ok(); } static const char* getCStr(const std::unique_ptr& data, const char* default_value = nullptr) { return data == nullptr ? default_value : data->c_str(); } binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid, const std::unique_ptr& packageName, const std::string& instructionSet, int32_t dexoptNeeded, const std::unique_ptr& outputPath, int32_t dexFlags, const std::string& compilerFilter, const std::unique_ptr& uuid, const std::unique_ptr& classLoaderContext, const std::unique_ptr& seInfo, bool downgrade, int32_t targetSdkVersion, const std::unique_ptr& profileName, const std::unique_ptr& dexMetadataPath, const std::unique_ptr& compilationReason) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PATH(apkPath); if (packageName && *packageName != "*") { CHECK_ARGUMENT_PACKAGE_NAME(*packageName); } CHECK_ARGUMENT_PATH(outputPath); CHECK_ARGUMENT_PATH(dexMetadataPath); std::lock_guard lock(mLock); const char* apk_path = apkPath.c_str(); const char* pkgname = getCStr(packageName, "*"); const char* instruction_set = instructionSet.c_str(); const char* oat_dir = getCStr(outputPath); const char* compiler_filter = compilerFilter.c_str(); const char* volume_uuid = getCStr(uuid); const char* class_loader_context = getCStr(classLoaderContext); const char* se_info = getCStr(seInfo); const char* profile_name = getCStr(profileName); const char* dm_path = getCStr(dexMetadataPath); const char* compilation_reason = getCStr(compilationReason); std::string error_msg; int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded, oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info, downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason, &error_msg); return res ? error(res, error_msg) : ok(); } binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath, const std::string& packageName, const std ::string& outDexFile, int uid, bool* _aidl_return) { const char* apk_path = apkPath.c_str(); const char* package_name = packageName.c_str(); const char* out_dex_file = outDexFile.c_str(); *_aidl_return = android::installd::view_compiler(apk_path, package_name, out_dex_file, uid); return *_aidl_return ? ok() : error("viewcompiler failed"); } binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); std::lock_guard lock(mLock); const char* instruction_set = instructionSet.c_str(); char boot_marker_path[PKG_PATH_MAX]; sprintf(boot_marker_path, "%s/%s/%s/.booting", android_data_dir.c_str(), DALVIK_CACHE, instruction_set); ALOGV("mark_boot_complete : %s", boot_marker_path); if (unlink(boot_marker_path) != 0) { return error(StringPrintf("Failed to unlink %s", boot_marker_path)); } return ok(); } binder::Status InstalldNativeService::linkNativeLibraryDirectory( const std::unique_ptr& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(nativeLibPath32); std::lock_guard lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); const char* asecLibDir = nativeLibPath32.c_str(); struct stat s, libStat; binder::Status res = ok(); auto _pkgdir = create_data_user_ce_package_path(uuid_, userId, pkgname); auto _libsymlink = _pkgdir + PKG_LIB_POSTFIX; const char* pkgdir = _pkgdir.c_str(); const char* libsymlink = _libsymlink.c_str(); if (stat(pkgdir, &s) < 0) { return error("Failed to stat " + _pkgdir); } char *con = nullptr; if (lgetfilecon(pkgdir, &con) < 0) { return error("Failed to lgetfilecon " + _pkgdir); } if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) { res = error("Failed to chown " + _pkgdir); goto out; } if (chmod(pkgdir, 0700) < 0) { res = error("Failed to chmod " + _pkgdir); goto out; } if (lstat(libsymlink, &libStat) < 0) { if (errno != ENOENT) { res = error("Failed to stat " + _libsymlink); goto out; } } else { if (S_ISDIR(libStat.st_mode)) { if (delete_dir_contents(libsymlink, 1, nullptr) < 0) { res = error("Failed to delete " + _libsymlink); goto out; } } else if (S_ISLNK(libStat.st_mode)) { if (unlink(libsymlink) < 0) { res = error("Failed to unlink " + _libsymlink); goto out; } } } if (symlink(asecLibDir, libsymlink) < 0) { res = error("Failed to symlink " + _libsymlink + " to " + nativeLibPath32); goto out; } if (lsetfilecon(libsymlink, con) < 0) { res = error("Failed to lsetfilecon " + _libsymlink); goto out; } out: free(con); if (chmod(pkgdir, s.st_mode) < 0) { auto msg = "Failed to cleanup chmod " + _pkgdir; if (res.isOk()) { res = error(msg); } else { PLOG(ERROR) << msg; } } if (chown(pkgdir, s.st_uid, s.st_gid) < 0) { auto msg = "Failed to cleanup chown " + _pkgdir; if (res.isOk()) { res = error(msg); } else { PLOG(ERROR) << msg; } } return res; } static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd) { execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk, StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr); PLOG(ERROR) << "execl (" << kIdMapPath << ") failed"; } static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd) { execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk, StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr); PLOG(ERROR) << "execl (" << kIdMapPath << ") failed"; } static bool delete_stale_idmap(const char* target_apk, const char* overlay_apk, const char* idmap_path, int32_t uid) { int idmap_fd = open(idmap_path, O_RDWR); if (idmap_fd < 0) { PLOG(ERROR) << "idmap open failed: " << idmap_path; unlink(idmap_path); return true; } pid_t pid; pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ if (setgid(uid) != 0) { LOG(ERROR) << "setgid(" << uid << ") failed during idmap"; exit(1); } if (setuid(uid) != 0) { LOG(ERROR) << "setuid(" << uid << ") failed during idmap"; exit(1); } if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) { PLOG(ERROR) << "flock(" << idmap_path << ") failed during idmap"; exit(1); } run_verify_idmap(target_apk, overlay_apk, idmap_fd); exit(1); /* only if exec call to deleting stale idmap failed */ } else { int status = wait_child(pid); close(idmap_fd); if (status != 0) { // Failed on verifying if idmap is made from target_apk and overlay_apk. LOG(DEBUG) << "delete stale idmap: " << idmap_path; unlink(idmap_path); return true; } } return false; } // Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix) // eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap static int flatten_path(const char *prefix, const char *suffix, const char *overlay_path, char *idmap_path, size_t N) { if (overlay_path == nullptr || idmap_path == nullptr) { return -1; } const size_t len_overlay_path = strlen(overlay_path); // will access overlay_path + 1 further below; requires absolute path if (len_overlay_path < 2 || *overlay_path != '/') { return -1; } const size_t len_idmap_root = strlen(prefix); const size_t len_suffix = strlen(suffix); if (SIZE_MAX - len_idmap_root < len_overlay_path || SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) { // additions below would cause overflow return -1; } if (N < len_idmap_root + len_overlay_path + len_suffix) { return -1; } memset(idmap_path, 0, N); snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix); char *ch = idmap_path + len_idmap_root; while (*ch != '\0') { if (*ch == '/') { *ch = '@'; } ++ch; } return 0; } binder::Status InstalldNativeService::idmap(const std::string& targetApkPath, const std::string& overlayApkPath, int32_t uid) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(targetApkPath); CHECK_ARGUMENT_PATH(overlayApkPath); std::lock_guard lock(mLock); const char* target_apk = targetApkPath.c_str(); const char* overlay_apk = overlayApkPath.c_str(); ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid); int idmap_fd = -1; char idmap_path[PATH_MAX]; struct stat idmap_stat; bool outdated = false; if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk, idmap_path, sizeof(idmap_path)) == -1) { ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk); goto fail; } if (stat(idmap_path, &idmap_stat) < 0) { outdated = true; } else { outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid); } if (outdated) { idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644); } else { idmap_fd = open(idmap_path, O_RDWR); } if (idmap_fd < 0) { ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno)); goto fail; } if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) { ALOGE("idmap cannot chown '%s'\n", idmap_path); goto fail; } if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) { ALOGE("idmap cannot chmod '%s'\n", idmap_path); goto fail; } if (!outdated) { close(idmap_fd); return ok(); } pid_t pid; pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ if (setgid(uid) != 0) { ALOGE("setgid(%d) failed during idmap\n", uid); exit(1); } if (setuid(uid) != 0) { ALOGE("setuid(%d) failed during idmap\n", uid); exit(1); } if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) { ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno)); exit(1); } run_idmap(target_apk, overlay_apk, idmap_fd); exit(1); /* only if exec call to idmap failed */ } else { int status = wait_child(pid); if (status != 0) { ALOGE("idmap failed, status=0x%04x\n", status); goto fail; } } close(idmap_fd); return ok(); fail: if (idmap_fd >= 0) { close(idmap_fd); unlink(idmap_path); } return error(); } binder::Status InstalldNativeService::removeIdmap(const std::string& overlayApkPath) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(overlayApkPath); std::lock_guard lock(mLock); const char* overlay_apk = overlayApkPath.c_str(); char idmap_path[PATH_MAX]; if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk, idmap_path, sizeof(idmap_path)) == -1) { ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk); return error(); } if (unlink(idmap_path) < 0) { ALOGE("couldn't unlink idmap file %s\n", idmap_path); return error(); } return ok(); } binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); binder::Status res = ok(); // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE; const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgName = packageName.c_str(); const char* seinfo = seInfo.c_str(); uid_t uid = multiuser_get_uid(userId, appId); if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgName); if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) { res = error("restorecon failed for " + path); } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgName); if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) { res = error("restorecon failed for " + path); } } return res; } binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(oatDir); std::lock_guard lock(mLock); const char* oat_dir = oatDir.c_str(); const char* instruction_set = instructionSet.c_str(); char oat_instr_dir[PKG_PATH_MAX]; if (validate_apk_path(oat_dir)) { return error("Invalid path " + oatDir); } if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) { return error("Failed to prepare " + oatDir); } if (selinux_android_restorecon(oat_dir, 0)) { return error("Failed to restorecon " + oatDir); } snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set); if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) { return error(StringPrintf("Failed to prepare %s", oat_instr_dir)); } return ok(); } binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(packageDir); std::lock_guard lock(mLock); if (validate_apk_path(packageDir.c_str())) { return error("Invalid path " + packageDir); } if (rm_package_dir(packageDir) != 0) { return error("Failed to delete " + packageDir); } return ok(); } binder::Status InstalldNativeService::linkFile(const std::string& relativePath, const std::string& fromBase, const std::string& toBase) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(fromBase); CHECK_ARGUMENT_PATH(toBase); std::lock_guard lock(mLock); const char* relative_path = relativePath.c_str(); const char* from_base = fromBase.c_str(); const char* to_base = toBase.c_str(); char from_path[PKG_PATH_MAX]; char to_path[PKG_PATH_MAX]; snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path); snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path); if (validate_apk_path_subdirs(from_path)) { return error(StringPrintf("Invalid from path %s", from_path)); } if (validate_apk_path_subdirs(to_path)) { return error(StringPrintf("Invalid to path %s", to_path)); } if (link(from_path, to_path) < 0) { return error(StringPrintf("Failed to link from %s to %s", from_path, to_path)); } return ok(); } binder::Status InstalldNativeService::moveAb(const std::string& apkPath, const std::string& instructionSet, const std::string& outputPath) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); std::lock_guard lock(mLock); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); const char* oat_dir = outputPath.c_str(); bool success = move_ab(apk_path, instruction_set, oat_dir); return success ? ok() : error(); } binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, const std::string& instructionSet, const std::unique_ptr& outputPath) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); std::lock_guard lock(mLock); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); const char* oat_dir = outputPath ? outputPath->c_str() : nullptr; bool res = delete_odex(apk_path, instruction_set, oat_dir); return res ? ok() : error(); } // This kernel feature is experimental. // TODO: remove local definition once upstreamed #ifndef FS_IOC_ENABLE_VERITY #define FS_IOC_ENABLE_VERITY _IO('f', 133) #define FS_IOC_SET_VERITY_MEASUREMENT _IOW('f', 134, struct fsverity_measurement) #define FS_VERITY_ALG_SHA256 1 struct fsverity_measurement { __u16 digest_algorithm; __u16 digest_size; __u32 reserved1; __u64 reserved2[3]; __u8 digest[]; }; #endif binder::Status InstalldNativeService::installApkVerity(const std::string& filePath, const ::android::base::unique_fd& verityInputAshmem, int32_t contentSize) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(filePath); std::lock_guard lock(mLock); if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); } #ifndef NDEBUG ASSERT_PAGE_SIZE_4K(); #endif // TODO: also check fsverity support in the current file system if compiled with DEBUG. // TODO: change ashmem to some temporary file to support huge apk. if (!ashmem_valid(verityInputAshmem.get())) { return error("FD is not an ashmem"); } // 1. Seek to the next page boundary beyond the end of the file. ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY)); if (wfd.get() < 0) { return error("Failed to open " + filePath); } struct stat st; if (fstat(wfd.get(), &st) < 0) { return error("Failed to stat " + filePath); } // fsverity starts from the block boundary. off_t padding = kVerityPageSize - st.st_size % kVerityPageSize; if (padding == kVerityPageSize) { padding = 0; } if (lseek(wfd.get(), st.st_size + padding, SEEK_SET) < 0) { return error("Failed to lseek " + filePath); } // 2. Write everything in the ashmem to the file. Note that allocated // ashmem size is multiple of page size, which is different from the // actual content size. int shmSize = ashmem_get_size_region(verityInputAshmem.get()); if (shmSize < 0) { return error("Failed to get ashmem size: " + std::to_string(shmSize)); } if (contentSize < 0) { return error("Invalid content size: " + std::to_string(contentSize)); } if (contentSize > shmSize) { return error("Content size overflow: " + std::to_string(contentSize) + " > " + std::to_string(shmSize)); } auto data = std::unique_ptr>( mmap(nullptr, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0), [contentSize] (void* ptr) { if (ptr != MAP_FAILED) { munmap(ptr, contentSize); } }); if (data.get() == MAP_FAILED) { return error("Failed to mmap the ashmem"); } char* cursor = reinterpret_cast(data.get()); int remaining = contentSize; while (remaining > 0) { int ret = TEMP_FAILURE_RETRY(write(wfd.get(), cursor, remaining)); if (ret < 0) { return error("Failed to write to " + filePath + " (" + std::to_string(remaining) + + "/" + std::to_string(contentSize) + ")"); } cursor += ret; remaining -= ret; } wfd.reset(); // 3. Enable fsverity (needs readonly fd. Once it's done, the file becomes immutable. ::android::base::unique_fd rfd(open(filePath.c_str(), O_RDONLY)); if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) { return error("Failed to enable fsverity on " + filePath); } return ok(); } binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath, const std::vector& expectedHash) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(filePath); std::lock_guard lock(mLock); if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); } // TODO: also check fsverity support in the current file system if compiled with DEBUG. if (expectedHash.size() != kSha256Size) { return error("verity hash size should be " + std::to_string(kSha256Size) + " but is " + std::to_string(expectedHash.size())); } ::android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY)); if (fd.get() < 0) { return error("Failed to open " + filePath + ": " + strerror(errno)); } unsigned int buffer_size = sizeof(fsverity_measurement) + kSha256Size; std::vector buffer(buffer_size, 0); fsverity_measurement* config = reinterpret_cast(buffer.data()); config->digest_algorithm = FS_VERITY_ALG_SHA256; config->digest_size = kSha256Size; memcpy(config->digest, expectedHash.data(), kSha256Size); if (ioctl(fd.get(), FS_IOC_SET_VERITY_MEASUREMENT, config) < 0) { // This includes an expected failure case with no FSVerity setup. It normally happens when // the apk does not contains the Merkle tree root hash. return error("Failed to measure fsverity on " + filePath + ": " + strerror(errno)); } return ok(); // hashes match } binder::Status InstalldNativeService::reconcileSecondaryDexFile( const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector& isas, const std::unique_ptr& volumeUuid, int32_t storage_flag, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(dexPath); std::lock_guard lock(mLock); bool result = android::installd::reconcile_secondary_dex_file( dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return); return result ? ok() : error(); } binder::Status InstalldNativeService::hashSecondaryDexFile( const std::string& dexPath, const std::string& packageName, int32_t uid, const std::unique_ptr& volumeUuid, int32_t storageFlag, std::vector* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(dexPath); // mLock is not taken here since we will never modify the file system. // If a file is modified just as we are reading it this may result in an // anomalous hash, but that's ok. bool result = android::installd::hash_secondary_dex_file( dexPath, packageName, uid, volumeUuid, storageFlag, _aidl_return); return result ? ok() : error(); } binder::Status InstalldNativeService::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); std::lock_guard lock(mMountsLock); mStorageMounts.clear(); #if !BYPASS_QUOTA if (!InvalidateQuotaMounts()) { return error("Failed to read mounts"); } #endif std::ifstream in("/proc/mounts"); if (!in.is_open()) { return error("Failed to read mounts"); } std::string source; std::string target; std::string ignored; while (!in.eof()) { std::getline(in, source, ' '); std::getline(in, target, ' '); std::getline(in, ignored); #if !BYPASS_SDCARDFS if (target.compare(0, 21, "/mnt/runtime/default/") == 0) { LOG(DEBUG) << "Found storage mount " << source << " at " << target; mStorageMounts[source] = target; } #endif } return ok(); } std::string InstalldNativeService::findDataMediaPath( const std::unique_ptr& uuid, userid_t userid) { std::lock_guard lock(mMountsLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str()); auto resolved = mStorageMounts[path]; if (resolved.empty()) { LOG(WARNING) << "Failed to find storage mount for " << path; resolved = path; } return StringPrintf("%s/%u", resolved.c_str(), userid); } binder::Status InstalldNativeService::isQuotaSupported( const std::unique_ptr& uuid, bool* _aidl_return) { auto uuidString = uuid ? *uuid : ""; *_aidl_return = IsQuotaSupported(uuidString); return ok(); } binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName, int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath, const std::unique_ptr& dexMetadata, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); std::lock_guard lock(mLock); *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath, dexMetadata); return ok(); } binder::Status InstalldNativeService::migrateLegacyObbData() { ENFORCE_UID(AID_SYSTEM); // NOTE: The lint warning doesn't apply to the use of system(3) with // absolute parse and no command line arguments. if (system("/system/bin/migrate_legacy_obb_data.sh") != 0) { // NOLINT(cert-env33-c) LOG(ERROR) << "Unable to migrate legacy obb data"; } return ok(); } } // namespace installd } // namespace android cmds/installd/InstalldNativeService.h0100644 0000000 0000000 00000022207 13756501734 016776 0ustar000000000 0000000 /* ** ** Copyright 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef COMMANDS_H_ #define COMMANDS_H_ #include #include #include #include #include #include #include #include "android/os/BnInstalld.h" #include "installd_constants.h" namespace android { namespace installd { class InstalldNativeService : public BinderService, public os::BnInstalld { public: static status_t start(); static char const* getServiceName() { return "installd"; } virtual status_t dump(int fd, const Vector &args) override; binder::Status createUserData(const std::unique_ptr& uuid, int32_t userId, int32_t userSerial, int32_t flags); binder::Status destroyUserData(const std::unique_ptr& uuid, int32_t userId, int32_t flags); binder::Status createAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); binder::Status restoreconAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); binder::Status migrateAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags); binder::Status clearAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); binder::Status destroyAppData(const std::unique_ptr& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); binder::Status fixupAppData(const std::unique_ptr& uuid, int32_t flags); binder::Status snapshotAppData(const std::unique_ptr& volumeUuid, const std::string& packageName, const int32_t user, const int32_t snapshotId, int32_t storageFlags, int64_t* _aidl_return); binder::Status restoreAppDataSnapshot(const std::unique_ptr& volumeUuid, const std::string& packageName, const int32_t appId, const std::string& seInfo, const int32_t user, const int32_t snapshotId, int32_t storageFlags); binder::Status destroyAppDataSnapshot(const std::unique_ptr &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags); binder::Status getAppSize(const std::unique_ptr& uuid, const std::vector& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector& ceDataInodes, const std::vector& codePaths, std::vector* _aidl_return); binder::Status getUserSize(const std::unique_ptr& uuid, int32_t userId, int32_t flags, const std::vector& appIds, std::vector* _aidl_return); binder::Status getExternalSize(const std::unique_ptr& uuid, int32_t userId, int32_t flags, const std::vector& appIds, std::vector* _aidl_return); binder::Status setAppQuota(const std::unique_ptr& uuid, int32_t userId, int32_t appId, int64_t cacheQuota); binder::Status moveCompleteApp(const std::unique_ptr& fromUuid, const std::unique_ptr& toUuid, const std::string& packageName, const std::string& dataAppName, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion); binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::unique_ptr& packageName, const std::string& instructionSet, int32_t dexoptNeeded, const std::unique_ptr& outputPath, int32_t dexFlags, const std::string& compilerFilter, const std::unique_ptr& uuid, const std::unique_ptr& classLoaderContext, const std::unique_ptr& seInfo, bool downgrade, int32_t targetSdkVersion, const std::unique_ptr& profileName, const std::unique_ptr& dexMetadataPath, const std::unique_ptr& compilationReason); binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName, const std::string& outDexFile, int uid, bool* _aidl_return); binder::Status rmdex(const std::string& codePath, const std::string& instructionSet); binder::Status mergeProfiles(int32_t uid, const std::string& packageName, const std::string& profileName, bool* _aidl_return); binder::Status dumpProfiles(int32_t uid, const std::string& packageName, const std::string& profileName, const std::string& codePath, bool* _aidl_return); binder::Status copySystemProfile(const std::string& systemProfile, int32_t uid, const std::string& packageName, const std::string& profileName, bool* _aidl_return); binder::Status clearAppProfiles(const std::string& packageName, const std::string& profileName); binder::Status destroyAppProfiles(const std::string& packageName); binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName, const std::string& profileName, const std::string& classpath, bool* _aidl_return); binder::Status destroyProfileSnapshot(const std::string& packageName, const std::string& profileName); binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath, int32_t uid); binder::Status removeIdmap(const std::string& overlayApkPath); binder::Status rmPackageDir(const std::string& packageDir); binder::Status markBootComplete(const std::string& instructionSet); binder::Status freeCache(const std::unique_ptr& uuid, int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags); binder::Status linkNativeLibraryDirectory(const std::unique_ptr& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId); binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet); binder::Status linkFile(const std::string& relativePath, const std::string& fromBase, const std::string& toBase); binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet, const std::string& outputPath); binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet, const std::unique_ptr& outputPath); binder::Status installApkVerity(const std::string& filePath, const ::android::base::unique_fd& verityInput, int32_t contentSize); binder::Status assertFsverityRootHashMatches(const std::string& filePath, const std::vector& expectedHash); binder::Status reconcileSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector& isa, const std::unique_ptr& volumeUuid, int32_t storage_flag, bool* _aidl_return); binder::Status hashSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::unique_ptr& volumeUuid, int32_t storageFlag, std::vector* _aidl_return); binder::Status invalidateMounts(); binder::Status isQuotaSupported(const std::unique_ptr& volumeUuid, bool* _aidl_return); binder::Status prepareAppProfile(const std::string& packageName, int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath, const std::unique_ptr& dexMetadata, bool* _aidl_return); binder::Status migrateLegacyObbData(); private: std::recursive_mutex mLock; std::recursive_mutex mMountsLock; std::recursive_mutex mQuotasLock; /* Map of all storage mounts from source to target */ std::unordered_map mStorageMounts; /* Map from UID to cache quota size */ std::unordered_map mCacheQuotas; std::string findDataMediaPath(const std::unique_ptr& uuid, userid_t userid); }; } // namespace installd } // namespace android #endif // COMMANDS_H_ cmds/installd/MatchExtensionGen.h0100644 0000000 0000000 00000053544 13756501734 016127 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ /****************************************************************** * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY * ******************************************************************/ #include int MatchExtension(const char* ext) { switch (ext[0]) { case '3': switch (ext[1]) { case 'g': case 'G': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'p': case 'P': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; case 'p': case 'P': switch (ext[4]) { case '\0': return AID_MEDIA_VIDEO; case '2': switch (ext[5]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; } break; } break; case 'a': case 'A': switch (ext[1]) { case 'a': case 'A': switch (ext[2]) { case 'c': case 'C': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'i': case 'I': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; case 'c': case 'C': switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'f': case 'F': switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; } break; case 'm': case 'M': switch (ext[2]) { case 'r': case 'R': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'r': case 'R': switch (ext[2]) { case 't': case 'T': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 's': case 'S': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'v': case 'V': switch (ext[2]) { case 'i': case 'I': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'w': case 'W': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; } break; case 'b': case 'B': switch (ext[1]) { case 'm': case 'M': switch (ext[2]) { case 'p': case 'P': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'c': case 'C': switch (ext[1]) { case 'r': case 'R': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'd': case 'D': switch (ext[1]) { case 'i': case 'I': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'l': case 'L': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'v': case 'V': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'f': case 'F': switch (ext[1]) { case 'l': case 'L': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case 'c': case 'C': switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'i': case 'I': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; case 'g': case 'G': switch (ext[1]) { case 'i': case 'I': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 's': case 'S': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; } break; case 'j': case 'J': switch (ext[1]) { case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'p': case 'P': switch (ext[2]) { case 'e': case 'E': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; case 'g': case 'G': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'l': case 'L': switch (ext[1]) { case 's': case 'S': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; case 'm': case 'M': switch (ext[1]) { case '3': switch (ext[2]) { case 'u': case 'U': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case '4': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'k': case 'K': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'o': case 'O': switch (ext[2]) { case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; case 'i': case 'I': switch (ext[4]) { case 'e': case 'E': switch (ext[5]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; } break; case 'p': case 'P': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case '3': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case '4': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'e': case 'E': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; case 'g': case 'G': switch (ext[4]) { case '\0': return AID_MEDIA_VIDEO; case 'a': case 'A': switch (ext[5]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; } break; case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; case 'a': case 'A': switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; } break; case 'x': case 'X': switch (ext[2]) { case 'u': case 'U': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; case 'n': case 'N': switch (ext[1]) { case 'e': case 'E': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'r': case 'R': switch (ext[2]) { case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'o': case 'O': switch (ext[1]) { case 'g': case 'G': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'r': case 'R': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'p': case 'P': switch (ext[1]) { case 'b': case 'B': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'c': case 'C': switch (ext[2]) { case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'e': case 'E': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'g': case 'G': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'l': case 'L': switch (ext[2]) { case 's': case 'S': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'p': case 'P': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 's': case 'S': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'q': case 'Q': switch (ext[1]) { case 't': case 'T': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'r': case 'R': switch (ext[1]) { case 'a': case 'A': switch (ext[2]) { case '\0': return AID_MEDIA_AUDIO; case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case 's': case 'S': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'g': case 'G': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'm': case 'M': switch (ext[2]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'w': case 'W': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 's': case 'S': switch (ext[1]) { case 'd': case 'D': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'n': case 'N': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'r': case 'R': switch (ext[2]) { case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'v': case 'V': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; case 'z': case 'Z': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; } break; case 't': case 'T': switch (ext[1]) { case 'i': case 'I': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; case 'f': case 'F': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 's': case 'S': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'v': case 'V': switch (ext[1]) { case 'o': case 'O': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; case 'w': case 'W': switch (ext[1]) { case 'a': case 'A': switch (ext[2]) { case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; } break; case 'b': case 'B': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case 'p': case 'P': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'e': case 'E': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case 'm': case 'M': switch (ext[4]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'p': case 'P': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; case 'm': case 'M': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } break; case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'r': case 'R': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; case 'v': case 'V': switch (ext[2]) { case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } break; } break; } break; case 'x': case 'X': switch (ext[1]) { case 'b': case 'B': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'p': case 'P': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; case 'w': case 'W': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } break; } break; } break; } return 0; } cmds/installd/OWNERS0100644 0000000 0000000 00000000244 13756501734 013320 0ustar000000000 0000000 set noparent agampe@google.com calin@google.com jsharkey@android.com maco@google.com mathieuc@google.com narayan@google.com ngeoffray@google.com toddke@google.com cmds/installd/QuotaUtils.cpp0100644 0000000 0000000 00000006565 13756501734 015212 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include "QuotaUtils.h" #include #include #include #include #include "utils.h" namespace android { namespace installd { namespace { std::recursive_mutex mMountsLock; /* Map of all quota mounts from target to source */ std::unordered_map mQuotaReverseMounts; std::string& FindQuotaDeviceForUuid(const std::string& uuid) { std::lock_guard lock(mMountsLock); auto path = create_data_path(uuid.empty() ? nullptr : uuid.c_str()); return mQuotaReverseMounts[path]; } } // namespace bool InvalidateQuotaMounts() { std::lock_guard lock(mMountsLock); mQuotaReverseMounts.clear(); std::ifstream in("/proc/mounts"); if (!in.is_open()) { return false; } std::string source; std::string target; std::string ignored; while (!in.eof()) { std::getline(in, source, ' '); std::getline(in, target, ' '); std::getline(in, ignored); if (source.compare(0, 11, "/dev/block/") == 0) { struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0, reinterpret_cast(&dq)) == 0) { LOG(DEBUG) << "Found quota mount " << source << " at " << target; mQuotaReverseMounts[target] = source; } } } return true; } bool IsQuotaSupported(const std::string& uuid) { return !FindQuotaDeviceForUuid(uuid).empty(); } int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid) { const std::string device = FindQuotaDeviceForUuid(uuid); if (device == "") { return -1; } struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid; } return -1; } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; #endif return dq.dqb_curspace; } } int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) { const std::string device = FindQuotaDeviceForUuid(uuid); if (device == "") { return -1; } struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), gid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << gid; } return -1; } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << gid << " " << dq.dqb_curspace; #endif return dq.dqb_curspace; } } } // namespace installd } // namespace android cmds/installd/QuotaUtils.h0100644 0000000 0000000 00000002506 13756501734 014646 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_INSTALLD_QUOTA_UTILS_H_ #define ANDROID_INSTALLD_QUOTA_UTILS_H_ #include #include namespace android { namespace installd { /* Clear and recompute the reverse mounts map */ bool InvalidateQuotaMounts(); /* Whether quota is supported in the device with the given uuid */ bool IsQuotaSupported(const std::string& uuid); /* Get the current occupied space in bytes for a uid or -1 if fails */ int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid); /* Get the current occupied space in bytes for a gid or -1 if fails */ int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid); } // namespace installd } // namespace android #endif // ANDROID_INSTALLD_QUOTA_UTILS_H_ cmds/installd/TEST_MAPPING0100644 0000000 0000000 00000000701 13756501734 014233 0ustar000000000 0000000 { "presubmit": [ { "name": "installd_cache_test" }, { "name": "installd_dexopt_test" }, { "name": "installd_otapreopt_test" }, { "name": "installd_service_test" }, { "name": "installd_utils_test" }, { "name": "CtsUsesLibraryHostTestCases" }, { "name": "CtsClassloaderSplitsHostTestCases" }, { "name": "CtsCompilationTestCases" } ] } cmds/installd/art_helper/0040755 0000000 0000000 00000000000 13756501734 014510 5ustar000000000 0000000 cmds/installd/art_helper/Android.bp0100644 0000000 0000000 00000000437 13756501734 016414 0ustar000000000 0000000 // Inherit image values. art_global_defaults { name: "libartimagevalues_defaults", } cc_library_static { name: "libartimagevalues", defaults: ["libartimagevalues_defaults"], srcs: ["art_image_values.cpp"], export_include_dirs: ["."], cflags: ["-Wconversion"], } cmds/installd/art_helper/art_image_values.cpp0100644 0000000 0000000 00000002115 13756501734 020517 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include "art_image_values.h" namespace android { namespace installd { namespace art { uint32_t GetImageBaseAddress() { return ART_BASE_ADDRESS; } int32_t GetImageMinBaseAddressDelta() { return ART_BASE_ADDRESS_MIN_DELTA; } int32_t GetImageMaxBaseAddressDelta() { return ART_BASE_ADDRESS_MAX_DELTA; } static_assert(ART_BASE_ADDRESS_MIN_DELTA < ART_BASE_ADDRESS_MAX_DELTA, "Inconsistent setup"); } // namespace art } // namespace installd } // namespace android cmds/installd/art_helper/art_image_values.h0100644 0000000 0000000 00000002106 13756501734 020164 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H #define FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H #include namespace android { namespace installd { namespace art { uint32_t GetImageBaseAddress(); int32_t GetImageMinBaseAddressDelta(); int32_t GetImageMaxBaseAddressDelta(); } // namespace art } // namespace installd } // namespace android #endif // FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H cmds/installd/binder/0040755 0000000 0000000 00000000000 13756501734 013626 5ustar000000000 0000000 cmds/installd/binder/android/0040755 0000000 0000000 00000000000 13756501734 015246 5ustar000000000 0000000 cmds/installd/binder/android/os/0040755 0000000 0000000 00000000000 13756501734 015667 5ustar000000000 0000000 cmds/installd/binder/android/os/IInstalld.aidl0100644 0000000 0000000 00000015413 13756501734 020406 0ustar000000000 0000000 /* * 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 android.os; /** {@hide} */ interface IInstalld { void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion); void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, int appId, @utf8InCpp String seInfo); void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags); void clearAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, long ceDataInode); void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, long ceDataInode); void fixupAppData(@nullable @utf8InCpp String uuid, int flags); long[] getAppSize(@nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames, int userId, int flags, int appId, in long[] ceDataInodes, in @utf8InCpp String[] codePaths); long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds); long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds); void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota); void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid, @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId, @utf8InCpp String seInfo, int targetSdkVersion); void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName, @utf8InCpp String instructionSet, int dexoptNeeded, @nullable @utf8InCpp String outputPath, int dexFlags, @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid, @nullable @utf8InCpp String sharedLibraries, @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion, @nullable @utf8InCpp String profileName, @nullable @utf8InCpp String dexMetadataPath, @nullable @utf8InCpp String compilationReason); boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName, @utf8InCpp String outDexFile, int uid); void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet); boolean mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName); boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName, @utf8InCpp String codePath); boolean copySystemProfile(@utf8InCpp String systemProfile, int uid, @utf8InCpp String packageName, @utf8InCpp String profileName); void clearAppProfiles(@utf8InCpp String packageName, @utf8InCpp String profileName); void destroyAppProfiles(@utf8InCpp String packageName); boolean createProfileSnapshot(int appId, @utf8InCpp String packageName, @utf8InCpp String profileName, @utf8InCpp String classpath); void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid); void removeIdmap(@utf8InCpp String overlayApkPath); void rmPackageDir(@utf8InCpp String packageDir); void markBootComplete(@utf8InCpp String instructionSet); void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, long cacheReservedBytes, int flags); void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId); void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet); void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase, @utf8InCpp String toBase); void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, @utf8InCpp String outputPath); void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput, int contentSize); void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash); boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, int storage_flag); byte[] hashSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, int uid, @nullable @utf8InCpp String volumeUuid, int storageFlag); void invalidateMounts(); boolean isQuotaSupported(@nullable @utf8InCpp String uuid); boolean prepareAppProfile(@utf8InCpp String packageName, int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int userId, int snapshotId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, long ceSnapshotInode, int snapshotId, int storageFlags); void migrateLegacyObbData(); const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; const int FLAG_CLEAR_CACHE_ONLY = 0x10; const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; const int FLAG_FREE_CACHE_V2 = 0x100; const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200; const int FLAG_FREE_CACHE_NOOP = 0x400; const int FLAG_USE_QUOTA = 0x1000; const int FLAG_FORCE = 0x2000; } cmds/installd/dexopt.cpp0100644 0000000 0000000 00000343650 13756501734 014402 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "installd" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO: Move everything to base/logging. #include #include #include #include #include #include #include "dexopt.h" #include "dexopt_return_codes.h" #include "globals.h" #include "installd_deps.h" #include "otapreopt_utils.h" #include "utils.h" using android::base::EndsWith; using android::base::GetBoolProperty; using android::base::GetProperty; using android::base::ReadFdToString; using android::base::ReadFully; using android::base::StringPrintf; using android::base::WriteFully; using android::base::unique_fd; namespace android { namespace installd { // Should minidebug info be included in compiled artifacts? Even if this value is // "true," usage might still be conditional to other constraints, e.g., system // property overrides. static constexpr bool kEnableMinidebugInfo = true; static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo"; static constexpr bool kMinidebugInfoSystemPropertyDefault = false; static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info"; static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none"; // Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below. struct FreeDelete { // NOTE: Deleting a const object is valid but free() takes a non-const pointer. void operator()(const void* ptr) const { free(const_cast(ptr)); } }; // Alias for std::unique_ptr<> that uses the C function free() to delete objects. template using UniqueCPtr = std::unique_ptr; static unique_fd invalid_unique_fd() { return unique_fd(-1); } static bool is_debug_runtime() { return android::base::GetProperty("persist.sys.dalvik.vm.lib.2", "") == "libartd.so"; } static bool is_debuggable_build() { return android::base::GetBoolProperty("ro.debuggable", false); } static bool clear_profile(const std::string& profile) { unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC)); if (ufd.get() < 0) { if (errno != ENOENT) { PLOG(WARNING) << "Could not open profile " << profile; return false; } else { // Nothing to clear. That's ok. return true; } } if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) { if (errno != EWOULDBLOCK) { PLOG(WARNING) << "Error locking profile " << profile; } // This implies that the app owning this profile is running // (and has acquired the lock). // // If we can't acquire the lock bail out since clearing is useless anyway // (the app will write again to the profile). // // Note: // This does not impact the this is not an issue for the profiling correctness. // In case this is needed because of an app upgrade, profiles will still be // eventually cleared by the app itself due to checksum mismatch. // If this is needed because profman advised, then keeping the data around // until the next run is again not an issue. // // If the app attempts to acquire a lock while we've held one here, // it will simply skip the current write cycle. return false; } bool truncated = ftruncate(ufd.get(), 0) == 0; if (!truncated) { PLOG(WARNING) << "Could not truncate " << profile; } if (flock(ufd.get(), LOCK_UN) != 0) { PLOG(WARNING) << "Error unlocking profile " << profile; } return truncated; } // Clear the reference profile for the given location. // The location is the profile name for primary apks or the dex path for secondary dex files. static bool clear_reference_profile(const std::string& package_name, const std::string& location, bool is_secondary_dex) { return clear_profile(create_reference_profile_path(package_name, location, is_secondary_dex)); } // Clear the reference profile for the given location. // The location is the profile name for primary apks or the dex path for secondary dex files. static bool clear_current_profile(const std::string& package_name, const std::string& location, userid_t user, bool is_secondary_dex) { return clear_profile(create_current_profile_path(user, package_name, location, is_secondary_dex)); } // Clear the reference profile for the primary apk of the given package. // The location is the profile name for primary apks or the dex path for secondary dex files. bool clear_primary_reference_profile(const std::string& package_name, const std::string& location) { return clear_reference_profile(package_name, location, /*is_secondary_dex*/false); } // Clear all current profile for the primary apk of the given package. // The location is the profile name for primary apks or the dex path for secondary dex files. bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) { bool success = true; // For secondary dex files, we don't really need the user but we use it for sanity checks. std::vector users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } return success; } // Clear the current profile for the primary apk of the given package and user. bool clear_primary_current_profile(const std::string& package_name, const std::string& location, userid_t user) { return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } static std::vector SplitBySpaces(const std::string& str) { if (str.empty()) { return {}; } return android::base::Split(str, " "); } static const char* get_location_from_path(const char* path) { static constexpr char kLocationSeparator = '/'; const char *location = strrchr(path, kLocationSeparator); if (location == nullptr) { return path; } else { // Skip the separator character. return location + 1; } } // ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations // need to be performed between the fork and exec. class ExecVHelper { public: // Store a placeholder for the binary name. ExecVHelper() : args_(1u, std::string()) {} void PrepareArgs(const std::string& bin) { CHECK(!args_.empty()); CHECK(args_[0].empty()); args_[0] = bin; // Write char* into array. for (const std::string& arg : args_) { argv_.push_back(arg.c_str()); } argv_.push_back(nullptr); // Add null terminator. } [[ noreturn ]] void Exec(int exit_code) { execv(argv_[0], (char * const *)&argv_[0]); PLOG(ERROR) << "execv(" << argv_[0] << ") failed"; exit(exit_code); } // Add an arg if it's not empty. void AddArg(const std::string& arg) { if (!arg.empty()) { args_.push_back(arg); } } // Add a runtime arg if it's not empty. void AddRuntimeArg(const std::string& arg) { if (!arg.empty()) { args_.push_back("--runtime-arg"); args_.push_back(arg); } } protected: // Holder arrays for backing arg storage. std::vector args_; // Argument poiners. std::vector argv_; }; static std::string MapPropertyToArg(const std::string& property, const std::string& format, const std::string& default_value = "") { std::string prop = GetProperty(property, default_value); if (!prop.empty()) { return StringPrintf(format.c_str(), prop.c_str()); } return ""; } // Determines which binary we should use for execution (the debug or non-debug version). // e.g. dex2oatd vs dex2oat static const char* select_execution_binary(const char* binary, const char* debug_binary, bool background_job_compile) { return select_execution_binary( binary, debug_binary, background_job_compile, is_debug_runtime(), (android::base::GetProperty("ro.build.version.codename", "") == "REL"), is_debuggable_build()); } // Determines which binary we should use for execution (the debug or non-debug version). // e.g. dex2oatd vs dex2oat // This is convenient method which is much easier to test because it doesn't read // system properties. const char* select_execution_binary( const char* binary, const char* debug_binary, bool background_job_compile, bool is_debug_runtime, bool is_release, bool is_debuggable_build) { // Do not use debug binaries for release candidates (to give more soak time). bool is_debug_bg_job = background_job_compile && is_debuggable_build && !is_release; // If the runtime was requested to use libartd.so, we'll run the debug version - assuming // the file is present (it may not be on images with very little space available). bool useDebug = (is_debug_runtime || is_debug_bg_job) && (access(debug_binary, X_OK) == 0); return useDebug ? debug_binary : binary; } // Namespace for Android Runtime flags applied during boot time. static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; // Feature flag name for running the JIT in Zygote experiment, b/119800099. static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; // Location of the apex image. static const char* kApexImage = "/system/framework/apex.art"; class RunDex2Oat : public ExecVHelper { public: RunDex2Oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd, const char* input_file_name, const char* output_file_name, int swap_fd, const char* instruction_set, const char* compiler_filter, bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd, const char* class_loader_context, const std::string& class_loader_context_fds, int target_sdk_version, bool enable_hidden_api_checks, bool generate_compact_dex, int dex_metadata_fd, const char* compilation_reason) { // Get the relative path to the input file. const char* relative_input_file_name = get_location_from_path(input_file_name); std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"); std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"); const char* threads_property = post_bootcomplete ? "dalvik.vm.dex2oat-threads" : "dalvik.vm.boot-dex2oat-threads"; std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s"); std::string bootclasspath; char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); if (dex2oat_bootclasspath != nullptr) { bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath); } // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query // BOOTCLASSPATH. const std::string dex2oat_isa_features_key = StringPrintf("dalvik.vm.isa.%s.features", instruction_set); std::string instruction_set_features_arg = MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s"); const std::string dex2oat_isa_variant_key = StringPrintf("dalvik.vm.isa.%s.variant", instruction_set); std::string instruction_set_variant_arg = MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s"); const char* dex2oat_norelocation = "-Xnorelocate"; const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); std::vector dex2oat_flags_args = SplitBySpaces(dex2oat_flags); ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str()); // If we are booting without the real /data, don't spend time compiling. std::string vold_decrypt = GetProperty("vold.decrypt", ""); bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || vold_decrypt == "1"; std::string resolve_startup_string_arg = MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings", "--resolve-startup-const-strings=%s"); if (resolve_startup_string_arg.empty()) { // If empty, fall back to system property. resolve_startup_string_arg = MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings", "--resolve-startup-const-strings=%s"); } const std::string image_block_size_arg = MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", "--max-image-block-size=%s"); const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false); std::string image_format_arg; if (image_fd >= 0) { image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"); } std::string dex2oat_large_app_threshold_arg = MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s"); const char* dex2oat_bin = select_execution_binary( kDex2oatPath, kDex2oatDebugPath, background_job_compile); bool generate_minidebug_info = kEnableMinidebugInfo && GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); std::string boot_image; std::string use_apex_image = server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, ENABLE_APEX_IMAGE, /*default_value=*/ ""); if (use_apex_image == "true") { boot_image = StringPrintf("-Ximage:%s", kApexImage); } else { boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s"); } // clang FORTIFY doesn't let us use strlen in constant array bounds, so we // use arraysize instead. std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd); std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name); std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd); std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd); std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd); std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name); std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set); std::string dex2oat_compiler_filter_arg; std::string dex2oat_swap_fd; std::string dex2oat_image_fd; std::string target_sdk_version_arg; if (target_sdk_version != 0) { target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); } std::string class_loader_context_arg; std::string class_loader_context_fds_arg; if (class_loader_context != nullptr) { class_loader_context_arg = StringPrintf("--class-loader-context=%s", class_loader_context); if (!class_loader_context_fds.empty()) { class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s", class_loader_context_fds.c_str()); } } if (swap_fd >= 0) { dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd); } if (image_fd >= 0) { dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd); } // Compute compiler filter. bool have_dex2oat_relocation_skip_flag = false; if (skip_compilation) { dex2oat_compiler_filter_arg = "--compiler-filter=extract"; have_dex2oat_relocation_skip_flag = true; } else if (compiler_filter != nullptr) { dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter); } if (dex2oat_compiler_filter_arg.empty()) { dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s"); } // Check whether all apps should be compiled debuggable. if (!debuggable) { debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1"; } std::string profile_arg; if (profile_fd != -1) { profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd); } // Get the directory of the apk to pass as a base classpath directory. std::string base_dir; std::string apk_dir(input_file_name); unsigned long dir_index = apk_dir.rfind('/'); bool has_base_dir = dir_index != std::string::npos; if (has_base_dir) { apk_dir = apk_dir.substr(0, dir_index); base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str()); } std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); std::string compilation_reason_arg = compilation_reason == nullptr ? "" : std::string("--compilation-reason=") + compilation_reason; ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); // Disable cdex if update input vdex is true since this combination of options is not // supported. const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd); AddArg(zip_fd_arg); AddArg(zip_location_arg); AddArg(input_vdex_fd_arg); AddArg(output_vdex_fd_arg); AddArg(oat_fd_arg); AddArg(oat_location_arg); AddArg(instruction_set_arg); AddArg(instruction_set_variant_arg); AddArg(instruction_set_features_arg); AddRuntimeArg(boot_image); AddRuntimeArg(bootclasspath); AddRuntimeArg(dex2oat_Xms_arg); AddRuntimeArg(dex2oat_Xmx_arg); AddArg(resolve_startup_string_arg); AddArg(image_block_size_arg); AddArg(dex2oat_compiler_filter_arg); AddArg(dex2oat_threads_arg); AddArg(dex2oat_swap_fd); AddArg(dex2oat_image_fd); if (generate_debug_info) { AddArg("--generate-debug-info"); } if (debuggable) { AddArg("--debuggable"); } AddArg(image_format_arg); AddArg(dex2oat_large_app_threshold_arg); if (have_dex2oat_relocation_skip_flag) { AddRuntimeArg(dex2oat_norelocation); } AddArg(profile_arg); AddArg(base_dir); AddArg(class_loader_context_arg); AddArg(class_loader_context_fds_arg); if (generate_minidebug_info) { AddArg(kMinidebugDex2oatFlag); } if (disable_cdex) { AddArg(kDisableCompactDexFlag); } AddRuntimeArg(target_sdk_version_arg); if (enable_hidden_api_checks) { AddRuntimeArg("-Xhidden-api-policy:enabled"); } if (dex_metadata_fd > -1) { AddArg(dex_metadata_fd_arg); } AddArg(compilation_reason_arg); // Do not add args after dex2oat_flags, they should override others for debugging. args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end()); PrepareArgs(dex2oat_bin); } }; /* * Whether dexopt should use a swap file when compiling an APK. * * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision * itself, anyways). * * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true". * * Otherwise, return true if this is a low-mem device. * * Otherwise, return default value. */ static bool kAlwaysProvideSwapFile = false; static bool kDefaultProvideSwapFile = true; static bool ShouldUseSwapFileForDexopt() { if (kAlwaysProvideSwapFile) { return true; } // Check the "override" property. If it exists, return value == "true". std::string dex2oat_prop_buf = GetProperty("dalvik.vm.dex2oat-swap", ""); if (!dex2oat_prop_buf.empty()) { return dex2oat_prop_buf == "true"; } // Shortcut for default value. This is an implementation optimization for the process sketched // above. If the default value is true, we can avoid to check whether this is a low-mem device, // as low-mem is never returning false. The compiler will optimize this away if it can. if (kDefaultProvideSwapFile) { return true; } if (GetBoolProperty("ro.config.low_ram", false)) { return true; } // Default value must be false here. return kDefaultProvideSwapFile; } static void SetDex2OatScheduling(bool set_to_bg) { if (set_to_bg) { if (set_sched_policy(0, SP_BACKGROUND) < 0) { PLOG(ERROR) << "set_sched_policy failed"; exit(DexoptReturnCodes::kSetSchedPolicy); } if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { PLOG(ERROR) << "setpriority failed"; exit(DexoptReturnCodes::kSetPriority); } } } static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) { unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600))); if (fd.get() < 0) { if (errno != EEXIST) { PLOG(ERROR) << "Failed to create profile " << profile; return invalid_unique_fd(); } } // Profiles should belong to the app; make sure of that by giving ownership to // the app uid. If we cannot do that, there's no point in returning the fd // since dex2oat/profman will fail with SElinux denials. if (fchown(fd.get(), uid, uid) < 0) { PLOG(ERROR) << "Could not chown profile " << profile; return invalid_unique_fd(); } return fd; } static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) { // Do not follow symlinks when opening a profile: // - primary profiles should not contain symlinks in their paths // - secondary dex paths should have been already resolved and validated flags |= O_NOFOLLOW; // Check if we need to create the profile // Reference profiles and snapshots are created on the fly; so they might not exist beforehand. unique_fd fd; if ((flags & O_CREAT) != 0) { fd = create_profile(uid, profile, flags); } else { fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags))); } if (fd.get() < 0) { if (errno != ENOENT) { // Profiles might be missing for various reasons. For example, in a // multi-user environment, the profile directory for one user can be created // after we start a merge. In this case the current profile for that user // will not be found. // Also, the secondary dex profiles might be deleted by the app at any time, // so we can't we need to prepare if they are missing. PLOG(ERROR) << "Failed to open profile " << profile; } return invalid_unique_fd(); } return fd; } static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& package_name, const std::string& location, bool is_secondary_dex) { std::string profile = create_current_profile_path(user, package_name, location, is_secondary_dex); return open_profile(uid, profile, O_RDONLY); } static unique_fd open_reference_profile(uid_t uid, const std::string& package_name, const std::string& location, bool read_write, bool is_secondary_dex) { std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex); return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY); } static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, const std::string& location) { std::string profile = create_snapshot_profile_path(package_name, location); return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC); } static void open_profile_files(uid_t uid, const std::string& package_name, const std::string& location, bool is_secondary_dex, /*out*/ std::vector* profiles_fd, /*out*/ unique_fd* reference_profile_fd) { // Open the reference profile in read-write mode as profman might need to save the merge. *reference_profile_fd = open_reference_profile(uid, package_name, location, /*read_write*/ true, is_secondary_dex); // For secondary dex files, we don't really need the user but we use it for sanity checks. // Note: the user owning the dex file should be the current user. std::vector users; if (is_secondary_dex){ users.push_back(multiuser_get_user_id(uid)); } else { users = get_known_users(/*volume_uuid*/ nullptr); } for (auto user : users) { unique_fd profile_fd = open_current_profile(uid, user, package_name, location, is_secondary_dex); // Add to the lists only if both fds are valid. if (profile_fd.get() >= 0) { profiles_fd->push_back(std::move(profile_fd)); } } } static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0; static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1; static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; class RunProfman : public ExecVHelper { public: void SetupArgs(const std::vector& profile_fds, const unique_fd& reference_profile_fd, const std::vector& apk_fds, const std::vector& dex_locations, bool copy_and_update, bool store_aggregation_counters) { // TODO(calin): Assume for now we run in the bg compile job (which is in // most of the invocation). With the current data flow, is not very easy or // clean to discover this in RunProfman (it will require quite a messy refactoring). const char* profman_bin = select_execution_binary( kProfmanPath, kProfmanDebugPath, /*background_job_compile=*/ true); if (copy_and_update) { CHECK_EQ(1u, profile_fds.size()); CHECK_EQ(1u, apk_fds.size()); } if (reference_profile_fd != -1) { AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get())); } for (const unique_fd& fd : profile_fds) { AddArg("--profile-file-fd=" + std::to_string(fd.get())); } for (const unique_fd& fd : apk_fds) { AddArg("--apk-fd=" + std::to_string(fd.get())); } for (const std::string& dex_location : dex_locations) { AddArg("--dex-location=" + dex_location); } if (copy_and_update) { AddArg("--copy-and-update-profile-key"); } if (store_aggregation_counters) { AddArg("--store-aggregation-counters"); } // Do not add after dex2oat_flags, they should override others for debugging. PrepareArgs(profman_bin); } void SetupMerge(const std::vector& profiles_fd, const unique_fd& reference_profile_fd, const std::vector& apk_fds = std::vector(), const std::vector& dex_locations = std::vector(), bool store_aggregation_counters = false) { SetupArgs(profiles_fd, reference_profile_fd, apk_fds, dex_locations, /*copy_and_update=*/false, store_aggregation_counters); } void SetupCopyAndUpdate(unique_fd&& profile_fd, unique_fd&& reference_profile_fd, unique_fd&& apk_fd, const std::string& dex_location) { // The fds need to stay open longer than the scope of the function, so put them into a local // variable vector. profiles_fd_.push_back(std::move(profile_fd)); apk_fds_.push_back(std::move(apk_fd)); reference_profile_fd_ = std::move(reference_profile_fd); std::vector dex_locations = {dex_location}; SetupArgs(profiles_fd_, reference_profile_fd_, apk_fds_, dex_locations, /*copy_and_update=*/true, /*store_aggregation_counters=*/false); } void SetupDump(const std::vector& profiles_fd, const unique_fd& reference_profile_fd, const std::vector& dex_locations, const std::vector& apk_fds, const unique_fd& output_fd) { AddArg("--dump-only"); AddArg(StringPrintf("--dump-output-to-fd=%d", output_fd.get())); SetupArgs(profiles_fd, reference_profile_fd, apk_fds, dex_locations, /*copy_and_update=*/false, /*store_aggregation_counters=*/false); } void Exec() { ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec); } private: unique_fd reference_profile_fd_; std::vector profiles_fd_; std::vector apk_fds_; }; // Decides if profile guided compilation is needed or not based on existing profiles. // The location is the package name for primary apks or the dex path for secondary dex files. // Returns true if there is enough information in the current profiles that makes it // worth to recompile the given location. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). static bool analyze_profiles(uid_t uid, const std::string& package_name, const std::string& location, bool is_secondary_dex) { std::vector profiles_fd; unique_fd reference_profile_fd; open_profile_files(uid, package_name, location, is_secondary_dex, &profiles_fd, &reference_profile_fd); if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) { // Skip profile guided compilation because no profiles were found. // Or if the reference profile info couldn't be opened. return false; } RunProfman profman_merge; profman_merge.SetupMerge(profiles_fd, reference_profile_fd); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); profman_merge.Exec(); } /* parent */ int return_code = wait_child(pid); bool need_to_compile = false; bool should_clear_current_profiles = false; bool should_clear_reference_profile = false; if (!WIFEXITED(return_code)) { LOG(WARNING) << "profman failed for location " << location << ": " << return_code; } else { return_code = WEXITSTATUS(return_code); switch (return_code) { case PROFMAN_BIN_RETURN_CODE_COMPILE: need_to_compile = true; should_clear_current_profiles = true; should_clear_reference_profile = false; break; case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION: need_to_compile = false; should_clear_current_profiles = false; should_clear_reference_profile = false; break; case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES: LOG(WARNING) << "Bad profiles for location " << location; need_to_compile = false; should_clear_current_profiles = true; should_clear_reference_profile = true; break; case PROFMAN_BIN_RETURN_CODE_ERROR_IO: // fall-through case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING: // Temporary IO problem (e.g. locking). Ignore but log a warning. LOG(WARNING) << "IO error while reading profiles for location " << location; need_to_compile = false; should_clear_current_profiles = false; should_clear_reference_profile = false; break; default: // Unknown return code or error. Unlink profiles. LOG(WARNING) << "Unknown error code while processing profiles for location " << location << ": " << return_code; need_to_compile = false; should_clear_current_profiles = true; should_clear_reference_profile = true; break; } } if (should_clear_current_profiles) { if (is_secondary_dex) { // For secondary dex files, the owning user is the current user. clear_current_profile(package_name, location, multiuser_get_user_id(uid), is_secondary_dex); } else { clear_primary_current_profiles(package_name, location); } } if (should_clear_reference_profile) { clear_reference_profile(package_name, location, is_secondary_dex); } return need_to_compile; } // Decides if profile guided compilation is needed or not based on existing profiles. // The analysis is done for the primary apks of the given package. // Returns true if there is enough information in the current profiles that makes it // worth to recompile the package. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). bool analyze_primary_profiles(uid_t uid, const std::string& package_name, const std::string& profile_name) { return analyze_profiles(uid, package_name, profile_name, /*is_secondary_dex*/false); } bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name, const std::string& code_path) { std::vector profile_fds; unique_fd reference_profile_fd; std::string out_file_name = StringPrintf("/data/misc/profman/%s-%s.txt", pkgname.c_str(), profile_name.c_str()); open_profile_files(uid, pkgname, profile_name, /*is_secondary_dex*/false, &profile_fds, &reference_profile_fd); const bool has_reference_profile = (reference_profile_fd.get() != -1); const bool has_profiles = !profile_fds.empty(); if (!has_reference_profile && !has_profiles) { LOG(ERROR) << "profman dump: no profiles to dump for " << pkgname; return false; } unique_fd output_fd(open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644)); if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { LOG(ERROR) << "installd cannot chmod file for dump_profile" << out_file_name; return false; } std::vector dex_locations; std::vector apk_fds; unique_fd apk_fd(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW)); if (apk_fd == -1) { PLOG(ERROR) << "installd cannot open " << code_path.c_str(); return false; } dex_locations.push_back(get_location_from_path(code_path.c_str())); apk_fds.push_back(std::move(apk_fd)); RunProfman profman_dump; profman_dump.SetupDump(profile_fds, reference_profile_fd, dex_locations, apk_fds, output_fd); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); profman_dump.Exec(); } /* parent */ int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code; return false; } return true; } bool copy_system_profile(const std::string& system_profile, uid_t packageUid, const std::string& package_name, const std::string& profile_name) { unique_fd in_fd(open(system_profile.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); unique_fd out_fd(open_reference_profile(packageUid, package_name, profile_name, /*read_write*/ true, /*secondary*/ false)); if (in_fd.get() < 0) { PLOG(WARNING) << "Could not open profile " << system_profile; return false; } if (out_fd.get() < 0) { PLOG(WARNING) << "Could not open profile " << package_name; return false; } // As a security measure we want to write the profile information with the reduced capabilities // of the package user id. So we fork and drop capabilities in the child. pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(packageUid); if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) { if (errno != EWOULDBLOCK) { PLOG(WARNING) << "Error locking profile " << package_name; } // This implies that the app owning this profile is running // (and has acquired the lock). // // The app never acquires the lock for the reference profiles of primary apks. // Only dex2oat from installd will do that. Since installd is single threaded // we should not see this case. Nevertheless be prepared for it. PLOG(WARNING) << "Failed to flock " << package_name; return false; } bool truncated = ftruncate(out_fd.get(), 0) == 0; if (!truncated) { PLOG(WARNING) << "Could not truncate " << package_name; } // Copy over data. static constexpr size_t kBufferSize = 4 * 1024; char buffer[kBufferSize]; while (true) { ssize_t bytes = read(in_fd.get(), buffer, kBufferSize); if (bytes == 0) { break; } write(out_fd.get(), buffer, bytes); } if (flock(out_fd.get(), LOCK_UN) != 0) { PLOG(WARNING) << "Error unlocking profile " << package_name; } // Use _exit since we don't want to run the global destructors in the child. // b/62597429 _exit(0); } /* parent */ int return_code = wait_child(pid); return return_code == 0; } static std::string replace_file_extension(const std::string& oat_path, const std::string& new_ext) { // A standard dalvik-cache entry. Replace ".dex" with `new_ext`. if (EndsWith(oat_path, ".dex")) { std::string new_path = oat_path; new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext); CHECK(EndsWith(new_path, new_ext)); return new_path; } // An odex entry. Not that this may not be an extension, e.g., in the OTA // case (where the base name will have an extension for the B artifact). size_t odex_pos = oat_path.rfind(".odex"); if (odex_pos != std::string::npos) { std::string new_path = oat_path; new_path.replace(odex_pos, strlen(".odex"), new_ext); CHECK_NE(new_path.find(new_ext), std::string::npos); return new_path; } // Don't know how to handle this. return ""; } // Translate the given oat path to an art (app image) path. An empty string // denotes an error. static std::string create_image_filename(const std::string& oat_path) { return replace_file_extension(oat_path, ".art"); } // Translate the given oat path to a vdex path. An empty string denotes an error. static std::string create_vdex_filename(const std::string& oat_path) { return replace_file_extension(oat_path, ".vdex"); } static int open_output_file(const char* file_name, bool recreate, int permissions) { int flags = O_RDWR | O_CREAT; if (recreate) { if (unlink(file_name) < 0) { if (errno != ENOENT) { PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name; } } flags |= O_EXCL; } return open(file_name, flags, permissions); } static bool set_permissions_and_ownership( int fd, bool is_public, int uid, const char* path, bool is_secondary_dex) { // Primary apks are owned by the system. Secondary dex files are owned by the app. int owning_uid = is_secondary_dex ? uid : AID_SYSTEM; if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP | (is_public ? S_IROTH : 0)) < 0) { ALOGE("installd cannot chmod '%s' during dexopt\n", path); return false; } else if (fchown(fd, owning_uid, uid) < 0) { ALOGE("installd cannot chown '%s' during dexopt\n", path); return false; } return true; } static bool IsOutputDalvikCache(const char* oat_dir) { // InstallerConnection.java (which invokes installd) transforms Java null arguments // into '!'. Play it safe by handling it both. // TODO: ensure we never get null. // TODO: pass a flag instead of inferring if the output is dalvik cache. return oat_dir == nullptr || oat_dir[0] == '!'; } // Best-effort check whether we can fit the the path into our buffers. // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run // without a swap file, if necessary. Reference profiles file also add an extra ".prof" // extension to the cache path (5 bytes). // TODO(calin): move away from char* buffers and PKG_PATH_MAX. static bool validate_dex_path_size(const std::string& dex_path) { if (dex_path.size() >= (PKG_PATH_MAX - 8)) { LOG(ERROR) << "dex_path too long: " << dex_path; return false; } return true; } static bool create_oat_out_path(const char* apk_path, const char* instruction_set, const char* oat_dir, bool is_secondary_dex, /*out*/ char* out_oat_path) { if (!validate_dex_path_size(apk_path)) { return false; } if (!IsOutputDalvikCache(oat_dir)) { // Oat dirs for secondary dex files are already validated. if (!is_secondary_dex && validate_apk_path(oat_dir)) { ALOGE("cannot validate apk path with oat_dir '%s'\n", oat_dir); return false; } if (!calculate_oat_file_path(out_oat_path, oat_dir, apk_path, instruction_set)) { return false; } } else { if (!create_cache_path(out_oat_path, apk_path, instruction_set)) { return false; } } return true; } // Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor // on destruction. It will also run the given cleanup (unless told not to) after closing. // // Usage example: // // Dex2oatFileWrapper file(open(...), // [name]() { // unlink(name.c_str()); // }); // // Note: care needs to be taken about name, as it needs to have a lifetime longer than the // wrapper if captured as a reference. // // if (file.get() == -1) { // // Error opening... // } // // ... // if (error) { // // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run // // and delete the file (after the fd is closed). // return -1; // } // // (Success case) // file.SetCleanup(false); // // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run // // (leaving the file around; after the fd is closed). // class Dex2oatFileWrapper { public: Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) { } Dex2oatFileWrapper(int value, std::function cleanup) : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {} Dex2oatFileWrapper(Dex2oatFileWrapper&& other) { value_ = other.value_; cleanup_ = other.cleanup_; do_cleanup_ = other.do_cleanup_; auto_close_ = other.auto_close_; other.release(); } Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) { value_ = other.value_; cleanup_ = other.cleanup_; do_cleanup_ = other.do_cleanup_; auto_close_ = other.auto_close_; other.release(); return *this; } ~Dex2oatFileWrapper() { reset(-1); } int get() { return value_; } void SetCleanup(bool cleanup) { do_cleanup_ = cleanup; } void reset(int new_value) { if (auto_close_ && value_ >= 0) { close(value_); } if (do_cleanup_ && cleanup_ != nullptr) { cleanup_(); } value_ = new_value; } void reset(int new_value, std::function new_cleanup) { if (auto_close_ && value_ >= 0) { close(value_); } if (do_cleanup_ && cleanup_ != nullptr) { cleanup_(); } value_ = new_value; cleanup_ = new_cleanup; } void DisableAutoClose() { auto_close_ = false; } private: void release() { value_ = -1; do_cleanup_ = false; cleanup_ = nullptr; } int value_; std::function cleanup_; bool do_cleanup_; bool auto_close_; }; // (re)Creates the app image if needed. Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) { // We don't create an image for secondary dex files. if (is_secondary_dex) { return Dex2oatFileWrapper(); } const std::string image_path = create_image_filename(out_oat_path); if (image_path.empty()) { // Happens when the out_oat_path has an unknown extension. return Dex2oatFileWrapper(); } // In case there is a stale image, remove it now. Ignore any error. unlink(image_path.c_str()); // Not enabled, exit. if (!generate_app_image) { return Dex2oatFileWrapper(); } std::string app_image_format = GetProperty("dalvik.vm.appimageformat", ""); if (app_image_format.empty()) { return Dex2oatFileWrapper(); } // Recreate is true since we do not want to modify a mapped image. If the app is // already running and we modify the image file, it can cause crashes (b/27493510). Dex2oatFileWrapper wrapper_fd( open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/), [image_path]() { unlink(image_path.c_str()); }); if (wrapper_fd.get() < 0) { // Could not create application image file. Go on since we can compile without it. LOG(ERROR) << "installd could not create '" << image_path << "' for image file during dexopt"; // If we have a valid image file path but no image fd, explicitly erase the image file. if (unlink(image_path.c_str()) < 0) { if (errno != ENOENT) { PLOG(ERROR) << "Couldn't unlink image file " << image_path; } } } else if (!set_permissions_and_ownership( wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str()); wrapper_fd.reset(-1); } return wrapper_fd; } // Creates the dexopt swap file if necessary and return its fd. // Returns -1 if there's no need for a swap or in case of errors. unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) { if (!ShouldUseSwapFileForDexopt()) { return invalid_unique_fd(); } auto swap_file_name = std::string(out_oat_path) + ".swap"; unique_fd swap_fd(open_output_file( swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600)); if (swap_fd.get() < 0) { // Could not create swap file. Optimistically go on and hope that we can compile // without it. ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name.c_str()); } else { // Immediately unlink. We don't really want to hit flash. if (unlink(swap_file_name.c_str()) < 0) { PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name; } } return swap_fd; } // Opens the reference profiles if needed. // Note that the reference profile might not exist so it's OK if the fd will be -1. Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path, const char* profile_name, bool profile_guided, bool is_public, int uid, bool is_secondary_dex) { // If we are not profile guided compilation, or we are compiling system server // do not bother to open the profiles; we won't be using them. if (!profile_guided || (pkgname[0] == '*')) { return Dex2oatFileWrapper(); } // If this is a secondary dex path which is public do not open the profile. // We cannot compile public secondary dex paths with profiles. That's because // it will expose how the dex files are used by their owner. // // Note that the PackageManager is responsible to set the is_public flag for // primary apks and we do not check it here. In some cases, e.g. when // compiling with a public profile from the .dm file the PackageManager will // set is_public toghether with the profile guided compilation. if (is_secondary_dex && is_public) { return Dex2oatFileWrapper(); } // Open reference profile in read only mode as dex2oat does not get write permissions. std::string location; if (is_secondary_dex) { location = dex_path; } else { if (profile_name == nullptr) { // This path is taken for system server re-compilation lunched from ZygoteInit. return Dex2oatFileWrapper(); } else { location = profile_name; } } unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false, is_secondary_dex); const auto& cleanup = [pkgname, location, is_secondary_dex]() { clear_reference_profile(pkgname, location, is_secondary_dex); }; return Dex2oatFileWrapper(ufd.release(), cleanup); } // Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to // out_vdex_wrapper_fd. Returns true for success or false in case of errors. bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed, const char* instruction_set, bool is_public, int uid, bool is_secondary_dex, bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd, Dex2oatFileWrapper* out_vdex_wrapper_fd) { CHECK(in_vdex_wrapper_fd != nullptr); CHECK(out_vdex_wrapper_fd != nullptr); // Open the existing VDEX. We do this before creating the new output VDEX, which will // unlink the old one. char in_odex_path[PKG_PATH_MAX]; int dexopt_action = abs(dexopt_needed); bool is_odex_location = dexopt_needed < 0; std::string in_vdex_path_str; // Infer the name of the output VDEX. const std::string out_vdex_path_str = create_vdex_filename(out_oat_path); if (out_vdex_path_str.empty()) { return false; } bool update_vdex_in_place = false; if (dexopt_action != DEX2OAT_FROM_SCRATCH) { // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd. const char* path = nullptr; if (is_odex_location) { if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) { path = in_odex_path; } else { ALOGE("installd cannot compute input vdex location for '%s'\n", apk_path); return false; } } else { path = out_oat_path; } in_vdex_path_str = create_vdex_filename(path); if (in_vdex_path_str.empty()) { ALOGE("installd cannot compute input vdex location for '%s'\n", path); return false; } // We can update in place when all these conditions are met: // 1) The vdex location to write to is the same as the vdex location to read (vdex files // on /system typically cannot be updated in place). // 2) We dex2oat due to boot image change, because we then know the existing vdex file // cannot be currently used by a running process. // 3) We are not doing a profile guided compilation, because dexlayout requires two // different vdex files to operate. update_vdex_in_place = (in_vdex_path_str == out_vdex_path_str) && (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) && !profile_guided; if (update_vdex_in_place) { // Open the file read-write to be able to update it. in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0)); if (in_vdex_wrapper_fd->get() == -1) { // If we failed to open the file, we cannot update it in place. update_vdex_in_place = false; } } else { in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0)); } } // If we are updating the vdex in place, we do not need to recreate a vdex, // and can use the same existing one. if (update_vdex_in_place) { // We unlink the file in case the invocation of dex2oat fails, to ensure we don't // have bogus stale vdex files. out_vdex_wrapper_fd->reset( in_vdex_wrapper_fd->get(), [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); }); // Disable auto close for the in wrapper fd (it will be done when destructing the out // wrapper). in_vdex_wrapper_fd->DisableAutoClose(); } else { out_vdex_wrapper_fd->reset( open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644), [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); }); if (out_vdex_wrapper_fd->get() < 0) { ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str()); return false; } } if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid, out_vdex_path_str.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str()); return false; } // If we got here we successfully opened the vdex files. return true; } // Opens the output oat file for the given apk. // If successful it stores the output path into out_oat_path and returns true. Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir, bool is_public, int uid, const char* instruction_set, bool is_secondary_dex, char* out_oat_path) { if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) { return Dex2oatFileWrapper(); } const std::string out_oat_path_str(out_oat_path); Dex2oatFileWrapper wrapper_fd( open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644), [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); }); if (wrapper_fd.get() < 0) { PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path; } else if (!set_permissions_and_ownership( wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path); wrapper_fd.reset(-1); } return wrapper_fd; } // Creates RDONLY fds for oat and vdex files, if exist. // Returns false if it fails to create oat out path for the given apk path. // Note that the method returns true even if the files could not be opened. bool maybe_open_oat_and_vdex_file(const std::string& apk_path, const std::string& oat_dir, const std::string& instruction_set, bool is_secondary_dex, unique_fd* oat_file_fd, unique_fd* vdex_file_fd) { char oat_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path.c_str(), instruction_set.c_str(), oat_dir.c_str(), is_secondary_dex, oat_path)) { LOG(ERROR) << "Could not create oat out path for " << apk_path << " with oat dir " << oat_dir; return false; } oat_file_fd->reset(open(oat_path, O_RDONLY)); if (oat_file_fd->get() < 0) { PLOG(INFO) << "installd cannot open oat file during dexopt" << oat_path; } std::string vdex_filename = create_vdex_filename(oat_path); vdex_file_fd->reset(open(vdex_filename.c_str(), O_RDONLY)); if (vdex_file_fd->get() < 0) { PLOG(INFO) << "installd cannot open vdex file during dexopt" << vdex_filename; } return true; } // Updates the access times of out_oat_path based on those from apk_path. void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) { struct stat input_stat; memset(&input_stat, 0, sizeof(input_stat)); if (stat(apk_path, &input_stat) != 0) { PLOG(ERROR) << "Could not stat " << apk_path << " during dexopt"; return; } struct utimbuf ut; ut.actime = input_stat.st_atime; ut.modtime = input_stat.st_mtime; if (utime(out_oat_path, &ut) != 0) { PLOG(WARNING) << "Could not update access times for " << apk_path << " during dexopt"; } } // Runs (execv) dexoptanalyzer on the given arguments. // The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter. // If this is for a profile guided compilation, profile_was_updated will tell whether or not // the profile has changed. class RunDexoptAnalyzer : public ExecVHelper { public: RunDexoptAnalyzer(const std::string& dex_file, int vdex_fd, int oat_fd, int zip_fd, const std::string& instruction_set, const std::string& compiler_filter, bool profile_was_updated, bool downgrade, const char* class_loader_context, const std::string& class_loader_context_fds) { CHECK_GE(zip_fd, 0); // We always run the analyzer in the background job. const char* dexoptanalyzer_bin = select_execution_binary( kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); std::string dex_file_arg = "--dex-file=" + dex_file; std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd); std::string vdex_fd_arg = "--vdex-fd=" + std::to_string(vdex_fd); std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd); std::string isa_arg = "--isa=" + instruction_set; std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter; const char* assume_profile_changed = "--assume-profile-changed"; const char* downgrade_flag = "--downgrade"; std::string class_loader_context_arg = "--class-loader-context="; if (class_loader_context != nullptr) { class_loader_context_arg += class_loader_context; } std::string class_loader_context_fds_arg = "--class-loader-context-fds="; if (!class_loader_context_fds.empty()) { class_loader_context_fds_arg += class_loader_context_fds; } // program name, dex file, isa, filter AddArg(dex_file_arg); AddArg(isa_arg); AddArg(compiler_filter_arg); if (oat_fd >= 0) { AddArg(oat_fd_arg); } if (vdex_fd >= 0) { AddArg(vdex_fd_arg); } AddArg(zip_fd_arg); if (profile_was_updated) { AddArg(assume_profile_changed); } if (downgrade) { AddArg(downgrade_flag); } if (class_loader_context != nullptr) { AddArg(class_loader_context_arg); if (!class_loader_context_fds.empty()) { AddArg(class_loader_context_fds_arg); } } PrepareArgs(dexoptanalyzer_bin); } // Dexoptanalyzer mode which flattens the given class loader context and // prints a list of its dex files in that flattened order. RunDexoptAnalyzer(const char* class_loader_context) { CHECK(class_loader_context != nullptr); // We always run the analyzer in the background job. const char* dexoptanalyzer_bin = select_execution_binary( kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); AddArg("--flatten-class-loader-context"); AddArg(std::string("--class-loader-context=") + class_loader_context); PrepareArgs(dexoptanalyzer_bin); } }; // Prepares the oat dir for the secondary dex files. static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid, const char* instruction_set) { unsigned long dirIndex = dex_path.rfind('/'); if (dirIndex == std::string::npos) { LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path; return false; } std::string dex_dir = dex_path.substr(0, dirIndex); // Create oat file output directory. mode_t oat_dir_mode = S_IRWXU | S_IRWXG | S_IXOTH; if (prepare_app_cache_dir(dex_dir, "oat", oat_dir_mode, uid, uid) != 0) { LOG(ERROR) << "Could not prepare oat dir for secondary dex: " << dex_path; return false; } char oat_dir[PKG_PATH_MAX]; snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str()); if (prepare_app_cache_dir(oat_dir, instruction_set, oat_dir_mode, uid, uid) != 0) { LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path; return false; } return true; } // Return codes for identifying the reason why dexoptanalyzer was not invoked when processing // secondary dex files. This return codes are returned by the child process created for // analyzing secondary dex files in process_secondary_dex_dexopt. enum DexoptAnalyzerSkipCodes { // The dexoptanalyzer was not invoked because of validation or IO errors. // Specific errors are encoded in the name. kSecondaryDexDexoptAnalyzerSkippedValidatePath = 200, kSecondaryDexDexoptAnalyzerSkippedOpenZip = 201, kSecondaryDexDexoptAnalyzerSkippedPrepareDir = 202, kSecondaryDexDexoptAnalyzerSkippedOpenOutput = 203, kSecondaryDexDexoptAnalyzerSkippedFailExec = 204, // The dexoptanalyzer was not invoked because the dex file does not exist anymore. kSecondaryDexDexoptAnalyzerSkippedNoFile = 205, }; // Verifies the result of analyzing secondary dex files from process_secondary_dex_dexopt. // If the result is valid returns true and sets dexopt_needed_out to a valid value. // Returns false for errors or unexpected result values. // The result is expected to be either one of SECONDARY_DEX_* codes or a valid exit code // of dexoptanalyzer. static bool process_secondary_dexoptanalyzer_result(const std::string& dex_path, int result, int* dexopt_needed_out, std::string* error_msg) { // The result values are defined in dexoptanalyzer. switch (result) { case 0: // dexoptanalyzer: no_dexopt_needed *dexopt_needed_out = NO_DEXOPT_NEEDED; return true; case 1: // dexoptanalyzer: dex2oat_from_scratch *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true; case 4: // dexoptanalyzer: dex2oat_for_bootimage_odex *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true; case 5: // dexoptanalyzer: dex2oat_for_filter_odex *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true; case 2: // dexoptanalyzer: dex2oat_for_bootimage_oat case 3: // dexoptanalyzer: dex2oat_for_filter_oat *error_msg = StringPrintf("Dexoptanalyzer return the status of an oat file." " Expected odex file status for secondary dex %s" " : dexoptanalyzer result=%d", dex_path.c_str(), result); return false; } // Use a second switch for enum switch-case analysis. switch (static_cast(result)) { case kSecondaryDexDexoptAnalyzerSkippedNoFile: // If the file does not exist there's no need for dexopt. *dexopt_needed_out = NO_DEXOPT_NEEDED; return true; case kSecondaryDexDexoptAnalyzerSkippedValidatePath: *error_msg = "Dexoptanalyzer path validation failed"; return false; case kSecondaryDexDexoptAnalyzerSkippedOpenZip: *error_msg = "Dexoptanalyzer open zip failed"; return false; case kSecondaryDexDexoptAnalyzerSkippedPrepareDir: *error_msg = "Dexoptanalyzer dir preparation failed"; return false; case kSecondaryDexDexoptAnalyzerSkippedOpenOutput: *error_msg = "Dexoptanalyzer open output failed"; return false; case kSecondaryDexDexoptAnalyzerSkippedFailExec: *error_msg = "Dexoptanalyzer failed to execute"; return false; } *error_msg = StringPrintf("Unexpected result from analyzing secondary dex %s result=%d", dex_path.c_str(), result); return false; } enum SecondaryDexAccess { kSecondaryDexAccessReadOk = 0, kSecondaryDexAccessDoesNotExist = 1, kSecondaryDexAccessPermissionError = 2, kSecondaryDexAccessIOError = 3 }; static SecondaryDexAccess check_secondary_dex_access(const std::string& dex_path) { // Check if the path exists and can be read. If not, there's nothing to do. if (access(dex_path.c_str(), R_OK) == 0) { return kSecondaryDexAccessReadOk; } else { if (errno == ENOENT) { LOG(INFO) << "Secondary dex does not exist: " << dex_path; return kSecondaryDexAccessDoesNotExist; } else { PLOG(ERROR) << "Could not access secondary dex " << dex_path; return errno == EACCES ? kSecondaryDexAccessPermissionError : kSecondaryDexAccessIOError; } } } static bool is_file_public(const std::string& filename) { struct stat file_stat; if (stat(filename.c_str(), &file_stat) == 0) { return (file_stat.st_mode & S_IROTH) != 0; } return false; } // Create the oat file structure for the secondary dex 'dex_path' and assign // the individual path component to the 'out_' parameters. static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa, char* out_oat_dir, char* out_oat_isa_dir, char* out_oat_path, std::string* error_msg) { size_t dirIndex = dex_path.rfind('/'); if (dirIndex == std::string::npos) { *error_msg = std::string("Unexpected dir structure for dex file ").append(dex_path); return false; } // TODO(calin): we have similar computations in at lest 3 other places // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by // using string append. std::string apk_dir = dex_path.substr(0, dirIndex); snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str()); snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str()); if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir, /*is_secondary_dex*/true, out_oat_path)) { *error_msg = std::string("Could not create oat path for secondary dex ").append(dex_path); return false; } return true; } // Validate that the dexopt_flags contain a valid storage flag and convert that to an installd // recognized storage flags (FLAG_STORAGE_CE or FLAG_STORAGE_DE). static bool validate_dexopt_storage_flags(int dexopt_flags, int* out_storage_flag, std::string* error_msg) { if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) { *out_storage_flag = FLAG_STORAGE_CE; if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) { *error_msg = "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set"; return false; } } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) { *out_storage_flag = FLAG_STORAGE_DE; } else { *error_msg = "Secondary dex storage flag must be set"; return false; } return true; } static bool get_class_loader_context_dex_paths(const char* class_loader_context, int uid, /* out */ std::vector* context_dex_paths) { if (class_loader_context == nullptr) { return true; } LOG(DEBUG) << "Getting dex paths for context " << class_loader_context; // Pipe to get the hash result back from our child process. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { PLOG(ERROR) << "Failed to create pipe"; return false; } pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); // Route stdout to `pipe_write` while ((dup2(pipe_write, STDOUT_FILENO) == -1) && (errno == EINTR)) {} pipe_write.reset(); pipe_read.reset(); RunDexoptAnalyzer run_dexopt_analyzer(class_loader_context); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } /* parent */ pipe_write.reset(); std::string str_dex_paths; if (!ReadFdToString(pipe_read, &str_dex_paths)) { PLOG(ERROR) << "Failed to read from pipe"; return false; } pipe_read.reset(); int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { PLOG(ERROR) << "Error waiting for child dexoptanalyzer process"; return false; } constexpr int kFlattenClassLoaderContextSuccess = 50; return_code = WEXITSTATUS(return_code); if (return_code != kFlattenClassLoaderContextSuccess) { LOG(ERROR) << "Dexoptanalyzer could not flatten class loader context, code=" << return_code; return false; } if (!str_dex_paths.empty()) { *context_dex_paths = android::base::Split(str_dex_paths, ":"); } return true; } static int open_dex_paths(const std::vector& dex_paths, /* out */ std::vector* zip_fds, /* out */ std::string* error_msg) { for (const std::string& dex_path : dex_paths) { zip_fds->emplace_back(open(dex_path.c_str(), O_RDONLY)); if (zip_fds->back().get() < 0) { *error_msg = StringPrintf( "installd cannot open '%s' for input during dexopt", dex_path.c_str()); if (errno == ENOENT) { return kSecondaryDexDexoptAnalyzerSkippedNoFile; } else { return kSecondaryDexDexoptAnalyzerSkippedOpenZip; } } } return 0; } static std::string join_fds(const std::vector& fds) { std::stringstream ss; bool is_first = true; for (const unique_fd& fd : fds) { if (is_first) { is_first = false; } else { ss << ":"; } ss << fd.get(); } return ss.str(); } // Processes the dex_path as a secondary dex files and return true if the path dex file should // be compiled. Returns false for errors (logged) or true if the secondary dex path was process // successfully. // When returning true, the output parameters will be: // - is_public_out: whether or not the oat file should not be made public // - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded // - oat_dir_out: the oat dir path where the oat file should be stored static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname, int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, const char* class_loader_context, const std::vector& context_dex_paths, /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { LOG(ERROR) << *error_msg; return false; } // Compute the oat dir as it's not easy to extract it from the child computation. char oat_path[PKG_PATH_MAX]; char oat_dir[PKG_PATH_MAX]; char oat_isa_dir[PKG_PATH_MAX]; if (!create_secondary_dex_oat_layout( dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path, error_msg)) { LOG(ERROR) << "Could not create secondary odex layout: " << *error_msg; return false; } oat_dir_out->assign(oat_dir); pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); // Validate the path structure. if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; _exit(kSecondaryDexDexoptAnalyzerSkippedValidatePath); } // Open the dex file. unique_fd zip_fd; zip_fd.reset(open(dex_path.c_str(), O_RDONLY)); if (zip_fd.get() < 0) { if (errno == ENOENT) { _exit(kSecondaryDexDexoptAnalyzerSkippedNoFile); } else { _exit(kSecondaryDexDexoptAnalyzerSkippedOpenZip); } } // Open class loader context dex files. std::vector context_zip_fds; int open_dex_paths_rc = open_dex_paths(context_dex_paths, &context_zip_fds, error_msg); if (open_dex_paths_rc != 0) { _exit(open_dex_paths_rc); } // Prepare the oat directories. if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) { _exit(kSecondaryDexDexoptAnalyzerSkippedPrepareDir); } // Open the vdex/oat files if any. unique_fd oat_file_fd; unique_fd vdex_file_fd; if (!maybe_open_oat_and_vdex_file(dex_path, *oat_dir_out, instruction_set, true /* is_secondary_dex */, &oat_file_fd, &vdex_file_fd)) { _exit(kSecondaryDexDexoptAnalyzerSkippedOpenOutput); } // Analyze profiles. bool profile_was_updated = analyze_profiles(uid, pkgname, dex_path, /*is_secondary_dex*/true); // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return. // Note that we do not do it before the fork since opening the files is required to happen // after forking. RunDexoptAnalyzer run_dexopt_analyzer(dex_path, vdex_file_fd.get(), oat_file_fd.get(), zip_fd.get(), instruction_set, compiler_filter, profile_was_updated, downgrade, class_loader_context, join_fds(context_zip_fds)); run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } /* parent */ int result = wait_child(pid); if (!WIFEXITED(result)) { *error_msg = StringPrintf("dexoptanalyzer failed for path %s: 0x%04x", dex_path.c_str(), result); LOG(ERROR) << *error_msg; return false; } result = WEXITSTATUS(result); // Check that we successfully executed dexoptanalyzer. bool success = process_secondary_dexoptanalyzer_result(dex_path, result, dexopt_needed_out, error_msg); if (!success) { LOG(ERROR) << *error_msg; } LOG(DEBUG) << "Processed secondary dex file " << dex_path << " result=" << result; // Run dexopt only if needed or forced. // Note that dexoptanalyzer is executed even if force compilation is enabled (because it // makes the code simpler; force compilation is only needed during tests). if (success && (result != kSecondaryDexDexoptAnalyzerSkippedNoFile) && ((dexopt_flags & DEXOPT_FORCE) != 0)) { *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; } // Check if we should make the oat file public. // Note that if the dex file is not public the compiled code cannot be made public. // It is ok to check this flag outside in the parent process. *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path); return success; } static std::string format_dexopt_error(int status, const char* dex_path) { if (WIFEXITED(status)) { int int_code = WEXITSTATUS(status); const char* code_name = get_return_code_name(static_cast(int_code)); if (code_name != nullptr) { return StringPrintf("Dex2oat invocation for %s failed: %s", dex_path, code_name); } } return StringPrintf("Dex2oat invocation for %s failed with 0x%04x", dex_path, status); } int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, bool downgrade, int target_sdk_version, const char* profile_name, const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg) { CHECK(pkgname != nullptr); CHECK(pkgname[0] != 0); CHECK(error_msg != nullptr); CHECK_EQ(dexopt_flags & ~DEXOPT_MASK, 0) << "dexopt flags contains unknown fields: " << dexopt_flags; if (!validate_dex_path_size(dex_path)) { *error_msg = StringPrintf("Failed to validate %s", dex_path); return -1; } if (class_loader_context != nullptr && strlen(class_loader_context) > PKG_PATH_MAX) { *error_msg = StringPrintf("Class loader context exceeds the allowed size: %s", class_loader_context); LOG(ERROR) << *error_msg; return -1; } bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0; bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0; bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0; bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0; bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0; bool background_job_compile = (dexopt_flags & DEXOPT_IDLE_BACKGROUND_JOB) != 0; bool enable_hidden_api_checks = (dexopt_flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) != 0; bool generate_compact_dex = (dexopt_flags & DEXOPT_GENERATE_COMPACT_DEX) != 0; bool generate_app_image = (dexopt_flags & DEXOPT_GENERATE_APP_IMAGE) != 0; // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; std::vector context_dex_paths; if (is_secondary_dex) { if (!get_class_loader_context_dex_paths(class_loader_context, uid, &context_dex_paths)) { *error_msg = "Failed acquiring context dex paths"; return -1; // We had an error, logged in the process method. } if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context, context_dex_paths, error_msg)) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. } } else { if (error_msg->empty()) { // TODO: Make this a CHECK. *error_msg = "Failed processing secondary."; } return -1; // We had an error, logged in the process method. } } else { // Currently these flags are only used for secondary dex files. // Verify that they are not set for primary apks. CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0); CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0); } // Open the input file. unique_fd input_fd(open(dex_path, O_RDONLY, 0)); if (input_fd.get() < 0) { *error_msg = StringPrintf("installd cannot open '%s' for input during dexopt", dex_path); LOG(ERROR) << *error_msg; return -1; } // Open class loader context dex files. std::vector context_input_fds; if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) { LOG(ERROR) << *error_msg; return -1; } // Create the output OAT file. char out_oat_path[PKG_PATH_MAX]; Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid, instruction_set, is_secondary_dex, out_oat_path); if (out_oat_fd.get() < 0) { *error_msg = "Could not open out oat file."; return -1; } // Open vdex files. Dex2oatFileWrapper in_vdex_fd; Dex2oatFileWrapper out_vdex_fd; if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) { *error_msg = "Could not open vdex files."; return -1; } // Ensure that the oat dir and the compiler artifacts of secondary dex files have the correct // selinux context (we generate them on the fly during the dexopt invocation and they don't // fully inherit their parent context). // Note that for primary apk the oat files are created before, in a separate installd // call which also does the restorecon. TODO(calin): unify the paths. if (is_secondary_dex) { if (selinux_android_restorecon_pkgdir(oat_dir, se_info, uid, SELINUX_ANDROID_RESTORECON_RECURSE)) { *error_msg = std::string("Failed to restorecon ").append(oat_dir); LOG(ERROR) << *error_msg; return -1; } } // Create a swap file if necessary. unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path); // Create the app image file if needed. Dex2oatFileWrapper image_fd = maybe_open_app_image( out_oat_path, generate_app_image, is_public, uid, is_secondary_dex); // Open the reference profile if needed. Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile( pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex); unique_fd dex_metadata_fd; if (dex_metadata_path != nullptr) { dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW))); if (dex_metadata_fd.get() < 0) { PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path; } } LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---"; RunDex2Oat runner(input_fd.get(), out_oat_fd.get(), in_vdex_fd.get(), out_vdex_fd.get(), image_fd.get(), dex_path, out_oat_path, swap_fd.get(), instruction_set, compiler_filter, debuggable, boot_complete, background_job_compile, reference_profile_fd.get(), class_loader_context, join_fds(context_input_fds), target_sdk_version, enable_hidden_api_checks, generate_compact_dex, dex_metadata_fd.get(), compilation_reason); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); SetDex2OatScheduling(boot_complete); if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) { PLOG(ERROR) << "flock(" << out_oat_path << ") failed"; _exit(DexoptReturnCodes::kFlock); } runner.Exec(DexoptReturnCodes::kDex2oatExec); } else { int res = wait_child(pid); if (res == 0) { LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---"; } else { LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x" << std::hex << std::setw(4) << res << ", process failed"; *error_msg = format_dexopt_error(res, dex_path); return res; } } update_out_oat_access_times(dex_path, out_oat_path); // We've been successful, don't delete output. out_oat_fd.SetCleanup(false); out_vdex_fd.SetCleanup(false); image_fd.SetCleanup(false); reference_profile_fd.SetCleanup(false); return 0; } // Try to remove the given directory. Log an error if the directory exists // and is empty but could not be removed. static bool rmdir_if_empty(const char* dir) { if (rmdir(dir) == 0) { return true; } if (errno == ENOENT || errno == ENOTEMPTY) { return true; } PLOG(ERROR) << "Failed to remove dir: " << dir; return false; } // Try to unlink the given file. Log an error if the file exists and could not // be unlinked. static bool unlink_if_exists(const std::string& file) { if (unlink(file.c_str()) == 0) { return true; } if (errno == ENOENT) { return true; } PLOG(ERROR) << "Could not unlink: " << file; return false; } enum ReconcileSecondaryDexResult { kReconcileSecondaryDexExists = 0, kReconcileSecondaryDexCleanedUp = 1, kReconcileSecondaryDexValidationError = 2, kReconcileSecondaryDexCleanUpError = 3, kReconcileSecondaryDexAccessIOError = 4, }; // Reconcile the secondary dex 'dex_path' and its generated oat files. // Return true if all the parameters are valid and the secondary dex file was // processed successfully (i.e. the dex_path either exists, or if not, its corresponding // oat/vdex/art files where deleted successfully). In this case, out_secondary_dex_exists // will be true if the secondary dex file still exists. If the secondary dex file does not exist, // the method cleans up any previously generated compiler artifacts (oat, vdex, art). // Return false if there were errors during processing. In this case // out_secondary_dex_exists will be set to false. bool reconcile_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::vector& isas, const std::unique_ptr& volume_uuid, int storage_flag, /*out*/bool* out_secondary_dex_exists) { *out_secondary_dex_exists = false; // start by assuming the file does not exist. if (isas.size() == 0) { LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector"; return false; } if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) { LOG(ERROR) << "reconcile_secondary_dex_file called with invalid storage_flag: " << storage_flag; return false; } // As a security measure we want to unlink art artifacts with the reduced capabilities // of the package user id. So we fork and drop capabilities in the child. pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; _exit(kReconcileSecondaryDexValidationError); } SecondaryDexAccess access_check = check_secondary_dex_access(dex_path); switch (access_check) { case kSecondaryDexAccessDoesNotExist: // File does not exist. Proceed with cleaning. break; case kSecondaryDexAccessReadOk: _exit(kReconcileSecondaryDexExists); case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError); case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError); default: LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check; _exit(kReconcileSecondaryDexValidationError); } // The secondary dex does not exist anymore or it's. Clear any generated files. char oat_path[PKG_PATH_MAX]; char oat_dir[PKG_PATH_MAX]; char oat_isa_dir[PKG_PATH_MAX]; bool result = true; for (size_t i = 0; i < isas.size(); i++) { std::string error_msg; if (!create_secondary_dex_oat_layout( dex_path,isas[i], oat_dir, oat_isa_dir, oat_path, &error_msg)) { LOG(ERROR) << error_msg; _exit(kReconcileSecondaryDexValidationError); } // Delete oat/vdex/art files. result = unlink_if_exists(oat_path) && result; result = unlink_if_exists(create_vdex_filename(oat_path)) && result; result = unlink_if_exists(create_image_filename(oat_path)) && result; // Delete profiles. std::string current_profile = create_current_profile_path( multiuser_get_user_id(uid), pkgname, dex_path, /*is_secondary*/true); std::string reference_profile = create_reference_profile_path( pkgname, dex_path, /*is_secondary*/true); result = unlink_if_exists(current_profile) && result; result = unlink_if_exists(reference_profile) && result; // We upgraded once the location of current profile for secondary dex files. // Check for any previous left-overs and remove them as well. std::string old_current_profile = dex_path + ".prof"; result = unlink_if_exists(old_current_profile); // Try removing the directories as well, they might be empty. result = rmdir_if_empty(oat_isa_dir) && result; result = rmdir_if_empty(oat_dir) && result; } if (!result) { PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path; } _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError); } int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code; } else { return_code = WEXITSTATUS(return_code); } LOG(DEBUG) << "Reconcile secondary dex path " << dex_path << " result=" << return_code; switch (return_code) { case kReconcileSecondaryDexCleanedUp: case kReconcileSecondaryDexValidationError: // If we couldn't validate assume the dex file does not exist. // This will purge the entry from the PM records. *out_secondary_dex_exists = false; return true; case kReconcileSecondaryDexExists: *out_secondary_dex_exists = true; return true; case kReconcileSecondaryDexAccessIOError: // We had an access IO error. // Return false so that we can try again. // The value of out_secondary_dex_exists does not matter in this case and by convention // is set to false. *out_secondary_dex_exists = false; return false; default: LOG(ERROR) << "Unexpected code from reconcile_secondary_dex_file: " << return_code; *out_secondary_dex_exists = false; return false; } } // Compute and return the hash (SHA-256) of the secondary dex file at dex_path. // Returns true if all parameters are valid and the hash successfully computed and stored in // out_secondary_dex_hash. // Also returns true with an empty hash if the file does not currently exist or is not accessible to // the app. // For any other errors (e.g. if any of the parameters are invalid) returns false. bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::unique_ptr& volume_uuid, int storage_flag, std::vector* out_secondary_dex_hash) { out_secondary_dex_hash->clear(); const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) { LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: " << storage_flag; return false; } // Pipe to get the hash result back from our child process. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { PLOG(ERROR) << "Failed to create pipe"; return false; } // Fork so that actual access to the files is done in the app's own UID, to ensure we only // access data the app itself can access. pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing drop_capabilities(uid); pipe_read.reset(); if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; _exit(DexoptReturnCodes::kHashValidatePath); } unique_fd fd(TEMP_FAILURE_RETRY(open(dex_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW))); if (fd == -1) { if (errno == EACCES || errno == ENOENT) { // Not treated as an error. _exit(0); } PLOG(ERROR) << "Failed to open secondary dex " << dex_path; _exit(DexoptReturnCodes::kHashOpenPath); } SHA256_CTX ctx; SHA256_Init(&ctx); std::vector buffer(65536); while (true) { ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size())); if (bytes_read == 0) { break; } else if (bytes_read == -1) { PLOG(ERROR) << "Failed to read secondary dex " << dex_path; _exit(DexoptReturnCodes::kHashReadDex); } SHA256_Update(&ctx, buffer.data(), bytes_read); } std::array hash; SHA256_Final(hash.data(), &ctx); if (!WriteFully(pipe_write, hash.data(), hash.size())) { _exit(DexoptReturnCodes::kHashWrite); } _exit(0); } // parent pipe_write.reset(); out_secondary_dex_hash->resize(SHA256_DIGEST_LENGTH); if (!ReadFully(pipe_read, out_secondary_dex_hash->data(), out_secondary_dex_hash->size())) { out_secondary_dex_hash->clear(); } return wait_child(pid) == 0; } // Helper for move_ab, so that we can have common failure-case cleanup. static bool unlink_and_rename(const char* from, const char* to) { // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise, // return a failure. struct stat s; if (stat(to, &s) == 0) { if (!S_ISREG(s.st_mode)) { LOG(ERROR) << from << " is not a regular file to replace for A/B."; return false; } if (unlink(to) != 0) { LOG(ERROR) << "Could not unlink " << to << " to move A/B."; return false; } } else { // This may be a permission problem. We could investigate the error code, but we'll just // let the rename failure do the work for us. } // Try to rename "to" to "from." if (rename(from, to) != 0) { PLOG(ERROR) << "Could not rename " << from << " to " << to; return false; } return true; } // Move/rename a B artifact (from) to an A artifact (to). static bool move_ab_path(const std::string& b_path, const std::string& a_path) { // Check whether B exists. { struct stat s; if (stat(b_path.c_str(), &s) != 0) { // Silently ignore for now. The service calling this isn't smart enough to understand // lack of artifacts at the moment. return false; } if (!S_ISREG(s.st_mode)) { LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file."; // Try to unlink, but swallow errors. unlink(b_path.c_str()); return false; } } // Rename B to A. if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) { // Delete the b_path so we don't try again (or fail earlier). if (unlink(b_path.c_str()) != 0) { PLOG(ERROR) << "Could not unlink " << b_path; } return false; } return true; } bool move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) { // Get the current slot suffix. No suffix, no A/B. const std::string slot_suffix = GetProperty("ro.boot.slot_suffix", ""); if (slot_suffix.empty()) { return false; } if (!ValidateTargetSlotSuffix(slot_suffix)) { LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; return false; } // Validate other inputs. if (validate_apk_path(apk_path) != 0) { LOG(ERROR) << "Invalid apk_path: " << apk_path; return false; } if (validate_apk_path(oat_dir) != 0) { LOG(ERROR) << "Invalid oat_dir: " << oat_dir; return false; } char a_path[PKG_PATH_MAX]; if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) { return false; } const std::string a_vdex_path = create_vdex_filename(a_path); const std::string a_image_path = create_image_filename(a_path); // B path = A path + slot suffix. const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str()); const std::string b_vdex_path = StringPrintf("%s.%s", a_vdex_path.c_str(), slot_suffix.c_str()); const std::string b_image_path = StringPrintf("%s.%s", a_image_path.c_str(), slot_suffix.c_str()); bool success = true; if (move_ab_path(b_path, a_path)) { if (move_ab_path(b_vdex_path, a_vdex_path)) { // Note: we can live without an app image. As such, ignore failure to move the image file. // If we decide to require the app image, or the app image being moved correctly, // then change accordingly. constexpr bool kIgnoreAppImageFailure = true; if (!a_image_path.empty()) { if (!move_ab_path(b_image_path, a_image_path)) { unlink(a_image_path.c_str()); if (!kIgnoreAppImageFailure) { success = false; } } } } else { // Cleanup: delete B image, ignore errors. unlink(b_image_path.c_str()); success = false; } } else { // Cleanup: delete B image, ignore errors. unlink(b_vdex_path.c_str()); unlink(b_image_path.c_str()); success = false; } return success; } bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) { // Delete the oat/odex file. char out_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path, instruction_set, oat_dir, /*is_secondary_dex*/false, out_path)) { return false; } // In case of a permission failure report the issue. Otherwise just print a warning. auto unlink_and_check = [](const char* path) -> bool { int result = unlink(path); if (result != 0) { if (errno == EACCES || errno == EPERM) { PLOG(ERROR) << "Could not unlink " << path; return false; } PLOG(WARNING) << "Could not unlink " << path; } return true; }; // Delete the oat/odex file. bool return_value_oat = unlink_and_check(out_path); // Derive and delete the app image. bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str()); // Derive and delete the vdex file. bool return_value_vdex = unlink_and_check(create_vdex_filename(out_path).c_str()); // Report success. return return_value_oat && return_value_art && return_value_vdex; } static bool is_absolute_path(const std::string& path) { if (path.find('/') != 0 || path.find("..") != std::string::npos) { LOG(ERROR) << "Invalid absolute path " << path; return false; } else { return true; } } static bool is_valid_instruction_set(const std::string& instruction_set) { // TODO: add explicit whitelisting of instruction sets if (instruction_set.find('/') != std::string::npos) { LOG(ERROR) << "Invalid instruction set " << instruction_set; return false; } else { return true; } } bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set) { std::string oat_dir_ = oat_dir; std::string apk_path_ = apk_path; std::string instruction_set_ = instruction_set; if (!is_absolute_path(oat_dir_)) return false; if (!is_absolute_path(apk_path_)) return false; if (!is_valid_instruction_set(instruction_set_)) return false; std::string::size_type end = apk_path_.rfind('.'); std::string::size_type start = apk_path_.rfind('/', end); if (end == std::string::npos || start == std::string::npos) { LOG(ERROR) << "Invalid apk_path " << apk_path_; return false; } std::string res_ = oat_dir_ + '/' + instruction_set + '/' + apk_path_.substr(start + 1, end - start - 1) + ".odex"; const char* res = res_.c_str(); if (strlen(res) >= PKG_PATH_MAX) { LOG(ERROR) << "Result too large"; return false; } else { strlcpy(path, res, PKG_PATH_MAX); return true; } } bool calculate_odex_file_path_default(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { std::string apk_path_ = apk_path; std::string instruction_set_ = instruction_set; if (!is_absolute_path(apk_path_)) return false; if (!is_valid_instruction_set(instruction_set_)) return false; std::string::size_type end = apk_path_.rfind('.'); std::string::size_type start = apk_path_.rfind('/', end); if (end == std::string::npos || start == std::string::npos) { LOG(ERROR) << "Invalid apk_path " << apk_path_; return false; } std::string oat_dir = apk_path_.substr(0, start + 1) + "oat"; return calculate_oat_file_path_default(path, oat_dir.c_str(), apk_path, instruction_set); } bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { std::string src_ = src; std::string instruction_set_ = instruction_set; if (!is_absolute_path(src_)) return false; if (!is_valid_instruction_set(instruction_set_)) return false; for (auto it = src_.begin() + 1; it < src_.end(); ++it) { if (*it == '/') { *it = '@'; } } std::string res_ = android_data_dir + DALVIK_CACHE + '/' + instruction_set_ + src_ + DALVIK_CACHE_POSTFIX; const char* res = res_.c_str(); if (strlen(res) >= PKG_PATH_MAX) { LOG(ERROR) << "Result too large"; return false; } else { strlcpy(path, res, PKG_PATH_MAX); return true; } } bool open_classpath_files(const std::string& classpath, std::vector* apk_fds, std::vector* dex_locations) { std::vector classpaths_elems = base::Split(classpath, ":"); for (const std::string& elem : classpaths_elems) { unique_fd fd(TEMP_FAILURE_RETRY(open(elem.c_str(), O_RDONLY))); if (fd < 0) { PLOG(ERROR) << "Could not open classpath elem " << elem; return false; } else { apk_fds->push_back(std::move(fd)); dex_locations->push_back(elem); } } return true; } static bool create_app_profile_snapshot(int32_t app_id, const std::string& package_name, const std::string& profile_name, const std::string& classpath) { int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); if (snapshot_fd < 0) { return false; } std::vector profiles_fd; unique_fd reference_profile_fd; open_profile_files(app_shared_gid, package_name, profile_name, /*is_secondary_dex*/ false, &profiles_fd, &reference_profile_fd); if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) { return false; } profiles_fd.push_back(std::move(reference_profile_fd)); // Open the class paths elements. These will be used to filter out profile data that does // not belong to the classpath during merge. std::vector apk_fds; std::vector dex_locations; if (!open_classpath_files(classpath, &apk_fds, &dex_locations)) { return false; } RunProfman args; args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(app_shared_gid); args.Exec(); } /* parent */ int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { LOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; return false; } return true; } static bool create_boot_image_profile_snapshot(const std::string& package_name, const std::string& profile_name, const std::string& classpath) { // The reference profile directory for the android package might not be prepared. Do it now. const std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name); if (fs_prepare_dir(ref_profile_dir.c_str(), 0770, AID_SYSTEM, AID_SYSTEM) != 0) { PLOG(ERROR) << "Failed to prepare " << ref_profile_dir; return false; } // Return false for empty class path since it may otherwise return true below if profiles is // empty. if (classpath.empty()) { PLOG(ERROR) << "Class path is empty"; return false; } // Open and create the snapshot profile. unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); // Collect all non empty profiles. // The collection will traverse all applications profiles and find the non empty files. // This has the potential of inspecting a large number of files and directories (depending // on the number of applications and users). So there is a slight increase in the chance // to get get occasionally I/O errors (e.g. for opening the file). When that happens do not // fail the snapshot and aggregate whatever profile we could open. // // The profile snapshot is a best effort based on available data it's ok if some data // from some apps is missing. It will be counter productive for the snapshot to fail // because we could not open or read some of the files. std::vector profiles; if (!collect_profiles(&profiles)) { LOG(WARNING) << "There were errors while collecting the profiles for the boot image."; } // If we have no profiles return early. if (profiles.empty()) { return true; } // Open the classpath elements. These will be used to filter out profile data that does // not belong to the classpath during merge. std::vector apk_fds; std::vector dex_locations; if (!open_classpath_files(classpath, &apk_fds, &dex_locations)) { return false; } // If we could not open any files from the classpath return an error. if (apk_fds.empty()) { LOG(ERROR) << "Could not open any of the classpath elements."; return false; } // Aggregate the profiles in batches of kAggregationBatchSize. // We do this to avoid opening a huge a amount of files. static constexpr size_t kAggregationBatchSize = 10; std::vector profiles_fd; for (size_t i = 0; i < profiles.size(); ) { for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) { unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY); if (fd.get() >= 0) { profiles_fd.push_back(std::move(fd)); } } RunProfman args; args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations, /*store_aggregation_counters=*/true); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(AID_SYSTEM); // The introduction of new access flags into boot jars causes them to // fail dex file verification. args.Exec(); } /* parent */ int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; return false; } return true; } return true; } bool create_profile_snapshot(int32_t app_id, const std::string& package_name, const std::string& profile_name, const std::string& classpath) { if (app_id == -1) { return create_boot_image_profile_snapshot(package_name, profile_name, classpath); } else { return create_app_profile_snapshot(app_id, package_name, profile_name, classpath); } } bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, const std::string& profile_name, const std::string& code_path, const std::unique_ptr& dex_metadata) { // Prepare the current profile. std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name, /*is_secondary_dex*/ false); uid_t uid = multiuser_get_uid(user_id, app_id); if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) { PLOG(ERROR) << "Failed to prepare " << cur_profile; return false; } // Check if we need to install the profile from the dex metadata. if (dex_metadata == nullptr) { return true; } // We have a dex metdata. Merge the profile into the reference profile. unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name, /*read_write*/ true, /*is_secondary_dex*/ false); unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY( open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW))); unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW))); if (apk_fd < 0) { PLOG(ERROR) << "Could not open code path " << code_path; return false; } RunProfman args; args.SetupCopyAndUpdate(std::move(dex_metadata_fd), std::move(ref_profile_fd), std::move(apk_fd), code_path); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ gid_t app_shared_gid = multiuser_get_shared_gid(user_id, app_id); drop_capabilities(app_shared_gid); // The copy and update takes ownership over the fds. args.Exec(); } /* parent */ int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; return false; } return true; } } // namespace installd } // namespace android cmds/installd/dexopt.h0100644 0000000 0000000 00000015075 13756501734 014044 0ustar000000000 0000000 /* * 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. */ #ifndef DEXOPT_H_ #define DEXOPT_H_ #include "installd_constants.h" #include #include namespace android { namespace installd { /* dexopt needed flags matching those in dalvik.system.DexFile */ static constexpr int NO_DEXOPT_NEEDED = 0; static constexpr int DEX2OAT_FROM_SCRATCH = 1; static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2; static constexpr int DEX2OAT_FOR_FILTER = 3; #define ANDROID_RUNTIME_APEX_BIN "/apex/com.android.runtime/bin" // Location of binaries in the Android Runtime APEX. static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat"; static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd"; static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profman"; static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand"; static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer"; static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd"; #undef ANDROID_RUNTIME_APEX_BIN // Clear the reference profile identified by the given profile name. bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name); // Clear the current profile identified by the given profile name (for single user). bool clear_primary_current_profile(const std::string& pkgname, const std::string& profile_name, userid_t user); // Clear all current profiles identified by the given profile name (all users). bool clear_primary_current_profiles(const std::string& pkgname, const std::string& profile_name); // Decide if profile guided compilation is needed or not based on existing profiles. // The analysis is done for a single profile name (which corresponds to a single code path). // Returns true if there is enough information in the current profiles that makes it // worth to recompile the package. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). bool analyze_primary_profiles(uid_t uid, const std::string& pkgname, const std::string& profile_name); // Create a snapshot of the profile information for the given package profile. // If appId is -1, the method creates the profile snapshot for the boot image. // // The profile snapshot is the aggregation of all existing profiles (all current user // profiles & the reference profile) and is meant to capture the all the profile information // without performing a merge into the reference profile which might impact future dex2oat // compilations. // The snapshot is created next to the reference profile of the package and the // ownership is assigned to AID_SYSTEM. // The snapshot location is reference_profile_location.snapshot. If a snapshot is already // there, it will be truncated and overwritten. // // The classpath acts as filter: only profiling data belonging to elements of the classpath // will end up in the snapshot. bool create_profile_snapshot(int32_t app_id, const std::string& package, const std::string& profile_name, const std::string& classpath); bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name, const std::string& code_path); bool copy_system_profile(const std::string& system_profile, uid_t packageUid, const std::string& pkgname, const std::string& profile_name); // Prepare the app profile for the given code path: // - create the current profile using profile_name // - merge the profile from the dex metadata file (if present) into // the reference profile. bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, const std::string& profile_name, const std::string& code_path, const std::unique_ptr& dex_metadata); bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path); bool reconcile_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::vector& isas, const std::unique_ptr& volumeUuid, int storage_flag, /*out*/bool* out_secondary_dex_exists); bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::unique_ptr& volume_uuid, int storage_flag, std::vector* out_secondary_dex_hash); int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, bool downgrade, int target_sdk_version, const char* profile_name, const char* dexMetadataPath, const char* compilation_reason, std::string* error_msg); bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set); bool calculate_odex_file_path_default(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set); bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src, const char *instruction_set); bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); const char* select_execution_binary( const char* binary, const char* debug_binary, bool background_job_compile, bool is_debug_runtime, bool is_release, bool is_debuggable_build); } // namespace installd } // namespace android #endif // DEXOPT_H_ cmds/installd/dexopt_return_codes.h0100644 0000000 0000000 00000007430 13756501734 016614 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include namespace android { namespace installd { // Constants for exit codes that installd code emits. These are failure situations before calling // any tools, e.g., in validation, and must not overlap with the exit codes of tools, so they // can be distinguished. enum DexoptReturnCodes : int { kSetGid = 64, kSetUid = 65, kCapSet = 66, kFlock = 67, kProfmanExec = 68, kSetSchedPolicy = 70, kSetPriority = 71, kDex2oatExec = 72, kInstructionSetLength = 73, kHashValidatePath = 74, kHashOpenPath = 75, kHashReadDex = 76, kHashWrite = 77, }; inline const char* get_installd_return_code_name(DexoptReturnCodes code) { switch (code) { case kSetGid: return "setgid"; case kSetUid: return "setuid"; case kCapSet: return "capset"; case kFlock: return "flock"; case kProfmanExec: return "exec(profman)"; case kSetSchedPolicy: return "setschedpolicy"; case kSetPriority: return "setpriority"; case kDex2oatExec: return "exec(dex2oat)"; case kInstructionSetLength: return "instruction-set-length"; case kHashValidatePath: return "hash(validate-path)"; case kHashOpenPath: return "hash(open-path)"; case kHashReadDex: return "hash(read-dex)"; case kHashWrite: return "hash(write)"; } return nullptr; } inline const char* get_dex2oat_return_code_name(art::dex2oat::ReturnCode code) { switch (code) { case art::dex2oat::ReturnCode::kNoFailure: return "dex2oat success"; case art::dex2oat::ReturnCode::kOther: return "unspecified dex2oat error"; case art::dex2oat::ReturnCode::kCreateRuntime: return "dex2oat failed to create a runtime"; } return nullptr; } // Get some slightly descriptive string for the return code. Handles both DexoptReturnCodes (local // exit codes) as well as art::dex2oat::ReturnCode. inline const char* get_return_code_name(int code) { // Try to enforce non-overlap (see comment on DexoptReturnCodes) // TODO: How could switch-case checks be used to enforce completeness? switch (code) { case kSetGid: case kSetUid: case kCapSet: case kFlock: case kProfmanExec: case kSetSchedPolicy: case kSetPriority: case kDex2oatExec: case kInstructionSetLength: case kHashValidatePath: case kHashOpenPath: case kHashReadDex: case kHashWrite: break; case static_cast(art::dex2oat::ReturnCode::kNoFailure): case static_cast(art::dex2oat::ReturnCode::kOther): case static_cast(art::dex2oat::ReturnCode::kCreateRuntime): break; } const char* value = get_installd_return_code_name(static_cast(code)); if (value != nullptr) { return value; } value = get_dex2oat_return_code_name(static_cast(code)); return value; } } // namespace installd } // namespace android cmds/installd/file_parsing.h0100644 0000000 0000000 00000002771 13756501734 015202 0ustar000000000 0000000 /* * 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. */ #ifndef OTAPREOPT_FILE_PARSING_H_ #define OTAPREOPT_FILE_PARSING_H_ #include #include #include namespace android { namespace installd { bool ParseFile(const std::string& strFile, std::function parse) { std::ifstream input_stream(strFile); if (!input_stream.is_open()) { return false; } while (!input_stream.eof()) { // Read the next line. std::string line; getline(input_stream, line); // Is the line empty? Simplifies the next check. if (line.empty()) { continue; } // Is this a comment (starts with pound)? if (line[0] == '#') { continue; } if (!parse(line)) { return false; } } return true; } } // namespace installd } // namespace android #endif // OTAPREOPT_FILE_PARSING_H_ cmds/installd/globals.cpp0100644 0000000 0000000 00000010507 13756501734 014512 0ustar000000000 0000000 /* ** Copyright 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. */ #define LOG_TAG "installd" #include #include #include #include #include #include namespace android { namespace installd { static constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA static constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA static constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under // ANDROID_DATA static constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA static constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA static constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under // ANDROID_DATA static constexpr const char* STAGING_SUBDIR = "app-staging/"; // sub-directory under ANDROID_DATA std::string android_app_dir; std::string android_app_ephemeral_dir; std::string android_app_lib_dir; std::string android_app_private_dir; std::string android_asec_dir; std::string android_data_dir; std::string android_media_dir; std::string android_mnt_expand_dir; std::string android_profiles_dir; std::string android_root_dir; std::string android_staging_dir; std::vector android_system_dirs; bool init_globals_from_data_and_root() { const char* data_path = getenv("ANDROID_DATA"); if (data_path == nullptr) { LOG(ERROR) << "Could not find ANDROID_DATA"; return false; } const char* root_path = getenv("ANDROID_ROOT"); if (root_path == nullptr) { LOG(ERROR) << "Could not find ANDROID_ROOT"; return false; } return init_globals_from_data_and_root(data_path, root_path); } static std::string ensure_trailing_slash(const std::string& path) { if (path.rfind('/') != path.size() - 1) { return path + '/'; } else { return path; } } bool init_globals_from_data_and_root(const char* data, const char* root) { // Get the android data directory. android_data_dir = ensure_trailing_slash(data); // Get the android root directory. android_root_dir = ensure_trailing_slash(root); // Get the android app directory. android_app_dir = android_data_dir + APP_SUBDIR; // Get the android protected app directory. android_app_private_dir = android_data_dir + PRIVATE_APP_SUBDIR; // Get the android ephemeral app directory. android_app_ephemeral_dir = android_data_dir + EPHEMERAL_APP_SUBDIR; // Get the android app native library directory. android_app_lib_dir = android_data_dir + APP_LIB_SUBDIR; // Get the sd-card ASEC mount point. android_asec_dir = ensure_trailing_slash(getenv(ASEC_MOUNTPOINT_ENV_NAME)); // Get the android media directory. android_media_dir = android_data_dir + MEDIA_SUBDIR; // Get the android external app directory. android_mnt_expand_dir = "/mnt/expand/"; // Get the android profiles directory. android_profiles_dir = android_data_dir + PROFILES_SUBDIR; // Get the android session staging directory. android_staging_dir = android_data_dir + STAGING_SUBDIR; // Take note of the system and vendor directories. android_system_dirs.clear(); android_system_dirs.push_back(android_root_dir + APP_SUBDIR); android_system_dirs.push_back(android_root_dir + PRIV_APP_SUBDIR); android_system_dirs.push_back("/vendor/app/"); android_system_dirs.push_back("/oem/app/"); return true; } } // namespace installd } // namespace android cmds/installd/globals.h0100644 0000000 0000000 00000003063 13756501734 014156 0ustar000000000 0000000 /* ** ** Copyright 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef GLOBALS_H_ #define GLOBALS_H_ #include #include #include namespace android { namespace installd { // Name of the environment variable that contains the asec mountpoint. static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT"; extern std::string android_app_dir; extern std::string android_app_ephemeral_dir; extern std::string android_app_lib_dir; extern std::string android_app_private_dir; extern std::string android_asec_dir; extern std::string android_data_dir; extern std::string android_media_dir; extern std::string android_mnt_expand_dir; extern std::string android_profiles_dir; extern std::string android_root_dir; extern std::string android_staging_dir; extern std::vector android_system_dirs; bool init_globals_from_data_and_root(); bool init_globals_from_data_and_root(const char* data, const char* root); } // namespace installd } // namespace android #endif // GLOBALS_H_ cmds/installd/installd.cpp0100644 0000000 0000000 00000016212 13756501734 014700 0ustar000000000 0000000 /* ** Copyright 2008, 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. */ #define LOG_TAG "installd" #include #include #include #include #include #include #include #include #include #include #include // TODO: Move everything to base::logging. #include #include "InstalldNativeService.h" #include "dexopt.h" #include "globals.h" #include "installd_constants.h" #include "installd_deps.h" // Need to fill in requirements of commands. #include "utils.h" namespace android { namespace installd { // Check that installd-deps sizes match cutils sizes. static_assert(kPropertyKeyMax == PROPERTY_KEY_MAX, "Size mismatch."); static_assert(kPropertyValueMax == PROPERTY_VALUE_MAX, "Size mismatch."); //////////////////////// // Plug-in functions. // //////////////////////// int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); } bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set) { return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set); } bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { return calculate_odex_file_path_default(path, apk_path, instruction_set); } bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { return create_cache_path_default(path, src, instruction_set); } static bool initialize_globals() { return init_globals_from_data_and_root(); } static int initialize_directories() { int res = -1; // Read current filesystem layout version to handle upgrade paths char version_path[PATH_MAX]; snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.c_str()); int oldVersion; if (fs_read_atomic_int(version_path, &oldVersion) == -1) { oldVersion = 0; } int version = oldVersion; if (version < 2) { SLOGD("Assuming that device has multi-user storage layout; upgrade no longer supported"); version = 2; } if (ensure_config_user_dirs(0) == -1) { SLOGE("Failed to setup misc for user 0"); goto fail; } if (version == 2) { SLOGD("Upgrading to /data/misc/user directories"); char misc_dir[PATH_MAX]; snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.c_str()); char keychain_added_dir[PATH_MAX]; snprintf(keychain_added_dir, PATH_MAX, "%s/keychain/cacerts-added", misc_dir); char keychain_removed_dir[PATH_MAX]; snprintf(keychain_removed_dir, PATH_MAX, "%s/keychain/cacerts-removed", misc_dir); DIR *dir; struct dirent *dirent; dir = opendir("/data/user"); if (dir != nullptr) { while ((dirent = readdir(dir))) { const char *name = dirent->d_name; // skip "." and ".." if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } uint32_t user_id = std::stoi(name); // /data/misc/user/ if (ensure_config_user_dirs(user_id) == -1) { goto fail; } char misc_added_dir[PATH_MAX]; snprintf(misc_added_dir, PATH_MAX, "%s/user/%s/cacerts-added", misc_dir, name); char misc_removed_dir[PATH_MAX]; snprintf(misc_removed_dir, PATH_MAX, "%s/user/%s/cacerts-removed", misc_dir, name); uid_t uid = multiuser_get_uid(user_id, AID_SYSTEM); gid_t gid = uid; if (access(keychain_added_dir, F_OK) == 0) { if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) { SLOGE("Some files failed to copy"); } } if (access(keychain_removed_dir, F_OK) == 0) { if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) { SLOGE("Some files failed to copy"); } } } closedir(dir); if (access(keychain_added_dir, F_OK) == 0) { delete_dir_contents(keychain_added_dir, 1, nullptr); } if (access(keychain_removed_dir, F_OK) == 0) { delete_dir_contents(keychain_removed_dir, 1, nullptr); } } version = 3; } // Persist layout version if changed if (version != oldVersion) { if (fs_write_atomic_int(version_path, version) == -1) { SLOGE("Failed to save version to %s: %s", version_path, strerror(errno)); goto fail; } } // Success! res = 0; fail: return res; } static int log_callback(int type, const char *fmt, ...) { // NOLINT va_list ap; int priority; switch (type) { case SELINUX_WARNING: priority = ANDROID_LOG_WARN; break; case SELINUX_INFO: priority = ANDROID_LOG_INFO; break; default: priority = ANDROID_LOG_ERROR; break; } va_start(ap, fmt); LOG_PRI_VA(priority, "SELinux", fmt, ap); va_end(ap); return 0; } static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) { int ret; int selinux_enabled = (is_selinux_enabled() > 0); setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(argv); SLOGI("installd firing up"); union selinux_callback cb; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); if (!initialize_globals()) { SLOGE("Could not initialize globals; exiting.\n"); exit(1); } if (initialize_directories() < 0) { SLOGE("Could not create directories; exiting.\n"); exit(1); } if (selinux_enabled && selinux_status_open(true) < 0) { SLOGE("Could not open selinux status; exiting.\n"); exit(1); } if ((ret = InstalldNativeService::start()) != android::OK) { SLOGE("Unable to start InstalldNativeService: %d", ret); exit(1); } IPCThreadState::self()->joinThreadPool(); LOG(INFO) << "installd shutting down"; return 0; } } // namespace installd } // namespace android int main(const int argc, char *argv[]) { return android::installd::installd_main(argc, argv); } cmds/installd/installd.rc0100644 0000000 0000000 00000011102 13756501734 014513 0ustar000000000 0000000 service installd /system/bin/installd class main on early-boot mkdir /config/sdcardfs/extensions/1055 mkdir /config/sdcardfs/extensions/1056 mkdir /config/sdcardfs/extensions/1057 mkdir /config/sdcardfs/extensions/1056/3gpp mkdir /config/sdcardfs/extensions/1056/3gp mkdir /config/sdcardfs/extensions/1056/3gpp2 mkdir /config/sdcardfs/extensions/1056/3g2 mkdir /config/sdcardfs/extensions/1056/avi mkdir /config/sdcardfs/extensions/1056/dl mkdir /config/sdcardfs/extensions/1056/dif mkdir /config/sdcardfs/extensions/1056/dv mkdir /config/sdcardfs/extensions/1056/fli mkdir /config/sdcardfs/extensions/1056/m4v mkdir /config/sdcardfs/extensions/1056/ts mkdir /config/sdcardfs/extensions/1056/mpeg mkdir /config/sdcardfs/extensions/1056/mpg mkdir /config/sdcardfs/extensions/1056/mpe mkdir /config/sdcardfs/extensions/1056/mp4 mkdir /config/sdcardfs/extensions/1056/vob mkdir /config/sdcardfs/extensions/1056/qt mkdir /config/sdcardfs/extensions/1056/mov mkdir /config/sdcardfs/extensions/1056/mxu mkdir /config/sdcardfs/extensions/1056/webm mkdir /config/sdcardfs/extensions/1056/lsf mkdir /config/sdcardfs/extensions/1056/lsx mkdir /config/sdcardfs/extensions/1056/mkv mkdir /config/sdcardfs/extensions/1056/mng mkdir /config/sdcardfs/extensions/1056/asf mkdir /config/sdcardfs/extensions/1056/asx mkdir /config/sdcardfs/extensions/1056/wm mkdir /config/sdcardfs/extensions/1056/wmv mkdir /config/sdcardfs/extensions/1056/wmx mkdir /config/sdcardfs/extensions/1056/wvx mkdir /config/sdcardfs/extensions/1056/movie mkdir /config/sdcardfs/extensions/1056/wrf mkdir /config/sdcardfs/extensions/1057/bmp mkdir /config/sdcardfs/extensions/1057/gif mkdir /config/sdcardfs/extensions/1057/jpg mkdir /config/sdcardfs/extensions/1057/jpeg mkdir /config/sdcardfs/extensions/1057/jpe mkdir /config/sdcardfs/extensions/1057/pcx mkdir /config/sdcardfs/extensions/1057/png mkdir /config/sdcardfs/extensions/1057/svg mkdir /config/sdcardfs/extensions/1057/svgz mkdir /config/sdcardfs/extensions/1057/tiff mkdir /config/sdcardfs/extensions/1057/tif mkdir /config/sdcardfs/extensions/1057/wbmp mkdir /config/sdcardfs/extensions/1057/webp mkdir /config/sdcardfs/extensions/1057/dng mkdir /config/sdcardfs/extensions/1057/cr2 mkdir /config/sdcardfs/extensions/1057/ras mkdir /config/sdcardfs/extensions/1057/art mkdir /config/sdcardfs/extensions/1057/jng mkdir /config/sdcardfs/extensions/1057/nef mkdir /config/sdcardfs/extensions/1057/nrw mkdir /config/sdcardfs/extensions/1057/orf mkdir /config/sdcardfs/extensions/1057/rw2 mkdir /config/sdcardfs/extensions/1057/pef mkdir /config/sdcardfs/extensions/1057/psd mkdir /config/sdcardfs/extensions/1057/pnm mkdir /config/sdcardfs/extensions/1057/pbm mkdir /config/sdcardfs/extensions/1057/pgm mkdir /config/sdcardfs/extensions/1057/ppm mkdir /config/sdcardfs/extensions/1057/srw mkdir /config/sdcardfs/extensions/1057/arw mkdir /config/sdcardfs/extensions/1057/rgb mkdir /config/sdcardfs/extensions/1057/xbm mkdir /config/sdcardfs/extensions/1057/xpm mkdir /config/sdcardfs/extensions/1057/xwd mkdir /config/sdcardfs/extensions/1055/aac mkdir /config/sdcardfs/extensions/1055/aac mkdir /config/sdcardfs/extensions/1055/amr mkdir /config/sdcardfs/extensions/1055/awb mkdir /config/sdcardfs/extensions/1055/snd mkdir /config/sdcardfs/extensions/1055/flac mkdir /config/sdcardfs/extensions/1055/flac mkdir /config/sdcardfs/extensions/1055/mp3 mkdir /config/sdcardfs/extensions/1055/mpga mkdir /config/sdcardfs/extensions/1055/mpega mkdir /config/sdcardfs/extensions/1055/mp2 mkdir /config/sdcardfs/extensions/1055/m4a mkdir /config/sdcardfs/extensions/1055/aif mkdir /config/sdcardfs/extensions/1055/aiff mkdir /config/sdcardfs/extensions/1055/aifc mkdir /config/sdcardfs/extensions/1055/gsm mkdir /config/sdcardfs/extensions/1055/mka mkdir /config/sdcardfs/extensions/1055/m3u mkdir /config/sdcardfs/extensions/1055/wma mkdir /config/sdcardfs/extensions/1055/wax mkdir /config/sdcardfs/extensions/1055/ra mkdir /config/sdcardfs/extensions/1055/rm mkdir /config/sdcardfs/extensions/1055/ram mkdir /config/sdcardfs/extensions/1055/ra mkdir /config/sdcardfs/extensions/1055/pls mkdir /config/sdcardfs/extensions/1055/sd2 mkdir /config/sdcardfs/extensions/1055/wav mkdir /config/sdcardfs/extensions/1055/ogg mkdir /config/sdcardfs/extensions/1055/oga cmds/installd/installd_constants.h0100644 0000000 0000000 00000006155 13756501734 016446 0ustar000000000 0000000 /* ** ** Copyright 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef INSTALLD_CONSTANTS_H_ #define INSTALLD_CONSTANTS_H_ namespace android { namespace installd { /* elements combined with a valid package name to form paths */ constexpr const char* PRIMARY_USER_PREFIX = "data/"; constexpr const char* SECONDARY_USER_PREFIX = "user/"; // This is used as a string literal, can't be constants. TODO: std::string... #define DALVIK_CACHE "dalvik-cache" constexpr const char* DALVIK_CACHE_POSTFIX = "@classes.dex"; constexpr size_t PKG_NAME_MAX = 128u; /* largest allowed package name */ constexpr size_t PKG_PATH_MAX = 1024u; /* max size of any path we use */ /**************************************************************************** * IMPORTANT: These values are passed from Java code. Keep them in sync with * frameworks/base/services/core/java/com/android/server/pm/Installer.java ***************************************************************************/ constexpr int DEXOPT_PUBLIC = 1 << 1; constexpr int DEXOPT_DEBUGGABLE = 1 << 2; constexpr int DEXOPT_BOOTCOMPLETE = 1 << 3; constexpr int DEXOPT_PROFILE_GUIDED = 1 << 4; constexpr int DEXOPT_SECONDARY_DEX = 1 << 5; // DEXOPT_FORCE, DEXOPT_STORAGE_CE, DEXOPT_STORAGE_DE are exposed for secondary // dex files only. Primary apks are analyzed in PackageManager and installd // does not need to know if the compilation is forced or on what kind of storage // the dex files are. constexpr int DEXOPT_FORCE = 1 << 6; constexpr int DEXOPT_STORAGE_CE = 1 << 7; constexpr int DEXOPT_STORAGE_DE = 1 << 8; // Tells the compiler that it is invoked from the background service. This // controls whether extra debugging flags can be used (taking more compile time.) constexpr int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9; constexpr int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10; constexpr int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11; constexpr int DEXOPT_GENERATE_APP_IMAGE = 1 << 12; /* all known values for dexopt flags */ constexpr int DEXOPT_MASK = DEXOPT_PUBLIC | DEXOPT_DEBUGGABLE | DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_SECONDARY_DEX | DEXOPT_FORCE | DEXOPT_STORAGE_CE | DEXOPT_STORAGE_DE | DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_ENABLE_HIDDEN_API_CHECKS | DEXOPT_GENERATE_COMPACT_DEX | DEXOPT_GENERATE_APP_IMAGE; // NOTE: keep in sync with StorageManager constexpr int FLAG_STORAGE_DE = 1 << 0; constexpr int FLAG_STORAGE_CE = 1 << 1; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) } // namespace installd } // namespace android #endif // INSTALLD_CONSTANTS_H_ cmds/installd/installd_deps.h0100644 0000000 0000000 00000004336 13756501734 015364 0ustar000000000 0000000 /* ** ** Copyright 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef INSTALLD_DEPS_H_ #define INSTALLD_DEPS_H_ #include #include namespace android { namespace installd { // Dependencies for a full binary. These functions need to be provided to // figure out parts of the configuration. // Retrieve a system property. Same API as cutils, just renamed. extern int get_property(const char *key, char *value, const char *default_value); // Size constants. Should be checked to be equal to the cutils requirements. constexpr size_t kPropertyKeyMax = 32u; constexpr size_t kPropertyValueMax = 92u; // Compute the output path for dex2oat. extern bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set); // Compute the output path for patchoat. // // Computes the odex file for the given apk_path and instruction_set, e.g., // /system/framework/whatever.jar -> /system/framework/oat//whatever.odex // // Returns false if it failed to determine the odex file path. // extern bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set); // Compute the output path into the dalvik cache. extern bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set); } // namespace installd } // namespace android #endif // INSTALLD_DEPS_H_ cmds/installd/matchgen.py0100644 0000000 0000000 00000007240 13756501734 014523 0ustar000000000 0000000 #!/usr/bin/env python # Copyright (C) 2017 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. import collections, sys TYPES = { "AID_MEDIA_AUDIO": ["aac","aac","amr","awb","snd","flac","flac","mp3","mpga","mpega","mp2","m4a","aif","aiff","aifc","gsm","mka","m3u","wma","wax","ra","rm","ram","ra","pls","sd2","wav","ogg","oga"], "AID_MEDIA_VIDEO": ["3gpp","3gp","3gpp2","3g2","avi","dl","dif","dv","fli","m4v","ts","mpeg","mpg","mpe","mp4","vob","qt","mov","mxu","webm","lsf","lsx","mkv","mng","asf","asx","wm","wmv","wmx","wvx","movie","wrf"], "AID_MEDIA_IMAGE": ["bmp","gif","jpg","jpeg","jpe","pcx","png","svg","svgz","tiff","tif","wbmp","webp","dng","cr2","ras","art","jng","nef","nrw","orf","rw2","pef","psd","pnm","pbm","pgm","ppm","srw","arw","rgb","xbm","xpm","xwd"] } if "--rc" in sys.argv: print "on early-boot" print " mkdir /config/sdcardfs/extensions/1055" print " mkdir /config/sdcardfs/extensions/1056" print " mkdir /config/sdcardfs/extensions/1057" for gid, exts in TYPES.iteritems(): if gid is "AID_MEDIA_AUDIO": gid = "1055" if gid is "AID_MEDIA_VIDEO": gid = "1056" if gid is "AID_MEDIA_IMAGE": gid = "1057" for ext in exts: print " mkdir /config/sdcardfs/extensions/%s/%s" % (gid, ext) exit() print """/* * Copyright (C) 2017 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. */ /****************************************************************** * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY * ******************************************************************/ #include int MatchExtension(const char* ext) { """ trie = collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: "")))))) for t in TYPES: for v in TYPES[t]: v = v.lower() target = trie for c in v: target = target[c] target["\0"] = t def dump(target, index): prefix = " " * (index + 1) print "%sswitch (ext[%d]) {" % (prefix, index) for k in sorted(target.keys()): if k == "\0": print "%scase '\\0': return %s;" % (prefix, target[k]) else: upper = k.upper() if k != upper: print "%scase '%s': case '%s':" % (prefix, k, upper) else: print "%scase '%s':" % (prefix, k) dump(target[k], index + 1) print "%s}" % (prefix) if index > 0: print "%sbreak;" % (prefix) dump(trie, 0) print """ return 0; } """ cmds/installd/migrate_legacy_obb_data.sh0100644 0000000 0000000 00000002554 13756501734 017511 0ustar000000000 0000000 #!/system/bin/sh # # Copyright (C) 2019 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. rm -rf /sdcard/Android/obb/test_probe mkdir -p /sdcard/Android/obb/ touch /sdcard/Android/obb/test_probe if ! test -f /data/media/0/Android/obb/test_probe ; then log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating" rm -rf /sdcard/Android/obb/test_probe exit 0 fi # Delete the test file, and remove the obb folder if it is empty rm -rf /sdcard/Android/obb/test_probe rmdir /data/media/obb if ! test -d /data/media/obb ; then log -p i -t migrate_legacy_obb_data "No legacy obb data to migrate." exit 0 fi log -p i -t migrate_legacy_obb_data "Migrating legacy obb data." rm -rf /data/media/0/Android/obb cp -F -p -R -P -d /data/media/obb /data/media/0/Android rm -rf /data/media/obb log -p i -t migrate_legacy_obb_data "Done." cmds/installd/otapreopt.cpp0100644 0000000 0000000 00000074433 13756501734 015114 0ustar000000000 0000000 /* ** Copyright 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dexopt.h" #include "file_parsing.h" #include "globals.h" #include "installd_constants.h" #include "installd_deps.h" // Need to fill in requirements of commands. #include "otapreopt_parameters.h" #include "otapreopt_utils.h" #include "system_properties.h" #include "utils.h" #ifndef LOG_TAG #define LOG_TAG "otapreopt" #endif #define BUFFER_MAX 1024 /* input buffer for commands */ #define TOKEN_MAX 16 /* max number of arguments in buffer */ #define REPLY_MAX 256 /* largest reply allowed */ using android::base::EndsWith; using android::base::Split; using android::base::StartsWith; using android::base::StringPrintf; namespace android { namespace installd { // Check expected values for dexopt flags. If you need to change this: // // RUN AN A/B OTA TO MAKE SURE THINGS STILL WORK! // // You most likely need to increase the protocol version and all that entails! static_assert(DEXOPT_PUBLIC == 1 << 1, "DEXOPT_PUBLIC unexpected."); static_assert(DEXOPT_DEBUGGABLE == 1 << 2, "DEXOPT_DEBUGGABLE unexpected."); static_assert(DEXOPT_BOOTCOMPLETE == 1 << 3, "DEXOPT_BOOTCOMPLETE unexpected."); static_assert(DEXOPT_PROFILE_GUIDED == 1 << 4, "DEXOPT_PROFILE_GUIDED unexpected."); static_assert(DEXOPT_SECONDARY_DEX == 1 << 5, "DEXOPT_SECONDARY_DEX unexpected."); static_assert(DEXOPT_FORCE == 1 << 6, "DEXOPT_FORCE unexpected."); static_assert(DEXOPT_STORAGE_CE == 1 << 7, "DEXOPT_STORAGE_CE unexpected."); static_assert(DEXOPT_STORAGE_DE == 1 << 8, "DEXOPT_STORAGE_DE unexpected."); static_assert(DEXOPT_ENABLE_HIDDEN_API_CHECKS == 1 << 10, "DEXOPT_ENABLE_HIDDEN_API_CHECKS unexpected"); static_assert(DEXOPT_GENERATE_COMPACT_DEX == 1 << 11, "DEXOPT_GENERATE_COMPACT_DEX unexpected"); static_assert(DEXOPT_GENERATE_APP_IMAGE == 1 << 12, "DEXOPT_GENERATE_APP_IMAGE unexpected"); static_assert(DEXOPT_MASK == (0x1dfe | DEXOPT_IDLE_BACKGROUND_JOB), "DEXOPT_MASK unexpected."); template static constexpr bool IsPowerOfTwo(T x) { static_assert(std::is_integral::value, "T must be integral"); // TODO: assert unsigned. There is currently many uses with signed values. return (x & (x - 1)) == 0; } template static constexpr T RoundDown(T x, typename std::decay::type n) { return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n); } template static constexpr T RoundUp(T x, typename std::remove_reference::type n) { return RoundDown(x + n - 1, n); } class OTAPreoptService { public: // Main driver. Performs the following steps. // // 1) Parse options (read system properties etc from B partition). // // 2) Read in package data. // // 3) Prepare environment variables. // // 4) Prepare(compile) boot image, if necessary. // // 5) Run update. int Main(int argc, char** argv) { if (!ReadArguments(argc, argv)) { LOG(ERROR) << "Failed reading command line."; return 1; } if (!ReadSystemProperties()) { LOG(ERROR)<< "Failed reading system properties."; return 2; } if (!ReadEnvironment()) { LOG(ERROR) << "Failed reading environment properties."; return 3; } if (!CheckAndInitializeInstalldGlobals()) { LOG(ERROR) << "Failed initializing globals."; return 4; } PrepareEnvironment(); if (!PrepareBootImage(/* force */ false)) { LOG(ERROR) << "Failed preparing boot image."; return 5; } int dexopt_retcode = RunPreopt(); return dexopt_retcode; } int GetProperty(const char* key, char* value, const char* default_value) const { const std::string* prop_value = system_properties_.GetProperty(key); if (prop_value == nullptr) { if (default_value == nullptr) { return 0; } // Copy in the default value. strlcpy(value, default_value, kPropertyValueMax - 1); value[kPropertyValueMax - 1] = 0; return strlen(default_value);// TODO: Need to truncate? } size_t size = std::min(kPropertyValueMax - 1, prop_value->length()) + 1; strlcpy(value, prop_value->data(), size); return static_cast(size - 1); } std::string GetOTADataDirectory() const { return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), GetTargetSlot().c_str()); } const std::string& GetTargetSlot() const { return parameters_.target_slot; } private: bool ReadSystemProperties() { static constexpr const char* kPropertyFiles[] = { "/default.prop", "/system/build.prop" }; for (size_t i = 0; i < arraysize(kPropertyFiles); ++i) { if (!system_properties_.Load(kPropertyFiles[i])) { return false; } } return true; } bool ReadEnvironment() { // Parse the environment variables from init.environ.rc, which have the form // export NAME VALUE // For simplicity, don't respect string quotation. The values we are interested in can be // encoded without them. std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)"); bool parse_result = ParseFile("/init.environ.rc", [&](const std::string& line) { std::smatch export_match; if (!std::regex_match(line, export_match, export_regex)) { return true; } if (export_match.size() != 3) { return true; } std::string name = export_match[1].str(); std::string value = export_match[2].str(); system_properties_.SetProperty(name, value); return true; }); if (!parse_result) { return false; } if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) { return false; } android_data_ = *system_properties_.GetProperty(kAndroidDataPathPropertyName); if (system_properties_.GetProperty(kAndroidRootPathPropertyName) == nullptr) { return false; } android_root_ = *system_properties_.GetProperty(kAndroidRootPathPropertyName); if (system_properties_.GetProperty(kBootClassPathPropertyName) == nullptr) { return false; } boot_classpath_ = *system_properties_.GetProperty(kBootClassPathPropertyName); if (system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) == nullptr) { return false; } asec_mountpoint_ = *system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME); return true; } const std::string& GetAndroidData() const { return android_data_; } const std::string& GetAndroidRoot() const { return android_root_; } const std::string GetOtaDirectoryPrefix() const { return GetAndroidData() + "/ota"; } bool CheckAndInitializeInstalldGlobals() { // init_globals_from_data_and_root requires "ASEC_MOUNTPOINT" in the environment. We // do not use any datapath that includes this, but we'll still have to set it. CHECK(system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) != nullptr); int result = setenv(ASEC_MOUNTPOINT_ENV_NAME, asec_mountpoint_.c_str(), 0); if (result != 0) { LOG(ERROR) << "Could not set ASEC_MOUNTPOINT environment variable"; return false; } if (!init_globals_from_data_and_root(GetAndroidData().c_str(), GetAndroidRoot().c_str())) { LOG(ERROR) << "Could not initialize globals; exiting."; return false; } // This is different from the normal installd. We only do the base // directory, the rest will be created on demand when each app is compiled. if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) { LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix(); return false; } return true; } bool ParseBool(const char* in) { if (strcmp(in, "true") == 0) { return true; } return false; } bool ParseUInt(const char* in, uint32_t* out) { char* end; long long int result = strtoll(in, &end, 0); if (in == end || *end != '\0') { return false; } if (result < std::numeric_limits::min() || std::numeric_limits::max() < result) { return false; } *out = static_cast(result); return true; } bool ReadArguments(int argc, char** argv) { return parameters_.ReadArguments(argc, const_cast(argv)); } void PrepareEnvironment() { environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str())); environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str())); environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str())); for (const std::string& e : environ_) { putenv(const_cast(e.c_str())); } } // Ensure that we have the right boot image. The first time any app is // compiled, we'll try to generate it. bool PrepareBootImage(bool force) const { if (parameters_.instruction_set == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; } const char* isa = parameters_.instruction_set; std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE; std::string isa_path = dalvik_cache + "/" + isa; // Reset umask in otapreopt, so that we control the the access for the files we create. umask(0); // Create the directories, if necessary. if (access(dalvik_cache.c_str(), F_OK) != 0) { if (!CreatePath(dalvik_cache)) { PLOG(ERROR) << "Could not create dalvik-cache dir " << dalvik_cache; return false; } } if (access(isa_path.c_str(), F_OK) != 0) { if (!CreatePath(isa_path)) { PLOG(ERROR) << "Could not create dalvik-cache isa dir"; return false; } } // Check whether we have files in /data. // TODO: check that the files are correct wrt/ jars. std::string art_path = isa_path + "/system@framework@boot.art"; std::string oat_path = isa_path + "/system@framework@boot.oat"; bool cleared = false; if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) { // Files exist, assume everything is alright if not forced. Otherwise clean up. if (!force) { return true; } ClearDirectory(isa_path); cleared = true; } // Check whether we have an image in /system. // TODO: check that the files are correct wrt/ jars. std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { // Note: we ignore |force| here. return true; } if (!cleared) { ClearDirectory(isa_path); } return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa); } static bool CreatePath(const std::string& path) { // Create the given path. Use string processing instead of dirname, as dirname's need for // a writable char buffer is painful. // First, try to use the full path. if (mkdir(path.c_str(), 0711) == 0) { return true; } if (errno != ENOENT) { PLOG(ERROR) << "Could not create path " << path; return false; } // Now find the parent and try that first. size_t last_slash = path.find_last_of('/'); if (last_slash == std::string::npos || last_slash == 0) { PLOG(ERROR) << "Could not create " << path; return false; } if (!CreatePath(path.substr(0, last_slash))) { return false; } if (mkdir(path.c_str(), 0711) == 0) { return true; } PLOG(ERROR) << "Could not create " << path; return false; } static void ClearDirectory(const std::string& dir) { DIR* c_dir = opendir(dir.c_str()); if (c_dir == nullptr) { PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents"; return; } for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) { const char* name = de->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } // We only want to delete regular files and symbolic links. std::string file = StringPrintf("%s/%s", dir.c_str(), name); if (de->d_type != DT_REG && de->d_type != DT_LNK) { LOG(WARNING) << "Unexpected file " << file << " of type " << std::hex << de->d_type << " encountered."; } else { // Try to unlink the file. if (unlink(file.c_str()) != 0) { PLOG(ERROR) << "Unable to unlink " << file; } } } CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; } bool Dex2oatBootImage(const std::string& boot_cp, const std::string& art_path, const std::string& oat_path, const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector cmd; cmd.push_back(kDex2oatPath); cmd.push_back(StringPrintf("--image=%s", art_path.c_str())); for (const std::string& boot_part : Split(boot_cp, ":")) { cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str())); } cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str())); int32_t base_offset = ChooseRelocationOffsetDelta(art::GetImageMinBaseAddressDelta(), art::GetImageMaxBaseAddressDelta()); cmd.push_back(StringPrintf("--base=0x%x", art::GetImageBaseAddress() + base_offset)); cmd.push_back(StringPrintf("--instruction-set=%s", isa)); // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp. AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms", "-Xms", true, cmd); AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx", "-Xmx", true, cmd); AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter", "--compiler-filter=", false, cmd); cmd.push_back("--image-classes=/system/etc/preloaded-classes"); // TODO: Compiled-classes. const std::string* extra_opts = system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags"); if (extra_opts != nullptr) { std::vector extra_vals = Split(*extra_opts, " "); cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end()); } // TODO: Should we lower this? It's usually set close to max, because // normally there's not much else going on at boot. AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads", "-j", false, cmd); AddCompilerOptionFromSystemProperty( StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(), "--instruction-set-variant=", false, cmd); AddCompilerOptionFromSystemProperty( StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(), "--instruction-set-features=", false, cmd); std::string error_msg; bool result = Exec(cmd, &error_msg); if (!result) { LOG(ERROR) << "Could not generate boot image: " << error_msg; } return result; } static const char* ParseNull(const char* arg) { return (strcmp(arg, "!") == 0) ? nullptr : arg; } bool ShouldSkipPreopt() const { // There's one thing we have to be careful about: we may/will be asked to compile an app // living in the system image. This may be a valid request - if the app wasn't compiled, // e.g., if the system image wasn't large enough to include preopted files. However, the // data we have is from the old system, so the driver (the OTA service) can't actually // know. Thus, we will get requests for apps that have preopted components. To avoid // duplication (we'd generate files that are not used and are *not* cleaned up), do two // simple checks: // // 1) Does the apk_path start with the value of ANDROID_ROOT? (~in the system image) // (For simplicity, assume the value of ANDROID_ROOT does not contain a symlink.) // // 2) If you replace the name in the apk_path with "oat," does the path exist? // (=have a subdirectory for preopted files) // // If the answer to both is yes, skip the dexopt. // // Note: while one may think it's OK to call dexopt and it will fail (because APKs should // be stripped), that's not true for APKs signed outside the build system (so the // jar content must be exactly the same). // (This is ugly as it's the only thing where we need to understand the contents // of parameters_, but it beats postponing the decision or using the call- // backs to do weird things.) const char* apk_path = parameters_.apk_path; CHECK(apk_path != nullptr); if (StartsWith(apk_path, android_root_)) { const char* last_slash = strrchr(apk_path, '/'); if (last_slash != nullptr) { std::string path(apk_path, last_slash - apk_path + 1); CHECK(EndsWith(path, "/")); path = path + "oat"; if (access(path.c_str(), F_OK) == 0) { LOG(INFO) << "Skipping A/B OTA preopt of already preopted package " << apk_path; return true; } } } // Another issue is unavailability of files in the new system. If the partition // layout changes, otapreopt_chroot may not know about this. Then files from that // partition will not be available and fail to build. This is problematic, as // this tool will wipe the OTA artifact cache and try again (for robustness after // a failed OTA with remaining cache artifacts). if (access(apk_path, F_OK) != 0) { LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path; return true; } return false; } // Run dexopt with the parameters of parameters_. // TODO(calin): embed the profile name in the parameters. int Dexopt() { std::string dummy; return dexopt(parameters_.apk_path, parameters_.uid, parameters_.pkgName, parameters_.instruction_set, parameters_.dexopt_needed, parameters_.oat_dir, parameters_.dexopt_flags, parameters_.compiler_filter, parameters_.volume_uuid, parameters_.shared_libraries, parameters_.se_info, parameters_.downgrade, parameters_.target_sdk_version, parameters_.profile_name, parameters_.dex_metadata_path, parameters_.compilation_reason, &dummy); } int RunPreopt() { if (ShouldSkipPreopt()) { return 0; } int dexopt_result = Dexopt(); if (dexopt_result == 0) { return 0; } // If the dexopt failed, we may have a stale boot image from a previous OTA run. // Then regenerate and retry. if (WEXITSTATUS(dexopt_result) == static_cast(::art::dex2oat::ReturnCode::kCreateRuntime)) { if (!PrepareBootImage(/* force */ true)) { LOG(ERROR) << "Forced boot image creating failed. Original error return was " << dexopt_result; return dexopt_result; } int dexopt_result_boot_image_retry = Dexopt(); if (dexopt_result_boot_image_retry == 0) { return 0; } } // If this was a profile-guided run, we may have profile version issues. Try to downgrade, // if possible. if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) { return dexopt_result; } LOG(WARNING) << "Downgrading compiler filter in an attempt to progress compilation"; parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED; return Dexopt(); } //////////////////////////////////// // Helpers, mostly taken from ART // //////////////////////////////////// // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { constexpr size_t kPageSize = PAGE_SIZE; CHECK_EQ(min_delta % kPageSize, 0u); CHECK_EQ(max_delta % kPageSize, 0u); CHECK_LT(min_delta, max_delta); std::default_random_engine generator; generator.seed(GetSeed()); std::uniform_int_distribution distribution(min_delta, max_delta); int32_t r = distribution(generator); if (r % 2 == 0) { r = RoundUp(r, kPageSize); } else { r = RoundDown(r, kPageSize); } CHECK_LE(min_delta, r); CHECK_GE(max_delta, r); CHECK_EQ(r % kPageSize, 0u); return r; } static uint64_t GetSeed() { #ifdef __BIONIC__ // Bionic exposes arc4random, use it. uint64_t random_data; arc4random_buf(&random_data, sizeof(random_data)); return random_data; #else #error "This is only supposed to run with bionic. Otherwise, implement..." #endif } void AddCompilerOptionFromSystemProperty(const char* system_property, const char* prefix, bool runtime, std::vector& out) const { const std::string* value = system_properties_.GetProperty(system_property); if (value != nullptr) { if (runtime) { out.push_back("--runtime-arg"); } if (prefix != nullptr) { out.push_back(StringPrintf("%s%s", prefix, value->c_str())); } else { out.push_back(*value); } } } static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; // The index of the instruction-set string inside the package parameters. Needed for // some special-casing that requires knowledge of the instruction-set. static constexpr size_t kISAIndex = 3; // Stores the system properties read out of the B partition. We need to use these properties // to compile, instead of the A properties we could get from init/get_property. SystemProperties system_properties_; // Some select properties that are always needed. std::string android_root_; std::string android_data_; std::string boot_classpath_; std::string asec_mountpoint_; OTAPreoptParameters parameters_; // Store environment values we need to set. std::vector environ_; }; OTAPreoptService gOps; //////////////////////// // Plug-in functions. // //////////////////////// int get_property(const char *key, char *value, const char *default_value) { return gOps.GetProperty(key, value, default_value); } // Compute the output path of bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set) { const char *file_name_start; const char *file_name_end; file_name_start = strrchr(apk_path, '/'); if (file_name_start == nullptr) { ALOGE("apk_path '%s' has no '/'s in it\n", apk_path); return false; } file_name_end = strrchr(file_name_start, '.'); if (file_name_end == nullptr) { ALOGE("apk_path '%s' has no extension\n", apk_path); return false; } // Calculate file_name file_name_start++; // Move past '/', is valid as file_name_end is valid. size_t file_name_len = file_name_end - file_name_start; std::string file_name(file_name_start, file_name_len); // /oat//.odex.b snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.%s", oat_dir, instruction_set, file_name.c_str(), gOps.GetTargetSlot().c_str()); return true; } /* * Computes the odex file for the given apk_path and instruction_set. * /system/framework/whatever.jar -> /system/framework/oat//whatever.odex * * Returns false if it failed to determine the odex file path. */ bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { const char *path_end = strrchr(apk_path, '/'); if (path_end == nullptr) { ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); return false; } std::string path_component(apk_path, path_end - apk_path); const char *name_begin = path_end + 1; const char *extension_start = strrchr(name_begin, '.'); if (extension_start == nullptr) { ALOGE("apk_path '%s' has no extension.\n", apk_path); return false; } std::string name_component(name_begin, extension_start - name_begin); std::string new_path = StringPrintf("%s/oat/%s/%s.odex.%s", path_component.c_str(), instruction_set, name_component.c_str(), gOps.GetTargetSlot().c_str()); if (new_path.length() >= PKG_PATH_MAX) { LOG(ERROR) << "apk_path of " << apk_path << " is too long: " << new_path; return false; } strcpy(path, new_path.c_str()); return true; } bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { size_t srclen = strlen(src); /* demand that we are an absolute path */ if ((src == 0) || (src[0] != '/') || strstr(src,"..")) { return false; } if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX? return false; } std::string from_src = std::string(src + 1); std::replace(from_src.begin(), from_src.end(), '/', '@'); std::string assembled_path = StringPrintf("%s/%s/%s/%s%s", gOps.GetOTADataDirectory().c_str(), DALVIK_CACHE, instruction_set, from_src.c_str(), DALVIK_CACHE_POSTFIX); if (assembled_path.length() + 1 > PKG_PATH_MAX) { return false; } strcpy(path, assembled_path.c_str()); return true; } static int log_callback(int type, const char *fmt, ...) { va_list ap; int priority; switch (type) { case SELINUX_WARNING: priority = ANDROID_LOG_WARN; break; case SELINUX_INFO: priority = ANDROID_LOG_INFO; break; default: priority = ANDROID_LOG_ERROR; break; } va_start(ap, fmt); LOG_PRI_VA(priority, "SELinux", fmt, ap); va_end(ap); return 0; } static int otapreopt_main(const int argc, char *argv[]) { int selinux_enabled = (is_selinux_enabled() > 0); setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(argv); if (argc < 2) { ALOGE("Expecting parameters"); exit(1); } union selinux_callback cb; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); if (selinux_enabled && selinux_status_open(true) < 0) { ALOGE("Could not open selinux status; exiting.\n"); exit(1); } int ret = android::installd::gOps.Main(argc, argv); return ret; } } // namespace installd } // namespace android int main(const int argc, char *argv[]) { return android::installd::otapreopt_main(argc, argv); } cmds/installd/otapreopt.rc0100644 0000000 0000000 00000001113 13756501734 014717 0ustar000000000 0000000 # When /data is available, look for A/B artifacts for the current slot and move them # into the dalvik-cache (relabeling them). on post-fs-data exec - root -- /system/bin/otapreopt_slot # The dalvik-cache was not moved itself, so as to restrict the rights of otapreopt_slot. # But now the relabeling is annoying as there is no force option available here. So # explicitly list all the ISAs we know. restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/mips /data/dalvik-cache/mips64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64 cmds/installd/otapreopt_chroot.cpp0100644 0000000 0000000 00000022734 13756501734 016467 0ustar000000000 0000000 /* ** Copyright 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "installd_constants.h" #include "otapreopt_utils.h" #ifndef LOG_TAG #define LOG_TAG "otapreopt" #endif using android::base::StringPrintf; namespace android { namespace installd { static void CloseDescriptor(int fd) { if (fd >= 0) { int result = close(fd); UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor // that we do *not* want. } } static void CloseDescriptor(const char* descriptor_string) { int fd = -1; std::istringstream stream(descriptor_string); stream >> fd; if (!stream.fail()) { CloseDescriptor(fd); } } static std::vector ActivateApexPackages() { // The logic here is (partially) copied and adapted from // system/apex/apexd/apexd_main.cpp. // // Only scan the APEX directory under /system (within the chroot dir). apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir); return apex::getActivePackages(); } static void DeactivateApexPackages(const std::vector& active_packages) { for (const apex::ApexFile& apex_file : active_packages) { const std::string& package_path = apex_file.GetPath(); apex::Status status = apex::deactivatePackage(package_path); if (!status.Ok()) { LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage(); } } } static void TryExtraMount(const char* name, const char* slot, const char* target) { std::string partition_name = StringPrintf("%s%s", name, slot); // See whether update_engine mounted a logical partition. { auto& dm = dm::DeviceMapper::Instance(); if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) { std::string path; if (dm.GetDmDevicePathByName(partition_name, &path)) { int mount_result = mount(path.c_str(), target, "ext4", MS_RDONLY, /* data */ nullptr); if (mount_result == 0) { return; } } } } // Fall back and attempt a direct mount. std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str()); int mount_result = mount(block_device.c_str(), target, "ext4", MS_RDONLY, /* data */ nullptr); UNUSED(mount_result); } // Entry for otapreopt_chroot. Expected parameters are: // [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] // The file descriptor denoted by status-fd will be closed. The rest of the parameters will // be passed on to otapreopt in the chroot. static int otapreopt_chroot(const int argc, char **arg) { // Validate arguments // We need the command, status channel and target slot, at a minimum. if(argc < 3) { PLOG(ERROR) << "Not enough arguments."; exit(208); } // Close all file descriptors. They are coming from the caller, we do not want to pass them // on across our fork/exec into a different domain. // 1) Default descriptors. CloseDescriptor(STDIN_FILENO); CloseDescriptor(STDOUT_FILENO); CloseDescriptor(STDERR_FILENO); // 2) The status channel. CloseDescriptor(arg[1]); // We need to run the otapreopt tool from the postinstall partition. As such, set up a // mount namespace and change root. // Create our own mount namespace. if (unshare(CLONE_NEWNS) != 0) { PLOG(ERROR) << "Failed to unshare() for otapreopt."; exit(200); } // Make postinstall private, so that our changes don't propagate. if (mount("", "/postinstall", nullptr, MS_PRIVATE, nullptr) != 0) { PLOG(ERROR) << "Failed to mount private."; exit(201); } // Bind mount necessary directories. constexpr const char* kBindMounts[] = { "/data", "/dev", "/proc", "/sys" }; for (size_t i = 0; i < arraysize(kBindMounts); ++i) { std::string trg = StringPrintf("/postinstall%s", kBindMounts[i]); if (mount(kBindMounts[i], trg.c_str(), nullptr, MS_BIND, nullptr) != 0) { PLOG(ERROR) << "Failed to bind-mount " << kBindMounts[i]; exit(202); } } // Try to mount the vendor partition. update_engine doesn't do this for us, but we // want it for vendor APKs. // Notes: // 1) We pretty much guess a name here and hope to find the partition by name. // It is just as complicated and brittle to scan /proc/mounts. But this requires // validating the target-slot so as not to try to mount some totally random path. // 2) We're in a mount namespace here, so when we die, this will be cleaned up. // 3) Ignore errors. Printing anything at this stage will open a file descriptor // for logging. if (!ValidateTargetSlotSuffix(arg[2])) { LOG(ERROR) << "Target slot suffix not legal: " << arg[2]; exit(207); } TryExtraMount("vendor", arg[2], "/postinstall/vendor"); // Try to mount the product partition. update_engine doesn't do this for us, but we // want it for product APKs. Same notes as vendor above. TryExtraMount("product", arg[2], "/postinstall/product"); // Setup APEX mount point and its security context. static constexpr const char* kPostinstallApexDir = "/postinstall/apex"; // The following logic is similar to the one in system/core/rootdir/init.rc: // // mount tmpfs tmpfs /apex nodev noexec nosuid // chmod 0755 /apex // chown root root /apex // restorecon /apex // // except we perform the `restorecon` step just after mounting the tmpfs // filesystem in /postinstall/apex, so that this directory is correctly // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in // following operations (`chmod`, `chown`, etc.) following policies // restricted to `postinstall_apex_mnt_dir`: // // mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid // restorecon /postinstall/apex // chmod 0755 /postinstall/apex // chown root root /postinstall/apex // if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr) != 0) { PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir; exit(209); } if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) { PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir; exit(214); } if (chmod(kPostinstallApexDir, 0755) != 0) { PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755"; exit(210); } if (chown(kPostinstallApexDir, 0, 0) != 0) { PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root"; exit(211); } // Chdir into /postinstall. if (chdir("/postinstall") != 0) { PLOG(ERROR) << "Unable to chdir into /postinstall."; exit(203); } // Make /postinstall the root in our mount namespace. if (chroot(".") != 0) { PLOG(ERROR) << "Failed to chroot"; exit(204); } if (chdir("/") != 0) { PLOG(ERROR) << "Unable to chdir into /."; exit(205); } // Try to mount APEX packages in "/apex" in the chroot dir. We need at least // the Android Runtime APEX, as it is required by otapreopt to run dex2oat. std::vector active_packages = ActivateApexPackages(); // Now go on and run otapreopt. // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1 std::vector cmd; cmd.reserve(argc); cmd.push_back("/system/bin/otapreopt"); // The first parameter is the status file descriptor, skip. for (size_t i = 2; i < static_cast(argc); ++i) { cmd.push_back(arg[i]); } // Fork and execute otapreopt in its own process. std::string error_msg; bool exec_result = Exec(cmd, &error_msg); if (!exec_result) { LOG(ERROR) << "Running otapreopt failed: " << error_msg; } // Tear down the work down by the apexd logic. (i.e. deactivate packages). DeactivateApexPackages(active_packages); if (!exec_result) { exit(213); } return 0; } } // namespace installd } // namespace android int main(const int argc, char *argv[]) { return android::installd::otapreopt_chroot(argc, argv); } cmds/installd/otapreopt_parameters.cpp0100644 0000000 0000000 00000027432 13756501734 017334 0ustar000000000 0000000 /* ** Copyright 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. */ #include "otapreopt_parameters.h" #include #include #include "dexopt.h" #include "installd_constants.h" #include "otapreopt_utils.h" #ifndef LOG_TAG #define LOG_TAG "otapreopt" #endif namespace android { namespace installd { static bool ParseBool(const char* in) { if (strcmp(in, "true") == 0) { return true; } return false; } static const char* ParseNull(const char* arg) { return (strcmp(arg, "!") == 0) ? nullptr : arg; } static bool ParseUInt(const char* in, uint32_t* out) { char* end; long long int result = strtoll(in, &end, 0); if (in == end || *end != '\0') { return false; } if (result < std::numeric_limits::min() || std::numeric_limits::max() < result) { return false; } *out = static_cast(result); return true; } bool OTAPreoptParameters::ReadArguments(int argc, const char** argv) { // Expected command line: // target-slot [version] dexopt {DEXOPT_PARAMETERS} const char* target_slot_arg = argv[1]; if (target_slot_arg == nullptr) { LOG(ERROR) << "Missing parameters"; return false; } // Sanitize value. Only allow (a-zA-Z0-9_)+. target_slot = target_slot_arg; if (!ValidateTargetSlotSuffix(target_slot)) { LOG(ERROR) << "Target slot suffix not legal: " << target_slot; return false; } // Check for version or "dexopt" next. if (argv[2] == nullptr) { LOG(ERROR) << "Missing parameters"; return false; } if (std::string("dexopt").compare(argv[2]) == 0) { // This is version 1 (N) or pre-versioning version 2. constexpr int kV2ArgCount = 1 // "otapreopt" + 1 // slot + 1 // "dexopt" + 1 // apk_path + 1 // uid + 1 // pkg + 1 // isa + 1 // dexopt_needed + 1 // oat_dir + 1 // dexopt_flags + 1 // filter + 1 // volume + 1 // libs + 1; // seinfo if (argc == kV2ArgCount) { return ReadArgumentsPostV1(2, argv, false); } else { return ReadArgumentsV1(argv); } } uint32_t version; if (!ParseUInt(argv[2], &version)) { LOG(ERROR) << "Could not parse version: " << argv[2]; return false; } return ReadArgumentsPostV1(version, argv, true); } static int ReplaceMask(int input, int old_mask, int new_mask) { return (input & old_mask) != 0 ? new_mask : 0; } void OTAPreoptParameters::SetDefaultsForPostV1Arguments() { // Set se_info to null. It is only relevant for secondary dex files, which we won't // receive from a v1 A side. se_info = nullptr; // Set downgrade to false. It is only relevant when downgrading compiler // filter, which is not the case during ota. downgrade = false; // Set target_sdk_version to 0, ie the platform SDK version. This is // conservative and may force some classes to verify at runtime. target_sdk_version = 0; // Set the profile name to the primary apk profile. profile_name = "primary.prof"; // By default we don't have a dex metadata file. dex_metadata_path = nullptr; // The compilation reason is ab-ota (match the system property pm.dexopt.ab-ota) compilation_reason = "ab-ota"; // Flag is enabled by default for A/B otas. dexopt_flags = DEXOPT_GENERATE_COMPACT_DEX; } bool OTAPreoptParameters::ReadArgumentsV1(const char** argv) { // Check for "dexopt". if (argv[2] == nullptr) { LOG(ERROR) << "Missing parameters"; return false; } if (std::string("dexopt").compare(argv[2]) != 0) { LOG(ERROR) << "Expected \"dexopt\" but found: " << argv[2]; return false; } SetDefaultsForPostV1Arguments(); size_t param_index = 0; for (;; ++param_index) { const char* param = argv[3 + param_index]; if (param == nullptr) { break; } switch (param_index) { case 0: apk_path = param; break; case 1: uid = atoi(param); break; case 2: pkgName = param; break; case 3: instruction_set = param; break; case 4: { // Version 1 had: // DEXOPT_DEX2OAT_NEEDED = 1 // DEXOPT_PATCHOAT_NEEDED = 2 // DEXOPT_SELF_PATCHOAT_NEEDED = 3 // We will simply use DEX2OAT_FROM_SCRATCH. dexopt_needed = DEX2OAT_FROM_SCRATCH; break; } case 5: oat_dir = param; break; case 6: { // Version 1 had: constexpr int OLD_DEXOPT_PUBLIC = 1 << 1; // Note: DEXOPT_SAFEMODE has been removed. // constexpr int OLD_DEXOPT_SAFEMODE = 1 << 2; constexpr int OLD_DEXOPT_DEBUGGABLE = 1 << 3; constexpr int OLD_DEXOPT_BOOTCOMPLETE = 1 << 4; constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5; constexpr int OLD_DEXOPT_OTA = 1 << 6; static_assert(DEXOPT_GENERATE_COMPACT_DEX > OLD_DEXOPT_OTA, "must not overlap"); int input = atoi(param); dexopt_flags |= ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) | ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) | ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) | ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) | ReplaceMask(input, OLD_DEXOPT_OTA, 0); break; } case 7: compiler_filter = param; break; case 8: volume_uuid = ParseNull(param); break; case 9: shared_libraries = ParseNull(param); break; default: LOG(ERROR) << "Too many arguments, got " << param; return false; } } if (param_index != 10) { LOG(ERROR) << "Not enough parameters"; return false; } return true; } bool OTAPreoptParameters::ReadArgumentsPostV1(uint32_t version, const char** argv, bool versioned) { size_t num_args_expected = 0; switch (version) { case 2: num_args_expected = 11; break; case 3: num_args_expected = 12; break; case 4: num_args_expected = 13; break; case 5: num_args_expected = 14; break; case 6: num_args_expected = 15; break; case 7: // Version 8 adds a new dexopt flag: DEXOPT_GENERATE_COMPACT_DEX case 8: num_args_expected = 16; break; // Version 9 adds a new dexopt flag: DEXOPT_GENERATE_APP_IMAGE case 9: num_args_expected = 16; break; // Version 10 is a compatibility bump. case 10: num_args_expected = 16; break; default: LOG(ERROR) << "Don't know how to read arguments for version " << version; return false; } size_t dexopt_index = versioned ? 3 : 2; // Check for "dexopt". if (argv[dexopt_index] == nullptr) { LOG(ERROR) << "Missing parameters"; return false; } if (std::string("dexopt").compare(argv[dexopt_index]) != 0) { LOG(ERROR) << "Expected \"dexopt\" but found: " << argv[dexopt_index]; return false; } // Validate the number of arguments. size_t num_args_actual = 0; while (argv[dexopt_index + 1 + num_args_actual] != nullptr) { num_args_actual++; } if (num_args_actual != num_args_expected) { LOG(ERROR) << "Invalid number of arguments. expected=" << num_args_expected << " actual=" << num_args_actual; return false; } // The number of arguments is OK. // Configure the default values for the parameters that were added after V1. // The default values will be overwritten in case they are passed as arguments. SetDefaultsForPostV1Arguments(); for (size_t param_index = 0; param_index < num_args_actual; ++param_index) { const char* param = argv[dexopt_index + 1 + param_index]; switch (param_index) { case 0: apk_path = param; break; case 1: uid = atoi(param); break; case 2: pkgName = param; break; case 3: instruction_set = param; break; case 4: dexopt_needed = atoi(param); break; case 5: oat_dir = param; break; case 6: dexopt_flags = atoi(param); // Add CompactDex generation flag for versions less than 8 since it wasn't passed // from the package manager. Only conditionally set the flag here so that it can // be fully controlled by the package manager. dexopt_flags |= (version < 8) ? DEXOPT_GENERATE_COMPACT_DEX : 0u; break; case 7: compiler_filter = param; break; case 8: volume_uuid = ParseNull(param); break; case 9: shared_libraries = ParseNull(param); break; case 10: se_info = ParseNull(param); break; case 11: downgrade = ParseBool(param); break; case 12: target_sdk_version = atoi(param); break; case 13: profile_name = ParseNull(param); break; case 14: dex_metadata_path = ParseNull(param); break; case 15: compilation_reason = ParseNull(param); break; default: LOG(FATAL) << "Should not get here. Did you call ReadArguments " << "with the right expectation? index=" << param_index << " num_args=" << num_args_actual; return false; } } if (version < 10) { // Do not accept '&' as shared libraries from versions prior to 10. These may lead // to runtime crashes. The server side of version 10+ should send the correct // context in almost all cases (e.g., only for actual shared packages). if (shared_libraries != nullptr && std::string("&") == shared_libraries) { return false; } } return true; } } // namespace installd } // namespace android cmds/installd/otapreopt_parameters.h0100644 0000000 0000000 00000003220 13756501734 016766 0ustar000000000 0000000 /* ** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef OTAPREOPT_PARAMETERS_H_ #define OTAPREOPT_PARAMETERS_H_ #include #include namespace android { namespace installd { class OTAPreoptParameters { public: bool ReadArguments(int argc, const char** argv); private: bool ReadArgumentsV1(const char** argv); bool ReadArgumentsPostV1(uint32_t version, const char** argv, bool versioned); void SetDefaultsForPostV1Arguments(); const char* apk_path; uid_t uid; const char* pkgName; const char* instruction_set; int dexopt_needed; const char* oat_dir; int dexopt_flags; const char* compiler_filter; const char* volume_uuid; const char* shared_libraries; const char* se_info; bool downgrade; int target_sdk_version; const char* profile_name; const char* dex_metadata_path; const char* compilation_reason; std::string target_slot; friend class OTAPreoptService; friend class OTAPreoptTest; }; } // namespace installd } // namespace android #endif // OTAPREOPT_PARAMETERS_H_ cmds/installd/otapreopt_script.sh0100644 0000000 0000000 00000004560 13756501734 016322 0ustar000000000 0000000 #!/system/bin/sh # # 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. # # This script will run as a postinstall step to drive otapreopt. TARGET_SLOT="$1" STATUS_FD="$2" # Maximum number of packages/steps. MAXIMUM_PACKAGES=1000 # First ensure the system is booted. This is to work around issues when cmd would # infinitely loop trying to get a service manager (which will never come up in that # mode). b/30797145 BOOT_PROPERTY_NAME="dev.bootcomplete" BOOT_COMPLETE=$(getprop $BOOT_PROPERTY_NAME) if [ "$BOOT_COMPLETE" != "1" ] ; then echo "Error: boot-complete not detected." # We must return 0 to not block sideload. exit 0 fi # Compute target slot suffix. # TODO: Once bootctl is not restricted, we should query from there. Or get this from # update_engine as a parameter. if [ "$TARGET_SLOT" = "0" ] ; then TARGET_SLOT_SUFFIX="_a" elif [ "$TARGET_SLOT" = "1" ] ; then TARGET_SLOT_SUFFIX="_b" else echo "Unknown target slot $TARGET_SLOT" exit 1 fi PREPARE=$(cmd otadexopt prepare) # Note: Ignore preparation failures. Step and done will fail and exit this. # This is necessary to support suspends - the OTA service will keep # the state around for us. PROGRESS=$(cmd otadexopt progress) print -u${STATUS_FD} "global_progress $PROGRESS" i=0 while ((i&- 2>&- PROGRESS=$(cmd otadexopt progress) print -u${STATUS_FD} "global_progress $PROGRESS" DONE=$(cmd otadexopt done) if [ "$DONE" = "OTA incomplete." ] ; then sleep 1 i=$((i+1)) continue fi break done DONE=$(cmd otadexopt done) if [ "$DONE" = "OTA incomplete." ] ; then echo "Incomplete." else echo "Complete or error." fi print -u${STATUS_FD} "global_progress 1.0" cmd otadexopt cleanup exit 0 cmds/installd/otapreopt_slot.sh0100644 0000000 0000000 00000002614 13756501734 015775 0ustar000000000 0000000 #!/system/bin/sh # # 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. # # This script will move artifacts for the currently active slot. SLOT_SUFFIX=$(getprop ro.boot.slot_suffix) if test -n "$SLOT_SUFFIX" ; then if test -d /data/ota/$SLOT_SUFFIX/dalvik-cache ; then log -p i -t otapreopt_slot "Moving A/B artifacts for slot ${SLOT_SUFFIX}." OLD_SIZE=$(du -h -s /data/dalvik-cache) rm -rf /data/dalvik-cache/* NEW_SIZE=$(du -h -s /data/ota/$SLOT_SUFFIX/dalvik-cache) mv /data/ota/$SLOT_SUFFIX/dalvik-cache/* /data/dalvik-cache/ rmdir /data/ota/$SLOT_SUFFIX/dalvik-cache rmdir /data/ota/$SLOT_SUFFIX log -p i -t otapreopt_slot "Moved ${NEW_SIZE} over ${OLD_SIZE}" else log -p i -t otapreopt_slot "No A/B artifacts found for slot ${SLOT_SUFFIX}." fi exit 0 else log -p w -t otapreopt_slot "Slot property empty." exit 1 fi cmds/installd/otapreopt_utils.cpp0100644 0000000 0000000 00000005437 13756501734 016332 0ustar000000000 0000000 /* ** Copyright 2019, 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. */ #include "otapreopt_utils.h" #include #include #include #include #include #include using android::base::Join; using android::base::StringPrintf; namespace android { namespace installd { bool Exec(const std::vector& arg_vector, std::string* error_msg) { const std::string command_line = Join(arg_vector, ' '); CHECK_GE(arg_vector.size(), 1U) << command_line; // Convert the args to char pointers. const char* program = arg_vector[0].c_str(); std::vector args; for (size_t i = 0; i < arg_vector.size(); ++i) { const std::string& arg = arg_vector[i]; char* arg_str = const_cast(arg.c_str()); CHECK(arg_str != nullptr) << i; args.push_back(arg_str); } args.push_back(nullptr); // Fork and exec. pid_t pid = fork(); if (pid == 0) { // No allocation allowed between fork and exec. // Change process groups, so we don't get reaped by ProcessManager. setpgid(0, 0); execv(program, &args[0]); PLOG(ERROR) << "Failed to execv(" << command_line << ")"; // _exit to avoid atexit handlers in child. _exit(1); } else { if (pid == -1) { *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", command_line.c_str(), strerror(errno)); return false; } // wait for subprocess to finish int status; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " "wanted %d, got %d: %s", command_line.c_str(), pid, got_pid, strerror(errno)); return false; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", command_line.c_str()); return false; } } return true; } } // namespace installd } // namespace android cmds/installd/otapreopt_utils.h0100644 0000000 0000000 00000002312 13756501734 015764 0ustar000000000 0000000 /* * 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. */ #ifndef OTAPREOPT_UTILS_H_ #define OTAPREOPT_UTILS_H_ #include #include #include namespace android { namespace installd { static inline bool ValidateTargetSlotSuffix(const std::string& input) { std::regex slot_suffix_regex("[a-zA-Z0-9_]+"); std::smatch slot_suffix_match; return std::regex_match(input, slot_suffix_match, slot_suffix_regex); } // Wrapper on fork/execv to run a command in a subprocess. bool Exec(const std::vector& arg_vector, std::string* error_msg); } // namespace installd } // namespace android #endif // OTAPREOPT_UTILS_H_ cmds/installd/system_properties.h0100644 0000000 0000000 00000004274 13756501734 016340 0ustar000000000 0000000 /* * 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. */ #ifndef OTAPREOPT_SYSTEM_PROPERTIES_H_ #define OTAPREOPT_SYSTEM_PROPERTIES_H_ #include #include #include #include namespace android { namespace installd { // Helper class to read system properties into and manage as a string->string map. class SystemProperties { public: bool Load(const std::string& strFile) { return ParseFile(strFile, [&](const std::string& line) { size_t equals_pos = line.find('='); if (equals_pos == std::string::npos || equals_pos == 0) { // Did not find equals sign, or it's the first character - isn't a valid line. return true; } std::string key = line.substr(0, equals_pos); std::string value = line.substr(equals_pos + 1, line.length() - equals_pos + 1); properties_.insert(std::make_pair(key, value)); return true; }); } // Look up the key in the map. Returns null if the key isn't mapped. const std::string* GetProperty(const std::string& key) const { auto it = properties_.find(key); if (it != properties_.end()) { return &it->second; } return nullptr; } void SetProperty(const std::string& key, const std::string& value) { properties_.insert(std::make_pair(key, value)); } private: // The actual map. std::unordered_map properties_; }; } // namespace installd } // namespace android #endif // OTAPREOPT_SYSTEM_PROPERTIES_H_ cmds/installd/tests/0040755 0000000 0000000 00000000000 13756501734 013525 5ustar000000000 0000000 cmds/installd/tests/Android.bp0100644 0000000 0000000 00000004613 13756501734 015431 0ustar000000000 0000000 // Build the unit tests for installd cc_test { name: "installd_utils_test", test_suites: ["device-tests"], clang: true, srcs: ["installd_utils_test.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libutils", "libcutils", ], static_libs: [ "libdiskusage", "libinstalld", "liblog", ], test_config: "installd_utils_test.xml", } cc_test { name: "installd_cache_test", test_suites: ["device-tests"], clang: true, srcs: ["installd_cache_test.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", "libcrypto", "libcutils", "libprocessgroup", "libselinux", "libutils", "server_configurable_flags", ], static_libs: [ "libdiskusage", "libinstalld", "liblog", "liblogwrap", ], test_config: "installd_cache_test.xml", } cc_test { name: "installd_service_test", test_suites: ["device-tests"], clang: true, srcs: ["installd_service_test.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", "libcrypto", "libcutils", "libprocessgroup", "libselinux", "libutils", "server_configurable_flags", ], static_libs: [ "libdiskusage", "libinstalld", "liblog", "liblogwrap", ], test_config: "installd_service_test.xml", } cc_test { name: "installd_dexopt_test", test_suites: ["device-tests"], clang: true, srcs: ["installd_dexopt_test.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", "libcrypto", "libcutils", "libprocessgroup", "libselinux", "libutils", "server_configurable_flags", ], static_libs: [ "libdiskusage", "libinstalld", "liblog", "liblogwrap", ], test_config: "installd_dexopt_test.xml", } cc_test { name: "installd_otapreopt_test", test_suites: ["device-tests"], clang: true, srcs: ["installd_otapreopt_test.cpp"], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libcutils", "libutils", "server_configurable_flags", ], static_libs: [ "liblog", "libotapreoptparameters" ], } cmds/installd/tests/binder_test_utils.h0100644 0000000 0000000 00000004031 13756501734 017413 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #pragma once #include #include #include #define ASSERT_BINDER_SUCCESS(expr) \ ({ \ binder::Status expect_status = (expr); \ ASSERT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str(); \ expect_status; \ }) #define ASSERT_BINDER_FAIL(expr) \ ({ \ binder::Status expect_status = (expr); \ ASSERT_FALSE(expect_status.isOk()); \ expect_status; \ }) #define EXPECT_BINDER_SUCCESS(expr) \ ({ \ binder::Status expect_status = (expr); \ EXPECT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str(); \ expect_status; \ }) #define EXPECT_BINDER_FAIL(expr) \ ({ \ binder::Status expect_status = (expr); \ EXPECT_FALSE(expect_status.isOk()); \ expect_status; \ }) cmds/installd/tests/installd_cache_test.cpp0100644 0000000 0000000 00000031076 13756501734 020231 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include #include #include #include #include #include #include #include #include "InstalldNativeService.h" #include "globals.h" #include "utils.h" using android::base::StringPrintf; namespace android { namespace installd { constexpr const char* kTestUuid = "TEST"; constexpr int64_t kKbInBytes = 1024; constexpr int64_t kMbInBytes = 1024 * kKbInBytes; constexpr int64_t kGbInBytes = 1024 * kMbInBytes; constexpr int64_t kTbInBytes = 1024 * kGbInBytes; #define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2 #define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); } bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, const char *oat_dir ATTRIBUTE_UNUSED, const char *apk_path ATTRIBUTE_UNUSED, const char *instruction_set ATTRIBUTE_UNUSED) { return false; } bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, const char *apk_path ATTRIBUTE_UNUSED, const char *instruction_set ATTRIBUTE_UNUSED) { return false; } bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, const char *src ATTRIBUTE_UNUSED, const char *instruction_set ATTRIBUTE_UNUSED) { return false; } static void mkdir(const char* path) { const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); ::mkdir(fullPath, 0755); } static void touch(const char* path, int len, int time) { const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); int fd = ::open(fullPath, O_RDWR | O_CREAT, 0644); ::fallocate(fd, 0, 0, len); ::close(fd); struct utimbuf times; times.actime = times.modtime = std::time(0) + time; ::utime(fullPath, ×); } static int exists(const char* path) { const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); return ::access(fullPath, F_OK); } static int64_t size(const char* path) { const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); struct stat buf; if (!stat(fullPath, &buf)) { return buf.st_size; } else { return -1; } } static int64_t free() { struct statvfs buf; if (!statvfs("/data/local/tmp", &buf)) { return static_cast(buf.f_bavail) * buf.f_frsize; } else { PLOG(ERROR) << "Failed to statvfs"; return -1; } } static void setxattr(const char* path, const char* key) { const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); ::setxattr(fullPath, key, "", 0, 0); } class CacheTest : public testing::Test { protected: InstalldNativeService* service; std::unique_ptr testUuid; virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); service = new InstalldNativeService(); testUuid = std::make_unique(); *testUuid = std::string(kTestUuid); system("mkdir -p /data/local/tmp/user/0"); } virtual void TearDown() { delete service; system("rm -rf /data/local/tmp/user"); } }; TEST_F(CacheTest, FreeCache_All) { LOG(INFO) << "FreeCache_All"; mkdir("com.example"); touch("com.example/normal", 1 * kMbInBytes, 60); mkdir("com.example/cache"); mkdir("com.example/cache/foo"); touch("com.example/cache/foo/one", 1 * kMbInBytes, 60); touch("com.example/cache/foo/two", 2 * kMbInBytes, 120); EXPECT_EQ(0, exists("com.example/normal")); EXPECT_EQ(0, exists("com.example/cache/foo/one")); EXPECT_EQ(0, exists("com.example/cache/foo/two")); service->freeCache(testUuid, kTbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(0, exists("com.example/normal")); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); EXPECT_EQ(-1, exists("com.example/cache/foo/two")); } TEST_F(CacheTest, FreeCache_Age) { LOG(INFO) << "FreeCache_Age"; mkdir("com.example"); mkdir("com.example/cache"); mkdir("com.example/cache/foo"); touch("com.example/cache/foo/one", kMbInBytes, 60); touch("com.example/cache/foo/two", kMbInBytes, 120); service->freeCache(testUuid, free() + kKbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); EXPECT_EQ(0, exists("com.example/cache/foo/two")); service->freeCache(testUuid, free() + kKbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/one")); EXPECT_EQ(-1, exists("com.example/cache/foo/two")); } TEST_F(CacheTest, FreeCache_Tombstone) { LOG(INFO) << "FreeCache_Tombstone"; mkdir("com.example"); mkdir("com.example/cache"); mkdir("com.example/cache/foo"); touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60); touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60); mkdir("com.example/cache/bar"); touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120); touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120); setxattr("com.example/cache/bar", "user.cache_tombstone"); EXPECT_EQ(0, exists("com.example/cache/foo/foo1")); EXPECT_EQ(0, exists("com.example/cache/foo/foo2")); EXPECT_EQ(0, exists("com.example/cache/bar/bar1")); EXPECT_EQ(0, exists("com.example/cache/bar/bar2")); EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1")); EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2")); service->freeCache(testUuid, kTbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); EXPECT_EQ(-1, exists("com.example/cache/foo/foo2")); EXPECT_EQ(0, exists("com.example/cache/bar/bar1")); EXPECT_EQ(0, exists("com.example/cache/bar/bar2")); EXPECT_EQ(0, size("com.example/cache/bar/bar1")); EXPECT_EQ(0, size("com.example/cache/bar/bar2")); } TEST_F(CacheTest, FreeCache_Group) { LOG(INFO) << "FreeCache_Group"; mkdir("com.example"); mkdir("com.example/cache"); mkdir("com.example/cache/foo"); touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60); touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120); setxattr("com.example/cache/foo", "user.cache_group"); service->freeCache(testUuid, free() + kKbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, exists("com.example/cache/foo/foo1")); EXPECT_EQ(-1, exists("com.example/cache/foo/foo2")); } TEST_F(CacheTest, FreeCache_GroupTombstone) { LOG(INFO) << "FreeCache_GroupTombstone"; mkdir("com.example"); mkdir("com.example/cache"); // this dir must look really old for some reason? mkdir("com.example/cache/group"); touch("com.example/cache/group/file1", kMbInBytes, 120); touch("com.example/cache/group/file2", kMbInBytes, 120); mkdir("com.example/cache/group/dir"); touch("com.example/cache/group/dir/file1", kMbInBytes, 120); touch("com.example/cache/group/dir/file2", kMbInBytes, 120); mkdir("com.example/cache/group/tomb"); touch("com.example/cache/group/tomb/file1", kMbInBytes, 120); touch("com.example/cache/group/tomb/file2", kMbInBytes, 120); mkdir("com.example/cache/group/tomb/dir"); touch("com.example/cache/group/tomb/dir/file1", kMbInBytes, 120); touch("com.example/cache/group/tomb/dir/file2", kMbInBytes, 120); mkdir("com.example/cache/tomb"); touch("com.example/cache/tomb/file1", kMbInBytes, 240); touch("com.example/cache/tomb/file2", kMbInBytes, 240); mkdir("com.example/cache/tomb/dir"); touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240); touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240); mkdir("com.example/cache/tomb/group"); touch("com.example/cache/tomb/group/file1", kMbInBytes, 60); touch("com.example/cache/tomb/group/file2", kMbInBytes, 60); mkdir("com.example/cache/tomb/group/dir"); touch("com.example/cache/tomb/group/dir/file1", kMbInBytes, 60); touch("com.example/cache/tomb/group/dir/file2", kMbInBytes, 60); setxattr("com.example/cache/group", "user.cache_group"); setxattr("com.example/cache/group/tomb", "user.cache_tombstone"); setxattr("com.example/cache/tomb", "user.cache_tombstone"); setxattr("com.example/cache/tomb/group", "user.cache_group"); service->freeCache(testUuid, free() + kKbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/group/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); service->freeCache(testUuid, free() + kKbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, size("com.example/cache/group/file1")); EXPECT_EQ(-1, size("com.example/cache/group/file2")); EXPECT_EQ(-1, size("com.example/cache/group/dir/file1")); EXPECT_EQ(-1, size("com.example/cache/group/dir/file2")); EXPECT_EQ(0, size("com.example/cache/group/tomb/file1")); EXPECT_EQ(0, size("com.example/cache/group/tomb/file2")); EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1")); EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1")); EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/group/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); service->freeCache(testUuid, kTbInBytes, 0, FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA); EXPECT_EQ(-1, size("com.example/cache/group/file1")); EXPECT_EQ(-1, size("com.example/cache/group/file2")); EXPECT_EQ(-1, size("com.example/cache/group/dir/file1")); EXPECT_EQ(-1, size("com.example/cache/group/dir/file2")); EXPECT_EQ(0, size("com.example/cache/group/tomb/file1")); EXPECT_EQ(0, size("com.example/cache/group/tomb/file2")); EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1")); EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/group/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/file2")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1")); EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2")); } } // namespace installd } // namespace android cmds/installd/tests/installd_cache_test.xml0100644 0000000 0000000 00000003013 13756501734 020235 0ustar000000000 0000000 cmds/installd/tests/installd_dexopt_test.cpp0100644 0000000 0000000 00000142320 13756501734 020464 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "binder_test_utils.h" #include "dexopt.h" #include "InstalldNativeService.h" #include "globals.h" #include "tests/test_utils.h" #include "utils.h" using android::base::ReadFully; using android::base::unique_fd; namespace android { namespace installd { // TODO(calin): try to dedup this code. #if defined(__arm__) static const std::string kRuntimeIsa = "arm"; #elif defined(__aarch64__) static const std::string kRuntimeIsa = "arm64"; #elif defined(__mips__) && !defined(__LP64__) static const std::string kRuntimeIsa = "mips"; #elif defined(__mips__) && defined(__LP64__) static const std::string kRuntimeIsa = "mips64"; #elif defined(__i386__) static const std::string kRuntimeIsa = "x86"; #elif defined(__x86_64__) static const std::string kRuntimeIsa = "x86_64"; #else static const std::string kRuntimeIsa = "none"; #endif int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); } bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set) { return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set); } bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { return calculate_odex_file_path_default(path, apk_path, instruction_set); } bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { return create_cache_path_default(path, src, instruction_set); } static void run_cmd(const std::string& cmd) { system(cmd.c_str()); } template static void run_cmd_and_process_output(const std::string& cmd, const Visitor& visitor) { FILE* file = popen(cmd.c_str(), "r"); CHECK(file != nullptr) << "Failed to ptrace " << cmd; char* line = nullptr; while (true) { size_t n = 0u; ssize_t value = getline(&line, &n, file); if (value == -1) { break; } visitor(line); } free(line); fclose(file); } static int mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) { int ret = ::mkdir(path.c_str(), mode); if (ret != 0) { return ret; } ret = ::chown(path.c_str(), owner, group); if (ret != 0) { return ret; } return ::chmod(path.c_str(), mode); } static int log_callback(int type, const char *fmt, ...) { // NOLINT va_list ap; int priority; switch (type) { case SELINUX_WARNING: priority = ANDROID_LOG_WARN; break; case SELINUX_INFO: priority = ANDROID_LOG_INFO; break; default: priority = ANDROID_LOG_ERROR; break; } va_start(ap, fmt); LOG_PRI_VA(priority, "SELinux", fmt, ap); va_end(ap); return 0; } static bool init_selinux() { int selinux_enabled = (is_selinux_enabled() > 0); union selinux_callback cb; cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); if (selinux_enabled && selinux_status_open(true) < 0) { LOG(ERROR) << "Could not open selinux status; exiting"; return false; } return true; } // Base64 encoding of a simple dex files with 2 methods. static const char kDexFile[] = "UEsDBBQAAAAIAOiOYUs9y6BLCgEAABQCAAALABwAY2xhc3Nlcy5kZXhVVAkAA/Ns+lkOHv1ZdXgL" "AAEEI+UCAASIEwAAS0mt4DIwNmX4qpn7j/2wA7v7N+ZvoQpCJRlVx5SWa4YaiDAxMBQwMDBUhJkI" "MUBBDyMDAzsDRJwFxAdioBDDHAYEYAbiFUAM1M5wAIhFGCGKDIDYAogdgNgDiH2BOAiI0xghekDm" "sQIxGxQzM6ACRijNhCbOhCZfyohdPYyuh8szgtVkMkLsLhAAqeCDi+ejibPZZOZlltgxsDnqZSWW" "JTKwOUFoZh9HayDhZM0g5AMS0M9JzEvX90/KSk0usWZgDAMaws5nAyXBzmpoYGlgAjsAyJoBMp0b" "zQ8gGhbOTEhhzYwU3qxIYc2GFN6MClC/AhUyKUDMAYU9M1Qc5F8GKBscVgIQM0FxCwBQSwECHgMU" "AAAACADojmFLPcugSwoBAAAUAgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAA/Ns" "+ll1eAsAAQQj5QIABIgTAABQSwUGAAAAAAEAAQBRAAAATwEAAAAA"; class DexoptTestEnvTest : public testing::Test { }; TEST_F(DexoptTestEnvTest, CheckSelinux) { ASSERT_EQ(1, is_selinux_enabled()); // Crude cutout for virtual devices. #if !defined(__i386__) && !defined(__x86_64__) constexpr bool kIsX86 = false; #else constexpr bool kIsX86 = true; #endif ASSERT_TRUE(1 == security_getenforce() || kIsX86 || true /* b/119032200 */); } class DexoptTest : public testing::Test { protected: static constexpr bool kDebug = false; static constexpr uid_t kSystemUid = 1000; static constexpr uid_t kSystemGid = 1000; static constexpr int32_t kOSdkVersion = 25; static constexpr int32_t kAppDataFlags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; static constexpr int32_t kTestUserId = 0; static constexpr uid_t kTestAppId = 19999; const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId); const uid_t kTestAppGid = multiuser_get_shared_gid(kTestUserId, kTestAppId); InstalldNativeService* service_; std::unique_ptr volume_uuid_; std::string package_name_; std::string apk_path_; std::string app_apk_dir_; std::string app_private_dir_ce_; std::string app_private_dir_de_; std::string se_info_; std::string app_oat_dir_; int64_t ce_data_inode_; std::string secondary_dex_ce_; std::string secondary_dex_ce_link_; std::string secondary_dex_de_; virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); // Initialize the globals holding the file system main paths (/data/, /system/ etc..). // This is needed in order to compute the application and profile paths. ASSERT_TRUE(init_globals_from_data_and_root()); // Initialize selinux log callbacks. // This ensures that selinux is up and running and re-directs the selinux messages // to logcat (in order to make it easier to investigate test results). ASSERT_TRUE(init_selinux()); service_ = new InstalldNativeService(); volume_uuid_ = nullptr; package_name_ = "com.installd.test.dexopt"; se_info_ = "default"; app_apk_dir_ = android_app_dir + package_name_; ASSERT_TRUE(create_mock_app()); } virtual void TearDown() { if (!kDebug) { service_->destroyAppData( volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_); run_cmd("rm -rf " + app_apk_dir_); run_cmd("rm -rf " + app_private_dir_ce_); run_cmd("rm -rf " + app_private_dir_de_); } delete service_; } ::testing::AssertionResult create_mock_app() { // Create the oat dir. app_oat_dir_ = app_apk_dir_ + "/oat"; // For debug mode, the directory might already exist. Avoid erroring out. if (mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755) != 0 && !kDebug) { return ::testing::AssertionFailure() << "Could not create app dir " << app_apk_dir_ << " : " << strerror(errno); } binder::Status status = service_->createOatDir(app_oat_dir_, kRuntimeIsa); if (!status.isOk()) { return ::testing::AssertionFailure() << "Could not create oat dir: " << status.toString8().c_str(); } // Copy the primary apk. apk_path_ = app_apk_dir_ + "/base.jar"; std::string error_msg; if (!WriteBase64ToFile(kDexFile, apk_path_, kSystemUid, kSystemGid, 0644, &error_msg)) { return ::testing::AssertionFailure() << "Could not write base64 file to " << apk_path_ << " : " << error_msg; } // Create the app user data. status = service_->createAppData( volume_uuid_, package_name_, kTestUserId, kAppDataFlags, kTestAppUid, se_info_, kOSdkVersion, &ce_data_inode_); if (!status.isOk()) { return ::testing::AssertionFailure() << "Could not create app data: " << status.toString8().c_str(); } // Create a secondary dex file on CE storage const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str(); app_private_dir_ce_ = create_data_user_ce_package_path( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar"; if (!WriteBase64ToFile(kDexFile, secondary_dex_ce_, kTestAppUid, kTestAppGid, 0600, &error_msg)) { return ::testing::AssertionFailure() << "Could not write base64 file to " << secondary_dex_ce_ << " : " << error_msg; } std::string app_private_dir_ce_link = create_data_user_ce_package_path_as_user_link( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_ce_link_ = app_private_dir_ce_link + "/secondary_ce.jar"; // Create a secondary dex file on DE storage. app_private_dir_de_ = create_data_user_de_package_path( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_de_ = app_private_dir_de_ + "/secondary_de.jar"; if (!WriteBase64ToFile(kDexFile, secondary_dex_de_, kTestAppUid, kTestAppGid, 0600, &error_msg)) { return ::testing::AssertionFailure() << "Could not write base64 file to " << secondary_dex_de_ << " : " << error_msg; } // Fix app data uid. status = service_->fixupAppData(volume_uuid_, kTestUserId); if (!status.isOk()) { return ::testing::AssertionFailure() << "Could not fixup app data: " << status.toString8().c_str(); } return ::testing::AssertionSuccess(); } std::string GetSecondaryDexArtifact(const std::string& path, const std::string& type) { std::string::size_type end = path.rfind('.'); std::string::size_type start = path.rfind('/', end); return path.substr(0, start) + "/oat/" + kRuntimeIsa + "/" + path.substr(start + 1, end - start) + type; } void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag, bool should_binder_call_succeed, bool should_dex_be_compiled = true, /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1, const char* class_loader_context = nullptr) { if (uid == -1) { uid = kTestAppUid; } if (class_loader_context == nullptr) { class_loader_context = "&"; } std::unique_ptr package_name_ptr(new std::string(package_name_)); int32_t dexopt_needed = 0; // does not matter; std::unique_ptr out_path = nullptr; // does not matter int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; std::string compiler_filter = "speed-profile"; std::unique_ptr class_loader_context_ptr( new std::string(class_loader_context)); std::unique_ptr se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default std::unique_ptr profile_name_ptr = nullptr; std::unique_ptr dm_path_ptr = nullptr; std::unique_ptr compilation_reason_ptr = nullptr; binder::Status result = service_->dexopt(path, uid, package_name_ptr, kRuntimeIsa, dexopt_needed, out_path, dex_flags, compiler_filter, volume_uuid_, class_loader_context_ptr, se_info_ptr, downgrade, target_sdk_version, profile_name_ptr, dm_path_ptr, compilation_reason_ptr); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); int expected_access = should_dex_be_compiled ? 0 : -1; std::string odex = GetSecondaryDexArtifact(path, "odex"); std::string vdex = GetSecondaryDexArtifact(path, "vdex"); std::string art = GetSecondaryDexArtifact(path, "art"); ASSERT_EQ(expected_access, access(odex.c_str(), R_OK)); ASSERT_EQ(expected_access, access(vdex.c_str(), R_OK)); ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image. if (binder_result != nullptr) { *binder_result = result; } } void reconcile_secondary_dex(const std::string& path, int32_t storage_flag, bool should_binder_call_succeed, bool should_dex_exist, bool should_dex_be_deleted, int32_t uid = -1, std::string* package_override = nullptr) { if (uid == -1) { uid = kTestAppUid; } std::vector isas; isas.push_back(kRuntimeIsa); bool out_secondary_dex_exists = false; binder::Status result = service_->reconcileSecondaryDexFile( path, package_override == nullptr ? package_name_ : *package_override, uid, isas, volume_uuid_, storage_flag, &out_secondary_dex_exists); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); ASSERT_EQ(should_dex_exist, out_secondary_dex_exists); int expected_access = should_dex_be_deleted ? -1 : 0; std::string odex = GetSecondaryDexArtifact(path, "odex"); std::string vdex = GetSecondaryDexArtifact(path, "vdex"); std::string art = GetSecondaryDexArtifact(path, "art"); ASSERT_EQ(expected_access, access(odex.c_str(), F_OK)); ASSERT_EQ(expected_access, access(vdex.c_str(), F_OK)); ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image. } void CheckFileAccess(const std::string& file, uid_t uid, gid_t gid, mode_t mode) { struct stat st; ASSERT_EQ(0, stat(file.c_str(), &st)); ASSERT_EQ(uid, st.st_uid); ASSERT_EQ(gid, st.st_gid); ASSERT_EQ(mode, st.st_mode); } void CompilePrimaryDexOk(std::string compiler_filter, int32_t dex_flags, const char* oat_dir, int32_t uid, int32_t dexopt_needed, binder::Status* binder_result = nullptr, const char* dm_path = nullptr, bool downgrade = false) { CompilePrimaryDex(compiler_filter, dex_flags, oat_dir, uid, dexopt_needed, dm_path, downgrade, true, binder_result); } void CompilePrimaryDexFail(std::string compiler_filter, int32_t dex_flags, const char* oat_dir, int32_t uid, int32_t dexopt_needed, binder::Status* binder_result = nullptr, const char* dm_path = nullptr, bool downgrade = false) { CompilePrimaryDex(compiler_filter, dex_flags, oat_dir, uid, dexopt_needed, dm_path, downgrade, false, binder_result); } void CompilePrimaryDex(std::string compiler_filter, int32_t dex_flags, const char* oat_dir, int32_t uid, int32_t dexopt_needed, const char* dm_path, bool downgrade, bool should_binder_call_succeed, /*out */ binder::Status* binder_result) { std::unique_ptr package_name_ptr(new std::string(package_name_)); std::unique_ptr out_path( oat_dir == nullptr ? nullptr : new std::string(oat_dir)); std::unique_ptr class_loader_context_ptr(new std::string("&")); std::unique_ptr se_info_ptr(new std::string(se_info_)); int32_t target_sdk_version = 0; // default std::unique_ptr profile_name_ptr(new std::string("primary.prof")); std::unique_ptr dm_path_ptr = nullptr; if (dm_path != nullptr) { dm_path_ptr.reset(new std::string(dm_path)); } std::unique_ptr compilation_reason_ptr(new std::string("test-reason")); bool prof_result; ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_, /*dex_metadata*/ nullptr, &prof_result)); ASSERT_TRUE(prof_result); binder::Status result = service_->dexopt(apk_path_, uid, package_name_ptr, kRuntimeIsa, dexopt_needed, out_path, dex_flags, compiler_filter, volume_uuid_, class_loader_context_ptr, se_info_ptr, downgrade, target_sdk_version, profile_name_ptr, dm_path_ptr, compilation_reason_ptr); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); if (!should_binder_call_succeed) { if (binder_result != nullptr) { *binder_result = result; } return; } // Check the access to the compiler output. // - speed-profile artifacts are not world-wide readable. // - files are owned by the system uid. std::string odex = GetPrimaryDexArtifact(oat_dir, apk_path_, "odex"); std::string vdex = GetPrimaryDexArtifact(oat_dir, apk_path_, "vdex"); std::string art = GetPrimaryDexArtifact(oat_dir, apk_path_, "art"); bool is_public = (dex_flags & DEXOPT_PUBLIC) != 0; mode_t mode = S_IFREG | (is_public ? 0644 : 0640); CheckFileAccess(odex, kSystemUid, uid, mode); CheckFileAccess(vdex, kSystemUid, uid, mode); if (compiler_filter == "speed-profile") { CheckFileAccess(art, kSystemUid, uid, mode); } if (binder_result != nullptr) { *binder_result = result; } } std::string GetPrimaryDexArtifact(const char* oat_dir, const std::string& dex_path, const std::string& type) { if (oat_dir == nullptr) { std::string path = dex_path; for (auto it = path.begin() + 1; it < path.end(); ++it) { if (*it == '/') { *it = '@'; } } return android_data_dir + DALVIK_CACHE + '/' + kRuntimeIsa + "/" + path + "@classes.dex"; } else { std::string::size_type name_end = dex_path.rfind('.'); std::string::size_type name_start = dex_path.rfind('/'); return std::string(oat_dir) + "/" + kRuntimeIsa + "/" + dex_path.substr(name_start + 1, name_end - name_start) + type; } } }; TEST_F(DexoptTest, DexoptSecondaryCe) { LOG(INFO) << "DexoptSecondaryCe"; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryCeLink) { LOG(INFO) << "DexoptSecondaryCeLink"; CompileSecondaryDex(secondary_dex_ce_link_, DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryCeWithContext) { LOG(INFO) << "DexoptSecondaryCeWithContext"; std::string class_loader_context = "PCL[" + secondary_dex_ce_ + "]"; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); } TEST_F(DexoptTest, DexoptSecondaryDe) { LOG(INFO) << "DexoptSecondaryDe"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true); } TEST_F(DexoptTest, DexoptSecondaryDeWithContext) { LOG(INFO) << "DexoptSecondaryDeWithContext"; std::string class_loader_context = "PCL[" + secondary_dex_de_ + "]"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); } TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) { LOG(INFO) << "DexoptSecondaryDoesNotExist"; // If the file validates but does not exist we do not treat it as an error. binder::Status status; CompileSecondaryDex(secondary_dex_ce_ + "not.there", DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ false, &status); EXPECT_STREQ(status.toString8().c_str(), "No error"); } TEST_F(DexoptTest, DexoptSecondaryStorageValidationError) { LOG(INFO) << "DexoptSecondaryStorageValidationError"; binder::Status status; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_DE, /*binder_ok*/ false, /*compile_ok*/ false, &status); EXPECT_STREQ(status.toString8().c_str(), "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer path validation failed'"); } TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) { LOG(INFO) << "DexoptSecondaryAppOwnershipValidationError"; binder::Status status; CompileSecondaryDex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE, /*binder_ok*/ false, /*compile_ok*/ false, &status); EXPECT_STREQ(status.toString8().c_str(), "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer path validation failed'"); } TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) { LOG(INFO) << "DexoptSecondaryAcessViaDifferentUidError"; binder::Status status; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ false, /*compile_ok*/ false, &status, kSystemUid); EXPECT_STREQ(status.toString8().c_str(), "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer open zip failed'"); } TEST_F(DexoptTest, DexoptPrimaryPublic) { LOG(INFO) << "DexoptPrimaryPublic"; CompilePrimaryDexOk("verify", DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); } TEST_F(DexoptTest, DexoptPrimaryFailedInvalidFilter) { LOG(INFO) << "DexoptPrimaryFailedInvalidFilter"; binder::Status status; CompilePrimaryDexFail("awesome-filter", DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PUBLIC, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH, &status); EXPECT_STREQ(status.toString8().c_str(), "Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for " "/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'"); } TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) { LOG(INFO) << "DexoptPrimaryProfileNonPublic"; CompilePrimaryDexOk("speed-profile", DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); } TEST_F(DexoptTest, DexoptPrimaryProfilePublic) { LOG(INFO) << "DexoptPrimaryProfilePublic"; CompilePrimaryDexOk("speed-profile", DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC | DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); } TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { LOG(INFO) << "DexoptPrimaryBackgroundOk"; CompilePrimaryDexOk("speed-profile", DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); } TEST_F(DexoptTest, ResolveStartupConstStrings) { LOG(INFO) << "DexoptDex2oatResolveStartupStrings"; const std::string property = "persist.device_config.runtime.dex2oat_resolve_startup_strings"; const std::string previous_value = android::base::GetProperty(property, ""); auto restore_property = android::base::make_scope_guard([=]() { android::base::SetProperty(property, previous_value); }); std::string odex = GetPrimaryDexArtifact(app_oat_dir_.c_str(), apk_path_, "odex"); // Disable the property to start. bool found_disable = false; ASSERT_TRUE(android::base::SetProperty(property, "false")) << property; CompilePrimaryDexOk("speed-profile", DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); run_cmd_and_process_output( "oatdump --header-only --oat-file=" + odex, [&](const std::string& line) { if (line.find("--resolve-startup-const-strings=false") != std::string::npos) { found_disable = true; } }); EXPECT_TRUE(found_disable); // Enable the property and inspect that .art artifact is larger. bool found_enable = false; ASSERT_TRUE(android::base::SetProperty(property, "true")) << property; CompilePrimaryDexOk("speed-profile", DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); run_cmd_and_process_output( "oatdump --header-only --oat-file=" + odex, [&](const std::string& line) { if (line.find("--resolve-startup-const-strings=true") != std::string::npos) { found_enable = true; } }); EXPECT_TRUE(found_enable); } class PrimaryDexReCompilationTest : public DexoptTest { public: virtual void SetUp() { DexoptTest::SetUp(); CompilePrimaryDexOk("verify", DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FROM_SCRATCH); std::string odex = GetSecondaryDexArtifact(apk_path_, "odex"); std::string vdex = GetSecondaryDexArtifact(apk_path_, "vdex"); first_compilation_odex_fd_.reset(open(odex.c_str(), O_RDONLY)); first_compilation_vdex_fd_.reset(open(vdex.c_str(), O_RDONLY)); } virtual void TearDown() { first_compilation_odex_fd_.reset(-1); first_compilation_vdex_fd_.reset(-1); DexoptTest::TearDown(); } protected: unique_fd first_compilation_odex_fd_; unique_fd first_compilation_vdex_fd_; }; TEST_F(PrimaryDexReCompilationTest, DexoptPrimaryUpdateInPlaceVdex) { LOG(INFO) << "DexoptPrimaryUpdateInPlaceVdex"; CompilePrimaryDexOk("verify", DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PUBLIC, app_oat_dir_.c_str(), kTestAppGid, DEX2OAT_FOR_BOOT_IMAGE); } class ReconcileTest : public DexoptTest { virtual void SetUp() { DexoptTest::SetUp(); CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ true, /*compile_ok*/ true); CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true); } }; TEST_F(ReconcileTest, ReconcileSecondaryCeExists) { LOG(INFO) << "ReconcileSecondaryCeExists"; reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE, /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false); } TEST_F(ReconcileTest, ReconcileSecondaryCeLinkExists) { LOG(INFO) << "ReconcileSecondaryCeLinkExists"; reconcile_secondary_dex(secondary_dex_ce_link_, FLAG_STORAGE_CE, /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false); } TEST_F(ReconcileTest, ReconcileSecondaryDeExists) { LOG(INFO) << "ReconcileSecondaryDeExists"; reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE, /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false); } TEST_F(ReconcileTest, ReconcileSecondaryDeDoesNotExist) { LOG(INFO) << "ReconcileSecondaryDeDoesNotExist"; run_cmd("rm -rf " + secondary_dex_de_); reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE, /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ true); } TEST_F(ReconcileTest, ReconcileSecondaryStorageValidationError) { // Validation errors will not clean the odex/vdex/art files but will mark // the file as non existent so that the PM knows it should purge it from its // records. LOG(INFO) << "ReconcileSecondaryStorageValidationError"; reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_DE, /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false); } TEST_F(ReconcileTest, ReconcileSecondaryAppOwnershipValidationError) { LOG(INFO) << "ReconcileSecondaryAppOwnershipValidationError"; // Attempt to reconcile the dex files of the test app from a different app. std::string another_app = "another.app"; reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE, /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid, &another_app); } TEST_F(ReconcileTest, ReconcileSecondaryAcessViaDifferentUidError) { LOG(INFO) << "ReconcileSecondaryAcessViaDifferentUidError"; reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE, /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid); } class ProfileTest : public DexoptTest { protected: std::string cur_profile_; std::string ref_profile_; std::string snap_profile_; static constexpr const char* kPrimaryProfile = "primary.prof"; virtual void SetUp() { DexoptTest::SetUp(); cur_profile_ = create_current_profile_path( kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/ false); ref_profile_ = create_reference_profile_path(package_name_, kPrimaryProfile, /*is_secondary_dex*/ false); snap_profile_ = create_snapshot_profile_path(package_name_, kPrimaryProfile); } void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode, int32_t num_dex) { run_cmd("profman --generate-test-profile-seed=" + std::to_string(num_dex) + " --generate-test-profile-num-dex=" + std::to_string(num_dex) + " --generate-test-profile=" + path); ::chmod(path.c_str(), mode); ::chown(path.c_str(), uid, gid); } void SetupProfiles(bool setup_ref) { SetupProfile(cur_profile_, kTestAppUid, kTestAppGid, 0600, 1); if (setup_ref) { SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0600, 2); } } void createProfileSnapshot(int32_t appid, const std::string& package_name, bool expected_result) { bool result; ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot( appid, package_name, kPrimaryProfile, apk_path_, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { // Do not check the files if we expect to fail. return; } // Check that the snapshot was created witht he expected acess flags. CheckFileAccess(snap_profile_, kSystemUid, kSystemGid, 0600 | S_IFREG); // The snapshot should be equivalent to the merge of profiles. std::string expected_profile_content = snap_profile_ + ".expected"; run_cmd("rm -f " + expected_profile_content); run_cmd("touch " + expected_profile_content); run_cmd("profman --profile-file=" + cur_profile_ + " --profile-file=" + ref_profile_ + " --reference-profile-file=" + expected_profile_content + " --apk=" + apk_path_); ASSERT_TRUE(AreFilesEqual(expected_profile_content, snap_profile_)); pid_t pid = fork(); if (pid == 0) { /* child */ TransitionToSystemServer(); // System server should be able to open the the spanshot. unique_fd fd(open(snap_profile_.c_str(), O_RDONLY)); ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno); _exit(0); } /* parent */ ASSERT_TRUE(WIFEXITED(wait_child(pid))); } void mergePackageProfiles(const std::string& package_name, const std::string& code_path, bool expected_result) { bool result; ASSERT_BINDER_SUCCESS(service_->mergeProfiles( kTestAppUid, package_name, code_path, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { // Do not check the files if we expect to fail. return; } // Check that the snapshot was created witht he expected acess flags. CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0600 | S_IFREG); // The snapshot should be equivalent to the merge of profiles. std::string ref_profile_content = ref_profile_ + ".expected"; run_cmd("rm -f " + ref_profile_content); run_cmd("touch " + ref_profile_content); run_cmd("profman --profile-file=" + cur_profile_ + " --profile-file=" + ref_profile_ + " --reference-profile-file=" + ref_profile_content); ASSERT_TRUE(AreFilesEqual(ref_profile_content, ref_profile_)); } // TODO(calin): add dex metadata tests once the ART change is merged. void preparePackageProfile(const std::string& package_name, const std::string& profile_name, bool expected_result) { bool result; ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( package_name, kTestUserId, kTestAppId, profile_name, apk_path_, /*dex_metadata*/ nullptr, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { // Do not check the files if we expect to fail. return; } std::string code_path_cur_prof = create_current_profile_path( kTestUserId, package_name, profile_name, /*is_secondary_dex*/ false); std::string code_path_ref_profile = create_reference_profile_path(package_name, profile_name, /*is_secondary_dex*/ false); // Check that we created the current profile. CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG); // Without dex metadata we don't generate a reference profile. ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK)); } protected: void TransitionToSystemServer() { ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid)); int32_t res = selinux_android_setcon("u:r:system_server:s0"); ASSERT_EQ(0, res) << "Failed to setcon " << strerror(errno); } bool AreFilesEqual(const std::string& file1, const std::string& file2) { std::vector content1; std::vector content2; if (!ReadAll(file1, &content1)) return false; if (!ReadAll(file2, &content2)) return false; return content1 == content2; } bool ReadAll(const std::string& file, std::vector* content) { unique_fd fd(open(file.c_str(), O_RDONLY)); if (fd < 0) { PLOG(ERROR) << "Failed to open " << file; return false; } struct stat st; if (fstat(fd, &st) != 0) { PLOG(ERROR) << "Failed to stat " << file; return false; } content->resize(st.st_size); bool result = ReadFully(fd, content->data(), content->size()); if (!result) { PLOG(ERROR) << "Failed to read " << file; } return result; } }; TEST_F(ProfileTest, ProfileSnapshotOk) { LOG(INFO) << "ProfileSnapshotOk"; SetupProfiles(/*setup_ref*/ true); createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true); } // The reference profile is created on the fly. We need to be able to // snapshot without one. TEST_F(ProfileTest, ProfileSnapshotOkNoReference) { LOG(INFO) << "ProfileSnapshotOkNoReference"; SetupProfiles(/*setup_ref*/ false); createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true); } TEST_F(ProfileTest, ProfileSnapshotFailWrongPackage) { LOG(INFO) << "ProfileSnapshotFailWrongPackage"; SetupProfiles(/*setup_ref*/ true); createProfileSnapshot(kTestAppId, "not.there", /*expected_result*/ false); } TEST_F(ProfileTest, ProfileSnapshotDestroySnapshot) { LOG(INFO) << "ProfileSnapshotDestroySnapshot"; SetupProfiles(/*setup_ref*/ true); createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true); ASSERT_BINDER_SUCCESS(service_->destroyProfileSnapshot(package_name_, kPrimaryProfile)); struct stat st; ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st)); ASSERT_EQ(ENOENT, errno); } TEST_F(ProfileTest, ProfileMergeOk) { LOG(INFO) << "ProfileMergeOk"; SetupProfiles(/*setup_ref*/ true); mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true); } // The reference profile is created on the fly. We need to be able to // merge without one. TEST_F(ProfileTest, ProfileMergeOkNoReference) { LOG(INFO) << "ProfileMergeOkNoReference"; SetupProfiles(/*setup_ref*/ false); mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true); } TEST_F(ProfileTest, ProfileMergeFailWrongPackage) { LOG(INFO) << "ProfileMergeFailWrongPackage"; SetupProfiles(/*setup_ref*/ true); mergePackageProfiles("not.there", "primary.prof", /*expected_result*/ false); } TEST_F(ProfileTest, ProfileDirOk) { LOG(INFO) << "ProfileDirOk"; std::string cur_profile_dir = create_primary_current_profile_package_dir_path( kTestUserId, package_name_); std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/false); std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_); CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR); CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR); } // Verify that the profile directories are fixed up during an upgrade. // (The reference profile directory is prepared lazily). TEST_F(ProfileTest, ProfileDirOkAfterFixup) { LOG(INFO) << "ProfileDirOkAfterFixup"; std::string cur_profile_dir = create_primary_current_profile_package_dir_path( kTestUserId, package_name_); std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/false); std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_); // Simulate a pre-P setup by changing the owner to kTestAppGid and permissions to 0700. ASSERT_EQ(0, chown(ref_profile_dir.c_str(), kTestAppGid, kTestAppGid)); ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700)); // Run createAppData again which will offer to fix-up the profile directories. ASSERT_BINDER_SUCCESS(service_->createAppData( volume_uuid_, package_name_, kTestUserId, kAppDataFlags, kTestAppUid, se_info_, kOSdkVersion, &ce_data_inode_)); // Check the file access. CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR); CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR); } TEST_F(ProfileTest, ProfilePrepareOk) { LOG(INFO) << "ProfilePrepareOk"; preparePackageProfile(package_name_, "split.prof", /*expected_result*/ true); } TEST_F(ProfileTest, ProfilePrepareFailInvalidPackage) { LOG(INFO) << "ProfilePrepareFailInvalidPackage"; preparePackageProfile("not.there.package", "split.prof", /*expected_result*/ false); } TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { LOG(INFO) << "ProfilePrepareFailProfileChangedUid"; SetupProfiles(/*setup_ref*/ false); // Change the uid on the profile to trigger a failure. ::chown(cur_profile_.c_str(), kTestAppUid + 1, kTestAppGid + 1); preparePackageProfile(package_name_, "primary.prof", /*expected_result*/ false); } class BootProfileTest : public ProfileTest { public: virtual void setup() { ProfileTest::SetUp(); intial_android_profiles_dir = android_profiles_dir; } virtual void TearDown() { android_profiles_dir = intial_android_profiles_dir; ProfileTest::TearDown(); } void UpdateAndroidProfilesDir(const std::string& profile_dir) { android_profiles_dir = profile_dir; // We need to create the reference profile directory in the new profile dir. run_cmd("mkdir -p " + profile_dir + "/ref"); } void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) { bool result; ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot( -1, "android", "android.prof", classpath, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { // Do not check the files if we expect to fail. return; } // Check that the snapshot was created with he expected access flags. const std::string boot_profile = create_snapshot_profile_path("android", "android.prof"); CheckFileAccess(boot_profile, kSystemUid, kSystemGid, 0600 | S_IFREG); pid_t pid = fork(); if (pid == 0) { /* child */ TransitionToSystemServer(); // System server should be able to open the snapshot. unique_fd fd(open(boot_profile.c_str(), O_RDONLY)); ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno); _exit(0); } /* parent */ ASSERT_TRUE(WIFEXITED(wait_child(pid))); } protected: std::string intial_android_profiles_dir; }; TEST_F(BootProfileTest, BootProfileSnapshotOk) { LOG(INFO) << "BootProfileSnapshotOk"; char* boot_classpath = getenv("BOOTCLASSPATH"); ASSERT_TRUE(boot_classpath != nullptr); createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true); } TEST_F(BootProfileTest, BootProfileSnapshotFailEmptyClasspath) { LOG(INFO) << "BootProfileSnapshotFailEmptyClasspath"; createBootImageProfileSnapshot(/*boot_classpath*/ "", /*expected_result*/ false); } TEST_F(BootProfileTest, BootProfileSnapshotOkNoProfiles) { LOG(INFO) << "BootProfileSnapshotOkNoProfiles"; char* boot_classpath = getenv("BOOTCLASSPATH"); ASSERT_TRUE(boot_classpath != nullptr); // The app_apk_dir has no profiles. So we shouldn't be able to merge anything. // Still, this is not a failure case. UpdateAndroidProfilesDir(app_apk_dir_); createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true); } // Verify that profile collection. TEST_F(BootProfileTest, CollectProfiles) { LOG(INFO) << "CollectProfiles"; // Create some profile directories mimicking the real profile structure. run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/ref"); run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/0/"); run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/1/"); // Create an empty profile. run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/1/primary.prof"); // Create a random file. run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/0/non.profile.file"); // Create some non-empty profiles. std::string current_prof = app_private_dir_de_ + "/profiles/cur/0/primary.prof"; run_cmd("echo 1 > " + current_prof); std::string ref_prof = app_private_dir_de_ + "/profiles/ref/primary.prof"; run_cmd("echo 1 > " + ref_prof); UpdateAndroidProfilesDir(app_private_dir_de_ + "/profiles"); std::vector profiles; collect_profiles(&profiles); // Only two profiles should be in the output. ASSERT_EQ(2u, profiles.size()); ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), current_prof) != profiles.end()); ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end()); } TEST_F(DexoptTest, select_execution_binary) { LOG(INFO) << "DexoptTestselect_execution_binary"; std::string release_str = app_private_dir_ce_ + "/release"; std::string debug_str = app_private_dir_ce_ + "/debug"; // Setup the binaries. Note that we only need executable files to actually // test the execution binary selection run_cmd("touch " + release_str); run_cmd("touch " + debug_str); run_cmd("chmod 777 " + release_str); run_cmd("chmod 777 " + debug_str); const char* release = release_str.c_str(); const char* debug = debug_str.c_str(); ASSERT_STREQ(release, select_execution_binary( release, debug, /*background_job_compile=*/ false, /*is_debug_runtime=*/ false, /*is_release=*/ false, /*is_debuggable_build=*/ false)); ASSERT_STREQ(release, select_execution_binary( release, debug, /*background_job_compile=*/ true, /*is_debug_runtime=*/ false, /*is_release=*/ true, /*is_debuggable_build=*/ true)); ASSERT_STREQ(debug, select_execution_binary( release, debug, /*background_job_compile=*/ false, /*is_debug_runtime=*/ true, /*is_release=*/ false, /*is_debuggable_build=*/ false)); ASSERT_STREQ(debug, select_execution_binary( release, debug, /*background_job_compile=*/ true, /*is_debug_runtime=*/ false, /*is_release=*/ false, /*is_debuggable_build=*/ true)); // Select the release when the debug file is not there. ASSERT_STREQ(release, select_execution_binary( release, "does_not_exist", /*background_job_compile=*/ false, /*is_debug_runtime=*/ true, /*is_release=*/ false, /*is_debuggable_build=*/ false)); } } // namespace installd } // namespace android cmds/installd/tests/installd_dexopt_test.xml0100644 0000000 0000000 00000003116 13756501734 020501 0ustar000000000 0000000 cmds/installd/tests/installd_otapreopt_test.cpp0100644 0000000 0000000 00000020156 13756501734 021200 0ustar000000000 0000000 /** * Copyright (C) 2018 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. */ #include #include #include #include #include "installd_constants.h" #include "otapreopt_parameters.h" namespace android { namespace installd { static bool ParseBool(const char* in) { if (strcmp(in, "true") == 0) { return true; } return false; } static const char* ParseNull(const char* arg) { return (strcmp(arg, "!") == 0) ? nullptr : arg; } class OTAPreoptTest : public testing::Test { protected: virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:f", 1); android::base::InitLogging(nullptr, android::base::StderrLogger); } void verifyPackageParameters(const OTAPreoptParameters& params, uint32_t version, bool versioned, const char** args) { // otapreopt target-slot [version] dexopt {DEXOPT_PARAMETERS} int i = 0; if (version > 2 || (version == 2 && versioned)) { i += 4; } else { i += 3; } ASSERT_STREQ(params.target_slot.c_str(), args[1]); ASSERT_STREQ(params.apk_path, args[i++]); ASSERT_EQ(params.uid, static_cast(atoi(args[i++]))); ASSERT_STREQ(params.pkgName, args[i++]); ASSERT_STREQ(params.instruction_set, args[i++]); ASSERT_EQ(params.dexopt_needed, atoi(args[i++])); ASSERT_STREQ(params.oat_dir, args[i++]); const int dexopt_flags = atoi(args[i++]); ASSERT_STREQ(params.compiler_filter, args[i++]); ASSERT_STREQ(params.volume_uuid, ParseNull(args[i++])); ASSERT_STREQ(params.shared_libraries, ParseNull(args[i++])); if (version > 1) { ASSERT_STREQ(params.se_info, ParseNull(args[i++])); } else { ASSERT_EQ(params.se_info, nullptr); } if (version > 2) { ASSERT_EQ(params.downgrade, ParseBool(args[i++])); } else { ASSERT_FALSE(params.downgrade); } if (version > 3) { ASSERT_EQ(params.target_sdk_version, atoi(args[i++])); } else { ASSERT_EQ(params.target_sdk_version, 0); } if (version > 4) { ASSERT_STREQ(params.profile_name, ParseNull(args[i++])); } else { ASSERT_STREQ(params.profile_name, "primary.prof"); } if (version > 5) { ASSERT_STREQ(params.dex_metadata_path, ParseNull(args[i++])); } else { ASSERT_EQ(params.dex_metadata_path, nullptr); } if (version > 6) { ASSERT_STREQ(params.compilation_reason, ParseNull(args[i++])); } else { ASSERT_STREQ(params.compilation_reason, "ab-ota"); } if (version > 7) { ASSERT_EQ(params.dexopt_flags, dexopt_flags); } else { ASSERT_EQ(params.dexopt_flags, dexopt_flags | DEXOPT_GENERATE_COMPACT_DEX); } } const char* getVersionCStr(uint32_t version) { switch (version) { case 1: return "1"; case 2: return "2"; case 3: return "3"; case 4: return "4"; case 5: return "5"; case 6: return "6"; case 7: return "7"; case 8: return "8"; case 9: return "9"; case 10: return "10"; } return nullptr; } std::vector getArgs(uint32_t version, bool versioned, const char* shared_libs = "shared.lib") { std::vector args; args.push_back("otapreopt"); // "otapreopt" args.push_back("a"); // slot if (versioned) { args.push_back(getVersionCStr(version)); } args.push_back("dexopt"); // "dexopt" args.push_back("foo.apk"); // apk_path args.push_back("123"); // uid args.push_back("pkgname"); // pkg args.push_back("arm"); // isa args.push_back("1"); // dexopt_needed (DEX2OAT_FROM_SCRATCH) args.push_back("oat_dir"); // oat_dir args.push_back("0"); // dexopt_flags args.push_back("speed"); // filter args.push_back("!"); // volume args.push_back(shared_libs); // libs if (version > 1) { args.push_back("!"); // seinfo } if (version > 2) { args.push_back("true"); // downgrade } if (version > 3) { args.push_back("28"); // sdk_version } if (version > 4) { args.push_back("split_a.prof"); // profile_name } if (version > 5) { args.push_back("dex_metadata.dm"); // dex_metadata_path } if (version > 6) { args.push_back("ab-ota-test"); // compilation_reason } args.push_back(nullptr); // we have to end with null. return args; } void VerifyReadArguments(uint32_t version, bool versioned, const char* shared_libs = "shared.lib") { OTAPreoptParameters params; std::vector args = getArgs(version, versioned, shared_libs); ASSERT_TRUE(params.ReadArguments(args.size() - 1, args.data())); verifyPackageParameters(params, version, versioned, args.data()); } }; TEST_F(OTAPreoptTest, ReadArgumentsV1) { VerifyReadArguments(1, false); } TEST_F(OTAPreoptTest, ReadArgumentsV2Unversioned) { VerifyReadArguments(2, false); } TEST_F(OTAPreoptTest, ReadArgumentsV2) { VerifyReadArguments(2, true); } TEST_F(OTAPreoptTest, ReadArgumentsV3) { VerifyReadArguments(3, true); } TEST_F(OTAPreoptTest, ReadArgumentsV4) { VerifyReadArguments(4, true); } TEST_F(OTAPreoptTest, ReadArgumentsV5) { VerifyReadArguments(5, true); } TEST_F(OTAPreoptTest, ReadArgumentsV6) { VerifyReadArguments(6, true); } TEST_F(OTAPreoptTest, ReadArgumentsV7) { VerifyReadArguments(7, true); } TEST_F(OTAPreoptTest, ReadArgumentsV9SharedLibsAmpersand) { OTAPreoptParameters params; std::vector args = getArgs(9, true, "&"); ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); } TEST_F(OTAPreoptTest, ReadArgumentsV10SharedLibsAmpersand) { OTAPreoptParameters params; std::vector args = getArgs(10, true, "&"); ASSERT_TRUE(params.ReadArguments(args.size() - 1, args.data())); } TEST_F(OTAPreoptTest, ReadArgumentsFailToManyArgs) { OTAPreoptParameters params; std::vector args = getArgs(5, true); args[2] = "3"; // pretend it's version 3. It should fail since there are too many args. ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); } TEST_F(OTAPreoptTest, ReadArgumentsFailInsufficientArgs) { OTAPreoptParameters params; std::vector args = getArgs(4, true); args[2] = "5"; // pretend it's version 5. It should fail since there are insufficient args. ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); } TEST_F(OTAPreoptTest, ReadArgumentsFailInvalidDexopt) { OTAPreoptParameters params; std::vector args = getArgs(4, true); args[3] = "dexopt-invalid"; ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); } TEST_F(OTAPreoptTest, ReadArgumentsFailInvalidSlot) { OTAPreoptParameters params; std::vector args = getArgs(3, true); args[1] = "invalid-slot???"; ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); } } // namespace installd } // namespace android cmds/installd/tests/installd_service_test.cpp0100644 0000000 0000000 00000065600 13756501734 020626 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "binder_test_utils.h" #include "InstalldNativeService.h" #include "dexopt.h" #include "globals.h" #include "utils.h" using android::base::StringPrintf; namespace android { namespace installd { constexpr const char* kTestUuid = "TEST"; #define FLAG_FORCE InstalldNativeService::FLAG_FORCE int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); } bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set) { return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set); } bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { return calculate_odex_file_path_default(path, apk_path, instruction_set); } bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { return create_cache_path_default(path, src, instruction_set); } static std::string get_full_path(const char* path) { return StringPrintf("/data/local/tmp/user/0/%s", path); } static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { const std::string fullPath = get_full_path(path); EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0); EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0); EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0); } static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode); EXPECT_NE(fd, -1); EXPECT_EQ(::fchown(fd, owner, group), 0); EXPECT_EQ(::fchmod(fd, mode), 0); EXPECT_EQ(::close(fd), 0); } static int stat_gid(const char* path) { struct stat buf; EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0); return buf.st_gid; } static int stat_mode(const char* path) { struct stat buf; EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0); return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); } class ServiceTest : public testing::Test { protected: InstalldNativeService* service; std::unique_ptr testUuid; virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); service = new InstalldNativeService(); testUuid = std::make_unique(); *testUuid = std::string(kTestUuid); system("mkdir -p /data/local/tmp/user/0"); init_globals_from_data_and_root(); } virtual void TearDown() { delete service; system("rm -rf /data/local/tmp/user"); } }; TEST_F(ServiceTest, FixupAppData_Upgrade) { LOG(INFO) << "FixupAppData_Upgrade"; mkdir("com.example", 10000, 10000, 0700); mkdir("com.example/normal", 10000, 10000, 0700); mkdir("com.example/cache", 10000, 10000, 0700); touch("com.example/cache/file", 10000, 10000, 0700); service->fixupAppData(testUuid, 0); EXPECT_EQ(10000, stat_gid("com.example/normal")); EXPECT_EQ(20000, stat_gid("com.example/cache")); EXPECT_EQ(20000, stat_gid("com.example/cache/file")); EXPECT_EQ(0700, stat_mode("com.example/normal")); EXPECT_EQ(02771, stat_mode("com.example/cache")); EXPECT_EQ(0700, stat_mode("com.example/cache/file")); } TEST_F(ServiceTest, FixupAppData_Moved) { LOG(INFO) << "FixupAppData_Moved"; mkdir("com.example", 10000, 10000, 0700); mkdir("com.example/foo", 10000, 10000, 0700); touch("com.example/foo/file", 10000, 20000, 0700); mkdir("com.example/bar", 10000, 20000, 0700); touch("com.example/bar/file", 10000, 20000, 0700); service->fixupAppData(testUuid, 0); EXPECT_EQ(10000, stat_gid("com.example/foo")); EXPECT_EQ(20000, stat_gid("com.example/foo/file")); EXPECT_EQ(10000, stat_gid("com.example/bar")); EXPECT_EQ(10000, stat_gid("com.example/bar/file")); service->fixupAppData(testUuid, FLAG_FORCE); EXPECT_EQ(10000, stat_gid("com.example/foo")); EXPECT_EQ(10000, stat_gid("com.example/foo/file")); EXPECT_EQ(10000, stat_gid("com.example/bar")); EXPECT_EQ(10000, stat_gid("com.example/bar/file")); } TEST_F(ServiceTest, HashSecondaryDex) { LOG(INFO) << "HashSecondaryDex"; mkdir("com.example", 10000, 10000, 0700); mkdir("com.example/foo", 10000, 10000, 0700); touch("com.example/foo/file", 10000, 20000, 0700); std::vector result; std::string dexPath = get_full_path("com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); EXPECT_EQ(result.size(), 32U); std::ostringstream output; output << std::hex << std::setfill('0'); for (auto b : result) { output << std::setw(2) << +b; } // This is the SHA256 of an empty string (sha256sum /dev/null) EXPECT_EQ(output.str(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); } TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { LOG(INFO) << "HashSecondaryDex_NoSuch"; std::vector result; std::string dexPath = get_full_path("com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); EXPECT_EQ(result.size(), 0U); } TEST_F(ServiceTest, HashSecondaryDex_Unreadable) { LOG(INFO) << "HashSecondaryDex_Unreadable"; mkdir("com.example", 10000, 10000, 0700); mkdir("com.example/foo", 10000, 10000, 0700); touch("com.example/foo/file", 10000, 20000, 0300); std::vector result; std::string dexPath = get_full_path("com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); EXPECT_EQ(result.size(), 0U); } TEST_F(ServiceTest, HashSecondaryDex_WrongApp) { LOG(INFO) << "HashSecondaryDex_WrongApp"; mkdir("com.example", 10000, 10000, 0700); mkdir("com.example/foo", 10000, 10000, 0700); touch("com.example/foo/file", 10000, 20000, 0700); std::vector result; std::string dexPath = get_full_path("com.example/foo/file"); EXPECT_BINDER_FAIL(service->hashSecondaryDexFile( dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result)); } TEST_F(ServiceTest, CalculateOat) { char buf[PKG_PATH_MAX]; EXPECT_TRUE(calculate_oat_file_path(buf, "/path/to/oat", "/path/to/file.apk", "isa")); EXPECT_EQ("/path/to/oat/isa/file.odex", std::string(buf)); EXPECT_FALSE(calculate_oat_file_path(buf, "/path/to/oat", "/path/to/file", "isa")); EXPECT_FALSE(calculate_oat_file_path(buf, "/path/to/oat", "file", "isa")); } TEST_F(ServiceTest, CalculateOdex) { char buf[PKG_PATH_MAX]; EXPECT_TRUE(calculate_odex_file_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/path/to/oat/isa/file.odex", std::string(buf)); } TEST_F(ServiceTest, CalculateCache) { char buf[PKG_PATH_MAX]; EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { return true; } if (!mkdirs(android::base::Dirname(path), mode)) { return false; } if (::mkdir(path.c_str(), mode) != 0) { PLOG(DEBUG) << "Failed to create folder " << path; return false; } return true; } class AppDataSnapshotTest : public testing::Test { private: std::string rollback_ce_base_dir; std::string rollback_de_base_dir; protected: InstalldNativeService* service; std::string fake_package_ce_path; std::string fake_package_de_path; virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); service = new InstalldNativeService(); ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700)); init_globals_from_data_and_root(); rollback_ce_base_dir = create_data_misc_ce_rollback_base_path("TEST", 0); rollback_de_base_dir = create_data_misc_de_rollback_base_path("TEST", 0); fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(rollback_ce_base_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_base_dir, 0700)); ASSERT_TRUE(mkdirs(fake_package_ce_path, 0700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 0700)); } virtual void TearDown() { ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_ce_base_dir, true)); ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_de_base_dir, true)); ASSERT_EQ(0, delete_dir_contents(fake_package_ce_path, true)); ASSERT_EQ(0, delete_dir_contents(fake_package_de_path, true)); delete service; ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user/0", true)); } }; TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 37); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 37); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the CE content but not the DE content. int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode)); struct stat buf; memset(&buf, 0, sizeof(buf)); ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf)); ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino); std::string ce_content, de_content; // At this point, we should have the CE content but not the DE content. ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_FALSE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", ce_content); // Modify the CE content, so we can assert later that it's reflected // in the snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the DE content but not the CE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode)); // Only DE content snapshot was requested. ASSERT_EQ(ce_snapshot_inode, 0); // At this point, both the CE as well as the DE content should be fully // populated. ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", ce_content); ASSERT_EQ("TEST_CONTENT_DE", de_content); // Modify the DE content, so we can assert later that it's reflected // in our final snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of both the CE as well as the DE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content); ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content); } TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_TwoSnapshotsWithTheSameId) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 67); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 67); auto another_fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.bar"); auto another_fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.bar"); // Since this test sets up data for another package, some bookkeeping is required. auto deleter = [&]() { ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_ce_path, true)); ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_de_path, true)); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_TRUE(mkdirs(another_fake_package_ce_path, 0700)); ASSERT_TRUE(mkdirs(another_fake_package_de_path, 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "ANOTHER_TEST_CONTENT_CE", another_fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "ANOTHER_TEST_CONTENT_DE", another_fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Request snapshot for the package com.foo. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Now request snapshot with the same id for the package com.bar ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Check that both snapshots have correct data in them. std::string com_foo_ce_content, com_foo_de_content; std::string com_bar_ce_content, com_bar_de_content; ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &com_foo_ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &com_foo_de_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.bar/file1", &com_bar_ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.bar/file1", &com_bar_de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", com_foo_ce_content); ASSERT_EQ("TEST_CONTENT_DE", com_foo_de_content); ASSERT_EQ("ANOTHER_TEST_CONTENT_CE", com_bar_ce_content); ASSERT_EQ("ANOTHER_TEST_CONTENT_DE", com_bar_de_content); } TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_AppDataAbsent) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 73); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 73); // Similuating app data absence. ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_ce_path, true)); ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true)); int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 73, FLAG_STORAGE_DE, nullptr)); // No CE content snapshot was performed. ASSERT_EQ(ce_snapshot_inode, 0); // The snapshot calls must succeed but there should be no snapshot // created. struct stat sb; ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); } TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { auto rollback_ce_dir = create_data_misc_ce_rollback_package_path("TEST", 0, 13, "com.foo"); auto rollback_de_dir = create_data_misc_de_rollback_package_path("TEST", 0, 13, "com.foo"); ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); // Simulate presence of an existing snapshot ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", rollback_ce_dir + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", rollback_de_dir + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Create app data. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_2_CE", fake_package_ce_path + "/file2", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_2_DE", fake_package_de_path + "/file2", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Previous snapshot (with data for file1) must be cleared. struct stat sb; ASSERT_EQ(-1, stat((rollback_ce_dir + "/file1").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/file1").c_str(), &sb)); // New snapshot (with data for file2) must be present. ASSERT_NE(-1, stat((rollback_ce_dir + "/file2").c_str(), &sb)); ASSERT_NE(-1, stat((rollback_de_dir + "/file2").c_str(), &sb)); } TEST_F(AppDataSnapshotTest, SnapshotAppData_WrongVolumeUuid) { // Setup rollback folders to make sure that fails due to wrong volumeUuid being // passed, not because of some other reason. auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 17); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 17); ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique("FOO"), "com.foo", 0, 17, FLAG_STORAGE_DE, nullptr)); } TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsCache) { auto fake_package_ce_cache_path = fake_package_ce_path + "/cache"; auto fake_package_ce_code_cache_path = fake_package_ce_path + "/code_cache"; auto fake_package_de_cache_path = fake_package_de_path + "/cache"; auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache"; ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 0700)); ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 0700)); ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 0700)); ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_code_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); // The snapshot call must clear cache. struct stat sb; ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb)); ASSERT_EQ(-1, stat((fake_package_ce_code_cache_path + "/file1").c_str(), &sb)); ASSERT_EQ(-1, stat((fake_package_de_cache_path + "/file1").c_str(), &sb)); ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb)); } TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 239); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 239); ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); // Write contents to the rollback location. We'll write the same files to the // app data location and make sure the restore has overwritten them. ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique("TEST"), "com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); std::string ce_content, de_content; ASSERT_TRUE(android::base::ReadFileToString( fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("CE_RESTORE_CONTENT", ce_content); ASSERT_EQ("DE_RESTORE_CONTENT", de_content); } TEST_F(AppDataSnapshotTest, CreateSnapshotThenDestroyIt) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 57); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 57); // Prepare data for snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); int64_t ce_snapshot_inode; // Request a snapshot of both the CE as well as the DE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique("TEST"), "com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); // Because CE data snapshot was requested, ce_snapshot_inode can't be null. ASSERT_NE(0, ce_snapshot_inode); // Check snapshot is there. struct stat sb; ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique("TEST"), "com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); } TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 1543); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 1543); // Create a snapshot ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique("TEST"), "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. struct stat sb; ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); // Check that deleting already deleted snapshot is no-op. ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique("TEST"), "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); } TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that test fails due to wrong volumeUuid // being passed, not because of some other reason. auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 43); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 43); ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique("BAR"), "com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk()); } TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that fails due to wrong volumeUuid being // passed, not because of some other reason. auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 41); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 41); ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique("BAR"), "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE)); } } // namespace installd } // namespace android cmds/installd/tests/installd_service_test.xml0100644 0000000 0000000 00000003023 13756501734 020633 0ustar000000000 0000000 cmds/installd/tests/installd_utils_test.cpp0100644 0000000 0000000 00000066400 13756501734 020325 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #include #include #include #include #include #include "InstalldNativeService.h" #include "MatchExtensionGen.h" #include "globals.h" #include "utils.h" #undef LOG_TAG #define LOG_TAG "utils_test" #define TEST_DATA_DIR "/data/" #define TEST_ROOT_DIR "/system/" #define TEST_APP_DIR "/data/app/" #define TEST_APP_PRIVATE_DIR "/data/app-private/" #define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/" #define TEST_ASEC_DIR "/mnt/asec/" #define TEST_EXPAND_DIR "/mnt/expand/00000000-0000-0000-0000-000000000000/" #define TEST_SYSTEM_DIR1 "/system/app/" #define TEST_SYSTEM_DIR2 "/vendor/app/" #define TEST_PROFILE_DIR "/data/misc/profiles" namespace android { namespace installd { class UtilsTest : public testing::Test { protected: virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); init_globals_from_data_and_root(TEST_DATA_DIR, TEST_ROOT_DIR); } virtual void TearDown() { } std::string create_too_long_path(const std::string& seed) { std::string result = seed; for (size_t i = seed.size(); i < PKG_PATH_MAX; i++) { result += "a"; } return result; } }; TEST_F(UtilsTest, IsValidApkPath_BadPrefix) { // Bad prefixes directories const char *badprefix1 = "/etc/passwd"; EXPECT_EQ(-1, validate_apk_path(badprefix1)) << badprefix1 << " should not be allowed as a valid path"; const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah"; EXPECT_EQ(-1, validate_apk_path(badprefix2)) << badprefix2 << " should not be allowed as a valid path"; const char *badprefix3 = "init.rc"; EXPECT_EQ(-1, validate_apk_path(badprefix3)) << badprefix3 << " should not be allowed as a valid path"; const char *badprefix4 = "/init.rc"; EXPECT_EQ(-1, validate_apk_path(badprefix4)) << badprefix4 << " should not be allowed as a valid path"; } TEST_F(UtilsTest, IsValidApkPath_Internal) { // Internal directories const char *internal1 = TEST_APP_DIR "example.apk"; EXPECT_EQ(0, validate_apk_path(internal1)) << internal1 << " should be allowed as a valid path"; // b/16888084 const char *path2 = TEST_APP_DIR "example.com/example.apk"; EXPECT_EQ(0, validate_apk_path(path2)) << path2 << " should be allowed as a valid path"; const char *badint1 = TEST_APP_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badint1)) << badint1 << " should be rejected as a invalid path"; const char *badint2 = TEST_APP_DIR "/../example.apk"; EXPECT_EQ(-1, validate_apk_path(badint2)) << badint2 << " should be rejected as a invalid path"; // Only one subdir should be allowed. const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path3)) << bad_path3 << " should be rejected as a invalid path"; const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path4)) << bad_path4 << " should be rejected as a invalid path"; const char *bad_path5 = TEST_APP_DIR "example.com1/../example.com2/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path5)) << bad_path5 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_TopDir) { EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example")); EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example")); EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example")); EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example")); } TEST_F(UtilsTest, IsValidApkPath_TopFile) { EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk")); EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk")); EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk")); EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk")); } TEST_F(UtilsTest, IsValidApkPath_OatDir) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat")); } TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64")); } TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex")); } TEST_F(UtilsTest, IsValidApkPath_Private) { // Internal directories const char *private1 = TEST_APP_PRIVATE_DIR "example.apk"; EXPECT_EQ(0, validate_apk_path(private1)) << private1 << " should be allowed as a valid path"; // b/16888084 const char *path2 = TEST_APP_DIR "example.com/example.apk"; EXPECT_EQ(0, validate_apk_path(path2)) << path2 << " should be allowed as a valid path"; const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badpriv1)) << badpriv1 << " should be rejected as a invalid path"; const char *badpriv2 = TEST_APP_PRIVATE_DIR "/../example.apk"; EXPECT_EQ(-1, validate_apk_path(badpriv2)) << badpriv2 << " should be rejected as a invalid path"; // Only one subdir should be allowed. const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path3)) << bad_path3 << " should be rejected as a invalid path"; const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path4)) << bad_path4 << " should be rejected as a invalid path"; const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path5)) << bad_path5 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_AsecGood1) { const char *asec1 = TEST_ASEC_DIR "example.apk"; EXPECT_EQ(0, validate_apk_path(asec1)) << asec1 << " should be allowed as a valid path"; } TEST_F(UtilsTest, IsValidApkPath_AsecGood2) { const char *asec2 = TEST_ASEC_DIR "com.example.asec/pkg.apk"; EXPECT_EQ(0, validate_apk_path(asec2)) << asec2 << " should be allowed as a valid path"; } TEST_F(UtilsTest, IsValidApkPath_EscapeFail) { const char *badasec1 = TEST_ASEC_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badasec1)) << badasec1 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeFail) { const char *badasec3 = TEST_ASEC_DIR "com.example.asec/../../../pkg.apk"; EXPECT_EQ(-1, validate_apk_path(badasec3)) << badasec3 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_SlashEscapeFail) { const char *badasec4 = TEST_ASEC_DIR "/../example.apk"; EXPECT_EQ(-1, validate_apk_path(badasec4)) << badasec4 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_CrazyDirFail) { const char *badasec5 = TEST_ASEC_DIR ".//../.."; EXPECT_EQ(-1, validate_apk_path(badasec5)) << badasec5 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeSingleFail) { const char *badasec6 = TEST_ASEC_DIR "com.example.asec/../pkg.apk"; EXPECT_EQ(-1, validate_apk_path(badasec6)) << badasec6 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) { const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(badasec7)) << badasec7 << " should be rejected as a invalid path"; } TEST_F(UtilsTest, CheckSystemApp_Dir1) { const char *sysapp1 = TEST_SYSTEM_DIR1 "Voice.apk"; EXPECT_EQ(0, validate_system_app_path(sysapp1)) << sysapp1 << " should be allowed as a system path"; } TEST_F(UtilsTest, CheckSystemApp_Dir2) { const char *sysapp2 = TEST_SYSTEM_DIR2 "com.example.myapp.apk"; EXPECT_EQ(0, validate_system_app_path(sysapp2)) << sysapp2 << " should be allowed as a system path"; } TEST_F(UtilsTest, CheckSystemApp_EscapeFail) { const char *badapp1 = TEST_SYSTEM_DIR1 "../com.example.apk"; EXPECT_EQ(-1, validate_system_app_path(badapp1)) << badapp1 << " should be rejected not a system path"; } TEST_F(UtilsTest, CheckSystemApp_DoubleEscapeFail) { const char *badapp2 = TEST_SYSTEM_DIR2 "/../../com.example.apk"; EXPECT_EQ(-1, validate_system_app_path(badapp2)) << badapp2 << " should be rejected not a system path"; } TEST_F(UtilsTest, CheckSystemApp_BadPathEscapeFail) { const char *badapp3 = TEST_APP_DIR "/../../com.example.apk"; EXPECT_EQ(-1, validate_system_app_path(badapp3)) << badapp3 << " should be rejected not a system path"; } TEST_F(UtilsTest, CheckSystemApp_Subdir) { const char *sysapp = TEST_SYSTEM_DIR1 "com.example/com.example.apk"; EXPECT_EQ(0, validate_system_app_path(sysapp)) << sysapp << " should be allowed as a system path"; const char *badapp = TEST_SYSTEM_DIR1 "com.example/subdir/com.example.apk"; EXPECT_EQ(-1, validate_system_app_path(badapp)) << badapp << " should be rejected not a system path"; const char *badapp1 = TEST_SYSTEM_DIR1 "com.example/subdir/../com.example.apk"; EXPECT_EQ(-1, validate_system_app_path(badapp1)) << badapp1 << " should be rejected not a system path"; const char *badapp2 = TEST_SYSTEM_DIR1 "com.example1/../com.example2/com.example.apk"; EXPECT_EQ(-1, validate_system_app_path(badapp2)) << badapp2 << " should be rejected not a system path"; } TEST_F(UtilsTest, CreateDataPath) { EXPECT_EQ("/data", create_data_path(nullptr)); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b", create_data_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b")); } TEST_F(UtilsTest, CreateDataAppPath) { EXPECT_EQ("/data/app", create_data_app_path(nullptr)); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/app", create_data_app_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b")); } TEST_F(UtilsTest, CreateDataUserPath) { EXPECT_EQ("/data/data", create_data_user_ce_path(nullptr, 0)); EXPECT_EQ("/data/user/10", create_data_user_ce_path(nullptr, 10)); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/0", create_data_user_ce_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0)); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/10", create_data_user_ce_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10)); } TEST_F(UtilsTest, CreateDataMediaPath) { EXPECT_EQ("/data/media/0", create_data_media_path(nullptr, 0)); EXPECT_EQ("/data/media/10", create_data_media_path(nullptr, 10)); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/media/0", create_data_media_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0)); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/media/10", create_data_media_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10)); } TEST_F(UtilsTest, CreateDataAppPackagePath) { EXPECT_EQ("/data/app/com.example", create_data_app_package_path(nullptr, "com.example")); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/app/com.example", create_data_app_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", "com.example")); } TEST_F(UtilsTest, CreateDataUserPackagePath) { EXPECT_EQ("/data/data/com.example", create_data_user_ce_package_path(nullptr, 0, "com.example")); EXPECT_EQ("/data/user/10/com.example", create_data_user_ce_package_path(nullptr, 10, "com.example")); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/0/com.example", create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/10/com.example", create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example")); } TEST_F(UtilsTest, IsValidPackageName) { EXPECT_EQ(true, is_valid_package_name("android")); EXPECT_EQ(true, is_valid_package_name("com.example")); EXPECT_EQ(true, is_valid_package_name("com.example-1")); EXPECT_EQ(true, is_valid_package_name("com.example-1024")); EXPECT_EQ(true, is_valid_package_name("com.example.foo---KiJFj4a_tePVw95pSrjg==")); EXPECT_EQ(true, is_valid_package_name("really_LONG.a1234.package_name")); EXPECT_EQ(false, is_valid_package_name("1234.package")); EXPECT_EQ(false, is_valid_package_name("com.1234.package")); EXPECT_EQ(false, is_valid_package_name("")); EXPECT_EQ(false, is_valid_package_name(".")); EXPECT_EQ(false, is_valid_package_name("..")); EXPECT_EQ(false, is_valid_package_name("../")); EXPECT_EQ(false, is_valid_package_name("com.example/../com.evil/")); EXPECT_EQ(false, is_valid_package_name("com.example-1/../com.evil/")); EXPECT_EQ(false, is_valid_package_name("/com.evil")); } TEST_F(UtilsTest, CreateDataUserProfilePath) { EXPECT_EQ("/data/misc/profiles/cur/0", create_primary_cur_profile_dir_path(0)); EXPECT_EQ("/data/misc/profiles/cur/1", create_primary_cur_profile_dir_path(1)); } TEST_F(UtilsTest, CreateDataUserProfilePackagePath) { EXPECT_EQ("/data/misc/profiles/cur/0/com.example", create_primary_current_profile_package_dir_path(0, "com.example")); EXPECT_EQ("/data/misc/profiles/cur/1/com.example", create_primary_current_profile_package_dir_path(1, "com.example")); } TEST_F(UtilsTest, CreateDataRefProfilePath) { EXPECT_EQ("/data/misc/profiles/ref", create_primary_ref_profile_dir_path()); } TEST_F(UtilsTest, CreateDataRefProfilePackagePath) { EXPECT_EQ("/data/misc/profiles/ref/com.example", create_primary_reference_profile_package_dir_path("com.example")); } TEST_F(UtilsTest, CreatePrimaryCurrentProfile) { std::string expected_base = create_primary_current_profile_package_dir_path(0, "com.example") + "/primary.prof"; EXPECT_EQ(expected_base, create_current_profile_path(/*user*/0, "com.example", "primary.prof", /*is_secondary*/false)); std::string expected_split = create_primary_current_profile_package_dir_path(0, "com.example") + "/split.prof"; EXPECT_EQ(expected_split, create_current_profile_path(/*user*/0, "com.example", "split.prof", /*is_secondary*/false)); } TEST_F(UtilsTest, CreatePrimaryReferenceProfile) { std::string expected_base = create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof"; EXPECT_EQ(expected_base, create_reference_profile_path("com.example", "primary.prof", /*is_secondary*/false)); std::string expected_split = create_primary_reference_profile_package_dir_path("com.example") + "/split.prof"; EXPECT_EQ(expected_split, create_reference_profile_path("com.example", "split.prof", /*is_secondary*/false)); } TEST_F(UtilsTest, CreateProfileSnapshot) { std::string expected_base = create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof.snapshot"; EXPECT_EQ(expected_base, create_snapshot_profile_path("com.example", "primary.prof")); std::string expected_split = create_primary_reference_profile_package_dir_path("com.example") + "/split.prof.snapshot"; EXPECT_EQ(expected_split, create_snapshot_profile_path("com.example", "split.prof")); } TEST_F(UtilsTest, CreateSecondaryCurrentProfile) { EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof", create_current_profile_path(/*user*/0, "com.example", "/data/user/0/com.example/secondary.dex", /*is_secondary*/true)); } TEST_F(UtilsTest, CreateSecondaryReferenceProfile) { EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.prof", create_reference_profile_path("com.example", "/data/user/0/com.example/secondary.dex", /*is_secondary*/true)); } static void pass_secondary_dex_validation(const std::string& package_name, const std::string& dex_path, int uid, int storage_flag) { EXPECT_TRUE(validate_secondary_dex_path(package_name, dex_path, /*volume_uuid*/ nullptr, uid, storage_flag)) << dex_path << " should be allowed as a valid secondary dex path"; } static void fail_secondary_dex_validation(const std::string& package_name, const std::string& dex_path, int uid, int storage_flag) { EXPECT_FALSE(validate_secondary_dex_path(package_name, dex_path, /*volume_uuid*/ nullptr, uid, storage_flag)) << dex_path << " should not be allowed as a valid secondary dex path"; } TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) { std::string package_name = "com.test.app"; std::string app_dir_ce_user_0 = "/data/data/" + package_name; std::string app_dir_ce_user_0_link = "/data/user/0/" + package_name; std::string app_dir_ce_user_10 = "/data/user/10/" + package_name; std::string app_dir_de_user_0 = "/data/user_de/0/" + package_name; std::string app_dir_de_user_10 = "/data/user_de/10/" + package_name; EXPECT_EQ(app_dir_ce_user_0, create_data_user_ce_package_path(nullptr, 0, package_name.c_str())); EXPECT_EQ(app_dir_ce_user_10, create_data_user_ce_package_path(nullptr, 10, package_name.c_str())); EXPECT_EQ(app_dir_de_user_0, create_data_user_de_package_path(nullptr, 0, package_name.c_str())); EXPECT_EQ(app_dir_de_user_10, create_data_user_de_package_path(nullptr, 10, package_name.c_str())); uid_t app_uid_for_user_0 = multiuser_get_uid(/*user_id*/0, /*app_id*/ 1234); uid_t app_uid_for_user_10 = multiuser_get_uid(/*user_id*/10, /*app_id*/ 1234); // Standard path for user 0 on CE storage. pass_secondary_dex_validation( package_name, app_dir_ce_user_0 + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE); pass_secondary_dex_validation( package_name, app_dir_ce_user_0_link + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE); // Standard path for user 10 on CE storage. pass_secondary_dex_validation( package_name, app_dir_ce_user_10 + "/ce10.dex", app_uid_for_user_10, FLAG_STORAGE_CE); // Standard path for user 0 on DE storage. pass_secondary_dex_validation( package_name, app_dir_de_user_0 + "/de0.dex", app_uid_for_user_0, FLAG_STORAGE_DE); // Standard path for user 10 on DE storage. pass_secondary_dex_validation( package_name, app_dir_de_user_10 + "/de0.dex", app_uid_for_user_10, FLAG_STORAGE_DE); // Dex path for user 0 accessed from user 10. fail_secondary_dex_validation( package_name, app_dir_ce_user_0 + "/path0_from10.dex", app_uid_for_user_10, FLAG_STORAGE_CE); // Dex path for CE storage accessed with DE. fail_secondary_dex_validation( package_name, app_dir_ce_user_0 + "/ce_from_de.dex", app_uid_for_user_0, FLAG_STORAGE_DE); // Dex path for DE storage accessed with CE. fail_secondary_dex_validation( package_name, app_dir_de_user_0 + "/de_from_ce.dex", app_uid_for_user_0, FLAG_STORAGE_CE); // Location which does not start with '/'. fail_secondary_dex_validation( package_name, "without_slash.dex", app_uid_for_user_10, FLAG_STORAGE_DE); // The dex file is not in the specified package directory. fail_secondary_dex_validation( "another.package", app_dir_ce_user_0 + "/for_another_package.dex", app_uid_for_user_0, FLAG_STORAGE_DE); // The dex path contains indirect directories. fail_secondary_dex_validation( package_name, app_dir_ce_user_0 + "/1/../foo.dex", app_uid_for_user_0, FLAG_STORAGE_CE); fail_secondary_dex_validation( package_name, app_dir_ce_user_0 + "/1/./foo.dex", app_uid_for_user_0, FLAG_STORAGE_CE); // Super long path. std::string too_long = create_too_long_path("too_long_"); fail_secondary_dex_validation( package_name, app_dir_ce_user_10 + "/" + too_long, app_uid_for_user_10, FLAG_STORAGE_CE); } TEST_F(UtilsTest, ValidateApkPath) { EXPECT_EQ(0, validate_apk_path("/data/app/com.example")); EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file")); EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir//file")); } TEST_F(UtilsTest, ValidateApkPathSubdirs) { EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example//file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file")); EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file")); EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file")); } TEST_F(UtilsTest, MatchExtension_Valid) { EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("mpg")); EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("mpeg")); EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("mPeG")); EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("MPEG")); } TEST_F(UtilsTest, MatchExtension_Invalid) { EXPECT_EQ(0, MatchExtension("log")); EXPECT_EQ(0, MatchExtension("3amp")); EXPECT_EQ(0, MatchExtension("fpe")); EXPECT_EQ(0, MatchExtension("docx")); } TEST_F(UtilsTest, TestRollbackPaths) { EXPECT_EQ("/data/misc_ce/0/rollback/239/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, 239, "com.foo")); EXPECT_EQ("/data/misc_ce/10/rollback/37/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 10, 37, "com.foo")); EXPECT_EQ("/data/misc_de/0/rollback/73/com.foo", create_data_misc_de_rollback_package_path(nullptr, 0, 73, "com.foo")); EXPECT_EQ("/data/misc_de/10/rollback/13/com.foo", create_data_misc_de_rollback_package_path(nullptr, 10, 13, "com.foo")); EXPECT_EQ("/data/misc_ce/0/rollback/57", create_data_misc_ce_rollback_path(nullptr, 0, 57)); EXPECT_EQ("/data/misc_ce/10/rollback/1543", create_data_misc_ce_rollback_path(nullptr, 10, 1543)); EXPECT_EQ("/data/misc_de/0/rollback/43", create_data_misc_de_rollback_path(nullptr, 0, 43)); EXPECT_EQ("/data/misc_de/10/rollback/41", create_data_misc_de_rollback_path(nullptr, 10, 41)); EXPECT_EQ("/data/misc_ce/0/rollback/17/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, 17, "com.foo", 0)); EXPECT_EQ("/data/misc_ce/0/rollback/19/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, 19, "com.foo", 239)); auto rollback_ce_path = create_data_misc_ce_rollback_path(nullptr, 0, 53); auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.foo"); auto deleter = [&rollback_ce_path]() { delete_dir_contents_and_dir(rollback_ce_path, true /* ignore_if_missing */); }; auto scope_guard = android::base::make_scope_guard(deleter); EXPECT_NE(-1, mkdir(rollback_ce_path.c_str(), 700)); EXPECT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700)); ino_t ce_data_inode; EXPECT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode)); EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.foo", ce_data_inode)); // Check that path defined by inode is picked even if it's not the same as // the fallback one. EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.bar", ce_data_inode)); // These last couple of cases are never exercised in production because we // only snapshot apps in the primary data partition. Exercise them here for // the sake of completeness. EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/7/com.example", create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 7, "com.example")); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/11/com.example", create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 11, "com.example")); } TEST_F(UtilsTest, TestCreateDirIfNeeded) { system("mkdir -p /data/local/tmp/user/0"); auto deleter = [&]() { delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */); }; auto scope_guard = android::base::make_scope_guard(deleter); // Create folder and check it's permissions. ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700)); struct stat st; ASSERT_EQ(0, stat("/data/local/tmp/user/0/foo", &st)); ASSERT_EQ(0700, st.st_mode & ALLPERMS); // Check that create_dir_if_needed is no-op if folder already exists with // correct permissions. ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700)); // Check -1 is returned if folder exists but with different permissions. ASSERT_EQ(-1, create_dir_if_needed("/data/local/tmp/user/0/foo", 0750)); // Check that call fails if parent doesn't exist. ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); } } // namespace installd } // namespace android cmds/installd/tests/installd_utils_test.xml0100644 0000000 0000000 00000003034 13756501734 020335 0ustar000000000 0000000 cmds/installd/tests/test_utils.h0100644 0000000 0000000 00000012460 13756501734 016075 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #pragma once #include #include #include #include #include #include uint8_t kBase64Map[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; uint8_t* DecodeBase64(const char* src, size_t* dst_size) { CHECK(dst_size != nullptr); std::vector tmp; uint32_t t = 0, y = 0; int g = 3; for (size_t i = 0; src[i] != '\0'; ++i) { uint8_t c = kBase64Map[src[i] & 0xFF]; if (c == 255) continue; // the final = symbols are read and used to trim the remaining bytes if (c == 254) { c = 0; // prevent g < 0 which would potentially allow an overflow later if (--g < 0) { *dst_size = 0; return nullptr; } } else if (g != 3) { // we only allow = to be at the end *dst_size = 0; return nullptr; } t = (t << 6) | c; if (++y == 4) { tmp.push_back((t >> 16) & 255); if (g > 1) { tmp.push_back((t >> 8) & 255); } if (g > 2) { tmp.push_back(t & 255); } y = t = 0; } } if (y != 0) { *dst_size = 0; return nullptr; } std::unique_ptr dst(new uint8_t[tmp.size()]); *dst_size = tmp.size(); std::copy(tmp.begin(), tmp.end(), dst.get()); return dst.release(); } bool WriteBase64ToFile(const char* base64, const std::string& file, uid_t uid, gid_t gid, int mode, std::string* error_msg) { CHECK(base64 != nullptr); size_t length; std::unique_ptr bytes(DecodeBase64(base64, &length)); CHECK(bytes != nullptr); int fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); using android::base::StringPrintf; if (fd < 0) { *error_msg = StringPrintf("Could not open file %s: %s", file.c_str(), strerror(errno)); return false; } size_t wrote = 0; while (wrote < length) { ssize_t cur = write(fd, bytes.get() + wrote, length - wrote); if (cur == -1) { *error_msg = StringPrintf("Could not write file %s: %s", file.c_str(), strerror(errno)); return false; } wrote += cur; } if (::chown(file.c_str(), uid, gid) != 0) { *error_msg = StringPrintf("Could not chown file %s: %s", file.c_str(), strerror(errno)); return false; } if (::chmod(file.c_str(), mode) != 0) { *error_msg = StringPrintf("Could not chmod file %s: %s", file.c_str(), strerror(errno)); return false; } return true; } // TODO(calin): fix dexopt drop_capabilities and move to general utils (b/69678790). bool DropCapabilities(uid_t uid, gid_t gid) { if (setgid(gid) != 0) { PLOG(ERROR) << "setgid failed: " << gid; return false; } if (setuid(uid) != 0) { PLOG(ERROR) << "setuid failed: " << uid; return false; } // drop capabilities struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; memset(&capheader, 0, sizeof(capheader)); memset(&capdata, 0, sizeof(capdata)); capheader.version = _LINUX_CAPABILITY_VERSION_3; if (capset(&capheader, &capdata[0]) < 0) { PLOG(ERROR) << "capset failed"; return false; } return true; } cmds/installd/utils.cpp0100644 0000000 0000000 00000114720 13756501734 014231 0ustar000000000 0000000 /* ** Copyright 2008, 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. */ #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dexopt_return_codes.h" #include "globals.h" // extern variables. #ifndef LOG_TAG #define LOG_TAG "installd" #endif #define DEBUG_XATTRS 0 using android::base::EndsWith; using android::base::Fdopendir; using android::base::StringPrintf; using android::base::unique_fd; namespace android { namespace installd { /** * Check that given string is valid filename, and that it attempts no * parent or child directory traversal. */ bool is_valid_filename(const std::string& name) { if (name.empty() || (name == ".") || (name == "..") || (name.find('/') != std::string::npos)) { return false; } else { return true; } } static void check_package_name(const char* package_name) { CHECK(is_valid_filename(package_name)); CHECK(is_valid_package_name(package_name)); } static std::string resolve_ce_path_by_inode_or_fallback(const std::string& root_path, ino_t ce_data_inode, const std::string& fallback) { if (ce_data_inode != 0) { DIR* dir = opendir(root_path.c_str()); if (dir == nullptr) { PLOG(ERROR) << "Failed to opendir " << root_path; return fallback; } struct dirent* ent; while ((ent = readdir(dir))) { if (ent->d_ino == ce_data_inode) { auto resolved = StringPrintf("%s/%s", root_path.c_str(), ent->d_name); if (resolved != fallback) { LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode << " instead of " << fallback; } closedir(dir); return resolved; } } LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback; closedir(dir); return fallback; } else { return fallback; } } /** * Create the path name where package app contents should be stored for * the given volume UUID and package name. An empty UUID is assumed to * be internal storage. */ std::string create_data_app_package_path(const char* volume_uuid, const char* package_name) { check_package_name(package_name); return StringPrintf("%s/%s", create_data_app_path(volume_uuid).c_str(), package_name); } /** * Create the path name where package data should be stored for the given * volume UUID, package name, and user ID. An empty UUID is assumed to be * internal storage. */ std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user, const char* package_name) { check_package_name(package_name); return StringPrintf("%s/%s", create_data_user_ce_path(volume_uuid, user).c_str(), package_name); } /** * Create the path name where package data should be stored for the given * volume UUID, package name, and user ID. An empty UUID is assumed to be * internal storage. * Compared to create_data_user_ce_package_path this method always return the * ".../user/..." directory. */ std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name) { check_package_name(package_name); std::string data(create_data_path(volume_uuid)); return StringPrintf("%s/user/%u/%s", data.c_str(), userid, package_name); } std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user, const char* package_name, ino_t ce_data_inode) { // For testing purposes, rely on the inode when defined; this could be // optimized to use access() in the future. auto fallback = create_data_user_ce_package_path(volume_uuid, user, package_name); auto user_path = create_data_user_ce_path(volume_uuid, user); return resolve_ce_path_by_inode_or_fallback(user_path, ce_data_inode, fallback); } std::string create_data_user_de_package_path(const char* volume_uuid, userid_t user, const char* package_name) { check_package_name(package_name); return StringPrintf("%s/%s", create_data_user_de_path(volume_uuid, user).c_str(), package_name); } std::string create_data_path(const char* volume_uuid) { if (volume_uuid == nullptr) { return "/data"; } else if (!strcmp(volume_uuid, "TEST")) { CHECK(property_get_bool("ro.debuggable", false)); return "/data/local/tmp"; } else { CHECK(is_valid_filename(volume_uuid)); return StringPrintf("/mnt/expand/%s", volume_uuid); } } /** * Create the path name for app data. */ std::string create_data_app_path(const char* volume_uuid) { return StringPrintf("%s/app", create_data_path(volume_uuid).c_str()); } /** * Create the path name for user data for a certain userid. * Keep same implementation as vold to minimize path walking overhead */ std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid) { std::string data(create_data_path(volume_uuid)); if (volume_uuid == nullptr && userid == 0) { std::string legacy = StringPrintf("%s/data", data.c_str()); struct stat sb; if (lstat(legacy.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) { /* /data/data is dir, return /data/data for legacy system */ return legacy; } } return StringPrintf("%s/user/%u", data.c_str(), userid); } /** * Create the path name for device encrypted user data for a certain userid. */ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { std::string data(create_data_path(volume_uuid)); return StringPrintf("%s/user_de/%u", data.c_str(), userid); } std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); } std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user) { return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user); } std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, int32_t snapshot_id) { return StringPrintf("%s/%d", create_data_misc_ce_rollback_base_path(volume_uuid, user).c_str(), snapshot_id); } std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user, int32_t snapshot_id) { return StringPrintf("%s/%d", create_data_misc_de_rollback_base_path(volume_uuid, user).c_str(), snapshot_id); } std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, userid_t user, int32_t snapshot_id, const char* package_name) { return StringPrintf("%s/%s", create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name); } std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode) { auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshot_id, package_name); auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id); return resolve_ce_path_by_inode_or_fallback(user_path, ce_rollback_inode, fallback); } std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, userid_t user, int32_t snapshot_id, const char* package_name) { return StringPrintf("%s/%s", create_data_misc_de_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name); } /** * Create the path name for media for a certain userid. */ std::string create_data_media_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid); } std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, const char* data_type, const char* package_name) { return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(), data_type, package_name); } std::string create_data_misc_legacy_path(userid_t userid) { return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid); } std::string create_primary_cur_profile_dir_path(userid_t userid) { return StringPrintf("%s/cur/%u", android_profiles_dir.c_str(), userid); } std::string create_primary_current_profile_package_dir_path(userid_t user, const std::string& package_name) { check_package_name(package_name.c_str()); return StringPrintf("%s/%s", create_primary_cur_profile_dir_path(user).c_str(), package_name.c_str()); } std::string create_primary_ref_profile_dir_path() { return StringPrintf("%s/ref", android_profiles_dir.c_str()); } std::string create_primary_reference_profile_package_dir_path(const std::string& package_name) { check_package_name(package_name.c_str()); return StringPrintf("%s/ref/%s", android_profiles_dir.c_str(), package_name.c_str()); } std::string create_data_dalvik_cache_path() { return "/data/dalvik-cache"; } // Keep profile paths in sync with ActivityThread and LoadedApk. const std::string PROFILE_EXT = ".prof"; const std::string CURRENT_PROFILE_EXT = ".cur"; const std::string SNAPSHOT_PROFILE_EXT = ".snapshot"; // Gets the parent directory and the file name for the given secondary dex path. // Returns true on success, false on failure (if the dex_path does not have the expected // structure). static bool get_secondary_dex_location(const std::string& dex_path, std::string* out_dir_name, std::string* out_file_name) { size_t dirIndex = dex_path.rfind('/'); if (dirIndex == std::string::npos) { return false; } if (dirIndex == dex_path.size() - 1) { return false; } *out_dir_name = dex_path.substr(0, dirIndex); *out_file_name = dex_path.substr(dirIndex + 1); return true; } std::string create_current_profile_path(userid_t user, const std::string& package_name, const std::string& location, bool is_secondary_dex) { if (is_secondary_dex) { // Secondary dex current profiles are stored next to the dex files under the oat folder. std::string dex_dir; std::string dex_name; CHECK(get_secondary_dex_location(location, &dex_dir, &dex_name)) << "Unexpected dir structure for secondary dex " << location; return StringPrintf("%s/oat/%s%s%s", dex_dir.c_str(), dex_name.c_str(), CURRENT_PROFILE_EXT.c_str(), PROFILE_EXT.c_str()); } else { // Profiles for primary apks are under /data/misc/profiles/cur. std::string profile_dir = create_primary_current_profile_package_dir_path( user, package_name); return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str()); } } std::string create_reference_profile_path(const std::string& package_name, const std::string& location, bool is_secondary_dex) { if (is_secondary_dex) { // Secondary dex reference profiles are stored next to the dex files under the oat folder. std::string dex_dir; std::string dex_name; CHECK(get_secondary_dex_location(location, &dex_dir, &dex_name)) << "Unexpected dir structure for secondary dex " << location; return StringPrintf("%s/oat/%s%s", dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str()); } else { // Reference profiles for primary apks are stored in /data/misc/profile/ref. std::string profile_dir = create_primary_reference_profile_package_dir_path(package_name); return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str()); } } std::string create_snapshot_profile_path(const std::string& package, const std::string& profile_name) { std::string ref_profile = create_reference_profile_path(package, profile_name, /*is_secondary_dex*/ false); return ref_profile + SNAPSHOT_PROFILE_EXT; } std::vector get_known_users(const char* volume_uuid) { std::vector users; // We always have an owner users.push_back(0); std::string path(create_data_path(volume_uuid) + "/" + SECONDARY_USER_PREFIX); DIR* dir = opendir(path.c_str()); if (dir == nullptr) { // Unable to discover other users, but at least return owner PLOG(ERROR) << "Failed to opendir " << path; return users; } struct dirent* ent; while ((ent = readdir(dir))) { if (ent->d_type != DT_DIR) { continue; } char* end; userid_t user = strtol(ent->d_name, &end, 10); if (*end == '\0' && user != 0) { LOG(DEBUG) << "Found valid user " << user; users.push_back(user); } } closedir(dir); return users; } int calculate_tree_size(const std::string& path, int64_t* size, int32_t include_gid, int32_t exclude_gid, bool exclude_apps) { FTS *fts; FTSENT *p; int64_t matchedSize = 0; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { if (errno != ENOENT) { PLOG(ERROR) << "Failed to fts_open " << path; } return -1; } while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: int32_t uid = p->fts_statp->st_uid; int32_t gid = p->fts_statp->st_gid; int32_t user_uid = multiuser_get_app_id(uid); int32_t user_gid = multiuser_get_app_id(gid); if (exclude_apps && ((user_uid >= AID_APP_START && user_uid <= AID_APP_END) || (user_gid >= AID_CACHE_GID_START && user_gid <= AID_CACHE_GID_END) || (user_gid >= AID_SHARED_GID_START && user_gid <= AID_SHARED_GID_END))) { // Don't traverse inside or measure fts_set(fts, p, FTS_SKIP); break; } if (include_gid != -1 && gid != include_gid) { break; } if (exclude_gid != -1 && gid == exclude_gid) { break; } matchedSize += (p->fts_statp->st_blocks * 512); break; } } fts_close(fts); #if MEASURE_DEBUG if ((include_gid == -1) && (exclude_gid == -1)) { LOG(DEBUG) << "Measured " << path << " size " << matchedSize; } else { LOG(DEBUG) << "Measured " << path << " size " << matchedSize << "; include " << include_gid << " exclude " << exclude_gid; } #endif *size += matchedSize; return 0; } /** * Checks whether the package name is valid. Returns -1 on error and * 0 on success. */ bool is_valid_package_name(const std::string& packageName) { // This logic is borrowed from PackageParser.java bool hasSep = false; bool front = true; auto it = packageName.begin(); for (; it != packageName.end() && *it != '-'; it++) { char c = *it; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { front = false; continue; } if (!front) { if ((c >= '0' && c <= '9') || c == '_') { continue; } } if (c == '.') { hasSep = true; front = true; continue; } LOG(WARNING) << "Bad package character " << c << " in " << packageName; return false; } if (front) { LOG(WARNING) << "Missing separator in " << packageName; return false; } for (; it != packageName.end(); it++) { char c = *it; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue; if ((c >= '0' && c <= '9') || c == '_' || c == '-' || c == '=') continue; LOG(WARNING) << "Bad suffix character " << c << " in " << packageName; return false; } return true; } static int _delete_dir_contents(DIR *d, int (*exclusion_predicate)(const char *name, const int is_dir)) { int result = 0; struct dirent *de; int dfd; dfd = dirfd(d); if (dfd < 0) return -1; while ((de = readdir(d))) { const char *name = de->d_name; /* check using the exclusion predicate, if provided */ if (exclusion_predicate && exclusion_predicate(name, (de->d_type == DT_DIR))) { continue; } if (de->d_type == DT_DIR) { int subfd; DIR *subdir; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); if (subfd < 0) { ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); result = -1; continue; } subdir = fdopendir(subfd); if (subdir == nullptr) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(subfd); result = -1; continue; } if (_delete_dir_contents(subdir, exclusion_predicate)) { result = -1; } closedir(subdir); if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); result = -1; } } else { if (unlinkat(dfd, name, 0) < 0) { ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); result = -1; } } } return result; } int create_dir_if_needed(const std::string& pathname, mode_t perms) { struct stat st; int rc; if ((rc = stat(pathname.c_str(), &st)) != 0) { if (errno == ENOENT) { return mkdir(pathname.c_str(), perms); } else { return rc; } } else if (!S_ISDIR(st.st_mode)) { LOG(DEBUG) << pathname << " is not a folder"; return -1; } mode_t actual_perms = st.st_mode & ALLPERMS; if (actual_perms != perms) { LOG(WARNING) << pathname << " permissions " << actual_perms << " expected " << perms; return -1; } return 0; } int delete_dir_contents(const std::string& pathname, bool ignore_if_missing) { return delete_dir_contents(pathname.c_str(), 0, nullptr, ignore_if_missing); } int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing) { return delete_dir_contents(pathname.c_str(), 1, nullptr, ignore_if_missing); } int delete_dir_contents(const char *pathname, int also_delete_dir, int (*exclusion_predicate)(const char*, const int), bool ignore_if_missing) { int res = 0; DIR *d; d = opendir(pathname); if (d == nullptr) { if (ignore_if_missing && (errno == ENOENT)) { return 0; } ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno)); return -errno; } res = _delete_dir_contents(d, exclusion_predicate); closedir(d); if (also_delete_dir) { if (rmdir(pathname)) { ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno)); res = -1; } } return res; } int delete_dir_contents_fd(int dfd, const char *name) { int fd, res; DIR *d; fd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); if (fd < 0) { ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); return -1; } d = fdopendir(fd); if (d == nullptr) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(fd); return -1; } res = _delete_dir_contents(d, nullptr); closedir(d); return res; } static int _copy_owner_permissions(int srcfd, int dstfd) { struct stat st; if (fstat(srcfd, &st) != 0) { return -1; } if (fchmod(dstfd, st.st_mode) != 0) { return -1; } return 0; } static int _copy_dir_files(int sdfd, int ddfd, uid_t owner, gid_t group) { int result = 0; if (_copy_owner_permissions(sdfd, ddfd) != 0) { ALOGE("_copy_dir_files failed to copy dir permissions\n"); } if (fchown(ddfd, owner, group) != 0) { ALOGE("_copy_dir_files failed to change dir owner\n"); } DIR *ds = fdopendir(sdfd); if (ds == nullptr) { ALOGE("Couldn't fdopendir: %s\n", strerror(errno)); return -1; } struct dirent *de; while ((de = readdir(ds))) { if (de->d_type != DT_REG) { continue; } const char *name = de->d_name; int fsfd = openat(sdfd, name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); int fdfd = openat(ddfd, name, O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_CREAT, 0600); if (fsfd == -1 || fdfd == -1) { ALOGW("Couldn't copy %s: %s\n", name, strerror(errno)); } else { if (_copy_owner_permissions(fsfd, fdfd) != 0) { ALOGE("Failed to change file permissions\n"); } if (fchown(fdfd, owner, group) != 0) { ALOGE("Failed to change file owner\n"); } char buf[8192]; ssize_t size; while ((size = read(fsfd, buf, sizeof(buf))) > 0) { write(fdfd, buf, size); } if (size < 0) { ALOGW("Couldn't copy %s: %s\n", name, strerror(errno)); result = -1; } } close(fdfd); close(fsfd); } return result; } int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, uid_t group) { int res = 0; DIR *ds = nullptr; DIR *dd = nullptr; ds = opendir(srcname); if (ds == nullptr) { ALOGE("Couldn't opendir %s: %s\n", srcname, strerror(errno)); return -errno; } mkdir(dstname, 0600); dd = opendir(dstname); if (dd == nullptr) { ALOGE("Couldn't opendir %s: %s\n", dstname, strerror(errno)); closedir(ds); return -errno; } int sdfd = dirfd(ds); int ddfd = dirfd(dd); if (sdfd != -1 && ddfd != -1) { res = _copy_dir_files(sdfd, ddfd, owner, group); } else { res = -errno; } closedir(dd); closedir(ds); return res; } int64_t data_disk_free(const std::string& data_path) { struct statvfs sfs; if (statvfs(data_path.c_str(), &sfs) == 0) { return static_cast(sfs.f_bavail) * sfs.f_frsize; } else { PLOG(ERROR) << "Couldn't statvfs " << data_path; return -1; } } int get_path_inode(const std::string& path, ino_t *inode) { struct stat buf; memset(&buf, 0, sizeof(buf)); if (stat(path.c_str(), &buf) != 0) { PLOG(WARNING) << "Failed to stat " << path; return -1; } else { *inode = buf.st_ino; return 0; } } /** * Write the inode of a specific child file into the given xattr on the * parent directory. This allows you to find the child later, even if its * name is encrypted. */ int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr) { ino_t inode = 0; uint64_t inode_raw = 0; auto path = StringPrintf("%s/%s", parent.c_str(), name); if (get_path_inode(path, &inode) != 0) { // Path probably doesn't exist yet; ignore return 0; } // Check to see if already set correctly if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) { if (inode_raw == inode) { // Already set correctly; skip writing return 0; } else { PLOG(WARNING) << "Mismatched inode value; found " << inode << " on disk but marked value was " << inode_raw << "; overwriting"; } } inode_raw = inode; if (setxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw), 0) != 0 && errno != EOPNOTSUPP) { PLOG(ERROR) << "Failed to write xattr " << inode_xattr << " at " << parent; return -1; } else { return 0; } } /** * Read the inode of a specific child file from the given xattr on the * parent directory. Returns a currently valid path for that child, which * might have an encrypted name. */ std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr) { ino_t inode = 0; uint64_t inode_raw = 0; auto fallback = StringPrintf("%s/%s", parent.c_str(), name); // Lookup the inode value written earlier if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) { inode = inode_raw; } // For testing purposes, rely on the inode when defined; this could be // optimized to use access() in the future. if (inode != 0) { DIR* dir = opendir(parent.c_str()); if (dir == nullptr) { PLOG(ERROR) << "Failed to opendir " << parent; return fallback; } struct dirent* ent; while ((ent = readdir(dir))) { if (ent->d_ino == inode) { auto resolved = StringPrintf("%s/%s", parent.c_str(), ent->d_name); #if DEBUG_XATTRS if (resolved != fallback) { LOG(DEBUG) << "Resolved path " << resolved << " for inode " << inode << " instead of " << fallback; } #endif closedir(dir); return resolved; } } LOG(WARNING) << "Failed to resolve inode " << inode << "; using " << fallback; closedir(dir); return fallback; } else { return fallback; } } void remove_path_xattr(const std::string& path, const char* inode_xattr) { if (removexattr(path.c_str(), inode_xattr) && errno != ENODATA) { PLOG(ERROR) << "Failed to remove xattr " << inode_xattr << " at " << path; } } /** * Validate that the path is valid in the context of the provided directory. * The path is allowed to have at most one subdirectory and no indirections * to top level directories (i.e. have ".."). */ static int validate_path(const std::string& dir, const std::string& path, int maxSubdirs) { // Argument sanity checking if (dir.find('/') != 0 || dir.rfind('/') != dir.size() - 1 || dir.find("..") != std::string::npos) { LOG(ERROR) << "Invalid directory " << dir; return -1; } if (path.find("..") != std::string::npos) { LOG(ERROR) << "Invalid path " << path; return -1; } if (path.compare(0, dir.size(), dir) != 0) { // Common case, path isn't under directory return -1; } // Count number of subdirectories auto pos = path.find('/', dir.size()); int count = 0; while (pos != std::string::npos) { auto next = path.find('/', pos + 1); if (next > pos + 1) { count++; } pos = next; } if (count > maxSubdirs) { LOG(ERROR) << "Invalid path depth " << path << " when tested against " << dir; return -1; } return 0; } /** * Checks whether a path points to a system app (.apk file). Returns 0 * if it is a system app or -1 if it is not. */ int validate_system_app_path(const char* path) { std::string path_ = path; for (const auto& dir : android_system_dirs) { if (validate_path(dir, path, 1) == 0) { return 0; } } return -1; } bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, const char* volume_uuid, int uid, int storage_flag) { CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE); // Empty paths are not allowed. if (dex_path.empty()) { return false; } // First character should always be '/'. No relative paths. if (dex_path[0] != '/') { return false; } // The last character should not be '/'. if (dex_path[dex_path.size() - 1] == '/') { return false; } // There should be no '.' after the directory marker. if (dex_path.find("/.") != std::string::npos) { return false; } // The path should be at most PKG_PATH_MAX long. if (dex_path.size() > PKG_PATH_MAX) { return false; } // The dex_path should be under the app data directory. std::string app_private_dir = storage_flag == FLAG_STORAGE_CE ? create_data_user_ce_package_path( volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()) : create_data_user_de_package_path( volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()); if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) { // The check above might fail if the dex file is accessed via the /data/user/0 symlink. // If that's the case, attempt to validate against the user data link. std::string app_private_dir_symlink = create_data_user_ce_package_path_as_user_link( volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()); if (strncmp(dex_path.c_str(), app_private_dir_symlink.c_str(), app_private_dir_symlink.size()) != 0) { return false; } } // If we got here we have a valid path. return true; } /** * Check whether path points to a valid path for an APK file. The path must * begin with a whitelisted prefix path and must be no deeper than |maxSubdirs| within * that path. Returns -1 when an invalid path is encountered and 0 when a valid path * is encountered. */ static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { if (validate_path(android_app_dir, path, maxSubdirs) == 0) { return 0; } else if (validate_path(android_staging_dir, path, maxSubdirs) == 0) { return 0; } else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) { return 0; } else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) { return 0; } else if (validate_path(android_asec_dir, path, maxSubdirs) == 0) { return 0; } else if (android::base::StartsWith(path, android_mnt_expand_dir)) { // Rewrite the path as if it were on internal storage, and test that size_t end = path.find('/', android_mnt_expand_dir.size() + 1); if (end != std::string::npos) { auto modified = path; modified.replace(0, end + 1, android_data_dir); return validate_apk_path_internal(modified, maxSubdirs); } } return -1; } int validate_apk_path(const char* path) { return validate_apk_path_internal(path, 1 /* maxSubdirs */); } int validate_apk_path_subdirs(const char* path) { return validate_apk_path_internal(path, 3 /* maxSubdirs */); } int ensure_config_user_dirs(userid_t userid) { // writable by system, readable by any app within the same user const int uid = multiuser_get_uid(userid, AID_SYSTEM); const int gid = multiuser_get_uid(userid, AID_EVERYBODY); // Ensure /data/misc/user/ exists auto path = create_data_misc_legacy_path(userid); return fs_prepare_dir(path.c_str(), 0750, uid, gid); } int wait_child(pid_t pid) { int status; pid_t got_pid; while (1) { got_pid = waitpid(pid, &status, 0); if (got_pid == -1 && errno == EINTR) { printf("waitpid interrupted, retrying\n"); } else { break; } } if (got_pid != pid) { ALOGW("waitpid failed: wanted %d, got %d: %s\n", (int) pid, (int) got_pid, strerror(errno)); return 1; } if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { return 0; } else { return status; /* always nonzero */ } } /** * Prepare an app cache directory, which offers to fix-up the GID and * directory mode flags during a platform upgrade. * The app cache directory path will be 'parent'/'name'. */ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid) { auto path = StringPrintf("%s/%s", parent.c_str(), name); struct stat st; if (stat(path.c_str(), &st) != 0) { if (errno == ENOENT) { // This is fine, just create it if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { PLOG(ERROR) << "Failed to prepare " << path; return -1; } else { return 0; } } else { PLOG(ERROR) << "Failed to stat " << path; return -1; } } mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); if (st.st_uid != uid) { // Mismatched UID is real trouble; we can't recover LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid << " but expected " << uid; return -1; } else if (st.st_gid == gid && actual_mode == target_mode) { // Everything looks good! return 0; } else { // Mismatched GID/mode is recoverable; fall through to update LOG(DEBUG) << "Mismatched cache GID/mode at " << path << ": found " << st.st_gid << "/" << actual_mode << " but expected " << gid << "/" << target_mode; } // Directory is owned correctly, but GID or mode mismatch means it's // probably a platform upgrade so we need to fix them FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(ERROR) << "Failed to fts_open " << path; return -1; } while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_DP: if (chmod(p->fts_path, target_mode) != 0) { PLOG(WARNING) << "Failed to chmod " << p->fts_path; } [[fallthrough]]; // to also set GID case FTS_F: if (chown(p->fts_path, -1, gid) != 0) { PLOG(WARNING) << "Failed to chown " << p->fts_path; } break; case FTS_SL: case FTS_SLNONE: if (lchown(p->fts_path, -1, gid) != 0) { PLOG(WARNING) << "Failed to chown " << p->fts_path; } break; } } fts_close(fts); return 0; } // Collect all non empty profiles from the given directory and puts then into profile_paths. // The profiles are identified based on PROFILE_EXT extension. // If a subdirectory or profile file cannot be opened the method logs a warning and moves on. // It returns true if there were no errors at all, and false otherwise. static bool collect_profiles(DIR* d, const std::string& current_path, std::vector* profiles_paths) { int32_t dir_fd = dirfd(d); if (dir_fd < 0) { return false; } bool result = true; struct dirent* dir_entry; while ((dir_entry = readdir(d))) { std::string name = dir_entry->d_name; std::string local_path = current_path + "/" + name; if (dir_entry->d_type == DT_REG) { // Check if this is a non empty profile file. if (EndsWith(name, PROFILE_EXT)) { struct stat st; if (stat(local_path.c_str(), &st) != 0) { PLOG(WARNING) << "Cannot stat local path " << local_path; result = false; continue; } else if (st.st_size > 0) { profiles_paths->push_back(local_path); } } } else if (dir_entry->d_type == DT_DIR) { // always skip "." and ".." if (name == "." || name == "..") { continue; } unique_fd subdir_fd(openat(dir_fd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); if (subdir_fd < 0) { PLOG(WARNING) << "Could not open dir path " << local_path; result = false; continue; } DIR* subdir = Fdopendir(std::move(subdir_fd)); if (subdir == nullptr) { PLOG(WARNING) << "Could not open dir path " << local_path; result = false; continue; } bool new_result = collect_profiles(subdir, local_path, profiles_paths); result = result && new_result; if (closedir(subdir) != 0) { PLOG(WARNING) << "Could not close dir path " << local_path; } } } return result; } bool collect_profiles(std::vector* profiles_paths) { DIR* d = opendir(android_profiles_dir.c_str()); if (d == nullptr) { return false; } else { return collect_profiles(d, android_profiles_dir, profiles_paths); } } void drop_capabilities(uid_t uid) { if (setgid(uid) != 0) { PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt"; exit(DexoptReturnCodes::kSetGid); } if (setuid(uid) != 0) { PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt"; exit(DexoptReturnCodes::kSetUid); } // drop capabilities struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[2]; memset(&capheader, 0, sizeof(capheader)); memset(&capdata, 0, sizeof(capdata)); capheader.version = _LINUX_CAPABILITY_VERSION_3; if (capset(&capheader, &capdata[0]) < 0) { PLOG(ERROR) << "capset failed"; exit(DexoptReturnCodes::kCapSet); } } } // namespace installd } // namespace android cmds/installd/utils.h0100644 0000000 0000000 00000014670 13756501734 013701 0ustar000000000 0000000 /* ** ** Copyright 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef UTILS_H_ #define UTILS_H_ #include #include #include #include #include #include #include #include #define MEASURE_DEBUG 0 #define FIXUP_DEBUG 0 #define BYPASS_QUOTA 0 #define BYPASS_SDCARDFS 0 namespace android { namespace installd { constexpr const char* kXattrInodeCache = "user.inode_cache"; constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache"; constexpr const char* kXattrCacheGroup = "user.cache_group"; constexpr const char* kXattrCacheTombstone = "user.cache_tombstone"; std::string create_data_path(const char* volume_uuid); std::string create_data_app_path(const char* volume_uuid); std::string create_data_app_package_path(const char* volume_uuid, const char* package_name); std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid); std::string create_data_user_de_path(const char* volume_uuid, userid_t userid); std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user, const char* package_name); std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user, const char* package_name, ino_t ce_data_inode); std::string create_data_user_de_package_path(const char* volume_uuid, userid_t user, const char* package_name); std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user); std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user); std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, int32_t snapshot_id); std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user, int32_t snapshot_id); std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, userid_t user, int32_t snapshot_id, const char* package_name); std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode); std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, userid_t user, int32_t snapshot_id, const char* package_name); std::string create_data_media_path(const char* volume_uuid, userid_t userid); std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, const char* data_type, const char* package_name); std::string create_data_misc_legacy_path(userid_t userid); std::string create_data_dalvik_cache_path(); std::string create_primary_cur_profile_dir_path(userid_t userid); std::string create_primary_current_profile_package_dir_path( userid_t user, const std::string& package_name); std::string create_primary_ref_profile_dir_path(); std::string create_primary_reference_profile_package_dir_path(const std::string& package_name); std::string create_current_profile_path( userid_t user, const std::string& package_name, const std::string& location, bool is_secondary_dex); std::string create_reference_profile_path( const std::string& package_name, const std::string& location, bool is_secondary_dex); std::string create_snapshot_profile_path( const std::string& package, const std::string& profile_name); std::vector get_known_users(const char* volume_uuid); int calculate_tree_size(const std::string& path, int64_t* size, int32_t include_gid = -1, int32_t exclude_gid = -1, bool exclude_apps = false); int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid); bool is_valid_filename(const std::string& name); bool is_valid_package_name(const std::string& packageName); int create_dir_if_needed(const std::string& pathname, mode_t mode); int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false); int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false); int delete_dir_contents(const char *pathname, int also_delete_dir, int (*exclusion_predicate)(const char *name, const int is_dir), bool ignore_if_missing = false); int delete_dir_contents_fd(int dfd, const char *name); int rm_package_dir(const std::string& package_dir); int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group); int64_t data_disk_free(const std::string& data_path); int get_path_inode(const std::string& path, ino_t *inode); int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr); std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr); void remove_path_xattr(const std::string& path, const char* inode_xattr); int validate_system_app_path(const char* path); bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, const char* volume_uuid, int uid, int storage_flag); int validate_apk_path(const char *path); int validate_apk_path_subdirs(const char *path); int ensure_config_user_dirs(userid_t userid); int wait_child(pid_t pid); int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid); // Collect all non empty profiles from the global profile directory and // put then into profile_paths. The profiles are identified based on PROFILE_EXT extension. // If a subdirectory or profile file cannot be opened the method logs a warning and moves on. // It returns true if there were no errors at all, and false otherwise. bool collect_profiles(std::vector* profiles_paths); void drop_capabilities(uid_t uid); } // namespace installd } // namespace android #endif // UTILS_H_ cmds/installd/utils_default.cpp0100644 0000000 0000000 00000001654 13756501734 015736 0ustar000000000 0000000 /* ** Copyright 2019, 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. */ #include "utils.h" namespace android { namespace installd { // In this file are default definitions of the functions that may contain // platform dependent logic. int rm_package_dir(const std::string& package_dir) { return delete_dir_contents_and_dir(package_dir); } } // namespace installd } // namespace android cmds/installd/view_compiler.cpp0100644 0000000 0000000 00000006301 13756501734 015730 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #include "view_compiler.h" #include #include #include #include #include #include #include "utils.h" #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "android-base/unique_fd.h" namespace android { namespace installd { using base::unique_fd; bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, int uid) { CHECK(apk_path != nullptr); CHECK(package_name != nullptr); CHECK(out_dex_file != nullptr); // viewcompiler won't have permission to open anything, so we have to open the files first // and pass file descriptors. // Open input file unique_fd infd{open(apk_path, O_RDONLY)}; // NOLINT(android-cloexec-open) if (infd.get() < 0) { PLOG(ERROR) << "Could not open input file: " << apk_path; return false; } // Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so // we close stdout and open it towards the right output. unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644)}; if (outfd.get() < 0) { PLOG(ERROR) << "Could not open output file: " << out_dex_file; return false; } if (fchmod(outfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) { PLOG(ERROR) << "Could not change output file permissions"; return false; } if (dup2(outfd, STDOUT_FILENO) < 0) { PLOG(ERROR) << "Could not duplicate output file descriptor"; return false; } // Prepare command line arguments for viewcompiler std::string args[] = {"/system/bin/viewcompiler", "--apk", "--infd", android::base::StringPrintf("%d", infd.get()), "--dex", "--package", package_name}; char* const argv[] = {const_cast(args[0].c_str()), const_cast(args[1].c_str()), const_cast(args[2].c_str()), const_cast(args[3].c_str()), const_cast(args[4].c_str()), const_cast(args[5].c_str()), const_cast(args[6].c_str()), nullptr}; pid_t pid = fork(); if (pid == 0) { // Now that we've opened the files we need, drop privileges. drop_capabilities(uid); execv("/system/bin/viewcompiler", argv); _exit(1); } return wait_child(pid) == 0; } } // namespace installd } // namespace android cmds/installd/view_compiler.h0100644 0000000 0000000 00000001620 13756501734 015374 0ustar000000000 0000000 /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VIEW_COMPILER_H_ #define VIEW_COMPILER_H_ namespace android { namespace installd { bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, int uid); } // namespace installd } // namespace android #endif // VIEW_COMPILER_H_ cmds/ip-up-vpn/0040755 0000000 0000000 00000000000 13756501734 012404 5ustar000000000 0000000 cmds/ip-up-vpn/Android.mk0100644 0000000 0000000 00000001571 13756501734 014316 0ustar000000000 0000000 # # Copyright (C) 2011 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. # LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := ip-up-vpn.c LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_MODULE := ip-up-vpn LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) cmds/ip-up-vpn/ip-up-vpn.c0100644 0000000 0000000 00000010642 13756501734 014403 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #define LOG_TAG "ip-up-vpn" #include #include #include #include #include #include #include #include #include #include #include #include #include #define DIR "/data/misc/vpn/" static const char *env(const char *name) { const char *value = getenv(name); return value ? value : ""; } static int set_address(struct sockaddr *sa, const char *address) { sa->sa_family = AF_INET; errno = EINVAL; return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr); } /* * The primary goal is to create a file with VPN parameters. Currently they * are interface, addresses, routes, DNS servers, and search domains and VPN * server address. Each parameter occupies one line in the file, and it can be * an empty string or space-separated values. The order and the format must be * consistent with com.android.server.connectivity.Vpn. Here is an example. * * ppp0 * 192.168.1.100/24 * 0.0.0.0/0 * 192.168.1.1 192.168.1.2 * example.org * 192.0.2.1 * * The secondary goal is to unify the outcome of VPN. The current baseline * is to have an interface configured with the given address and netmask * and maybe add a host route to protect the tunnel. PPP-based VPN already * does this, but others might not. Routes, DNS servers, and search domains * are handled by the framework since they can be overridden by the users. */ int main(int argc, char **argv) { FILE *state = fopen(DIR ".tmp", "wb"); if (!state) { ALOGE("Cannot create state: %s", strerror(errno)); return 1; } if (argc >= 6) { /* Invoked by pppd. */ fprintf(state, "%s\n", argv[1]); fprintf(state, "%s/32\n", argv[4]); fprintf(state, "0.0.0.0/0\n"); fprintf(state, "%s %s\n", env("DNS1"), env("DNS2")); fprintf(state, "\n"); fprintf(state, "\n"); } else if (argc == 2) { /* Invoked by racoon. */ const char *interface = env("INTERFACE"); const char *address = env("INTERNAL_ADDR4"); const char *routes = env("SPLIT_INCLUDE_CIDR"); int s = socket(AF_INET, SOCK_DGRAM, 0); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); /* Bring up the interface. */ ifr.ifr_flags = IFF_UP; strncpy(ifr.ifr_name, interface, IFNAMSIZ); if (ioctl(s, SIOCSIFFLAGS, &ifr)) { ALOGE("Cannot bring up %s: %s", interface, strerror(errno)); fclose(state); return 1; } /* Set the address. */ if (!set_address(&ifr.ifr_addr, address) || ioctl(s, SIOCSIFADDR, &ifr)) { ALOGE("Cannot set address: %s", strerror(errno)); fclose(state); return 1; } /* Set the netmask. */ if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) { if (ioctl(s, SIOCSIFNETMASK, &ifr)) { ALOGE("Cannot set netmask: %s", strerror(errno)); fclose(state); return 1; } } /* TODO: Send few packets to trigger phase 2? */ fprintf(state, "%s\n", interface); fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4")); fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0"); fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST")); fprintf(state, "%s\n", env("DEFAULT_DOMAIN")); fprintf(state, "%s\n", env("REMOTE_ADDR")); } else { ALOGE("Cannot parse parameters"); fclose(state); return 1; } fclose(state); if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) { ALOGE("Cannot write state: %s", strerror(errno)); return 1; } return 0; } cmds/lshal/0040755 0000000 0000000 00000000000 13756501734 011654 5ustar000000000 0000000 cmds/lshal/Android.bp0100644 0000000 0000000 00000003506 13756501734 013560 0ustar000000000 0000000 // 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. cc_library_shared { name: "liblshal", shared_libs: [ "libbase", "libcutils", "libutils", "libhidlbase", "libhidltransport", "libhidl-gen-hash", "libhidl-gen-utils", "libvintf", ], static_libs: [ "libprocpartition", ], srcs: [ "DebugCommand.cpp", "HelpCommand.cpp", "Lshal.cpp", "ListCommand.cpp", "PipeRelay.cpp", "TableEntry.cpp", "TextTable.cpp", "utils.cpp", ], cflags: [ "-Wall", "-Werror", ], } cc_defaults { name: "lshal_defaults", shared_libs: [ "libbase", "libhidlbase", "libhidl-gen-utils", "libhidltransport", "liblshal", "libutils", ], static_libs: [ "libprocpartition", ], cflags: ["-Wall", "-Werror"], } cc_binary { name: "lshal", defaults: ["lshal_defaults"], srcs: [ "main.cpp" ] } cc_test { name: "lshal_test", defaults: ["lshal_defaults"], gtest: true, static_libs: [ "libgmock" ], shared_libs: [ "libvintf", "android.hardware.tests.baz@1.0" ], srcs: [ "test.cpp" ] } cmds/lshal/Command.h0100644 0000000 0000000 00000002566 13756501734 013411 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_ #include "utils.h" namespace android { namespace lshal { class Lshal; // Base class for all *Commands class Command { public: explicit Command(Lshal& lshal) : mLshal(lshal) {} virtual ~Command() = default; // Expect optind to be set by Lshal::main and points to the next argument // to process. virtual Status main(const Arg &arg) = 0; virtual void usage() const = 0; // e.g. "list" virtual std::string getName() const = 0; // e.g. "list HALs" virtual std::string getSimpleDescription() const = 0; protected: Lshal& mLshal; }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ cmds/lshal/DebugCommand.cpp0100644 0000000 0000000 00000005206 13756501734 014705 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "DebugCommand.h" #include "Lshal.h" #include namespace android { namespace lshal { std::string DebugCommand::getName() const { return "debug"; } std::string DebugCommand::getSimpleDescription() const { return "Debug a specified HAL."; } Status DebugCommand::parseArgs(const Arg &arg) { if (optind >= arg.argc) { return USAGE; } // Optargs cannnot be used because the flag should not be considered set // if it should really be contained in mOptions. if (std::string(arg.argv[optind]) == "-E") { mExcludesParentInstances = true; optind++; } mInterfaceName = arg.argv[optind]; ++optind; for (; optind < arg.argc; ++optind) { mOptions.push_back(arg.argv[optind]); } return OK; } Status DebugCommand::main(const Arg &arg) { Status status = parseArgs(arg); if (status != OK) { return status; } auto pair = splitFirst(mInterfaceName, '/'); FQName fqName; if (!FQName::parse(pair.first, &fqName) || fqName.isIdentifier() || !fqName.isFullyQualified()) { mLshal.err() << "Invalid fully-qualified name '" << pair.first << "'\n\n"; return USAGE; } return mLshal.emitDebugInfo( pair.first, pair.second.empty() ? "default" : pair.second, mOptions, mExcludesParentInstances, mLshal.out().buf(), mLshal.err()); } void DebugCommand::usage() const { static const std::string debug = "debug:\n" " lshal debug [-E] [options [options [...]]] \n" " Print debug information of a specified interface.\n" " -E: excludes debug output if HAL is actually a subclass.\n" " : Format is `android.hardware.foo@1.0::IFoo/default`.\n" " If instance name is missing `default` is used.\n" " options: space separated options to IBase::debug.\n"; mLshal.err() << debug; } } // namespace lshal } // namespace android cmds/lshal/DebugCommand.h0100644 0000000 0000000 00000003150 13756501734 014346 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ #include #include #include "Command.h" #include "utils.h" namespace android { namespace lshal { class Lshal; class DebugCommand : public Command { public: explicit DebugCommand(Lshal &lshal) : Command(lshal) {} ~DebugCommand() = default; Status main(const Arg &arg) override; void usage() const override; std::string getSimpleDescription() const override; std::string getName() const override; private: Status parseArgs(const Arg &arg); std::string mInterfaceName; std::vector mOptions; // Outputs the actual descriptor of a hal instead of the debug output // if the arguments provided are a superclass of the actual hal impl. bool mExcludesParentInstances; DISALLOW_COPY_AND_ASSIGN(DebugCommand); }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ cmds/lshal/HelpCommand.cpp0100644 0000000 0000000 00000003725 13756501734 014553 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "HelpCommand.h" #include "Lshal.h" namespace android { namespace lshal { std::string HelpCommand::GetName() { return "help"; } std::string HelpCommand::getSimpleDescription() const { return "Print help message."; } Status HelpCommand::main(const Arg &arg) { if (optind >= arg.argc) { // `lshal help` prints global usage. mLshal.usage(); return OK; } (void)usageOfCommand(arg.argv[optind]); return OK; } Status HelpCommand::usageOfCommand(const std::string& c) const { if (c.empty()) { mLshal.usage(); return USAGE; } auto command = mLshal.selectCommand(c); if (command == nullptr) { // from HelpCommand::main, `lshal help unknown` mLshal.usage(); return USAGE; } command->usage(); return USAGE; } void HelpCommand::usage() const { mLshal.err() << "help:" << std::endl << " lshal -h" << std::endl << " lshal --help" << std::endl << " lshal help" << std::endl << " Print this help message" << std::endl; mLshal.forEachCommand([&](const Command* e) { mLshal.err() << " lshal help " << e->getName() << std::endl << " Print help message for " << e->getName() << std::endl; }); } } // namespace lshal } // namespace android cmds/lshal/HelpCommand.h0100644 0000000 0000000 00000002564 13756501734 014220 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ #include #include #include "Command.h" #include "utils.h" namespace android { namespace lshal { class Lshal; class HelpCommand : public Command { public: explicit HelpCommand(Lshal &lshal) : Command(lshal) {} ~HelpCommand() = default; Status main(const Arg &arg) override; void usage() const override; std::string getSimpleDescription() const override; std::string getName() const override { return GetName(); } static std::string GetName(); Status usageOfCommand(const std::string& c) const; }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ cmds/lshal/ListCommand.cpp0100644 0000000 0000000 00000136106 13756501734 014576 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "ListCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Lshal.h" #include "PipeRelay.h" #include "Timeout.h" #include "utils.h" using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; namespace android { namespace lshal { vintf::SchemaType toSchemaType(Partition p) { return (p == Partition::SYSTEM) ? vintf::SchemaType::FRAMEWORK : vintf::SchemaType::DEVICE; } Partition toPartition(vintf::SchemaType t) { switch (t) { case vintf::SchemaType::FRAMEWORK: return Partition::SYSTEM; // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. case vintf::SchemaType::DEVICE: return Partition::VENDOR; } return Partition::UNKNOWN; } std::string getPackageAndVersion(const std::string& fqInstance) { return splitFirst(fqInstance, ':').first; } NullableOStream ListCommand::out() const { return mLshal.out(); } NullableOStream ListCommand::err() const { return mLshal.err(); } std::string ListCommand::GetName() { return "list"; } std::string ListCommand::getSimpleDescription() const { return "List HALs."; } std::string ListCommand::parseCmdline(pid_t pid) const { return android::procpartition::getCmdline(pid); } const std::string &ListCommand::getCmdline(pid_t pid) { static const std::string kEmptyString{}; if (pid == NO_PID) return kEmptyString; auto pair = mCmdlines.find(pid); if (pair != mCmdlines.end()) { return pair->second; } mCmdlines[pid] = parseCmdline(pid); return mCmdlines[pid]; } void ListCommand::removeDeadProcesses(Pids *pids) { static const pid_t myPid = getpid(); pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) { return pid == myPid || this->getCmdline(pid).empty(); }), pids->end()); } Partition ListCommand::getPartition(pid_t pid) { if (pid == NO_PID) return Partition::UNKNOWN; auto it = mPartitions.find(pid); if (it != mPartitions.end()) { return it->second; } Partition partition = android::procpartition::getPartition(pid); mPartitions.emplace(pid, partition); return partition; } // Give sensible defaults when nothing can be inferred from runtime. // process: Partition inferred from executable location or cmdline. Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqInstance) const { if (fqInstance.inPackage("vendor") || fqInstance.inPackage("com")) { return Partition::VENDOR; } if (fqInstance.inPackage("android.frameworks") || fqInstance.inPackage("android.system") || fqInstance.inPackage("android.hidl")) { return Partition::SYSTEM; } // Some android.hardware HALs are served from system. Check the value from executable // location / cmdline first. if (fqInstance.inPackage("android.hardware")) { if (process != Partition::UNKNOWN) { return process; } return Partition::VENDOR; } return process; } bool match(const vintf::ManifestInstance& instance, const FqInstance& fqInstance, vintf::TransportArch ta) { // For hwbinder libs, allow missing arch in manifest. // For passthrough libs, allow missing interface/instance in table. return (ta.transport == instance.transport()) && (ta.transport == vintf::Transport::HWBINDER || vintf::contains(instance.arch(), ta.arch)) && (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) && (!fqInstance.hasInstance() || fqInstance.getInstance() == instance.instance()); } bool match(const vintf::MatrixInstance& instance, const FqInstance& fqInstance, vintf::TransportArch /* ta */) { return (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) && (!fqInstance.hasInstance() || instance.matchInstance(fqInstance.getInstance())); } template VintfInfo getVintfInfo(const std::shared_ptr& object, const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) { bool found = false; (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(), [&](const auto& instance) { found = match(instance, fqInstance, ta); return !found; // continue if not found }); return found ? value : VINTF_INFO_EMPTY; } std::shared_ptr ListCommand::getDeviceManifest() const { return vintf::VintfObject::GetDeviceHalManifest(); } std::shared_ptr ListCommand::getDeviceMatrix() const { return vintf::VintfObject::GetDeviceCompatibilityMatrix(); } std::shared_ptr ListCommand::getFrameworkManifest() const { return vintf::VintfObject::GetFrameworkHalManifest(); } std::shared_ptr ListCommand::getFrameworkMatrix() const { return vintf::VintfObject::GetFrameworkCompatibilityMatrix(); } VintfInfo ListCommand::getVintfInfo(const std::string& fqInstanceName, vintf::TransportArch ta) const { FqInstance fqInstance; if (!fqInstance.setTo(fqInstanceName) && // Ignore interface / instance for passthrough libs !fqInstance.setTo(getPackageAndVersion(fqInstanceName))) { err() << "Warning: Cannot parse '" << fqInstanceName << "'; no VINTF info." << std::endl; return VINTF_INFO_EMPTY; } return lshal::getVintfInfo(getDeviceManifest(), fqInstance, ta, DEVICE_MANIFEST) | lshal::getVintfInfo(getFrameworkManifest(), fqInstance, ta, FRAMEWORK_MANIFEST) | lshal::getVintfInfo(getDeviceMatrix(), fqInstance, ta, DEVICE_MATRIX) | lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX); } static bool scanBinderContext(pid_t pid, const std::string &contextName, std::function eachLine) { std::ifstream ifs("/d/binder/proc/" + std::to_string(pid)); if (!ifs.is_open()) { return false; } static const std::regex kContextLine("^context (\\w+)$"); bool isDesiredContext = false; std::string line; std::smatch match; while(getline(ifs, line)) { if (std::regex_search(line, match, kContextLine)) { isDesiredContext = match.str(1) == contextName; continue; } if (!isDesiredContext) { continue; } eachLine(line); } return true; } bool ListCommand::getPidInfo( pid_t serverPid, PidInfo *pidInfo) const { static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+"); static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)"); std::smatch match; return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) { if (std::regex_search(line, match, kReferencePrefix)) { const std::string &ptrString = "0x" + match.str(2); // use number after c uint64_t ptr; if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) { // Should not reach here, but just be tolerant. err() << "Could not parse number " << ptrString << std::endl; return; } const std::string proc = " proc "; auto pos = line.rfind(proc); if (pos != std::string::npos) { for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) { int32_t pid; if (!::android::base::ParseInt(pidStr, &pid)) { err() << "Could not parse number " << pidStr << std::endl; return; } pidInfo->refPids[ptr].push_back(pid); } } return; } if (std::regex_search(line, match, kThreadPrefix)) { // "1" is waiting in binder driver // "2" is poll. It's impossible to tell if these are in use. // and HIDL default code doesn't use it. bool isInUse = match.str(1) != "1"; // "0" is a thread that has called into binder // "1" is looper thread // "2" is main looper thread bool isHwbinderThread = match.str(2) != "0"; if (!isHwbinderThread) { return; } if (isInUse) { pidInfo->threadUsage++; } pidInfo->threadCount++; return; } // not reference or thread line return; }); } const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) { auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}}); if (pair.second /* did insertion take place? */) { if (!getPidInfo(serverPid, &pair.first->second)) { return nullptr; } } return &pair.first->second; } bool ListCommand::shouldFetchHalType(const HalType &type) const { return (std::find(mFetchTypes.begin(), mFetchTypes.end(), type) != mFetchTypes.end()); } Table* ListCommand::tableForType(HalType type) { switch (type) { case HalType::BINDERIZED_SERVICES: return &mServicesTable; case HalType::PASSTHROUGH_CLIENTS: return &mPassthroughRefTable; case HalType::PASSTHROUGH_LIBRARIES: return &mImplementationsTable; case HalType::VINTF_MANIFEST: return &mManifestHalsTable; case HalType::LAZY_HALS: return &mLazyHalsTable; default: LOG(FATAL) << "Unknown HAL type " << static_cast(type); return nullptr; } } const Table* ListCommand::tableForType(HalType type) const { return const_cast(this)->tableForType(type); } void ListCommand::forEachTable(const std::function &f) { for (const auto& type : mListTypes) { f(*tableForType(type)); } } void ListCommand::forEachTable(const std::function &f) const { for (const auto& type : mListTypes) { f(*tableForType(type)); } } void ListCommand::postprocess() { forEachTable([this](Table &table) { if (mSortColumn) { std::sort(table.begin(), table.end(), mSortColumn); } for (TableEntry &entry : table) { entry.serverCmdline = getCmdline(entry.serverPid); removeDeadProcesses(&entry.clientPids); for (auto pid : entry.clientPids) { entry.clientCmdlines.push_back(this->getCmdline(pid)); } } for (TableEntry& entry : table) { if (entry.partition == Partition::UNKNOWN) { entry.partition = getPartition(entry.serverPid); } entry.vintfInfo = getVintfInfo(entry.interfaceName, {entry.transport, entry.arch}); } }); // use a double for loop here because lshal doesn't care about efficiency. for (TableEntry &packageEntry : mImplementationsTable) { std::string packageName = packageEntry.interfaceName; FQName fqPackageName; if (!FQName::parse(packageName.substr(0, packageName.find("::")), &fqPackageName)) { continue; } for (TableEntry &interfaceEntry : mPassthroughRefTable) { if (interfaceEntry.arch != vintf::Arch::ARCH_EMPTY) { continue; } FQName interfaceName; if (!FQName::parse(splitFirst(interfaceEntry.interfaceName, '/').first, &interfaceName)) { continue; } if (interfaceName.getPackageAndVersion() == fqPackageName) { interfaceEntry.arch = packageEntry.arch; } } } mServicesTable.setDescription( "| All binderized services (registered with hwservicemanager)"); mPassthroughRefTable.setDescription( "| All interfaces that getService() has ever returned as a passthrough interface;\n" "| PIDs / processes shown below might be inaccurate because the process\n" "| might have relinquished the interface or might have died.\n" "| The Server / Server CMD column can be ignored.\n" "| The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" "| the library and successfully fetched the passthrough implementation."); mImplementationsTable.setDescription( "| All available passthrough implementations (all -impl.so files).\n" "| These may return subclasses through their respective HIDL_FETCH_I* functions."); mManifestHalsTable.setDescription( "| All HALs that are in VINTF manifest."); mLazyHalsTable.setDescription( "| All HALs that are declared in VINTF manifest:\n" "| - as hwbinder HALs but are not registered to hwservicemanager, and\n" "| - as hwbinder/passthrough HALs with no implementation."); } bool ListCommand::addEntryWithInstance(const TableEntry& entry, vintf::HalManifest* manifest) const { FqInstance fqInstance; if (!fqInstance.setTo(entry.interfaceName)) { err() << "Warning: '" << entry.interfaceName << "' is not a valid FqInstance." << std::endl; return false; } if (fqInstance.getPackage() == gIBaseFqName.package()) { return true; // always remove IBase from manifest } Partition partition = resolvePartition(entry.partition, fqInstance); if (partition == Partition::UNKNOWN) { err() << "Warning: Cannot guess the partition of FqInstance " << fqInstance.string() << std::endl; return false; } if (partition != mVintfPartition) { return true; // strip out instances that is in a different partition. } vintf::Arch arch; if (entry.transport == vintf::Transport::HWBINDER) { arch = vintf::Arch::ARCH_EMPTY; // no need to specify arch in manifest } else if (entry.transport == vintf::Transport::PASSTHROUGH) { if (entry.arch == vintf::Arch::ARCH_EMPTY) { err() << "Warning: '" << entry.interfaceName << "' doesn't have bitness info."; return false; } arch = entry.arch; } else { err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; return false; } std::string e; if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) { err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl; return false; } return true; } bool ListCommand::addEntryWithoutInstance(const TableEntry& entry, const vintf::HalManifest* manifest) const { const auto& packageAndVersion = splitFirst(getPackageAndVersion(entry.interfaceName), '@'); const auto& package = packageAndVersion.first; vintf::Version version; if (!vintf::parse(packageAndVersion.second, &version)) { err() << "Warning: Cannot parse version '" << packageAndVersion.second << "' for entry '" << entry.interfaceName << "'" << std::endl; return false; } bool found = false; (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) { found = true; return false; // break }); return found; } void ListCommand::dumpVintf(const NullableOStream& out) const { using vintf::operator|=; using vintf::operator<<; using namespace std::placeholders; vintf::HalManifest manifest; manifest.setType(toSchemaType(mVintfPartition)); std::vector error; for (const TableEntry& entry : mServicesTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); for (const TableEntry& entry : mPassthroughRefTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); for (const TableEntry& entry : mManifestHalsTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); std::vector passthrough; for (const TableEntry& entry : mImplementationsTable) if (!addEntryWithoutInstance(entry, &manifest)) passthrough.push_back(entry.interfaceName); out << "" << std::endl; out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlags::HALS_ONLY); } std::string ListCommand::INIT_VINTF_NOTES{ " 1. If a HAL is supported in both hwbinder and passthrough transport,\n" " only hwbinder is shown.\n" " 2. It is likely that HALs in passthrough transport does not have\n" " declared; users will have to write them by hand.\n" " 3. A HAL with lower minor version can be overridden by a HAL with\n" " higher minor version if they have the same name and major version.\n" " 4. This output is intended for launch devices.\n" " Upgrading devices should not use this tool to generate device\n" " manifest and replace the existing manifest directly, but should\n" " edit the existing manifest manually.\n" " Specifically, devices which launched at Android O-MR1 or earlier\n" " should not use the 'fqname' format for required HAL entries and\n" " should instead use the legacy package, name, instance-name format\n" " until they are updated.\n" }; static vintf::Arch fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { switch (a) { case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT: return vintf::Arch::ARCH_64; case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT: return vintf::Arch::ARCH_32; case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough default: return vintf::Arch::ARCH_EMPTY; } } void ListCommand::dumpTable(const NullableOStream& out) const { if (mNeat) { std::vector tables; forEachTable([&tables](const Table &table) { tables.push_back(&table); }); MergedTable(std::move(tables)).createTextTable().dump(out.buf()); return; } forEachTable([this, &out](const Table &table) { // We're only interested in dumping debug info for already // instantiated services. There's little value in dumping the // debug info for a service we create on the fly, so we only operate // on the "mServicesTable". std::function emitDebugInfo = nullptr; if (mEmitDebugInfo && &table == &mServicesTable) { emitDebugInfo = [this](const auto& iName) { std::stringstream ss; auto pair = splitFirst(iName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, false /* excludesParentInstances */, ss, NullableOStream(nullptr)); return ss.str(); }; } table.createTextTable(mNeat, emitDebugInfo).dump(out.buf()); out << std::endl; }); } Status ListCommand::dump() { auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable; if (mFileOutputPath.empty()) { (*this.*dump)(out()); return OK; } std::ofstream fileOutput(mFileOutputPath); if (!fileOutput.is_open()) { err() << "Could not open file '" << mFileOutputPath << "'." << std::endl; return IO_ERROR; } chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL); (*this.*dump)(NullableOStream(fileOutput)); fileOutput.flush(); fileOutput.close(); return OK; } void ListCommand::putEntry(HalType type, TableEntry &&entry) { tableForType(type)->add(std::forward(entry)); } Status ListCommand::fetchAllLibraries(const sp &manager) { if (!shouldFetchHalType(HalType::PASSTHROUGH_LIBRARIES)) { return OK; } using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; using std::literals::chrono_literals::operator""s; auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) { std::map entries; for (const auto &info : infos) { std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" + std::string{info.instanceName.c_str()}; entries.emplace(interfaceName, TableEntry{ .interfaceName = interfaceName, .transport = vintf::Transport::PASSTHROUGH, .clientPids = info.clientPids, }).first->second.arch |= fromBaseArchitecture(info.arch); } for (auto &&pair : entries) { putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second)); } }); if (!ret.isOk()) { err() << "Error: Failed to call list on getPassthroughServiceManager(): " << ret.description() << std::endl; return DUMP_ALL_LIBS_ERROR; } return OK; } Status ListCommand::fetchPassthrough(const sp &manager) { if (!shouldFetchHalType(HalType::PASSTHROUGH_CLIENTS)) { return OK; } using namespace ::android::hardware; using namespace ::android::hardware::details; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) { for (const auto &info : infos) { if (info.clientPids.size() <= 0) { continue; } putEntry(HalType::PASSTHROUGH_CLIENTS, { .interfaceName = std::string{info.interfaceName.c_str()} + "/" + std::string{info.instanceName.c_str()}, .transport = vintf::Transport::PASSTHROUGH, .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, .clientPids = info.clientPids, .arch = fromBaseArchitecture(info.arch) }); } }); if (!ret.isOk()) { err() << "Error: Failed to call debugDump on defaultServiceManager(): " << ret.description() << std::endl; return DUMP_PASSTHROUGH_ERROR; } return OK; } Status ListCommand::fetchBinderized(const sp &manager) { using vintf::operator<<; if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; } const vintf::Transport mode = vintf::Transport::HWBINDER; hidl_vec fqInstanceNames; // copying out for timeoutIPC auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) { fqInstanceNames = names; }); if (!listRet.isOk()) { err() << "Error: Failed to list services for " << mode << ": " << listRet.description() << std::endl; return DUMP_BINDERIZED_ERROR; } Status status = OK; std::map allTableEntries; for (const auto &fqInstanceName : fqInstanceNames) { // create entry and default assign all fields. TableEntry& entry = allTableEntries[fqInstanceName]; entry.interfaceName = fqInstanceName; entry.transport = mode; entry.serviceStatus = ServiceStatus::NON_RESPONSIVE; status |= fetchBinderizedEntry(manager, &entry); } for (auto& pair : allTableEntries) { putEntry(HalType::BINDERIZED_SERVICES, std::move(pair.second)); } return status; } Status ListCommand::fetchBinderizedEntry(const sp &manager, TableEntry *entry) { Status status = OK; const auto handleError = [&](Status additionalError, const std::string& msg) { err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl; status |= DUMP_BINDERIZED_ERROR | additionalError; }; const auto pair = splitFirst(entry->interfaceName, '/'); const auto &serviceName = pair.first; const auto &instanceName = pair.second; auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); if (!getRet.isOk()) { handleError(TRANSACTION_ERROR, "cannot be fetched from service manager:" + getRet.description()); return status; } sp service = getRet; if (service == nullptr) { handleError(NO_INTERFACE, "cannot be fetched from service manager (null)"); return status; } // getDebugInfo do { DebugInfo debugInfo; auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) { debugInfo = received; }); if (!debugRet.isOk()) { handleError(TRANSACTION_ERROR, "debugging information cannot be retrieved: " + debugRet.description()); break; // skip getPidInfo } entry->serverPid = debugInfo.pid; entry->serverObjectAddress = debugInfo.ptr; entry->arch = fromBaseArchitecture(debugInfo.arch); if (debugInfo.pid != NO_PID) { const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid); if (pidInfo == nullptr) { handleError(IO_ERROR, "no information for PID " + std::to_string(debugInfo.pid) + ", are you root?"); break; } if (debugInfo.ptr != NO_PTR) { auto it = pidInfo->refPids.find(debugInfo.ptr); if (it != pidInfo->refPids.end()) { entry->clientPids = it->second; } } entry->threadUsage = pidInfo->threadUsage; entry->threadCount = pidInfo->threadCount; } } while (0); // hash do { ssize_t hashIndex = -1; auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) { for (size_t i = 0; i < c.size(); ++i) { if (serviceName == c[i]) { hashIndex = static_cast(i); break; } } }); if (!ifaceChainRet.isOk()) { handleError(TRANSACTION_ERROR, "interfaceChain fails: " + ifaceChainRet.description()); break; // skip getHashChain } if (hashIndex < 0) { handleError(BAD_IMPL, "Interface name does not exist in interfaceChain."); break; // skip getHashChain } auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) { if (static_cast(hashIndex) >= hashChain.size()) { handleError(BAD_IMPL, "interfaceChain indicates position " + std::to_string(hashIndex) + " but getHashChain returns " + std::to_string(hashChain.size()) + " hashes"); return; } auto&& hashArray = hashChain[hashIndex]; std::vector hashVec{hashArray.data(), hashArray.data() + hashArray.size()}; entry->hash = Hash::hexString(hashVec); }); if (!hashRet.isOk()) { handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); } } while (0); if (status == OK) { entry->serviceStatus = ServiceStatus::ALIVE; } return status; } Status ListCommand::fetchManifestHals() { if (!shouldFetchHalType(HalType::VINTF_MANIFEST)) { return OK; } Status status = OK; for (auto manifest : {getDeviceManifest(), getFrameworkManifest()}) { if (manifest == nullptr) { status |= VINTF_ERROR; continue; } std::map entries; manifest->forEachInstance([&] (const vintf::ManifestInstance& manifestInstance) { TableEntry entry{ .interfaceName = manifestInstance.getFqInstance().string(), .transport = manifestInstance.transport(), .arch = manifestInstance.arch(), // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. .partition = toPartition(manifest->type()), .serviceStatus = ServiceStatus::DECLARED}; std::string key = entry.interfaceName; entries.emplace(std::move(key), std::move(entry)); return true; }); for (auto&& pair : entries) mManifestHalsTable.add(std::move(pair.second)); } return status; } Status ListCommand::fetchLazyHals() { using vintf::operator<<; if (!shouldFetchHalType(HalType::LAZY_HALS)) { return OK; } Status status = OK; for (const TableEntry& manifestEntry : mManifestHalsTable) { if (manifestEntry.transport == vintf::Transport::HWBINDER) { if (!hasHwbinderEntry(manifestEntry)) { mLazyHalsTable.add(TableEntry(manifestEntry)); } continue; } if (manifestEntry.transport == vintf::Transport::PASSTHROUGH) { if (!hasPassthroughEntry(manifestEntry)) { mLazyHalsTable.add(TableEntry(manifestEntry)); } continue; } err() << "Warning: unrecognized transport in VINTF manifest: " << manifestEntry.transport; status |= VINTF_ERROR; } return status; } bool ListCommand::hasHwbinderEntry(const TableEntry& entry) const { for (const TableEntry& existing : mServicesTable) { if (existing.interfaceName == entry.interfaceName) { return true; } } return false; } bool ListCommand::hasPassthroughEntry(const TableEntry& entry) const { FqInstance entryFqInstance; if (!entryFqInstance.setTo(entry.interfaceName)) { return false; // cannot parse, so add it anyway. } for (const TableEntry& existing : mImplementationsTable) { FqInstance existingFqInstance; if (!existingFqInstance.setTo(getPackageAndVersion(existing.interfaceName))) { continue; } // For example, manifest may say graphics.mapper@2.1 but passthroughServiceManager // can only list graphics.mapper@2.0. if (entryFqInstance.getPackage() == existingFqInstance.getPackage() && vintf::Version{entryFqInstance.getVersion()} .minorAtLeast(vintf::Version{existingFqInstance.getVersion()})) { return true; } } return false; } Status ListCommand::fetch() { Status status = OK; auto bManager = mLshal.serviceManager(); if (bManager == nullptr) { err() << "Failed to get defaultServiceManager()!" << std::endl; status |= NO_BINDERIZED_MANAGER; } else { status |= fetchBinderized(bManager); // Passthrough PIDs are registered to the binderized manager as well. status |= fetchPassthrough(bManager); } auto pManager = mLshal.passthroughManager(); if (pManager == nullptr) { err() << "Failed to get getPassthroughServiceManager()!" << std::endl; status |= NO_PASSTHROUGH_MANAGER; } else { status |= fetchAllLibraries(pManager); } status |= fetchManifestHals(); status |= fetchLazyHals(); return status; } void ListCommand::initFetchTypes() { // TODO: refactor to do polymorphism on each table (so that dependency graph is not hardcoded). static const std::map> kDependencyGraph{ {HalType::LAZY_HALS, {HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_LIBRARIES, HalType::VINTF_MANIFEST}}, }; mFetchTypes.insert(mListTypes.begin(), mListTypes.end()); for (HalType listType : mListTypes) { auto it = kDependencyGraph.find(listType); if (it != kDependencyGraph.end()) { mFetchTypes.insert(it->second.begin(), it->second.end()); } } } void ListCommand::registerAllOptions() { int v = mOptions.size(); // A list of acceptable command line options // key: value returned by getopt_long // long options with short alternatives mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) { return USAGE; }, ""}); mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME); return OK; }, "print the instance name column"}); mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::RELEASED); return OK; }, "print the 'is released?' column\n(Y=released, N=unreleased, ?=unknown)"}); mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT); return OK; }, "print the transport mode column"}); mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::ARCH); return OK; }, "print the bitness column"}); mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::HASH); return OK; }, "print hash of the interface"}); mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID); return OK; }, "print the server PID, or server cmdline if -m is set"}); mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR); return OK; }, "print the server object address column"}); mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS); return OK; }, "print the client PIDs, or client cmdlines if -m is set"}); mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::THREADS); return OK; }, "print currently used/available threads\n(note, available threads created lazily)"}); mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mEnableCmdlines = true; return OK; }, "print cmdline instead of PIDs"}); mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) { thiz->mEmitDebugInfo = true; if (arg) thiz->mFileOutputPath = arg; return OK; }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n" "Writes to specified file if 'arg' is provided, otherwise stdout."}); mOptions.push_back({'V', "vintf", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::VINTF); return OK; }, "print VINTF info. This column contains a comma-separated list of:\n" " - DM: if the HAL is in the device manifest\n" " - DC: if the HAL is in the device compatibility matrix\n" " - FM: if the HAL is in the framework manifest\n" " - FC: if the HAL is in the framework compatibility matrix"}); mOptions.push_back({'S', "service-status", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::SERVICE_STATUS); return OK; }, "print service status column. Possible values are:\n" " - alive: alive and running hwbinder service;\n" " - registered;dead: registered to hwservicemanager but is not responsive;\n" " - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n" " - N/A: no information for passthrough HALs."}); // long options without short alternatives mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) { thiz->mVintf = true; if (thiz->mVintfPartition == Partition::UNKNOWN) thiz->mVintfPartition = Partition::VENDOR; if (arg) thiz->mFileOutputPath = arg; return OK; }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."}); mOptions.push_back({'\0', "init-vintf-partition", required_argument, v++, [](ListCommand* thiz, const char* arg) { if (!arg) return USAGE; thiz->mVintfPartition = android::procpartition::parsePartition(arg); if (thiz->mVintfPartition == Partition::UNKNOWN) return USAGE; return OK; }, "Specify the partition of the HAL manifest\ngenerated by --init-vintf.\n" "Valid values are 'system', 'vendor', and 'odm'. Default is 'vendor'."}); mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) { if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) { thiz->mSortColumn = TableEntry::sortByInterfaceName; } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) { thiz->mSortColumn = TableEntry::sortByServerPid; } else { thiz->err() << "Unrecognized sorting column: " << arg << std::endl; return USAGE; } return OK; }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."}); mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mNeat = true; return OK; }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."}); mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) { if (!arg) { return USAGE; } static const std::map kHalTypeMap { {"binderized", HalType::BINDERIZED_SERVICES}, {"b", HalType::BINDERIZED_SERVICES}, {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS}, {"c", HalType::PASSTHROUGH_CLIENTS}, {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES}, {"l", HalType::PASSTHROUGH_LIBRARIES}, {"vintf", HalType::VINTF_MANIFEST}, {"v", HalType::VINTF_MANIFEST}, {"lazy", HalType::LAZY_HALS}, {"z", HalType::LAZY_HALS}, }; std::vector halTypesArgs = split(std::string(arg), ','); for (const auto& halTypeArg : halTypesArgs) { if (halTypeArg.empty()) continue; const auto& halTypeIter = kHalTypeMap.find(halTypeArg); if (halTypeIter == kHalTypeMap.end()) { thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl; return USAGE; } // Append unique (non-repeated) HAL types to the reporting list HalType halType = halTypeIter->second; if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) == thiz->mListTypes.end()) { thiz->mListTypes.push_back(halType); } } if (thiz->mListTypes.empty()) { return USAGE; } return OK; }, "comma-separated list of one or more sections.\nThe output is restricted to the selected " "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|" "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."}); } // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining // the lifetime of "options" during the usage of the returned array. static std::unique_ptr getLongOptions( const ListCommand::RegisteredOptions& options, int* longOptFlag) { std::unique_ptr ret{new struct option[options.size() + 1]}; int i = 0; for (const auto& e : options) { ret[i].name = e.longOption.c_str(); ret[i].has_arg = e.hasArg; ret[i].flag = longOptFlag; ret[i].val = e.val; i++; } // getopt_long last option has all zeros ret[i].name = nullptr; ret[i].has_arg = 0; ret[i].flag = nullptr; ret[i].val = 0; return ret; } // Create 'optstring' argument to getopt_long. static std::string getShortOptions(const ListCommand::RegisteredOptions& options) { std::stringstream ss; for (const auto& e : options) { if (e.shortOption != '\0') { ss << e.shortOption; } } return ss.str(); } Status ListCommand::parseArgs(const Arg &arg) { mListTypes.clear(); if (mOptions.empty()) { registerAllOptions(); } int longOptFlag; std::unique_ptr longOptions = getLongOptions(mOptions, &longOptFlag); std::string shortOptions = getShortOptions(mOptions); // suppress output to std::err for unknown options opterr = 0; int optionIndex; int c; // Lshal::parseArgs has set optind to the next option to parse for (;;) { c = getopt_long(arg.argc, arg.argv, shortOptions.c_str(), longOptions.get(), &optionIndex); if (c == -1) { break; } const RegisteredOption* found = nullptr; if (c == 0) { // see long option for (const auto& e : mOptions) { if (longOptFlag == e.val) found = &e; } } else { // see short option for (const auto& e : mOptions) { if (c == e.shortOption) found = &e; } } if (found == nullptr) { // see unrecognized options err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl; return USAGE; } Status status = found->op(this, optarg); if (status != OK) { return status; } } if (optind < arg.argc) { // see non option err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl; return USAGE; } if (mNeat && mEmitDebugInfo) { err() << "Error: --neat should not be used with --debug." << std::endl; return USAGE; } if (mSelectedColumns.empty()) { mSelectedColumns = {TableColumnType::VINTF, TableColumnType::RELEASED, TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } if (mEnableCmdlines) { for (size_t i = 0; i < mSelectedColumns.size(); ++i) { if (mSelectedColumns[i] == TableColumnType::SERVER_PID) { mSelectedColumns[i] = TableColumnType::SERVER_CMD; } if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) { mSelectedColumns[i] = TableColumnType::CLIENT_CMDS; } } } // By default, list all HAL types if (mListTypes.empty()) { mListTypes = {HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_CLIENTS, HalType::PASSTHROUGH_LIBRARIES}; } initFetchTypes(); forEachTable([this] (Table& table) { table.setSelectedColumns(this->mSelectedColumns); }); return OK; } Status ListCommand::main(const Arg &arg) { Status status = parseArgs(arg); if (status != OK) { return status; } status = fetch(); postprocess(); status |= dump(); return status; } const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const { static const std::string empty{}; static const std::string optional{"[=]"}; static const std::string required{"="}; if (hasArg == optional_argument) { return optional; } if (hasArg == required_argument) { return required; } return empty; } void ListCommand::usage() const { err() << "list:" << std::endl << " lshal" << std::endl << " lshal list" << std::endl << " List all hals with default ordering and columns (`lshal list -Vliepc`)" << std::endl << " lshal list [-h|--help]" << std::endl << " -h, --help: Print help message for list (`lshal help list`)" << std::endl << " lshal [list] [OPTIONS...]" << std::endl; for (const auto& e : mOptions) { if (e.help.empty()) { continue; } err() << " "; if (e.shortOption != '\0') err() << "-" << e.shortOption << e.getHelpMessageForArgument(); if (e.shortOption != '\0' && !e.longOption.empty()) err() << ", "; if (!e.longOption.empty()) err() << "--" << e.longOption << e.getHelpMessageForArgument(); err() << ": "; std::vector lines = split(e.help, '\n'); for (const auto& line : lines) { if (&line != &lines.front()) err() << " "; err() << line << std::endl; } } } } // namespace lshal } // namespace android cmds/lshal/ListCommand.h0100644 0000000 0000000 00000017217 13756501734 014244 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ #include #include #include #include #include #include #include #include #include #include #include "Command.h" #include "NullableOStream.h" #include "TableEntry.h" #include "TextTable.h" #include "utils.h" namespace android { namespace lshal { class Lshal; struct PidInfo { std::map refPids; // pids that are referenced uint32_t threadUsage; // number of threads in use uint32_t threadCount; // number of threads total }; enum class HalType { BINDERIZED_SERVICES = 0, PASSTHROUGH_CLIENTS, PASSTHROUGH_LIBRARIES, VINTF_MANIFEST, LAZY_HALS, }; class ListCommand : public Command { public: explicit ListCommand(Lshal &lshal) : Command(lshal) {} virtual ~ListCommand() = default; Status main(const Arg &arg) override; void usage() const override; std::string getSimpleDescription() const override; std::string getName() const override { return GetName(); } static std::string GetName(); struct RegisteredOption { // short alternative, e.g. 'v'. If '\0', no short options is available. char shortOption; // long alternative, e.g. 'init-vintf' std::string longOption; // no_argument, required_argument or optional_argument int hasArg; // value written to 'flag' by getopt_long int val; // operation when the argument is present std::function op; // help message std::string help; const std::string& getHelpMessageForArgument() const; }; // A list of acceptable command line options // key: value returned by getopt_long using RegisteredOptions = std::vector; static std::string INIT_VINTF_NOTES; protected: Status parseArgs(const Arg &arg); // Retrieve first-hand information Status fetch(); // Retrieve derived information base on existing table virtual void postprocess(); Status dump(); void putEntry(HalType type, TableEntry &&entry); Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchManifestHals(); Status fetchLazyHals(); Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager, TableEntry *entry); // Get relevant information for a PID by parsing files under /d/binder. // It is a virtual member function so that it can be mocked. virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; // Retrieve from mCachedPidInfos and call getPidInfo if necessary. const PidInfo* getPidInfoCached(pid_t serverPid); void dumpTable(const NullableOStream& out) const; void dumpVintf(const NullableOStream& out) const; void addLine(TextTable *table, const std::string &interfaceName, const std::string &transport, const std::string &arch, const std::string &threadUsage, const std::string &server, const std::string &serverCmdline, const std::string &address, const std::string &clients, const std::string &clientCmdlines) const; void addLine(TextTable *table, const TableEntry &entry); // Read and return /proc/{pid}/cmdline. virtual std::string parseCmdline(pid_t pid) const; // Return /proc/{pid}/cmdline if it exists, else empty string. const std::string& getCmdline(pid_t pid); // Call getCmdline on all pid in pids. If it returns empty string, the process might // have died, and the pid is removed from pids. void removeDeadProcesses(Pids *pids); virtual Partition getPartition(pid_t pid); Partition resolvePartition(Partition processPartition, const FqInstance &fqInstance) const; VintfInfo getVintfInfo(const std::string &fqInstanceName, vintf::TransportArch ta) const; // Allow to mock these functions for testing. virtual std::shared_ptr getDeviceManifest() const; virtual std::shared_ptr getDeviceMatrix() const; virtual std::shared_ptr getFrameworkManifest() const; virtual std::shared_ptr getFrameworkMatrix() const; void forEachTable(const std::function &f); void forEachTable(const std::function &f) const; Table* tableForType(HalType type); const Table* tableForType(HalType type) const; NullableOStream err() const; NullableOStream out() const; void registerAllOptions(); // helper functions to dumpVintf. bool addEntryWithInstance(const TableEntry &entry, vintf::HalManifest *manifest) const; bool addEntryWithoutInstance(const TableEntry &entry, const vintf::HalManifest *manifest) const; // Helper function. Whether to fetch entries corresponding to a given HAL type. bool shouldFetchHalType(const HalType &type) const; void initFetchTypes(); // Helper functions ti add HALs that are listed in VINTF manifest to LAZY_HALS table. bool hasHwbinderEntry(const TableEntry& entry) const; bool hasPassthroughEntry(const TableEntry& entry) const; Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; Table mManifestHalsTable{}; Table mLazyHalsTable{}; std::string mFileOutputPath; TableEntryCompare mSortColumn = nullptr; bool mEmitDebugInfo = false; // If true, output in VINTF format. Output only entries from the specified partition. bool mVintf = false; Partition mVintfPartition = Partition::UNKNOWN; // If true, explanatory text are not emitted. bool mNeat = false; // Type(s) of HAL associations to list. std::vector mListTypes{}; // Type(s) of HAL associations to fetch. std::set mFetchTypes{}; // If an entry does not exist, need to ask /proc/{pid}/cmdline to get it. // If an entry exist but is an empty string, process might have died. // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline. std::map mCmdlines; // Cache for getPidInfo. std::map mCachedPidInfos; // Cache for getPartition. std::map mPartitions; RegisteredOptions mOptions; // All selected columns std::vector mSelectedColumns; // If true, emit cmdlines instead of PIDs bool mEnableCmdlines = false; private: DISALLOW_COPY_AND_ASSIGN(ListCommand); }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ cmds/lshal/Lshal.cpp0100644 0000000 0000000 00000016544 13756501734 013432 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #define LOG_TAG "lshal" #include #include "Lshal.h" #include #include #include #include #include "DebugCommand.h" #include "ListCommand.h" #include "PipeRelay.h" namespace android { namespace lshal { using ::android::hidl::manager::V1_0::IServiceManager; Lshal::Lshal() : Lshal(std::cout, std::cerr, ::android::hardware::defaultServiceManager(), ::android::hardware::getPassthroughServiceManager()) { } Lshal::Lshal(std::ostream &out, std::ostream &err, sp serviceManager, sp passthroughManager) : mOut(out), mErr(err), mServiceManager(serviceManager), mPassthroughManager(passthroughManager) { mRegisteredCommands.push_back({std::make_unique(*this)}); mRegisteredCommands.push_back({std::make_unique(*this)}); mRegisteredCommands.push_back({std::make_unique(*this)}); } void Lshal::forEachCommand(const std::function& f) const { for (const auto& e : mRegisteredCommands) f(e.get()); } void Lshal::usage() { err() << "lshal: List and debug HALs." << std::endl << std::endl << "commands:" << std::endl; size_t nameMaxLength = 0; forEachCommand([&](const Command* e) { nameMaxLength = std::max(nameMaxLength, e->getName().length()); }); bool first = true; forEachCommand([&](const Command* e) { if (!first) err() << std::endl; first = false; err() << " " << std::left << std::setw(nameMaxLength + 8) << e->getName() << e->getSimpleDescription(); }); err() << std::endl << "If no command is specified, `" << ListCommand::GetName() << "` is the default." << std::endl << std::endl; first = true; forEachCommand([&](const Command* e) { if (!first) err() << std::endl; first = false; e->usage(); }); } // A unique_ptr type using a custom deleter function. template using deleted_unique_ptr = std::unique_ptr >; static hardware::hidl_vec convert(const std::vector &v) { hardware::hidl_vec hv; hv.resize(v.size()); for (size_t i = 0; i < v.size(); ++i) { hv[i].setToExternal(v[i].c_str(), v[i].size()); } return hv; } Status Lshal::emitDebugInfo( const std::string &interfaceName, const std::string &instanceName, const std::vector &options, bool excludesParentInstances, std::ostream &out, NullableOStream err) const { using android::hidl::base::V1_0::IBase; using android::hardware::details::getDescriptor; hardware::Return> retBase = serviceManager()->get(interfaceName, instanceName); if (!retBase.isOk()) { std::string msg = "Cannot get " + interfaceName + "/" + instanceName + ": " + retBase.description(); err << msg << std::endl; LOG(ERROR) << msg; return TRANSACTION_ERROR; } sp base = retBase; if (base == nullptr) { std::string msg = interfaceName + "/" + instanceName + " does not exist, or " + "no permission to connect."; err << msg << std::endl; LOG(ERROR) << msg; return NO_INTERFACE; } if (excludesParentInstances) { const std::string descriptor = getDescriptor(base.get()); if (descriptor.empty()) { std::string msg = interfaceName + "/" + instanceName + " getDescriptor failed"; err << msg << std::endl; LOG(ERROR) << msg; } if (descriptor != interfaceName) { return OK; } } PipeRelay relay(out); if (relay.initCheck() != OK) { std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck()); err << msg << std::endl; LOG(ERROR) << msg; return IO_ERROR; } deleted_unique_ptr fdHandle( native_handle_create(1 /* numFds */, 0 /* numInts */), native_handle_delete); fdHandle->data[0] = relay.fd(); hardware::Return ret = base->debug(fdHandle.get(), convert(options)); if (!ret.isOk()) { std::string msg = "debug() FAILED on " + interfaceName + "/" + instanceName + ": " + ret.description(); err << msg << std::endl; LOG(ERROR) << msg; return TRANSACTION_ERROR; } return OK; } Status Lshal::parseArgs(const Arg &arg) { optind = 1; if (optind >= arg.argc) { // no options at all. return OK; } mCommand = arg.argv[optind]; if (selectCommand(mCommand) != nullptr) { ++optind; return OK; // mCommand is set correctly } if (mCommand.size() > 0 && mCommand[0] == '-') { // first argument is an option, set command to "" (which is recognized as "list") mCommand.clear(); return OK; } err() << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "'" << std::endl; return USAGE; } void signalHandler(int sig) { if (sig == SIGINT) { int retVal; pthread_exit(&retVal); } } Command* Lshal::selectCommand(const std::string& command) const { if (command.empty()) { return selectCommand(ListCommand::GetName()); } for (const auto& e : mRegisteredCommands) { if (e->getName() == command) { return e.get(); } } return nullptr; } Status Lshal::main(const Arg &arg) { // Allow SIGINT to terminate all threads. signal(SIGINT, signalHandler); Status status = parseArgs(arg); if (status != OK) { usage(); return status; } auto c = selectCommand(mCommand); if (c == nullptr) { // unknown command, print global usage usage(); return USAGE; } status = c->main(arg); if (status == USAGE) { // bad options. Run `lshal help ${mCommand}` instead. // For example, `lshal --unknown-option` becomes `lshal help` (prints global help) // and `lshal list --unknown-option` becomes `lshal help list` auto&& help = selectCommand(HelpCommand::GetName()); return static_cast(help)->usageOfCommand(mCommand); } return status; } NullableOStream Lshal::err() const { return mErr; } NullableOStream Lshal::out() const { return mOut; } const sp &Lshal::serviceManager() const { return mServiceManager; } const sp &Lshal::passthroughManager() const { return mPassthroughManager; } } // namespace lshal } // namespace android cmds/lshal/Lshal.h0100644 0000000 0000000 00000004727 13756501734 013077 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ #include #include #include #include #include #include "Command.h" #include "HelpCommand.h" #include "NullableOStream.h" #include "utils.h" namespace android { namespace lshal { class Lshal { public: Lshal(); virtual ~Lshal() {} Lshal(std::ostream &out, std::ostream &err, sp serviceManager, sp passthroughManager); Status main(const Arg &arg); // global usage void usage(); virtual NullableOStream err() const; virtual NullableOStream out() const; const sp &serviceManager() const; const sp &passthroughManager() const; Status emitDebugInfo( const std::string &interfaceName, const std::string &instanceName, const std::vector &options, bool excludesParentInstances, std::ostream &out, NullableOStream err) const; Command* selectCommand(const std::string& command) const; void forEachCommand(const std::function& f) const; private: Status parseArgs(const Arg &arg); std::string mCommand; NullableOStream mOut; NullableOStream mErr; sp mServiceManager; sp mPassthroughManager; std::vector> mRegisteredCommands; DISALLOW_COPY_AND_ASSIGN(Lshal); }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ cmds/lshal/NullableOStream.h0100644 0000000 0000000 00000003650 13756501734 015057 0ustar000000000 0000000 /* * 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. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_ #include namespace android { namespace lshal { template class NullableOStream { public: explicit NullableOStream(S &os) : mOs(&os) {} explicit NullableOStream(S *os) : mOs(os) {} NullableOStream &operator=(S &os) { mOs = &os; return *this; } NullableOStream &operator=(S *os) { mOs = os; return *this; } template NullableOStream &operator=(const NullableOStream &other) { mOs = other.mOs; return *this; } const NullableOStream &operator<<(std::ostream& (*pf)(std::ostream&)) const { if (mOs) { (*mOs) << pf; } return *this; } template const NullableOStream &operator<<(const T &rhs) const { if (mOs) { (*mOs) << rhs; } return *this; } S& buf() const { return *mOs; } operator bool() const { // NOLINT(google-explicit-constructor) return mOs != nullptr; } private: template friend class NullableOStream; S *mOs = nullptr; }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_ cmds/lshal/OWNERS0100644 0000000 0000000 00000000045 13756501734 012610 0ustar000000000 0000000 elsk@google.com smoreland@google.com cmds/lshal/PipeRelay.cpp0100644 0000000 0000000 00000006703 13756501734 014255 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "PipeRelay.h" #include #include #include #include #include #include #include namespace android { namespace lshal { static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 }; struct PipeRelay::RelayThread : public Thread { explicit RelayThread(int fd, std::ostream &os); bool threadLoop() override; void setFinished(); private: int mFd; std::ostream &mOutStream; // If we were to use requestExit() and exitPending() instead, threadLoop() // may not run at all by the time ~PipeRelay is called (i.e. debug() has // returned from HAL). By using our own flag, we ensure that select() and // read() are executed until data are drained. std::atomic_bool mFinished; DISALLOW_COPY_AND_ASSIGN(RelayThread); }; //////////////////////////////////////////////////////////////////////////////// PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os) : mFd(fd), mOutStream(os), mFinished(false) {} bool PipeRelay::RelayThread::threadLoop() { char buffer[1024]; fd_set set; FD_ZERO(&set); FD_SET(mFd, &set); struct timeval timeout = READ_TIMEOUT; int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout)); if (res < 0) { PLOG(INFO) << "select() failed"; return false; } if (res == 0 || !FD_ISSET(mFd, &set)) { if (mFinished) { LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated."; return false; } // timeout, but debug() has not returned, so wait for HAL to finish. return true; } // FD_ISSET(mFd, &set) == true. Data available, start reading ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer))); if (n < 0) { PLOG(ERROR) << "read() failed"; } if (n <= 0) { return false; } mOutStream.write(buffer, n); return true; } void PipeRelay::RelayThread::setFinished() { mFinished = true; } //////////////////////////////////////////////////////////////////////////////// PipeRelay::PipeRelay(std::ostream &os) : mInitCheck(NO_INIT) { int res = pipe(mFds); if (res < 0) { mInitCheck = -errno; return; } mThread = new RelayThread(mFds[0], os); mInitCheck = mThread->run("RelayThread"); } void PipeRelay::CloseFd(int *fd) { if (*fd >= 0) { close(*fd); *fd = -1; } } PipeRelay::~PipeRelay() { CloseFd(&mFds[1]); if (mThread != nullptr) { mThread->setFinished(); mThread->join(); mThread.clear(); } CloseFd(&mFds[0]); } status_t PipeRelay::initCheck() const { return mInitCheck; } int PipeRelay::fd() const { return mFds[1]; } } // namespace lshal } // namespace android cmds/lshal/PipeRelay.h0100644 0000000 0000000 00000003007 13756501734 013714 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_ #define FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_ #include #include #include #include namespace android { namespace lshal { /* Creates an AF_UNIX socketpair and spawns a thread that relays any data * written to the "write"-end of the pair to the specified output stream "os". */ struct PipeRelay { explicit PipeRelay(std::ostream &os); ~PipeRelay(); status_t initCheck() const; // Returns the file descriptor corresponding to the "write"-end of the // connection. int fd() const; private: struct RelayThread; status_t mInitCheck; int mFds[2]; sp mThread; static void CloseFd(int *fd); DISALLOW_COPY_AND_ASSIGN(PipeRelay); }; } // namespace lshal } // namespace android #endif // FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_ cmds/lshal/TableEntry.cpp0100644 0000000 0000000 00000015712 13756501734 014434 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #define LOG_TAG "lshal" #include #include #include #include #include #include "TableEntry.h" #include "TextTable.h" #include "utils.h" namespace android { namespace lshal { static const std::string &getArchString(vintf::Arch arch) { static const std::string sStr64 = "64"; static const std::string sStr32 = "32"; static const std::string sStrBoth = "32+64"; static const std::string sStrUnknown = "?"; switch (arch) { case vintf::Arch::ARCH_64: return sStr64; case vintf::Arch::ARCH_32: return sStr32; case vintf::Arch::ARCH_32_64: return sStrBoth; case vintf::Arch::ARCH_EMPTY: // fall through default: return sStrUnknown; } } static std::string getTitle(TableColumnType type) { switch (type) { case TableColumnType::INTERFACE_NAME: return "Interface"; case TableColumnType::TRANSPORT: return "Transport"; case TableColumnType::SERVER_PID: return "Server"; case TableColumnType::SERVER_CMD: return "Server CMD"; case TableColumnType::SERVER_ADDR: return "PTR"; case TableColumnType::CLIENT_PIDS: return "Clients"; case TableColumnType::CLIENT_CMDS: return "Clients CMD"; case TableColumnType::ARCH: return "Arch"; case TableColumnType::THREADS: return "Thread Use"; case TableColumnType::RELEASED: return "R"; case TableColumnType::HASH: return "Hash"; case TableColumnType::VINTF: return "VINTF"; case TableColumnType::SERVICE_STATUS: return "Status"; default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast(type); return ""; } } std::string TableEntry::getField(TableColumnType type) const { switch (type) { case TableColumnType::INTERFACE_NAME: return interfaceName; case TableColumnType::TRANSPORT: return vintf::to_string(transport); case TableColumnType::SERVER_PID: return serverPid == NO_PID ? "N/A" : std::to_string(serverPid); case TableColumnType::SERVER_CMD: return serverCmdline; case TableColumnType::SERVER_ADDR: return serverObjectAddress == NO_PTR ? "N/A" : toHexString(serverObjectAddress); case TableColumnType::CLIENT_PIDS: return join(clientPids, " "); case TableColumnType::CLIENT_CMDS: return join(clientCmdlines, ";"); case TableColumnType::ARCH: return getArchString(arch); case TableColumnType::THREADS: return getThreadUsage(); case TableColumnType::RELEASED: return isReleased(); case TableColumnType::HASH: return hash; case TableColumnType::VINTF: return getVintfInfo(); case TableColumnType::SERVICE_STATUS: return lshal::to_string(serviceStatus); default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast(type); return ""; } } std::string TableEntry::isReleased() const { static const std::string unreleased = Hash::hexString(Hash::kEmptyHash); if (hash.empty()) { return "?"; } if (hash == unreleased) { return "N"; // unknown or unreleased } return "Y"; // released } std::string TableEntry::getVintfInfo() const { static const std::map values{ {DEVICE_MANIFEST, "DM"}, {DEVICE_MATRIX, "DC"}, {FRAMEWORK_MANIFEST, "FM"}, {FRAMEWORK_MATRIX, "FC"}, }; std::vector ret; for (const auto& pair : values) { if (vintfInfo & pair.first) { ret.push_back(pair.second); } } auto joined = base::Join(ret, ','); return joined.empty() ? "X" : joined; } std::string to_string(ServiceStatus s) { switch (s) { case ServiceStatus::ALIVE: return "alive"; case ServiceStatus::NON_RESPONSIVE: return "non-responsive"; case ServiceStatus::DECLARED: return "declared"; case ServiceStatus::UNKNOWN: return "N/A"; } LOG(FATAL) << __func__ << "Should not reach here." << static_cast(s); return ""; } TextTable Table::createTextTable(bool neat, const std::function& emitDebugInfo) const { TextTable textTable; std::vector row; if (!neat) { textTable.add(mDescription); row.clear(); for (TableColumnType type : mSelectedColumns) { row.push_back(getTitle(type)); } textTable.add(std::move(row)); } for (const auto& entry : mEntries) { row.clear(); for (TableColumnType type : mSelectedColumns) { row.push_back(entry.getField(type)); } textTable.add(std::move(row)); if (emitDebugInfo) { std::string debugInfo = emitDebugInfo(entry.interfaceName); if (!debugInfo.empty()) textTable.add(debugInfo); } } return textTable; } TextTable MergedTable::createTextTable() { TextTable textTable; for (const Table* table : mTables) { textTable.addAll(table->createTextTable()); } return textTable; } bool TableEntry::operator==(const TableEntry& other) const { if (this == &other) { return true; } return interfaceName == other.interfaceName && transport == other.transport && serverPid == other.serverPid && threadUsage == other.threadUsage && threadCount == other.threadCount && serverCmdline == other.serverCmdline && serverObjectAddress == other.serverObjectAddress && clientPids == other.clientPids && clientCmdlines == other.clientCmdlines && arch == other.arch; } std::string TableEntry::to_string() const { using vintf::operator<<; std::stringstream ss; ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage() << ";server=" << serverPid << "(" << serverObjectAddress << ";" << serverCmdline << ");clients=[" << join(clientPids, ";") << "](" << join(clientCmdlines, ";") << ");arch=" << getArchString(arch); return ss.str(); } } // namespace lshal } // namespace android cmds/lshal/TableEntry.h0100644 0000000 0000000 00000010630 13756501734 014073 0ustar000000000 0000000 /* * 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. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ #include #include #include #include #include #include #include #include "TextTable.h" namespace android { namespace lshal { using android::procpartition::Partition; using Pids = std::vector; enum class TableColumnType : unsigned int { INTERFACE_NAME, TRANSPORT, SERVER_PID, SERVER_CMD, SERVER_ADDR, CLIENT_PIDS, CLIENT_CMDS, ARCH, THREADS, RELEASED, HASH, VINTF, SERVICE_STATUS, }; enum : unsigned int { VINTF_INFO_EMPTY = 0, DEVICE_MANIFEST = 1 << 0, DEVICE_MATRIX = 1 << 1, FRAMEWORK_MANIFEST = 1 << 2, FRAMEWORK_MATRIX = 1 << 3, }; using VintfInfo = unsigned int; enum { NO_PID = -1, NO_PTR = 0 }; enum class ServiceStatus { UNKNOWN, // For passthrough ALIVE, NON_RESPONSIVE, // registered but not respond to calls DECLARED, // in VINTF manifest }; std::string to_string(ServiceStatus s); struct TableEntry { std::string interfaceName{}; vintf::Transport transport{vintf::Transport::EMPTY}; int32_t serverPid{NO_PID}; uint32_t threadUsage{0}; uint32_t threadCount{0}; std::string serverCmdline{}; uint64_t serverObjectAddress{NO_PTR}; Pids clientPids{}; std::vector clientCmdlines{}; vintf::Arch arch{vintf::Arch::ARCH_EMPTY}; // empty: unknown, all zeros: unreleased, otherwise: released std::string hash{}; Partition partition{Partition::UNKNOWN}; VintfInfo vintfInfo{VINTF_INFO_EMPTY}; // true iff hwbinder and service started ServiceStatus serviceStatus{ServiceStatus::UNKNOWN}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; }; static bool sortByServerPid(const TableEntry &a, const TableEntry &b) { return a.serverPid < b.serverPid; }; std::string getThreadUsage() const { if (threadCount == 0) { return "N/A"; } return std::to_string(threadUsage) + "/" + std::to_string(threadCount); } std::string isReleased() const; std::string getVintfInfo() const; std::string getField(TableColumnType type) const; bool operator==(const TableEntry& other) const; std::string to_string() const; }; using SelectedColumns = std::vector; class Table { public: using Entries = std::vector; Entries::iterator begin() { return mEntries.begin(); } Entries::const_iterator begin() const { return mEntries.begin(); } Entries::iterator end() { return mEntries.end(); } Entries::const_iterator end() const { return mEntries.end(); } size_t size() const { return mEntries.size(); } void add(TableEntry&& entry) { mEntries.push_back(std::move(entry)); } void setSelectedColumns(const SelectedColumns& s) { mSelectedColumns = s; } const SelectedColumns& getSelectedColumns() const { return mSelectedColumns; } void setDescription(std::string&& d) { mDescription = std::move(d); } // Write table content. TextTable createTextTable(bool neat = true, const std::function& emitDebugInfo = nullptr) const; private: std::string mDescription; Entries mEntries; SelectedColumns mSelectedColumns; }; using TableEntryCompare = std::function; class MergedTable { public: explicit MergedTable(std::vector&& tables) : mTables(std::move(tables)) {} TextTable createTextTable(); private: std::vector mTables; }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ cmds/lshal/TextTable.cpp0100644 0000000 0000000 00000003470 13756501734 014255 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include #include #include "TextTable.h" namespace android { namespace lshal { void TextTable::computeWidth(const std::vector& v) { if (mWidths.size() < v.size()) { mWidths.resize(v.size()); } for (size_t i = 0; i < v.size(); ++i) { mWidths[i] = std::max(mWidths[i], v[i].length()); } } void TextTable::dump(std::ostream& out) const { out << std::left; for (const auto& row : mTable) { if (!row.isRow()) { out << row.line() << std::endl; continue; } for (size_t i = 0; i < row.fields().size(); ++i) { if (i != 0) { out << " "; } // last column does not std::setw to avoid printing unnecessary spaces. if (i < row.fields().size() - 1) { out << std::setw(mWidths[i]); } out << row.fields()[i]; } out << std::endl; } } void TextTable::addAll(TextTable&& other) { for (auto&& row : other.mTable) { if (row.isRow()) { computeWidth(row.fields()); } mTable.emplace_back(std::move(row)); } } } // namespace lshal } // namespace android cmds/lshal/TextTable.h0100644 0000000 0000000 00000004736 13756501734 013730 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ #include #include #include namespace android { namespace lshal { // An element in TextTable. This is either an actual row (an array of cells // in this row), or a string of explanatory text. // To see if this is an actual row, test fields().empty(). class TextTableRow { public: // An empty line. TextTableRow() {} // A row of cells. explicit TextTableRow(std::vector&& v) : mFields(std::move(v)) {} // A single comment string. explicit TextTableRow(std::string&& s) : mLine(std::move(s)) {} explicit TextTableRow(const std::string& s) : mLine(s) {} // Whether this row is an actual row of cells. bool isRow() const { return !fields().empty(); } // Get all cells. const std::vector& fields() const { return mFields; } // Get the single comment string. const std::string& line() const { return mLine; } private: std::vector mFields; std::string mLine; }; // A TextTable is a 2D array of strings. class TextTable { public: // Add a TextTableRow. void add() { mTable.emplace_back(); } void add(std::vector&& v) { computeWidth(v); mTable.emplace_back(std::move(v)); } void add(const std::string& s) { mTable.emplace_back(s); } void add(std::string&& s) { mTable.emplace_back(std::move(s)); } void addAll(TextTable&& other); // Prints the table to out, with column widths adjusted appropriately according // to the content. void dump(std::ostream& out) const; private: void computeWidth(const std::vector& v); std::vector mWidths; std::vector mTable; }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ cmds/lshal/Timeout.h0100644 0000000 0000000 00000006410 13756501734 013451 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include namespace android { namespace lshal { static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500}; class BackgroundTaskState { public: explicit BackgroundTaskState(std::function &&func) : mFunc(std::forward(func)) {} void notify() { std::unique_lock lock(mMutex); mFinished = true; lock.unlock(); mCondVar.notify_all(); } template bool wait(std::chrono::time_point end) { std::unique_lock lock(mMutex); mCondVar.wait_until(lock, end, [this](){ return this->mFinished; }); return mFinished; } void operator()() { mFunc(); } private: std::mutex mMutex; std::condition_variable mCondVar; bool mFinished = false; std::function mFunc; }; void *callAndNotify(void *data) { BackgroundTaskState &state = *static_cast(data); state(); state.notify(); return nullptr; } template bool timeout(std::chrono::duration delay, std::function &&func) { auto now = std::chrono::system_clock::now(); BackgroundTaskState state{std::forward(func)}; pthread_t thread; if (pthread_create(&thread, nullptr, callAndNotify, &state)) { std::cerr << "FATAL: could not create background thread." << std::endl; return false; } bool success = state.wait(now + delay); if (!success) { pthread_kill(thread, SIGINT); } pthread_join(thread, nullptr); return success; } template typename std::result_of::type timeoutIPC(std::chrono::duration wait, const sp &interfaceObject, Function &&func, Args &&... args) { using ::android::hardware::Status; typename std::result_of::type ret{Status::ok()}; auto boundFunc = std::bind(std::forward(func), interfaceObject.get(), std::forward(args)...); bool success = timeout(wait, [&ret, &boundFunc] { ret = std::move(boundFunc()); }); if (!success) { return Status::fromStatusT(TIMED_OUT); } return ret; } template typename std::result_of::type timeoutIPC(const sp &interfaceObject, Function &&func, Args &&... args) { return timeoutIPC(IPC_CALL_WAIT, interfaceObject, func, args...); } } // namespace lshal } // namespace android cmds/lshal/libprocpartition/0040755 0000000 0000000 00000000000 13756501734 015240 5ustar000000000 0000000 cmds/lshal/libprocpartition/Android.bp0100644 0000000 0000000 00000001544 13756501734 017144 0ustar000000000 0000000 // Copyright (C) 2018 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. cc_library_static { name: "libprocpartition", shared_libs: [ "libbase", ], srcs: [ "procpartition.cpp", ], cflags: [ "-Wall", "-Werror", ], export_include_dirs: [ "include", ] } cmds/lshal/libprocpartition/include/0040755 0000000 0000000 00000000000 13756501734 016663 5ustar000000000 0000000 cmds/lshal/libprocpartition/include/procpartition/0040755 0000000 0000000 00000000000 13756501734 021560 5ustar000000000 0000000 cmds/lshal/libprocpartition/include/procpartition/procpartition.h0100644 0000000 0000000 00000002604 13756501734 024625 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_PROCPARTITION_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_PROCPARTITION_H_ #include #include #include namespace android { namespace procpartition { enum class Partition { UNKNOWN = 0, SYSTEM, VENDOR, ODM }; std::ostream& operator<<(std::ostream& os, Partition p); Partition parsePartition(const std::string& s); // Return the path that /proc//exe points to. std::string getExe(pid_t pid); // Return the content of /proc//cmdline. std::string getCmdline(pid_t pid); // Infer the partition of a process from /proc//exe and /proc//cmdline. Partition getPartition(pid_t pid); } // namespace procpartition } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_PROCPARTITION_H_ cmds/lshal/libprocpartition/procpartition.cpp0100644 0000000 0000000 00000006102 13756501734 020635 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include namespace android { namespace procpartition { std::ostream& operator<<(std::ostream& os, Partition p) { switch (p) { case Partition::SYSTEM: return os << "system"; case Partition::VENDOR: return os << "vendor"; case Partition::ODM: return os << "odm"; case Partition::UNKNOWN: // fallthrough default: return os << "(unknown)"; } } std::string getExe(pid_t pid) { std::string exe; std::string real; if (!android::base::Readlink("/proc/" + std::to_string(pid) + "/exe", &exe)) { return ""; } if (!android::base::Realpath(exe, &real)) { return ""; } return real; } std::string getCmdline(pid_t pid) { std::string content; if (!android::base::ReadFileToString("/proc/" + std::to_string(pid) + "/cmdline", &content, false /* follow symlinks */)) { return ""; } return std::string{content.c_str()}; } Partition parsePartition(const std::string& s) { if (s == "system") { return Partition::SYSTEM; } if (s == "vendor") { return Partition::VENDOR; } if (s == "odm") { return Partition::ODM; } return Partition::UNKNOWN; } Partition getPartitionFromRealpath(const std::string& path) { if (path == "/system/bin/app_process64" || path == "/system/bin/app_process32") { return Partition::UNKNOWN; // cannot determine } size_t backslash = path.find_first_of('/', 1); std::string partition = (backslash != std::string::npos) ? path.substr(1, backslash - 1) : path; return parsePartition(partition); } Partition getPartitionFromCmdline(pid_t pid) { const auto& cmdline = getCmdline(pid); if (cmdline == "system_server") { return Partition::SYSTEM; } if (cmdline.empty() || cmdline.front() != '/') { return Partition::UNKNOWN; } return getPartitionFromRealpath(cmdline); } Partition getPartitionFromExe(pid_t pid) { const auto& real = getExe(pid); if (real.empty() || real.front() != '/') { return Partition::UNKNOWN; } return getPartitionFromRealpath(real); } Partition getPartition(pid_t pid) { Partition partition = getPartitionFromExe(pid); if (partition == Partition::UNKNOWN) { partition = getPartitionFromCmdline(pid); } return partition; } } // namespace procpartition } // namespace android cmds/lshal/main.cpp0100644 0000000 0000000 00000001365 13756501734 013306 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "Lshal.h" int main(int argc, char **argv) { using namespace ::android::lshal; return Lshal{}.main(Arg{argc, argv}); } cmds/lshal/test.cpp0100644 0000000 0000000 00000110070 13756501734 013333 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #define LOG_TAG "Lshal" #include #include #include #include #include #include #include #include #include #include #include "ListCommand.h" #include "Lshal.h" #define NELEMS(array) static_cast(sizeof(array) / sizeof(array[0])) using namespace testing; using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; using ::android::hidl::manager::V1_0::IServiceNotification; using ::android::hardware::hidl_array; using ::android::hardware::hidl_death_recipient; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using android::vintf::Arch; using android::vintf::CompatibilityMatrix; using android::vintf::gCompatibilityMatrixConverter; using android::vintf::gHalManifestConverter; using android::vintf::HalManifest; using android::vintf::Transport; using android::vintf::VintfObject; using InstanceDebugInfo = IServiceManager::InstanceDebugInfo; using hidl_hash = hidl_array; namespace android { namespace hardware { namespace tests { namespace baz { namespace V1_0 { namespace implementation { struct Quux : android::hardware::tests::baz::V1_0::IQuux { ::android::hardware::Return debug(const hidl_handle& hh, const hidl_vec& options) override { const native_handle_t *handle = hh.getNativeHandle(); if (handle->numFds < 1) { return Void(); } int fd = handle->data[0]; std::string content{descriptor}; for (const auto &option : options) { content += "\n"; content += option.c_str(); } ssize_t written = write(fd, content.c_str(), content.size()); if (written != (ssize_t)content.size()) { LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < " << content.size() << " bytes, errno = " << errno; } return Void(); } }; } // namespace implementation } // namespace V1_0 } // namespace baz } // namespace tests } // namespace hardware namespace lshal { class MockServiceManager : public IServiceManager { public: template using R = ::android::hardware::Return; using String = const hidl_string&; ~MockServiceManager() = default; #define MOCK_METHOD_CB(name) MOCK_METHOD1(name, R(IServiceManager::name##_cb)) MOCK_METHOD2(get, R>(String, String)); MOCK_METHOD2(add, R(String, const sp&)); MOCK_METHOD2(getTransport, R(String, String)); MOCK_METHOD_CB(list); MOCK_METHOD2(listByInterface, R(String, listByInterface_cb)); MOCK_METHOD3(registerForNotifications, R(String, String, const sp&)); MOCK_METHOD_CB(debugDump); MOCK_METHOD2(registerPassthroughClient, R(String, String)); MOCK_METHOD_CB(interfaceChain); MOCK_METHOD2(debug, R(const hidl_handle&, const hidl_vec&)); MOCK_METHOD_CB(interfaceDescriptor); MOCK_METHOD_CB(getHashChain); MOCK_METHOD0(setHalInstrumentation, R()); MOCK_METHOD2(linkToDeath, R(const sp&, uint64_t)); MOCK_METHOD0(ping, R()); MOCK_METHOD_CB(getDebugInfo); MOCK_METHOD0(notifySyspropsChanged, R()); MOCK_METHOD1(unlinkToDeath, R(const sp&)); }; class DebugTest : public ::testing::Test { public: void SetUp() override { using ::android::hardware::tests::baz::V1_0::IQuux; using ::android::hardware::tests::baz::V1_0::implementation::Quux; err.str(""); out.str(""); serviceManager = new testing::NiceMock(); ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( [](const auto &iface, const auto &inst) -> ::android::hardware::Return> { if (iface == IQuux::descriptor && inst == "default") return new Quux(); return nullptr; })); lshal = std::make_unique(out, err, serviceManager, serviceManager); } void TearDown() override {} std::stringstream err; std::stringstream out; sp serviceManager; std::unique_ptr lshal; }; static Arg createArg(const std::vector& args) { return Arg{static_cast(args.size()), const_cast(args.data())}; } template static Status callMain(const std::unique_ptr& lshal, const std::vector& args) { return lshal->main(createArg(args)); } TEST_F(DebugTest, Debug) { EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar" })); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar")); EXPECT_THAT(err.str(), IsEmpty()); } TEST_F(DebugTest, Debug2) { EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux" })); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux")); EXPECT_THAT(err.str(), IsEmpty()); } TEST_F(DebugTest, Debug3) { EXPECT_NE(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist", })); EXPECT_THAT(err.str(), HasSubstr("does not exist")); } class MockLshal : public Lshal { public: MockLshal() {} ~MockLshal() = default; MOCK_CONST_METHOD0(out, NullableOStream()); MOCK_CONST_METHOD0(err, NullableOStream()); }; // expose protected fields and methods for ListCommand class MockListCommand : public ListCommand { public: explicit MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); } Status main(const Arg& arg) { return ListCommand::main(arg); } void forEachTable(const std::function &f) { return ListCommand::forEachTable(f); } void forEachTable(const std::function &f) const { return ListCommand::forEachTable(f); } Status fetch() { return ListCommand::fetch(); } void dumpVintf(const NullableOStream& out) { return ListCommand::dumpVintf(out); } void internalPostprocess() { ListCommand::postprocess(); } const PidInfo* getPidInfoCached(pid_t serverPid) { return ListCommand::getPidInfoCached(serverPid); } MOCK_METHOD0(postprocess, void()); MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); MOCK_METHOD1(getPartition, Partition(pid_t)); MOCK_CONST_METHOD0(getDeviceManifest, std::shared_ptr()); MOCK_CONST_METHOD0(getDeviceMatrix, std::shared_ptr()); MOCK_CONST_METHOD0(getFrameworkManifest, std::shared_ptr()); MOCK_CONST_METHOD0(getFrameworkMatrix, std::shared_ptr()); }; class ListParseArgsTest : public ::testing::Test { public: void SetUp() override { mockLshal = std::make_unique>(); mockList = std::make_unique(mockLshal.get()); ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream(err))); // ListCommand::parseArgs should parse arguments from the second element optind = 1; } std::unique_ptr mockLshal; std::unique_ptr mockList; std::stringstream err; }; TEST_F(ListParseArgsTest, Args) { EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"}))); mockList->forEachTable([](const Table& table) { EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME, TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}), table.getSelectedColumns()); }); EXPECT_EQ("", err.str()); } TEST_F(ListParseArgsTest, Cmds) { EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"}))); mockList->forEachTable([](const Table& table) { EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID))) << "should not print server PID with -m"; EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS))) << "should not print client PIDs with -m"; EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD)) << "should print server cmd with -m"; EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS)) << "should print client cmds with -m"; }); EXPECT_EQ("", err.str()); } TEST_F(ListParseArgsTest, DebugAndNeat) { EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"}))); EXPECT_THAT(err.str(), HasSubstr("--neat should not be used with --debug.")); } /// Fetch Test // A set of deterministic functions to generate fake debug infos. static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; } static std::vector getClients(pid_t serverId) { return {serverId + 1, serverId + 3}; } static PidInfo getPidInfoFromId(pid_t serverId) { PidInfo info; info.refPids[getPtr(serverId)] = getClients(serverId); info.threadUsage = 10 + serverId; info.threadCount = 20 + serverId; return info; } static std::string getInterfaceName(pid_t serverId) { return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo"; } static std::string getInstanceName(pid_t serverId) { return std::to_string(serverId); } static pid_t getIdFromInstanceName(const hidl_string& instance) { return atoi(instance.c_str()); } static std::string getFqInstanceName(pid_t serverId) { return getInterfaceName(serverId) + "/" + getInstanceName(serverId); } static std::string getCmdlineFromId(pid_t serverId) { if (serverId == NO_PID) return ""; return "command_line_" + std::to_string(serverId); } static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; } static hidl_hash getHashFromId(pid_t serverId) { hidl_hash hash; bool isReleased = getIsReleasedFromId(serverId); for (size_t i = 0; i < hash.size(); ++i) { hash[i] = isReleased ? static_cast(serverId) : 0u; } return hash; } // Fake service returned by mocked IServiceManager::get. class TestService : public IBase { public: explicit TestService(pid_t id) : mId(id) {} hardware::Return getDebugInfo(getDebugInfo_cb cb) override { cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT }); return hardware::Void(); } hardware::Return interfaceChain(interfaceChain_cb cb) override { cb({getInterfaceName(mId), IBase::descriptor}); return hardware::Void(); } hardware::Return getHashChain(getHashChain_cb cb) override { cb({getHashFromId(mId), getHashFromId(0xff)}); return hardware::Void(); } private: pid_t mId; }; class ListTest : public ::testing::Test { public: virtual void SetUp() override { initMockServiceManager(); lshal = std::make_unique(out, err, serviceManager, passthruManager); initMockList(); } void initMockList() { mockList = std::make_unique>(lshal.get()); ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke( [](pid_t serverPid, PidInfo* info) { *info = getPidInfoFromId(serverPid); return true; })); ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId)); ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() { mockList->internalPostprocess(); size_t i = 0; mockList->forEachTable([&](Table& table) { table.setDescription("[fake description " + std::to_string(i++) + "]"); }); })); ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR)); ON_CALL(*mockList, getDeviceManifest()) .WillByDefault(Return(std::make_shared())); ON_CALL(*mockList, getDeviceMatrix()) .WillByDefault(Return(std::make_shared())); ON_CALL(*mockList, getFrameworkManifest()) .WillByDefault(Return(std::make_shared())); ON_CALL(*mockList, getFrameworkMatrix()) .WillByDefault(Return(std::make_shared())); } void initMockServiceManager() { serviceManager = new testing::NiceMock(); passthruManager = new testing::NiceMock(); using A = DebugInfo::Architecture; ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke( [] (IServiceManager::list_cb cb) { cb({ getFqInstanceName(1), getFqInstanceName(2) }); return hardware::Void(); })); ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( [&](const hidl_string&, const hidl_string& instance) { int id = getIdFromInstanceName(instance); return sp(new TestService(id)); })); ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke( [] (IServiceManager::debugDump_cb cb) { cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3, getClients(3), A::IS_32BIT}, InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4, getClients(4), A::IS_32BIT}}); return hardware::Void(); })); ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke( [] (IServiceManager::debugDump_cb cb) { cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5, getClients(5), A::IS_32BIT}, InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6, getClients(6), A::IS_32BIT}}); return hardware::Void(); })); } std::stringstream err; std::stringstream out; std::unique_ptr lshal; std::unique_ptr mockList; sp serviceManager; sp passthruManager; }; TEST_F(ListTest, GetPidInfoCached) { EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1); EXPECT_NE(nullptr, mockList->getPidInfoCached(5)); EXPECT_NE(nullptr, mockList->getPidInfoCached(5)); } TEST_F(ListTest, Fetch) { optind = 1; // mimic Lshal::parseArg() ASSERT_EQ(0u, mockList->parseArgs(createArg({"lshal"}))); ASSERT_EQ(0u, mockList->fetch()); vintf::TransportArch hwbinder{Transport::HWBINDER, Arch::ARCH_64}; vintf::TransportArch passthrough{Transport::PASSTHROUGH, Arch::ARCH_32}; std::array transportArchs{{hwbinder, hwbinder, passthrough, passthrough, passthrough, passthrough}}; int i = 0; mockList->forEachTable([&](const Table& table) { for (const auto& entry : table) { if (i >= transportArchs.size()) { break; } int id = i + 1; auto transport = transportArchs.at(i).transport; TableEntry expected{ .interfaceName = getFqInstanceName(id), .transport = transport, .serverPid = transport == Transport::HWBINDER ? id : NO_PID, .threadUsage = transport == Transport::HWBINDER ? getPidInfoFromId(id).threadUsage : 0, .threadCount = transport == Transport::HWBINDER ? getPidInfoFromId(id).threadCount : 0, .serverCmdline = {}, .serverObjectAddress = transport == Transport::HWBINDER ? getPtr(id) : NO_PTR, .clientPids = getClients(id), .clientCmdlines = {}, .arch = transportArchs.at(i).arch, }; EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string(); ++i; } }); EXPECT_EQ(transportArchs.size(), i) << "Not all entries are tested."; } TEST_F(ListTest, DumpVintf) { const std::string expected = "\n" " \n" " a.h.foo1\n" " hwbinder\n" " @1.0::IFoo/1\n" " \n" " \n" " a.h.foo2\n" " hwbinder\n" " @2.0::IFoo/2\n" " \n" " \n" " a.h.foo3\n" " passthrough\n" " @3.0::IFoo/3\n" " \n" " \n" " a.h.foo4\n" " passthrough\n" " @4.0::IFoo/4\n" " \n" ""; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"}))); auto output = out.str(); EXPECT_THAT(output, HasSubstr(expected)); EXPECT_THAT(output, HasSubstr("a.h.foo5@5.0::IFoo/5")); EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6")); EXPECT_EQ("", err.str()); vintf::HalManifest m; EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str())) << "--init-vintf does not emit valid HAL manifest: " << vintf::gHalManifestConverter.lastError(); } // test default columns TEST_F(ListTest, DumpDefault) { const std::string expected = "[fake description 0]\n" "R Interface Thread Use Server Clients\n" "N a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" "Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" "\n" "[fake description 1]\n" "R Interface Thread Use Server Clients\n" "? a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" "? a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" "\n" "[fake description 2]\n" "R Interface Thread Use Server Clients\n" "? a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" "? a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpHash) { const std::string expected = "[fake description 0]\n" "Interface R Hash\n" "a.h.foo1@1.0::IFoo/1 N 0000000000000000000000000000000000000000000000000000000000000000\n" "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n" "\n" "[fake description 1]\n" "Interface R Hash\n" "a.h.foo3@3.0::IFoo/3 ? \n" "a.h.foo4@4.0::IFoo/4 ? \n" "\n" "[fake description 2]\n" "Interface R Hash\n" "a.h.foo5@5.0::IFoo/5 ? \n" "a.h.foo6@6.0::IFoo/6 ? \n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, Dump) { const std::string expected = "[fake description 0]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" "\n" "[fake description 1]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" "\n" "[fake description 2]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpCmdline) { const std::string expected = "[fake description 0]\n" "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n" "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 command_line_2 0000000000002712 command_line_3;command_line_5\n" "\n" "[fake description 1]\n" "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n" "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A command_line_5;command_line_7\n" "\n" "[fake description 2]\n" "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n" "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A command_line_7;command_line_9\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpNeat) { const std::string expected = "a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" "a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" "a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" "a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" "a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" "a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpSingleHalType) { const std::string expected = "[fake description 0]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=binderized"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpReorderedHalTypes) { const std::string expected = "[fake description 0]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" "\n" "[fake description 1]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" "\n" "[fake description 2]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=passthrough_clients", "--types=passthrough_libs", "--types=binderized"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpAbbreviatedHalTypes) { const std::string expected = "[fake description 0]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" "\n" "[fake description 1]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,l"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, DumpEmptyAndDuplicateHalTypes) { const std::string expected = "[fake description 0]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" "\n" "[fake description 1]\n" "Interface Transport Arch Thread Use Server PTR Clients\n" "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" "\n"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,l,,,l,l,c,", "--types=passthrough_libs,passthrough_clients"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } TEST_F(ListTest, UnknownHalType) { optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"}))); EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a")); } TEST_F(ListTest, Vintf) { std::string deviceManifestXml = "\n" " \n" " a.h.foo1\n" " hwbinder\n" " @1.0::IFoo/1\n" " \n" " \n" " a.h.foo3\n" " passthrough\n" " @3.0::IFoo/3\n" " \n" "\n"; std::string frameworkManifestXml = "\n" " \n" " a.h.foo5\n" " passthrough\n" " @5.0::IFoo/5\n" " \n" "\n"; std::string deviceMatrixXml = "\n" " \n" " a.h.foo5\n" " 5.0\n" " \n" " IFoo\n" " 5\n" " \n" " \n" "\n"; std::string frameworkMatrixXml = "\n" " \n" " a.h.foo1\n" " 1.0\n" " \n" " IFoo\n" " 1\n" " \n" " \n" " \n" " a.h.foo3\n" " 3.0\n" " \n" " IFoo\n" " 3\n" " \n" " \n" "\n"; std::string expected = "DM,FC a.h.foo1@1.0::IFoo/1\n" "X a.h.foo2@2.0::IFoo/2\n" "DM,FC a.h.foo3@3.0::IFoo/3\n" "X a.h.foo4@4.0::IFoo/4\n" "DC,FM a.h.foo5@5.0::IFoo/5\n" "X a.h.foo6@6.0::IFoo/6\n"; auto deviceManifest = std::make_shared(); auto frameworkManifest = std::make_shared(); auto deviceMatrix = std::make_shared(); auto frameworkMatrix = std::make_shared(); ASSERT_TRUE(gHalManifestConverter(deviceManifest.get(), deviceManifestXml)); ASSERT_TRUE(gHalManifestConverter(frameworkManifest.get(), frameworkManifestXml)); ASSERT_TRUE(gCompatibilityMatrixConverter(deviceMatrix.get(), deviceMatrixXml)); ASSERT_TRUE(gCompatibilityMatrixConverter(frameworkMatrix.get(), frameworkMatrixXml)); ON_CALL(*mockList, getDeviceManifest()).WillByDefault(Return(deviceManifest)); ON_CALL(*mockList, getDeviceMatrix()).WillByDefault(Return(deviceMatrix)); ON_CALL(*mockList, getFrameworkManifest()).WillByDefault(Return(frameworkManifest)); ON_CALL(*mockList, getFrameworkMatrix()).WillByDefault(Return(frameworkMatrix)); optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Vi", "--neat"}))); EXPECT_THAT(out.str(), HasSubstr(expected)); EXPECT_EQ("", err.str()); } class ListVintfTest : public ListTest { public: virtual void SetUp() override { ListTest::SetUp(); const std::string mockManifestXml = "\n" " \n" " a.h.foo1\n" " hwbinder\n" " @1.0::IFoo/1\n" " \n" " \n" " a.h.bar1\n" " hwbinder\n" " @1.0::IBar/1\n" " \n" " \n" " a.h.bar2\n" " passthrough\n" " @2.0::IBar/2\n" " \n" ""; auto manifest = std::make_shared(); EXPECT_TRUE(gHalManifestConverter(manifest.get(), mockManifestXml)); EXPECT_CALL(*mockList, getDeviceManifest()) .Times(AnyNumber()) .WillRepeatedly(Return(manifest)); } }; TEST_F(ListVintfTest, ManifestHals) { optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=v", "--neat"}))); EXPECT_THAT(out.str(), HasSubstr("a.h.bar1@1.0::IBar/1 declared hwbinder ?")); EXPECT_THAT(out.str(), HasSubstr("a.h.bar2@2.0::IBar/2 declared passthrough 32+64")); EXPECT_THAT(out.str(), HasSubstr("a.h.foo1@1.0::IFoo/1 declared hwbinder ?")); EXPECT_EQ("", err.str()); } TEST_F(ListVintfTest, Lazy) { optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=z", "--neat"}))); EXPECT_THAT(out.str(), HasSubstr("a.h.bar1@1.0::IBar/1 declared hwbinder ?")); EXPECT_THAT(out.str(), HasSubstr("a.h.bar2@2.0::IBar/2 declared passthrough 32+64")); EXPECT_EQ("", err.str()); } TEST_F(ListVintfTest, NoLazy) { optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=b,c,l", "--neat"}))); EXPECT_THAT(out.str(), Not(HasSubstr("IBar"))); EXPECT_EQ("", err.str()); } class HelpTest : public ::testing::Test { public: void SetUp() override { lshal = std::make_unique(out, err, new MockServiceManager() /* serviceManager */, new MockServiceManager() /* passthruManager */); } std::stringstream err; std::stringstream out; std::unique_ptr lshal; }; TEST_F(HelpTest, GlobalUsage) { (void)callMain(lshal, {"lshal", "--help"}); // ignore return std::string errStr = err.str(); EXPECT_THAT(errStr, ContainsRegex("(^|\n)commands:($|\n)")) << "`lshal --help` does not contain global usage"; EXPECT_THAT(errStr, ContainsRegex("(^|\n)list:($|\n)")) << "`lshal --help` does not contain usage for 'list' command"; EXPECT_THAT(errStr, ContainsRegex("(^|\n)debug:($|\n)")) << "`lshal --help` does not contain usage for 'debug' command"; EXPECT_THAT(errStr, ContainsRegex("(^|\n)help:($|\n)")) << "`lshal --help` does not contain usage for 'help' command"; err.str(""); (void)callMain(lshal, {"lshal", "help"}); // ignore return EXPECT_EQ(errStr, err.str()) << "`lshal help` should have the same output as `lshal --help`"; err.str(""); EXPECT_NE(0u, callMain(lshal, {"lshal", "--unknown-option"})); EXPECT_THAT(err.str(), ContainsRegex("unrecognized option")); EXPECT_THAT(err.str(), EndsWith(errStr)) << "`lshal --unknown-option` should have the same output as `lshal --help`"; EXPECT_EQ("", out.str()); } TEST_F(HelpTest, UnknownOptionList1) { (void)callMain(lshal, {"lshal", "help", "list"}); EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)")) << "`lshal help list` does not contain usage for 'list' command"; } TEST_F(HelpTest, UnknownOptionList2) { EXPECT_NE(0u, callMain(lshal, {"lshal", "list", "--unknown-option"})); EXPECT_THAT(err.str(), ContainsRegex("unrecognized option")); EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)")) << "`lshal list --unknown-option` does not contain usage for 'list' command"; EXPECT_EQ("", out.str()); } TEST_F(HelpTest, UnknownOptionHelp1) { (void)callMain(lshal, {"lshal", "help", "help"}); EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)")) << "`lshal help help` does not contain usage for 'help' command"; } TEST_F(HelpTest, UnknownOptionHelp2) { (void)callMain(lshal, {"lshal", "help", "--unknown-option"}); EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)")) << "`lshal help --unknown-option` does not contain usage for 'help' command"; EXPECT_EQ("", out.str()); } } // namespace lshal } // namespace android int main(int argc, char **argv) { ::testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); } cmds/lshal/utils.cpp0100644 0000000 0000000 00000002737 13756501734 013526 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include "utils.h" namespace android { namespace lshal { std::string toHexString(uint64_t t) { std::ostringstream os; os << std::hex << std::setfill('0') << std::setw(16) << t; return os.str(); } std::vector split(const std::string &s, char c) { std::vector components{}; size_t startPos = 0; size_t matchPos; while ((matchPos = s.find(c, startPos)) != std::string::npos) { components.push_back(s.substr(startPos, matchPos - startPos)); startPos = matchPos + 1; } if (startPos <= s.length()) { components.push_back(s.substr(startPos)); } return components; } void replaceAll(std::string *s, char from, char to) { for (size_t i = 0; i < s->size(); ++i) { if (s->at(i) == from) { s->at(i) = to; } } } } // namespace lshal } // namespace android cmds/lshal/utils.h0100644 0000000 0000000 00000005424 13756501734 013167 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_ #include #include #include #include #include #include namespace android { namespace lshal { enum : unsigned int { OK = 0, // Return to Lshal::main to print help info. USAGE = 1 << 0, // no service managers NO_BINDERIZED_MANAGER = 1 << 1, NO_PASSTHROUGH_MANAGER = 1 << 2, // general error in getting information from the three sources DUMP_BINDERIZED_ERROR = 1 << 3, DUMP_PASSTHROUGH_ERROR = 1 << 4, DUMP_ALL_LIBS_ERROR = 1 << 5, // I/O error in reading files IO_ERROR = 1 << 6, // Interface does not exist (IServiceManager::get fails) NO_INTERFACE = 1 << 7, // Transaction error from hwbinder transactions TRANSACTION_ERROR = 1 << 8, // No transaction error, but return value is unexpected. BAD_IMPL = 1 << 9, // Cannot fetch VINTF data. VINTF_ERROR = 1 << 10, }; using Status = unsigned int; struct Arg { int argc; char **argv; }; template std::string join(const A &components, const std::string &separator) { std::stringstream out; bool first = true; for (const auto &component : components) { if (!first) { out << separator; } out << component; first = false; } return out.str(); } std::string toHexString(uint64_t t); template std::pair splitFirst(const String &s, char c) { const char *pos = strchr(s.c_str(), c); if (pos == nullptr) { return {s, {}}; } return {String(s.c_str(), pos - s.c_str()), String(pos + 1)}; } std::vector split(const std::string &s, char c); void replaceAll(std::string *s, char from, char to); } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_ cmds/rawbu/0040755 0000000 0000000 00000000000 13756501734 011671 5ustar000000000 0000000 cmds/rawbu/Android.bp0100644 0000000 0000000 00000000312 13756501734 013565 0ustar000000000 0000000 // Copyright 2009 The Android Open Source Project cc_binary { name: "rawbu", srcs: ["backup.cpp"], cflags: [ "-Wall", "-Werror", ], shared_libs: ["libcutils"], } cmds/rawbu/NOTICE0100644 0000000 0000000 00000024707 13756501734 012604 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS cmds/rawbu/backup.cpp0100644 0000000 0000000 00000051447 13756501734 013652 0ustar000000000 0000000 // Copyright 2009 The Android Open Source Project #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 4096 #endif // First version. #define FILE_VERSION_1 0xffff0001 // Introduces backup all option to header. #define FILE_VERSION_2 0xffff0002 #define FILE_VERSION FILE_VERSION_2 namespace android { static char nameBuffer[PATH_MAX]; static struct stat statBuffer; static char copyBuffer[8192]; static char *backupFilePath = nullptr; static uint32_t inputFileVersion; static int opt_backupAll; #define SPECIAL_NO_TOUCH 0 #define SPECIAL_NO_BACKUP 1 struct special_dir { const char* path; int type; }; /* Directory paths that we will not backup/restore */ static const struct special_dir SKIP_PATHS[] = { { "/data/misc", SPECIAL_NO_TOUCH }, { "/data/system/batterystats.bin", SPECIAL_NO_TOUCH }, { "/data/system/location", SPECIAL_NO_TOUCH }, { "/data/dalvik-cache", SPECIAL_NO_BACKUP }, { nullptr, 0 }, }; /* This is just copied from the shell's built-in wipe command. */ static int wipe (const char *path) { DIR *dir; struct dirent *de; int ret; int i; dir = opendir(path); if (dir == nullptr) { fprintf (stderr, "Error opendir'ing %s: %s\n", path, strerror(errno)); return 0; } char *filenameOffset; strcpy(nameBuffer, path); strcat(nameBuffer, "/"); filenameOffset = nameBuffer + strlen(nameBuffer); for (;;) { de = readdir(dir); if (de == nullptr) { break; } if (0 == strcmp(de->d_name, ".") || 0 == strcmp(de->d_name, "..") || 0 == strcmp(de->d_name, "lost+found") ) { continue; } strcpy(filenameOffset, de->d_name); bool noBackup = false; /* See if this is a path we should skip. */ for (i = 0; SKIP_PATHS[i].path; i++) { if (strcmp(SKIP_PATHS[i].path, nameBuffer) == 0) { if (opt_backupAll || SKIP_PATHS[i].type == SPECIAL_NO_BACKUP) { // In this case we didn't back up the directory -- // we do want to wipe its contents, but not the // directory itself, since the restore file won't // contain the directory. noBackup = true; } break; } } if (!noBackup && SKIP_PATHS[i].path != nullptr) { // This is a SPECIAL_NO_TOUCH directory. continue; } ret = lstat (nameBuffer, &statBuffer); if (ret != 0) { fprintf(stderr, "warning -- stat() error on '%s': %s\n", nameBuffer, strerror(errno)); continue; } if(S_ISDIR(statBuffer.st_mode)) { char *newpath; newpath = strdup(nameBuffer); if (wipe(newpath) == 0) { free(newpath); closedir(dir); return 0; } if (!noBackup) { ret = rmdir(newpath); if (ret != 0) { fprintf(stderr, "warning -- rmdir() error on '%s': %s\n", newpath, strerror(errno)); } } free(newpath); strcpy(nameBuffer, path); strcat(nameBuffer, "/"); } else { // Don't delete the backup file if (backupFilePath && strcmp(backupFilePath, nameBuffer) == 0) { continue; } ret = unlink(nameBuffer); if (ret != 0) { fprintf(stderr, "warning -- unlink() error on '%s': %s\n", nameBuffer, strerror(errno)); } } } closedir(dir); return 1; } static int write_int32(FILE* fh, int32_t val) { int res = fwrite(&val, 1, sizeof(val), fh); if (res != sizeof(val)) { fprintf(stderr, "unable to write int32 (%d bytes): %s\n", res, strerror(errno)); return 0; } return 1; } static int write_int64(FILE* fh, int64_t val) { int res = fwrite(&val, 1, sizeof(val), fh); if (res != sizeof(val)) { fprintf(stderr, "unable to write int64 (%d bytes): %s\n", res, strerror(errno)); return 0; } return 1; } static int copy_file(FILE* dest, FILE* src, off_t size, const char* destName, const char* srcName) { errno = 0; off_t origSize = size; while (size > 0) { int amt = size > (off_t)sizeof(copyBuffer) ? sizeof(copyBuffer) : (int)size; int readLen = fread(copyBuffer, 1, amt, src); if (readLen <= 0) { if (srcName != nullptr) { fprintf(stderr, "unable to read source (%d of %ld bytes) file '%s': %s\n", amt, origSize, srcName, errno != 0 ? strerror(errno) : "unexpected EOF"); } else { fprintf(stderr, "unable to read buffer (%d of %ld bytes): %s\n", amt, origSize, errno != 0 ? strerror(errno) : "unexpected EOF"); } return 0; } int writeLen = fwrite(copyBuffer, 1, readLen, dest); if (writeLen != readLen) { if (destName != nullptr) { fprintf(stderr, "unable to write file (%d of %d bytes) '%s': '%s'\n", writeLen, readLen, destName, strerror(errno)); } else { fprintf(stderr, "unable to write buffer (%d of %d bytes): '%s'\n", writeLen, readLen, strerror(errno)); } return 0; } size -= readLen; } return 1; } #define TYPE_END 0 #define TYPE_DIR 1 #define TYPE_FILE 2 static int write_header(FILE* fh, int type, const char* path, const struct stat* st) { int pathLen = strlen(path); if (!write_int32(fh, type)) return 0; if (!write_int32(fh, pathLen)) return 0; if (fwrite(path, 1, pathLen, fh) != (size_t)pathLen) { fprintf(stderr, "unable to write: %s\n", strerror(errno)); return 0; } if (!write_int32(fh, st->st_uid)) return 0; if (!write_int32(fh, st->st_gid)) return 0; if (!write_int32(fh, st->st_mode)) return 0; if (!write_int64(fh, ((int64_t)st->st_atime)*1000*1000*1000)) return 0; if (!write_int64(fh, ((int64_t)st->st_mtime)*1000*1000*1000)) return 0; if (!write_int64(fh, ((int64_t)st->st_ctime)*1000*1000*1000)) return 0; return 1; } static int backup_dir(FILE* fh, const char* srcPath) { DIR *dir; struct dirent *de; char* fullPath = nullptr; int srcLen = strlen(srcPath); int result = 1; int i; dir = opendir(srcPath); if (dir == nullptr) { fprintf (stderr, "error opendir'ing '%s': %s\n", srcPath, strerror(errno)); return 0; } for (;;) { de = readdir(dir); if (de == nullptr) { break; } if (0 == strcmp(de->d_name, ".") || 0 == strcmp(de->d_name, "..") || 0 == strcmp(de->d_name, "lost+found") ) { continue; } if (fullPath != nullptr) { free(fullPath); } fullPath = (char*)malloc(srcLen + strlen(de->d_name) + 2); strcpy(fullPath, srcPath); fullPath[srcLen] = '/'; strcpy(fullPath+srcLen+1, de->d_name); /* See if this is a path we should skip. */ if (!opt_backupAll) { for (i = 0; SKIP_PATHS[i].path; i++) { if (strcmp(SKIP_PATHS[i].path, fullPath) == 0) { break; } } if (SKIP_PATHS[i].path != nullptr) { continue; } } int ret = lstat(fullPath, &statBuffer); if (ret != 0) { fprintf(stderr, "stat() error on '%s': %s\n", fullPath, strerror(errno)); result = 0; goto done; } if(S_ISDIR(statBuffer.st_mode)) { printf("Saving dir %s...\n", fullPath); if (write_header(fh, TYPE_DIR, fullPath, &statBuffer) == 0) { result = 0; goto done; } if (backup_dir(fh, fullPath) == 0) { result = 0; goto done; } } else if (S_ISREG(statBuffer.st_mode)) { // Skip the backup file if (backupFilePath && strcmp(fullPath, backupFilePath) == 0) { printf("Skipping backup file %s...\n", backupFilePath); continue; } else { printf("Saving file %s...\n", fullPath); } if (write_header(fh, TYPE_FILE, fullPath, &statBuffer) == 0) { result = 0; goto done; } off_t size = statBuffer.st_size; if (!write_int64(fh, size)) { result = 0; goto done; } FILE* src = fopen(fullPath, "r"); if (src == nullptr) { fprintf(stderr, "unable to open source file '%s': %s\n", fullPath, strerror(errno)); result = 0; goto done; } int copyres = copy_file(fh, src, size, nullptr, fullPath); fclose(src); if (!copyres) { result = 0; goto done; } } } done: if (fullPath != nullptr) { free(fullPath); } closedir(dir); return result; } static int backup_data(const char* destPath) { int res = -1; FILE* fh = fopen(destPath, "w"); if (fh == nullptr) { fprintf(stderr, "unable to open destination '%s': %s\n", destPath, strerror(errno)); return -1; } printf("Backing up /data to %s...\n", destPath); // The path that shouldn't be backed up backupFilePath = strdup(destPath); if (!write_int32(fh, FILE_VERSION)) goto done; if (!write_int32(fh, opt_backupAll)) goto done; if (!backup_dir(fh, "/data")) goto done; if (!write_int32(fh, 0)) goto done; res = 0; done: if (fflush(fh) != 0) { fprintf(stderr, "error flushing destination '%s': %s\n", destPath, strerror(errno)); res = -1; goto donedone; } if (fsync(fileno(fh)) != 0) { fprintf(stderr, "error syncing destination '%s': %s\n", destPath, strerror(errno)); res = -1; goto donedone; } fclose(fh); sync(); donedone: return res; } static int32_t read_int32(FILE* fh, int32_t defVal) { int32_t val; if (fread(&val, 1, sizeof(val), fh) != sizeof(val)) { fprintf(stderr, "unable to read: %s\n", strerror(errno)); return defVal; } return val; } static int64_t read_int64(FILE* fh, int64_t defVal) { int64_t val; if (fread(&val, 1, sizeof(val), fh) != sizeof(val)) { fprintf(stderr, "unable to read: %s\n", strerror(errno)); return defVal; } return val; } static int read_header(FILE* fh, int* type, char** path, struct stat* st) { *type = read_int32(fh, -1); if (*type == TYPE_END) { return 1; } if (*type < 0) { fprintf(stderr, "bad token %d in restore file\n", *type); return 0; } int32_t pathLen = read_int32(fh, -1); if (pathLen <= 0) { fprintf(stderr, "bad path length %d in restore file\n", pathLen); return 0; } char* readPath = (char*)malloc(pathLen+1); if (fread(readPath, 1, pathLen, fh) != (size_t)pathLen) { fprintf(stderr, "truncated path in restore file\n"); free(readPath); return 0; } readPath[pathLen] = 0; *path = readPath; st->st_uid = read_int32(fh, -1); if (st->st_uid == (uid_t)-1) { fprintf(stderr, "bad uid in restore file at '%s'\n", readPath); return 0; } st->st_gid = read_int32(fh, -1); if (st->st_gid == (gid_t)-1) { fprintf(stderr, "bad gid in restore file at '%s'\n", readPath); return 0; } st->st_mode = read_int32(fh, -1); if (st->st_mode == (mode_t)-1) { fprintf(stderr, "bad mode in restore file at '%s'\n", readPath); return 0; } int64_t ltime = read_int64(fh, -1); if (ltime < 0) { fprintf(stderr, "bad atime in restore file at '%s'\n", readPath); return 0; } st->st_atime = (time_t)(ltime/1000/1000/1000); ltime = read_int64(fh, -1); if (ltime < 0) { fprintf(stderr, "bad mtime in restore file at '%s'\n", readPath); return 0; } st->st_mtime = (time_t)(ltime/1000/1000/1000); ltime = read_int64(fh, -1); if (ltime < 0) { fprintf(stderr, "bad ctime in restore file at '%s'\n", readPath); return 0; } st->st_ctime = (time_t)(ltime/1000/1000/1000); st->st_mode &= (S_IRWXU|S_IRWXG|S_IRWXO); return 1; } static int restore_data(const char* srcPath) { int res = -1; FILE* fh = fopen(srcPath, "r"); if (fh == nullptr) { fprintf(stderr, "Unable to open source '%s': %s\n", srcPath, strerror(errno)); return -1; } inputFileVersion = read_int32(fh, 0); if (inputFileVersion < FILE_VERSION_1 || inputFileVersion > FILE_VERSION) { fprintf(stderr, "Restore file has bad version: 0x%x\n", inputFileVersion); goto done; } if (inputFileVersion >= FILE_VERSION_2) { opt_backupAll = read_int32(fh, 0); } else { opt_backupAll = 0; } // The path that shouldn't be deleted backupFilePath = strdup(srcPath); printf("Wiping contents of /data...\n"); if (!wipe("/data")) { goto done; } printf("Restoring from %s to /data...\n", srcPath); while (1) { int type; char* path = nullptr; if (read_header(fh, &type, &path, &statBuffer) == 0) { goto done; } if (type == 0) { break; } const char* typeName = "?"; if (type == TYPE_DIR) { typeName = "dir"; printf("Restoring dir %s...\n", path); if (mkdir(path, statBuffer.st_mode) != 0) { if (errno != EEXIST) { fprintf(stderr, "unable to create directory '%s': %s\n", path, strerror(errno)); free(path); goto done; } } } else if (type == TYPE_FILE) { typeName = "file"; off_t size = read_int64(fh, -1); if (size < 0) { fprintf(stderr, "bad file size %ld in restore file\n", size); free(path); goto done; } printf("Restoring file %s...\n", path); FILE* dest = fopen(path, "w"); if (dest == nullptr) { fprintf(stderr, "unable to open destination file '%s': %s\n", path, strerror(errno)); free(path); goto done; } int copyres = copy_file(dest, fh, size, path, nullptr); fclose(dest); if (!copyres) { free(path); goto done; } } else { fprintf(stderr, "unknown node type %d\n", type); goto done; } // Do this even for directories, since the dir may have already existed // so we need to make sure it gets the correct mode. if (chmod(path, statBuffer.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO)) != 0) { fprintf(stderr, "unable to chmod destination %s '%s' to 0x%x: %s\n", typeName, path, statBuffer.st_mode, strerror(errno)); free(path); goto done; } if (chown(path, statBuffer.st_uid, statBuffer.st_gid) != 0) { fprintf(stderr, "unable to chown destination %s '%s' to uid %d / gid %d: %s\n", typeName, path, (int)statBuffer.st_uid, (int)statBuffer.st_gid, strerror(errno)); free(path); goto done; } struct utimbuf timbuf; timbuf.actime = statBuffer.st_atime; timbuf.modtime = statBuffer.st_mtime; if (utime(path, &timbuf) != 0) { fprintf(stderr, "unable to utime destination %s '%s': %s\n", typeName, path, strerror(errno)); free(path); goto done; } free(path); } res = 0; done: fclose(fh); return res; } static void show_help(const char *cmd) { fprintf(stderr,"Usage: %s COMMAND [options] [backup-file-path]\n", cmd); fprintf(stderr, "commands are:\n" " help Show this help text.\n" " backup Perform a backup of /data.\n" " restore Perform a restore of /data.\n"); fprintf(stderr, "options include:\n" " -h Show this help text.\n" " -a Backup all files.\n"); fprintf(stderr, "\n backup-file-path Defaults to /sdcard/backup.dat .\n" " On devices that emulate the sdcard, you will need to\n" " explicitly specify the directory it is mapped to,\n" " to avoid recursive backup or deletion of the backup file\n" " during restore.\n\n" " Eg. /data/media/0/backup.dat\n"); fprintf(stderr, "\nThe %s command allows you to perform low-level\n" "backup and restore of the /data partition. This is\n" "where all user data is kept, allowing for a fairly\n" "complete restore of a device's state. Note that\n" "because this is low-level, it will only work across\n" "builds of the same (or very similar) device software.\n", cmd); } } /* namespace android */ int main (int argc, char **argv) { int restore = 0; if (getuid() != AID_ROOT) { fprintf(stderr, "error -- %s must run as root\n", argv[0]); exit(-1); } if (argc < 2) { fprintf(stderr, "No command specified.\n"); android::show_help(argv[0]); exit(-1); } if (0 == strcmp(argv[1], "restore")) { restore = 1; } else if (0 == strcmp(argv[1], "help")) { android::show_help(argv[0]); exit(0); } else if (0 != strcmp(argv[1], "backup")) { fprintf(stderr, "Unknown command: %s\n", argv[1]); android::show_help(argv[0]); exit(-1); } android::opt_backupAll = 0; optind = 2; for (;;) { int ret; ret = getopt(argc, argv, "ah"); if (ret < 0) { break; } switch(ret) { case 'a': android::opt_backupAll = 1; if (restore) fprintf(stderr, "Warning: -a option ignored on restore\n"); break; case 'h': android::show_help(argv[0]); exit(0); break; default: fprintf(stderr,"Unrecognized Option\n"); android::show_help(argv[0]); exit(-1); break; } } const char* backupFile = "/sdcard/backup.dat"; if (argc > optind) { backupFile = argv[optind]; optind++; if (argc != optind) { fprintf(stderr, "Too many arguments\n"); android::show_help(argv[0]); exit(-1); } } printf("Stopping system...\n"); property_set("ctl.stop", "runtime"); property_set("ctl.stop", "zygote"); sleep(1); int res; if (restore) { res = android::restore_data(backupFile); if (res != 0) { // Don't restart system, since the data partition is hosed. return res; } printf("Restore complete! Restarting system, cross your fingers...\n"); } else { res = android::backup_data(backupFile); if (res == 0) { printf("Backup complete! Restarting system...\n"); } else { printf("Restarting system...\n"); } } property_set("ctl.start", "zygote"); property_set("ctl.start", "runtime"); } cmds/rss_hwm_reset/0040755 0000000 0000000 00000000000 13756501734 013435 5ustar000000000 0000000 cmds/rss_hwm_reset/Android.bp0100644 0000000 0000000 00000001444 13756501734 015340 0ustar000000000 0000000 // Copyright (C) 2018 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 // // https://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. cc_binary { name: "rss_hwm_reset", srcs: [ "rss_hwm_reset.cc", ], shared_libs: [ "libbase", "liblog", ], init_rc: ["rss_hwm_reset.rc"], } cmds/rss_hwm_reset/rss_hwm_reset.cc0100644 0000000 0000000 00000004273 13756501734 016633 0ustar000000000 0000000 /* * Copyright (C) 2018 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 * * https://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. */ /* * rss_hwm_reset clears the RSS high-water mark counters for all currently * running processes. It writes "5" to /proc/PID/clear_refs for every PID. * * It runs in its own process becuase dac_override capability is required * in order to write to other processes' clear_refs. * * It is invoked from a system service by flipping sys.rss_hwm_reset.on * property to "1". */ #define LOG_TAG "rss_hwm_reset" #include #include #include #include #include namespace { // Resets RSS HWM counter for the selected process by writing 5 to // /proc/PID/clear_refs. void reset_rss_hwm(const char* pid) { std::string clear_refs_path = ::android::base::StringPrintf("/proc/%s/clear_refs", pid); ::android::base::WriteStringToFile("5", clear_refs_path); } } // Clears RSS HWM counters for all currently running processes. int main(int /* argc */, char** /* argv[] */) { DIR* dirp = opendir("/proc"); if (dirp == nullptr) { ALOGE("unable to read /proc"); return 1; } struct dirent* entry; while ((entry = readdir(dirp)) != nullptr) { // Skip entries that are not directories. if (entry->d_type != DT_DIR) continue; // Skip entries that do not contain only numbers. const char* pid = entry->d_name; while (*pid) { if (*pid < '0' || *pid > '9') break; pid++; } if (*pid != 0) continue; pid = entry->d_name; reset_rss_hwm(pid); } closedir(dirp); return 0; } cmds/rss_hwm_reset/rss_hwm_reset.rc0100644 0000000 0000000 00000001620 13756501734 016643 0ustar000000000 0000000 # Copyright (C) 2018 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 # # https://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. service rss_hwm_reset /system/bin/rss_hwm_reset class late_start disabled oneshot user nobody group nobody readproc writepid /dev/cpuset/system-background/tasks capabilities DAC_OVERRIDE on property:sys.rss_hwm_reset.on=1 start rss_hwm_reset setprop sys.rss_hwm_reset.on 0 cmds/service/0040755 0000000 0000000 00000000000 13756501734 012211 5ustar000000000 0000000 cmds/service/Android.bp0100644 0000000 0000000 00000000733 13756501734 014114 0ustar000000000 0000000 cc_binary { name: "service", srcs: ["service.cpp"], shared_libs: [ "libutils", "libbinder", ], cflags: [ "-DXP_UNIX", "-Wall", "-Werror", ], } cc_binary { name: "vndservice", proprietary: true, srcs: ["service.cpp"], shared_libs: [ "libutils", "libbinder", ], cflags: [ "-DXP_UNIX", "-DVENDORSERVICES", "-Wall", "-Werror", ], } cmds/service/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501734 015331 0ustar000000000 0000000 cmds/service/NOTICE0100644 0000000 0000000 00000024707 13756501734 013124 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS cmds/service/service.cpp0100644 0000000 0000000 00000030340 13756501734 014352 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #include #include #include #include #include #include #include using namespace android; void writeString16(Parcel& parcel, const char* string) { if (string != nullptr) { parcel.writeString16(String16(string)); } else { parcel.writeInt32(-1); } } // get the name of the generic interface we hold a reference to static String16 get_interface_name(sp service) { if (service != nullptr) { Parcel data, reply; status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply); if (err == NO_ERROR) { return reply.readString16(); } } return String16(); } static String8 good_old_string(const String16& src) { String8 name8; char ch8[2]; ch8[1] = 0; for (unsigned j = 0; j < src.size(); j++) { char16_t ch = src[j]; if (ch < 128) ch8[0] = (char)ch; name8.append(ch8); } return name8; } int main(int argc, char* const argv[]) { bool wantsUsage = false; int result = 0; while (1) { int ic = getopt(argc, argv, "h?"); if (ic < 0) break; switch (ic) { case 'h': case '?': wantsUsage = true; break; default: aerr << "service: Unknown option -" << ic << endl; wantsUsage = true; result = 10; break; } } #ifdef VENDORSERVICES ProcessState::initWithDriver("/dev/vndbinder"); #endif sp sm = defaultServiceManager(); fflush(stdout); if (sm == nullptr) { aerr << "service: Unable to get default service manager!" << endl; return 20; } if (optind >= argc) { wantsUsage = true; } else if (!wantsUsage) { if (strcmp(argv[optind], "check") == 0) { optind++; if (optind < argc) { sp service = sm->checkService(String16(argv[optind])); aout << "Service " << argv[optind] << (service == nullptr ? ": not found" : ": found") << endl; } else { aerr << "service: No service specified for check" << endl; wantsUsage = true; result = 10; } } else if (strcmp(argv[optind], "list") == 0) { Vector services = sm->listServices(); aout << "Found " << services.size() << " services:" << endl; for (unsigned i = 0; i < services.size(); i++) { String16 name = services[i]; sp service = sm->checkService(name); aout << i << "\t" << good_old_string(name) << ": [" << good_old_string(get_interface_name(service)) << "]" << endl; } } else if (strcmp(argv[optind], "call") == 0) { optind++; if (optind+1 < argc) { int serviceArg = optind; sp service = sm->checkService(String16(argv[optind++])); String16 ifName = get_interface_name(service); int32_t code = atoi(argv[optind++]); if (service != nullptr && ifName.size() > 0) { Parcel data, reply; // the interface name is first data.writeInterfaceToken(ifName); // then the rest of the call arguments while (optind < argc) { if (strcmp(argv[optind], "i32") == 0) { optind++; if (optind >= argc) { aerr << "service: no integer supplied for 'i32'" << endl; wantsUsage = true; result = 10; break; } data.writeInt32(atoi(argv[optind++])); } else if (strcmp(argv[optind], "i64") == 0) { optind++; if (optind >= argc) { aerr << "service: no integer supplied for 'i64'" << endl; wantsUsage = true; result = 10; break; } data.writeInt64(atoll(argv[optind++])); } else if (strcmp(argv[optind], "s16") == 0) { optind++; if (optind >= argc) { aerr << "service: no string supplied for 's16'" << endl; wantsUsage = true; result = 10; break; } data.writeString16(String16(argv[optind++])); } else if (strcmp(argv[optind], "f") == 0) { optind++; if (optind >= argc) { aerr << "service: no number supplied for 'f'" << endl; wantsUsage = true; result = 10; break; } data.writeFloat(atof(argv[optind++])); } else if (strcmp(argv[optind], "d") == 0) { optind++; if (optind >= argc) { aerr << "service: no number supplied for 'd'" << endl; wantsUsage = true; result = 10; break; } data.writeDouble(atof(argv[optind++])); } else if (strcmp(argv[optind], "null") == 0) { optind++; data.writeStrongBinder(nullptr); } else if (strcmp(argv[optind], "intent") == 0) { char* action = nullptr; char* dataArg = nullptr; char* type = nullptr; int launchFlags = 0; char* component = nullptr; int categoryCount = 0; char* categories[16]; char* context1 = nullptr; optind++; while (optind < argc) { char* key = strtok_r(argv[optind], "=", &context1); char* value = strtok_r(nullptr, "=", &context1); // we have reached the end of the XXX=XXX args. if (key == nullptr) break; if (strcmp(key, "action") == 0) { action = value; } else if (strcmp(key, "data") == 0) { dataArg = value; } else if (strcmp(key, "type") == 0) { type = value; } else if (strcmp(key, "launchFlags") == 0) { launchFlags = atoi(value); } else if (strcmp(key, "component") == 0) { component = value; } else if (strcmp(key, "categories") == 0) { char* context2 = nullptr; categories[categoryCount] = strtok_r(value, ",", &context2); while (categories[categoryCount] != nullptr) { categoryCount++; categories[categoryCount] = strtok_r(nullptr, ",", &context2); } } optind++; } writeString16(data, action); writeString16(data, dataArg); writeString16(data, type); data.writeInt32(launchFlags); writeString16(data, component); if (categoryCount > 0) { data.writeInt32(categoryCount); for (int i = 0 ; i < categoryCount ; i++) { writeString16(data, categories[i]); } } else { data.writeInt32(0); } // for now just set the extra field to be null. data.writeInt32(-1); } else { aerr << "service: unknown option " << argv[optind] << endl; wantsUsage = true; result = 10; break; } } service->transact(code, data, &reply); aout << "Result: " << reply << endl; } else { aerr << "service: Service " << argv[serviceArg] << " does not exist" << endl; result = 10; } } else { if (optind < argc) { aerr << "service: No service specified for call" << endl; } else { aerr << "service: No code specified for call" << endl; } wantsUsage = true; result = 10; } } else { aerr << "service: Unknown command " << argv[optind] << endl; wantsUsage = true; result = 10; } } if (wantsUsage) { aout << "Usage: service [-h|-?]\n" " service list\n" " service check SERVICE\n" " service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR ] ...\n" "Options:\n" " i32: Write the 32-bit integer N into the send parcel.\n" " i64: Write the 64-bit integer N into the send parcel.\n" " f: Write the 32-bit single-precision number N into the send parcel.\n" " d: Write the 64-bit double-precision number N into the send parcel.\n" " s16: Write the UTF-16 string STR into the send parcel.\n"; // " intent: Write and Intent int the send parcel. ARGS can be\n" // " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n"; return result; } return result; } cmds/servicemanager/0040755 0000000 0000000 00000000000 13756501734 013544 5ustar000000000 0000000 cmds/servicemanager/Android.bp0100644 0000000 0000000 00000001666 13756501734 015455 0ustar000000000 0000000 cc_defaults { name: "servicemanager_flags", cflags: [ "-Wall", "-Wextra", "-Werror", ], product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, shared_libs: ["liblog"], } cc_binary { name: "bctest", defaults: ["servicemanager_flags"], srcs: [ "bctest.c", "binder.c", ], } cc_binary { name: "servicemanager", defaults: ["servicemanager_flags"], srcs: [ "service_manager.c", "binder.c", ], shared_libs: ["libcutils", "libselinux"], init_rc: ["servicemanager.rc"], } cc_binary { name: "vndservicemanager", defaults: ["servicemanager_flags"], vendor: true, srcs: [ "service_manager.c", "binder.c", ], cflags: [ "-DVENDORSERVICEMANAGER=1", ], shared_libs: ["libcutils", "libselinux"], init_rc: ["vndservicemanager.rc"], } cmds/servicemanager/bctest.c0100644 0000000 0000000 00000005251 13756501734 015174 0ustar000000000 0000000 /* Copyright 2008 The Android Open Source Project */ #include #include #include #include #include "binder.h" uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name) { uint32_t handle; unsigned iodata[512/4]; struct binder_io msg, reply; bio_init(&msg, iodata, sizeof(iodata), 4); bio_put_uint32(&msg, 0); // strict mode header bio_put_string16_x(&msg, SVC_MGR_NAME); bio_put_string16_x(&msg, name); if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE)) return 0; handle = bio_get_ref(&reply); if (handle) binder_acquire(bs, handle); binder_done(bs, &msg, &reply); return handle; } int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr) { int status; unsigned iodata[512/4]; struct binder_io msg, reply; bio_init(&msg, iodata, sizeof(iodata), 4); bio_put_uint32(&msg, 0); // strict mode header bio_put_string16_x(&msg, SVC_MGR_NAME); bio_put_string16_x(&msg, name); bio_put_obj(&msg, ptr); if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)) return -1; status = bio_get_uint32(&reply); binder_done(bs, &msg, &reply); return status; } unsigned token; int main(int argc, char **argv) { struct binder_state *bs; uint32_t svcmgr = BINDER_SERVICE_MANAGER; uint32_t handle; bs = binder_open("/dev/binder", 128*1024); if (!bs) { fprintf(stderr, "failed to open binder driver\n"); return -1; } argc--; argv++; while (argc > 0) { if (!strcmp(argv[0],"alt")) { handle = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr"); if (!handle) { fprintf(stderr,"cannot find alt_svc_mgr\n"); return -1; } svcmgr = handle; fprintf(stderr,"svcmgr is via %x\n", handle); } else if (!strcmp(argv[0],"lookup")) { if (argc < 2) { fprintf(stderr,"argument required\n"); return -1; } handle = svcmgr_lookup(bs, svcmgr, argv[1]); fprintf(stderr,"lookup(%s) = %x\n", argv[1], handle); argc--; argv++; } else if (!strcmp(argv[0],"publish")) { if (argc < 2) { fprintf(stderr,"argument required\n"); return -1; } svcmgr_publish(bs, svcmgr, argv[1], &token); argc--; argv++; } else { fprintf(stderr,"unknown command %s\n", argv[0]); return -1; } argc--; argv++; } return 0; } cmds/servicemanager/binder.c0100644 0000000 0000000 00000043724 13756501734 015162 0ustar000000000 0000000 /* Copyright 2008 The Android Open Source Project */ #define LOG_TAG "Binder" #include #include #include #include #include #include #include #include #include #include "binder.h" #define MAX_BIO_SIZE (1 << 30) #define TRACE 0 void bio_init_from_txn(struct binder_io *io, struct binder_transaction_data *txn); #if TRACE void hexdump(void *_data, size_t len) { unsigned char *data = _data; size_t count; for (count = 0; count < len; count++) { if ((count & 15) == 0) fprintf(stderr,"%04zu:", count); fprintf(stderr," %02x %c", *data, (*data < 32) || (*data > 126) ? '.' : *data); data++; if ((count & 15) == 15) fprintf(stderr,"\n"); } if ((count & 15) != 0) fprintf(stderr,"\n"); } void binder_dump_txn(struct binder_transaction_data *txn) { struct flat_binder_object *obj; binder_size_t *offs = (binder_size_t *)(uintptr_t)txn->data.ptr.offsets; size_t count = txn->offsets_size / sizeof(binder_size_t); fprintf(stderr," target %016"PRIx64" cookie %016"PRIx64" code %08x flags %08x\n", (uint64_t)txn->target.ptr, (uint64_t)txn->cookie, txn->code, txn->flags); fprintf(stderr," pid %8d uid %8d data %"PRIu64" offs %"PRIu64"\n", txn->sender_pid, txn->sender_euid, (uint64_t)txn->data_size, (uint64_t)txn->offsets_size); hexdump((void *)(uintptr_t)txn->data.ptr.buffer, txn->data_size); while (count--) { obj = (struct flat_binder_object *) (((char*)(uintptr_t)txn->data.ptr.buffer) + *offs++); fprintf(stderr," - type %08x flags %08x ptr %016"PRIx64" cookie %016"PRIx64"\n", obj->type, obj->flags, (uint64_t)obj->binder, (uint64_t)obj->cookie); } } #define NAME(n) case n: return #n const char *cmd_name(uint32_t cmd) { switch(cmd) { NAME(BR_NOOP); NAME(BR_TRANSACTION_COMPLETE); NAME(BR_INCREFS); NAME(BR_ACQUIRE); NAME(BR_RELEASE); NAME(BR_DECREFS); NAME(BR_TRANSACTION); NAME(BR_REPLY); NAME(BR_FAILED_REPLY); NAME(BR_DEAD_REPLY); NAME(BR_DEAD_BINDER); default: return "???"; } } #else #define hexdump(a,b) do{} while (0) #define binder_dump_txn(txn) do{} while (0) #endif #define BIO_F_SHARED 0x01 /* needs to be buffer freed */ #define BIO_F_OVERFLOW 0x02 /* ran out of space */ #define BIO_F_IOERROR 0x04 #define BIO_F_MALLOCED 0x08 /* needs to be free()'d */ struct binder_state { int fd; void *mapped; size_t mapsize; }; struct binder_state *binder_open(const char* driver, size_t mapsize) { struct binder_state *bs; struct binder_version vers; bs = malloc(sizeof(*bs)); if (!bs) { errno = ENOMEM; return NULL; } bs->fd = open(driver, O_RDWR | O_CLOEXEC); if (bs->fd < 0) { fprintf(stderr,"binder: cannot open %s (%s)\n", driver, strerror(errno)); goto fail_open; } if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) { fprintf(stderr, "binder: kernel driver version (%d) differs from user space version (%d)\n", vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION); goto fail_open; } bs->mapsize = mapsize; bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); if (bs->mapped == MAP_FAILED) { fprintf(stderr,"binder: cannot map device (%s)\n", strerror(errno)); goto fail_map; } return bs; fail_map: close(bs->fd); fail_open: free(bs); return NULL; } void binder_close(struct binder_state *bs) { munmap(bs->mapped, bs->mapsize); close(bs->fd); free(bs); } int binder_become_context_manager(struct binder_state *bs) { struct flat_binder_object obj; memset(&obj, 0, sizeof(obj)); obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX; int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj); // fallback to original method if (result != 0) { android_errorWriteLog(0x534e4554, "121035042"); result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); } return result; } int binder_write(struct binder_state *bs, void *data, size_t len) { struct binder_write_read bwr; int res; bwr.write_size = len; bwr.write_consumed = 0; bwr.write_buffer = (uintptr_t) data; bwr.read_size = 0; bwr.read_consumed = 0; bwr.read_buffer = 0; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { fprintf(stderr,"binder_write: ioctl failed (%s)\n", strerror(errno)); } return res; } void binder_free_buffer(struct binder_state *bs, binder_uintptr_t buffer_to_free) { struct { uint32_t cmd_free; binder_uintptr_t buffer; } __attribute__((packed)) data; data.cmd_free = BC_FREE_BUFFER; data.buffer = buffer_to_free; binder_write(bs, &data, sizeof(data)); } void binder_send_reply(struct binder_state *bs, struct binder_io *reply, binder_uintptr_t buffer_to_free, int status) { struct { uint32_t cmd_free; binder_uintptr_t buffer; uint32_t cmd_reply; struct binder_transaction_data txn; } __attribute__((packed)) data; data.cmd_free = BC_FREE_BUFFER; data.buffer = buffer_to_free; data.cmd_reply = BC_REPLY; data.txn.target.ptr = 0; data.txn.cookie = 0; data.txn.code = 0; if (status) { data.txn.flags = TF_STATUS_CODE; data.txn.data_size = sizeof(int); data.txn.offsets_size = 0; data.txn.data.ptr.buffer = (uintptr_t)&status; data.txn.data.ptr.offsets = 0; } else { data.txn.flags = 0; data.txn.data_size = reply->data - reply->data0; data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0); data.txn.data.ptr.buffer = (uintptr_t)reply->data0; data.txn.data.ptr.offsets = (uintptr_t)reply->offs0; } binder_write(bs, &data, sizeof(data)); } int binder_parse(struct binder_state *bs, struct binder_io *bio, uintptr_t ptr, size_t size, binder_handler func) { int r = 1; uintptr_t end = ptr + (uintptr_t) size; while (ptr < end) { uint32_t cmd = *(uint32_t *) ptr; ptr += sizeof(uint32_t); #if TRACE fprintf(stderr,"%s:\n", cmd_name(cmd)); #endif switch(cmd) { case BR_NOOP: break; case BR_TRANSACTION_COMPLETE: break; case BR_INCREFS: case BR_ACQUIRE: case BR_RELEASE: case BR_DECREFS: #if TRACE fprintf(stderr," %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *))); #endif ptr += sizeof(struct binder_ptr_cookie); break; case BR_TRANSACTION_SEC_CTX: case BR_TRANSACTION: { struct binder_transaction_data_secctx txn; if (cmd == BR_TRANSACTION_SEC_CTX) { if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) { ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n"); return -1; } memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx)); ptr += sizeof(struct binder_transaction_data_secctx); } else /* BR_TRANSACTION */ { if ((end - ptr) < sizeof(struct binder_transaction_data)) { ALOGE("parse: txn too small (binder_transaction_data)!\n"); return -1; } memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data)); ptr += sizeof(struct binder_transaction_data); txn.secctx = 0; } binder_dump_txn(&txn.transaction_data); if (func) { unsigned rdata[256/4]; struct binder_io msg; struct binder_io reply; int res; bio_init(&reply, rdata, sizeof(rdata), 4); bio_init_from_txn(&msg, &txn.transaction_data); res = func(bs, &txn, &msg, &reply); if (txn.transaction_data.flags & TF_ONE_WAY) { binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer); } else { binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res); } } break; } case BR_REPLY: { struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; if ((end - ptr) < sizeof(*txn)) { ALOGE("parse: reply too small!\n"); return -1; } binder_dump_txn(txn); if (bio) { bio_init_from_txn(bio, txn); bio = 0; } else { /* todo FREE BUFFER */ } ptr += sizeof(*txn); r = 0; break; } case BR_DEAD_BINDER: { struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr; ptr += sizeof(binder_uintptr_t); death->func(bs, death->ptr); break; } case BR_FAILED_REPLY: r = -1; break; case BR_DEAD_REPLY: r = -1; break; default: ALOGE("parse: OOPS %d\n", cmd); return -1; } } return r; } void binder_acquire(struct binder_state *bs, uint32_t target) { uint32_t cmd[2]; cmd[0] = BC_ACQUIRE; cmd[1] = target; binder_write(bs, cmd, sizeof(cmd)); } void binder_release(struct binder_state *bs, uint32_t target) { uint32_t cmd[2]; cmd[0] = BC_RELEASE; cmd[1] = target; binder_write(bs, cmd, sizeof(cmd)); } void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death) { struct { uint32_t cmd; struct binder_handle_cookie payload; } __attribute__((packed)) data; data.cmd = BC_REQUEST_DEATH_NOTIFICATION; data.payload.handle = target; data.payload.cookie = (uintptr_t) death; binder_write(bs, &data, sizeof(data)); } int binder_call(struct binder_state *bs, struct binder_io *msg, struct binder_io *reply, uint32_t target, uint32_t code) { int res; struct binder_write_read bwr; struct { uint32_t cmd; struct binder_transaction_data txn; } __attribute__((packed)) writebuf; unsigned readbuf[32]; if (msg->flags & BIO_F_OVERFLOW) { fprintf(stderr,"binder: txn buffer overflow\n"); goto fail; } writebuf.cmd = BC_TRANSACTION; writebuf.txn.target.handle = target; writebuf.txn.code = code; writebuf.txn.flags = 0; writebuf.txn.data_size = msg->data - msg->data0; writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0); writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0; writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0; bwr.write_size = sizeof(writebuf); bwr.write_consumed = 0; bwr.write_buffer = (uintptr_t) &writebuf; hexdump(msg->data0, msg->data - msg->data0); for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno)); goto fail; } res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0); if (res == 0) return 0; if (res < 0) goto fail; } fail: memset(reply, 0, sizeof(*reply)); reply->flags |= BIO_F_IOERROR; return -1; } void binder_loop(struct binder_state *bs, binder_handler func) { int res; struct binder_write_read bwr; uint32_t readbuf[32]; bwr.write_size = 0; bwr.write_consumed = 0; bwr.write_buffer = 0; readbuf[0] = BC_ENTER_LOOPER; binder_write(bs, readbuf, sizeof(uint32_t)); for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf; res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); break; } res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); if (res == 0) { ALOGE("binder_loop: unexpected reply?!\n"); break; } if (res < 0) { ALOGE("binder_loop: io error %d %s\n", res, strerror(errno)); break; } } } void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn) { bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer; bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets; bio->data_avail = txn->data_size; bio->offs_avail = txn->offsets_size / sizeof(size_t); bio->flags = BIO_F_SHARED; } void bio_init(struct binder_io *bio, void *data, size_t maxdata, size_t maxoffs) { size_t n = maxoffs * sizeof(size_t); if (n > maxdata) { bio->flags = BIO_F_OVERFLOW; bio->data_avail = 0; bio->offs_avail = 0; return; } bio->data = bio->data0 = (char *) data + n; bio->offs = bio->offs0 = data; bio->data_avail = maxdata - n; bio->offs_avail = maxoffs; bio->flags = 0; } static void *bio_alloc(struct binder_io *bio, size_t size) { size = (size + 3) & (~3); if (size > bio->data_avail) { bio->flags |= BIO_F_OVERFLOW; return NULL; } else { void *ptr = bio->data; bio->data += size; bio->data_avail -= size; return ptr; } } void binder_done(struct binder_state *bs, __unused struct binder_io *msg, struct binder_io *reply) { struct { uint32_t cmd; uintptr_t buffer; } __attribute__((packed)) data; if (reply->flags & BIO_F_SHARED) { data.cmd = BC_FREE_BUFFER; data.buffer = (uintptr_t) reply->data0; binder_write(bs, &data, sizeof(data)); reply->flags = 0; } } static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio) { struct flat_binder_object *obj; obj = bio_alloc(bio, sizeof(*obj)); if (obj && bio->offs_avail) { bio->offs_avail--; *bio->offs++ = ((char*) obj) - ((char*) bio->data0); return obj; } bio->flags |= BIO_F_OVERFLOW; return NULL; } void bio_put_uint32(struct binder_io *bio, uint32_t n) { uint32_t *ptr = bio_alloc(bio, sizeof(n)); if (ptr) *ptr = n; } void bio_put_obj(struct binder_io *bio, void *ptr) { struct flat_binder_object *obj; obj = bio_alloc_obj(bio); if (!obj) return; obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; obj->hdr.type = BINDER_TYPE_BINDER; obj->binder = (uintptr_t)ptr; obj->cookie = 0; } void bio_put_ref(struct binder_io *bio, uint32_t handle) { struct flat_binder_object *obj; if (handle) obj = bio_alloc_obj(bio); else obj = bio_alloc(bio, sizeof(*obj)); if (!obj) return; obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; obj->hdr.type = BINDER_TYPE_HANDLE; obj->handle = handle; obj->cookie = 0; } void bio_put_string16(struct binder_io *bio, const uint16_t *str) { size_t len; uint16_t *ptr; if (!str) { bio_put_uint32(bio, 0xffffffff); return; } len = 0; while (str[len]) len++; if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) { bio_put_uint32(bio, 0xffffffff); return; } /* Note: The payload will carry 32bit size instead of size_t */ bio_put_uint32(bio, (uint32_t) len); len = (len + 1) * sizeof(uint16_t); ptr = bio_alloc(bio, len); if (ptr) memcpy(ptr, str, len); } void bio_put_string16_x(struct binder_io *bio, const char *_str) { unsigned char *str = (unsigned char*) _str; size_t len; uint16_t *ptr; if (!str) { bio_put_uint32(bio, 0xffffffff); return; } len = strlen(_str); if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) { bio_put_uint32(bio, 0xffffffff); return; } /* Note: The payload will carry 32bit size instead of size_t */ bio_put_uint32(bio, len); ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t)); if (!ptr) return; while (*str) *ptr++ = *str++; *ptr++ = 0; } static void *bio_get(struct binder_io *bio, size_t size) { size = (size + 3) & (~3); if (bio->data_avail < size){ bio->data_avail = 0; bio->flags |= BIO_F_OVERFLOW; return NULL; } else { void *ptr = bio->data; bio->data += size; bio->data_avail -= size; return ptr; } } uint32_t bio_get_uint32(struct binder_io *bio) { uint32_t *ptr = bio_get(bio, sizeof(*ptr)); return ptr ? *ptr : 0; } uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz) { size_t len; /* Note: The payload will carry 32bit size instead of size_t */ len = (size_t) bio_get_uint32(bio); if (sz) *sz = len; return bio_get(bio, (len + 1) * sizeof(uint16_t)); } static struct flat_binder_object *_bio_get_obj(struct binder_io *bio) { size_t n; size_t off = bio->data - bio->data0; /* TODO: be smarter about this? */ for (n = 0; n < bio->offs_avail; n++) { if (bio->offs[n] == off) return bio_get(bio, sizeof(struct flat_binder_object)); } bio->data_avail = 0; bio->flags |= BIO_F_OVERFLOW; return NULL; } uint32_t bio_get_ref(struct binder_io *bio) { struct flat_binder_object *obj; obj = _bio_get_obj(bio); if (!obj) return 0; if (obj->hdr.type == BINDER_TYPE_HANDLE) return obj->handle; return 0; } cmds/servicemanager/binder.h0100644 0000000 0000000 00000005762 13756501734 015167 0ustar000000000 0000000 /* Copyright 2008 The Android Open Source Project */ #ifndef _BINDER_H_ #define _BINDER_H_ #include #include struct binder_state; struct binder_io { char *data; /* pointer to read/write from */ binder_size_t *offs; /* array of offsets */ size_t data_avail; /* bytes available in data buffer */ size_t offs_avail; /* entries available in offsets array */ char *data0; /* start of data buffer */ binder_size_t *offs0; /* start of offsets buffer */ uint32_t flags; uint32_t unused; }; struct binder_death { void (*func)(struct binder_state *bs, void *ptr); void *ptr; }; /* the one magic handle */ #define BINDER_SERVICE_MANAGER 0U #define SVC_MGR_NAME "android.os.IServiceManager" enum { /* Must match definitions in IBinder.h and IServiceManager.h */ PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), SVC_MGR_GET_SERVICE = 1, SVC_MGR_CHECK_SERVICE, SVC_MGR_ADD_SERVICE, SVC_MGR_LIST_SERVICES, }; typedef int (*binder_handler)(struct binder_state *bs, struct binder_transaction_data_secctx *txn, struct binder_io *msg, struct binder_io *reply); struct binder_state *binder_open(const char* driver, size_t mapsize); void binder_close(struct binder_state *bs); /* initiate a blocking binder call * - returns zero on success */ int binder_call(struct binder_state *bs, struct binder_io *msg, struct binder_io *reply, uint32_t target, uint32_t code); /* release any state associate with the binder_io * - call once any necessary data has been extracted from the * binder_io after binder_call() returns * - can safely be called even if binder_call() fails */ void binder_done(struct binder_state *bs, struct binder_io *msg, struct binder_io *reply); /* manipulate strong references */ void binder_acquire(struct binder_state *bs, uint32_t target); void binder_release(struct binder_state *bs, uint32_t target); void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death); void binder_loop(struct binder_state *bs, binder_handler func); int binder_become_context_manager(struct binder_state *bs); /* allocate a binder_io, providing a stack-allocated working * buffer, size of the working buffer, and how many object * offset entries to reserve from the buffer */ void bio_init(struct binder_io *bio, void *data, size_t maxdata, size_t maxobjects); void bio_put_obj(struct binder_io *bio, void *ptr); void bio_put_ref(struct binder_io *bio, uint32_t handle); void bio_put_uint32(struct binder_io *bio, uint32_t n); void bio_put_string16(struct binder_io *bio, const uint16_t *str); void bio_put_string16_x(struct binder_io *bio, const char *_str); uint32_t bio_get_uint32(struct binder_io *bio); uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz); uint32_t bio_get_ref(struct binder_io *bio); #endif cmds/servicemanager/service_manager.c0100644 0000000 0000000 00000027040 13756501734 017042 0ustar000000000 0000000 /* Copyright 2008 The Android Open Source Project */ #include #include #include #include #include #include #include #include #include #include #include "binder.h" #ifdef VENDORSERVICEMANAGER #define LOG_TAG "VendorServiceManager" #else #define LOG_TAG "ServiceManager" #endif #include struct audit_data { pid_t pid; uid_t uid; const char *name; }; const char *str8(const uint16_t *x, size_t x_len) { static char buf[128]; size_t max = 127; char *p = buf; if (x_len < max) { max = x_len; } if (x) { while ((max > 0) && (*x != '\0')) { *p++ = *x++; max--; } } *p++ = 0; return buf; } int str16eq(const uint16_t *a, const char *b) { while (*a && *b) if (*a++ != *b++) return 0; if (*a || *b) return 0; return 1; } static char *service_manager_context; static struct selabel_handle* sehandle; static bool check_mac_perms(pid_t spid, const char* sid, uid_t uid, const char *tctx, const char *perm, const char *name) { char *lookup_sid = NULL; const char *class = "service_manager"; bool allowed; struct audit_data ad; if (sid == NULL && getpidcon(spid, &lookup_sid) < 0) { ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid); return false; } ad.pid = spid; ad.uid = uid; ad.name = name; if (sid == NULL) { android_errorWriteLog(0x534e4554, "121035042"); } int result = selinux_check_access(sid ? sid : lookup_sid, tctx, class, perm, (void *) &ad); allowed = (result == 0); freecon(lookup_sid); return allowed; } static bool check_mac_perms_from_getcon(pid_t spid, const char* sid, uid_t uid, const char *perm) { return check_mac_perms(spid, sid, uid, service_manager_context, perm, NULL); } static bool check_mac_perms_from_lookup(pid_t spid, const char* sid, uid_t uid, const char *perm, const char *name) { bool allowed; char *tctx = NULL; if (!sehandle) { ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n"); abort(); } if (selabel_lookup(sehandle, &tctx, name, 0) != 0) { ALOGE("SELinux: No match for %s in service_contexts.\n", name); return false; } allowed = check_mac_perms(spid, sid, uid, tctx, perm, name); freecon(tctx); return allowed; } static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid) { const char *perm = "add"; if (multiuser_get_app_id(uid) >= AID_APP) { return 0; /* Don't allow apps to register services */ } return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0; } static int svc_can_list(pid_t spid, const char* sid, uid_t uid) { const char *perm = "list"; return check_mac_perms_from_getcon(spid, sid, uid, perm) ? 1 : 0; } static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid) { const char *perm = "find"; return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0; } struct svcinfo { struct svcinfo *next; uint32_t handle; struct binder_death death; int allow_isolated; uint32_t dumpsys_priority; size_t len; uint16_t name[0]; }; struct svcinfo *svclist = NULL; struct svcinfo *find_svc(const uint16_t *s16, size_t len) { struct svcinfo *si; for (si = svclist; si; si = si->next) { if ((len == si->len) && !memcmp(s16, si->name, len * sizeof(uint16_t))) { return si; } } return NULL; } void svcinfo_death(struct binder_state *bs, void *ptr) { struct svcinfo *si = (struct svcinfo* ) ptr; ALOGI("service '%s' died\n", str8(si->name, si->len)); if (si->handle) { binder_release(bs, si->handle); si->handle = 0; } } uint16_t svcmgr_id[] = { 'a','n','d','r','o','i','d','.','o','s','.', 'I','S','e','r','v','i','c','e','M','a','n','a','g','e','r' }; uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid) { struct svcinfo *si = find_svc(s, len); if (!si || !si->handle) { return 0; } if (!si->allow_isolated) { // If this service doesn't allow access from isolated processes, // then check the uid to see if it is isolated. uid_t appid = uid % AID_USER; if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) { return 0; } } if (!svc_can_find(s, len, spid, sid, uid)) { return 0; } return si->handle; } int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) { struct svcinfo *si; //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle, // allow_isolated ? "allow_isolated" : "!allow_isolated", uid); if (!handle || (len == 0) || (len > 127)) return -1; if (!svc_can_register(s, len, spid, sid, uid)) { ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", str8(s, len), handle, uid); return -1; } si = find_svc(s, len); if (si) { if (si->handle) { ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n", str8(s, len), handle, uid); svcinfo_death(bs, si); } si->handle = handle; } else { si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); if (!si) { ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n", str8(s, len), handle, uid); return -1; } si->handle = handle; si->len = len; memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); si->name[len] = '\0'; si->death.func = (void*) svcinfo_death; si->death.ptr = si; si->allow_isolated = allow_isolated; si->dumpsys_priority = dumpsys_priority; si->next = svclist; svclist = si; } binder_acquire(bs, handle); binder_link_to_death(bs, handle, &si->death); return 0; } int svcmgr_handler(struct binder_state *bs, struct binder_transaction_data_secctx *txn_secctx, struct binder_io *msg, struct binder_io *reply) { struct svcinfo *si; uint16_t *s; size_t len; uint32_t handle; uint32_t strict_policy; int allow_isolated; uint32_t dumpsys_priority; struct binder_transaction_data *txn = &txn_secctx->transaction_data; //ALOGI("target=%p code=%d pid=%d uid=%d\n", // (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid); if (txn->target.ptr != BINDER_SERVICE_MANAGER) return -1; if (txn->code == PING_TRANSACTION) return 0; // Equivalent to Parcel::enforceInterface(), reading the RPC // header with the strict mode policy mask and the interface name. // Note that we ignore the strict_policy and don't propagate it // further (since we do no outbound RPCs anyway). strict_policy = bio_get_uint32(msg); bio_get_uint32(msg); // Ignore worksource header. s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } if ((len != (sizeof(svcmgr_id) / 2)) || memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { fprintf(stderr,"invalid id %s\n", str8(s, len)); return -1; } if (sehandle && selinux_status_updated() > 0) { #ifdef VENDORSERVICEMANAGER struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle(); #else struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle(); #endif if (tmp_sehandle) { selabel_close(sehandle); sehandle = tmp_sehandle; } } switch(txn->code) { case SVC_MGR_GET_SERVICE: case SVC_MGR_CHECK_SERVICE: s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid, (const char*) txn_secctx->secctx); if (!handle) break; bio_put_ref(reply, handle); return 0; case SVC_MGR_ADD_SERVICE: s = bio_get_string16(msg, &len); if (s == NULL) { return -1; } handle = bio_get_ref(msg); allow_isolated = bio_get_uint32(msg) ? 1 : 0; dumpsys_priority = bio_get_uint32(msg); if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, txn->sender_pid, (const char*) txn_secctx->secctx)) return -1; break; case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); uint32_t req_dumpsys_priority = bio_get_uint32(msg); if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) { ALOGE("list_service() uid=%d - PERMISSION DENIED\n", txn->sender_euid); return -1; } si = svclist; // walk through the list of services n times skipping services that // do not support the requested priority while (si) { if (si->dumpsys_priority & req_dumpsys_priority) { if (n == 0) break; n--; } si = si->next; } if (si) { bio_put_string16(reply, si->name); return 0; } return -1; } default: ALOGE("unknown code %d\n", txn->code); return -1; } bio_put_uint32(reply, 0); return 0; } static int audit_callback(void *data, __unused security_class_t cls, char *buf, size_t len) { struct audit_data *ad = (struct audit_data *)data; if (!ad || !ad->name) { ALOGE("No service manager audit data"); return 0; } snprintf(buf, len, "service=%s pid=%d uid=%d", ad->name, ad->pid, ad->uid); return 0; } int main(int argc, char** argv) { struct binder_state *bs; union selinux_callback cb; char *driver; if (argc > 1) { driver = argv[1]; } else { driver = "/dev/binder"; } bs = binder_open(driver, 128*1024); if (!bs) { #ifdef VENDORSERVICEMANAGER ALOGW("failed to open binder driver %s\n", driver); while (true) { sleep(UINT_MAX); } #else ALOGE("failed to open binder driver %s\n", driver); #endif return -1; } if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); #ifdef VENDORSERVICEMANAGER cb.func_log = selinux_vendor_log_callback; #else cb.func_log = selinux_log_callback; #endif selinux_set_callback(SELINUX_CB_LOG, cb); #ifdef VENDORSERVICEMANAGER sehandle = selinux_android_vendor_service_context_handle(); #else sehandle = selinux_android_service_context_handle(); #endif selinux_status_open(true); if (sehandle == NULL) { ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n"); abort(); } if (getcon(&service_manager_context) != 0) { ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n"); abort(); } binder_loop(bs, svcmgr_handler); return 0; } cmds/servicemanager/servicemanager.rc0100644 0000000 0000000 00000001055 13756501734 017063 0ustar000000000 0000000 service servicemanager /system/bin/servicemanager class core animation user system group system readproc critical onrestart restart healthd onrestart restart zygote onrestart restart audioserver onrestart restart media onrestart restart surfaceflinger onrestart restart inputflinger onrestart restart drm onrestart restart cameraserver onrestart restart keystore onrestart restart gatekeeperd onrestart restart thermalservice writepid /dev/cpuset/system-background/tasks shutdown critical cmds/servicemanager/vndservicemanager.rc0100644 0000000 0000000 00000000307 13756501734 017572 0ustar000000000 0000000 service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder class core user system group system readproc writepid /dev/cpuset/system-background/tasks shutdown critical cmds/surfacereplayer/0040755 0000000 0000000 00000000000 13756501734 013745 5ustar000000000 0000000 cmds/surfacereplayer/Android.bp0100644 0000000 0000000 00000000052 13756501734 015642 0ustar000000000 0000000 subdirs = [ "proto", "replayer", ]cmds/surfacereplayer/OWNERS0100644 0000000 0000000 00000000045 13756501734 014701 0ustar000000000 0000000 mathias@google.com racarr@google.com cmds/surfacereplayer/proto/0040755 0000000 0000000 00000000000 13756501734 015110 5ustar000000000 0000000 cmds/surfacereplayer/proto/Android.bp0100644 0000000 0000000 00000000347 13756501734 017014 0ustar000000000 0000000 cc_library_static { name: "libtrace_proto", srcs: [ "src/trace.proto", ], cflags: [ "-Wall", "-Werror", ], proto: { type: "lite", export_proto_headers: true, }, } cmds/surfacereplayer/proto/src/0040755 0000000 0000000 00000000000 13756501734 015677 5ustar000000000 0000000 cmds/surfacereplayer/proto/src/trace.proto0100644 0000000 0000000 00000010434 13756501734 020061 0ustar000000000 0000000 syntax = "proto2"; option optimize_for = LITE_RUNTIME; message Trace { repeated Increment increment = 1; } message Increment { required int64 time_stamp = 1; oneof increment { Transaction transaction = 2; SurfaceCreation surface_creation = 3; SurfaceDeletion surface_deletion = 4; BufferUpdate buffer_update = 5; VSyncEvent vsync_event = 6; DisplayCreation display_creation = 7; DisplayDeletion display_deletion = 8; PowerModeUpdate power_mode_update = 9; } } message Transaction { repeated SurfaceChange surface_change = 1; repeated DisplayChange display_change = 2; required bool synchronous = 3; required bool animation = 4; } message SurfaceChange { required int32 id = 1; reserved 7; oneof SurfaceChange { PositionChange position = 2; SizeChange size = 3; AlphaChange alpha = 4; LayerChange layer = 5; CropChange crop = 6; MatrixChange matrix = 8; OverrideScalingModeChange override_scaling_mode = 9; TransparentRegionHintChange transparent_region_hint = 10; LayerStackChange layer_stack = 11; HiddenFlagChange hidden_flag = 12; OpaqueFlagChange opaque_flag = 13; SecureFlagChange secure_flag = 14; DeferredTransactionChange deferred_transaction = 15; CornerRadiusChange corner_radius = 16; } } message PositionChange { required float x = 1; required float y = 2; } message SizeChange { required uint32 w = 1; required uint32 h = 2; } message AlphaChange { required float alpha = 1; } message CornerRadiusChange { required float corner_radius = 1; } message LayerChange { required uint32 layer = 1; } message CropChange { required Rectangle rectangle = 1; } message MatrixChange { required float dsdx = 1; required float dtdx = 2; required float dsdy = 3; required float dtdy = 4; } message OverrideScalingModeChange { required int32 override_scaling_mode = 1; } message TransparentRegionHintChange { repeated Rectangle region = 1; } message LayerStackChange { required uint32 layer_stack = 1; } message HiddenFlagChange { required bool hidden_flag = 1; } message OpaqueFlagChange { required bool opaque_flag = 1; } message SecureFlagChange { required bool secure_flag = 1; } message DeferredTransactionChange { required int32 layer_id = 1; required uint64 frame_number = 2; } message DisplayChange { required int32 id = 1; oneof DisplayChange { DispSurfaceChange surface = 2; LayerStackChange layer_stack = 3; SizeChange size = 4; ProjectionChange projection = 5; } } message DispSurfaceChange { required uint64 buffer_queue_id = 1; required string buffer_queue_name = 2; } message ProjectionChange { required int32 orientation = 1; required Rectangle viewport = 2; required Rectangle frame = 3; } message Rectangle { required int32 left = 1; required int32 top = 2; required int32 right = 3; required int32 bottom = 4; } message SurfaceCreation { required int32 id = 1; required string name = 2; required uint32 w = 3; required uint32 h = 4; } message SurfaceDeletion { required int32 id = 1; } message BufferUpdate { required int32 id = 1; required uint32 w = 2; required uint32 h = 3; required uint64 frame_number = 4; } message VSyncEvent { required int64 when = 1; } message DisplayCreation { required int32 id = 1; required string name = 2; optional uint64 display_id = 3; required bool is_secure = 4; } message DisplayDeletion { required int32 id = 1; } message PowerModeUpdate { required int32 id = 1; required int32 mode = 2; } cmds/surfacereplayer/replayer/0040755 0000000 0000000 00000000000 13756501734 015570 5ustar000000000 0000000 cmds/surfacereplayer/replayer/Android.bp0100644 0000000 0000000 00000002272 13756501734 017473 0ustar000000000 0000000 cc_library_shared { name: "libsurfacereplayer", srcs: [ "BufferQueueScheduler.cpp", "Event.cpp", "Replayer.cpp", ], cppflags: [ "-Werror", "-Wno-unused-parameter", "-Wno-format", "-Wno-c++98-compat-pedantic", "-Wno-float-conversion", "-Wno-disabled-macro-expansion", "-Wno-float-equal", "-Wno-sign-conversion", "-Wno-padded", ], static_libs: [ "libtrace_proto", ], shared_libs: [ "libEGL", "libGLESv2", "libbinder", "liblog", "libcutils", "libgui", "libui", "libutils", "libprotobuf-cpp-lite", "libbase", "libnativewindow", ], export_include_dirs: [ ".", ], } cc_binary { name: "surfacereplayer", srcs: [ "Main.cpp", ], shared_libs: [ "libprotobuf-cpp-lite", "libsurfacereplayer", "libutils", "libgui", ], static_libs: [ "libtrace_proto", ], cppflags: [ "-Werror", "-Wno-unused-parameter", "-Wno-c++98-compat-pedantic", "-Wno-float-conversion", "-Wno-disabled-macro-expansion", "-Wno-float-equal", ], } cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp0100644 0000000 0000000 00000006513 13756501734 022353 0ustar000000000 0000000 /* * Copyright 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. */ #define LOG_TAG "BufferQueueScheduler" #include "BufferQueueScheduler.h" #include #include using namespace android; BufferQueueScheduler::BufferQueueScheduler( const sp& surfaceControl, const HSV& color, int id) : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {} void BufferQueueScheduler::startScheduling() { ALOGV("Starting Scheduler for %d Layer", mSurfaceId); std::unique_lock lock(mMutex); if (mSurfaceControl == nullptr) { mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); }); } while (mContinueScheduling) { while (true) { if (mBufferEvents.empty()) { break; } BufferEvent event = mBufferEvents.front(); lock.unlock(); bufferUpdate(event.dimensions); fillSurface(event.event); mColor.modulate(); lock.lock(); mBufferEvents.pop(); } mCondition.wait(lock); } } void BufferQueueScheduler::addEvent(const BufferEvent& event) { std::lock_guard lock(mMutex); mBufferEvents.push(event); mCondition.notify_one(); } void BufferQueueScheduler::stopScheduling() { std::lock_guard lock(mMutex); mContinueScheduling = false; mCondition.notify_one(); } void BufferQueueScheduler::setSurfaceControl( const sp& surfaceControl, const HSV& color) { std::lock_guard lock(mMutex); mSurfaceControl = surfaceControl; mColor = color; mCondition.notify_one(); } void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) { sp s = mSurfaceControl->getSurface(); s->setBuffersDimensions(dimensions.width, dimensions.height); } void BufferQueueScheduler::fillSurface(const std::shared_ptr& event) { ANativeWindow_Buffer outBuffer; sp s = mSurfaceControl->getSurface(); status_t status = s->lock(&outBuffer, nullptr); if (status != NO_ERROR) { ALOGE("fillSurface: failed to lock buffer, (%d)", status); return; } auto color = mColor.getRGB(); auto img = reinterpret_cast(outBuffer.bits); for (int y = 0; y < outBuffer.height; y++) { for (int x = 0; x < outBuffer.width; x++) { uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); pixel[0] = color.r; pixel[1] = color.g; pixel[2] = color.b; pixel[3] = LAYER_ALPHA; } } event->readyToExecute(); status = s->unlockAndPost(); ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status); } cmds/surfacereplayer/replayer/BufferQueueScheduler.h0100644 0000000 0000000 00000004163 13756501734 022017 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H #define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H #include "Color.h" #include "Event.h" #include #include #include #include #include #include #include namespace android { auto constexpr LAYER_ALPHA = 190; struct Dimensions { Dimensions() = default; Dimensions(int w, int h) : width(w), height(h) {} int width = 0; int height = 0; }; struct BufferEvent { BufferEvent() = default; BufferEvent(std::shared_ptr e, Dimensions d) : event(e), dimensions(d) {} std::shared_ptr event; Dimensions dimensions; }; class BufferQueueScheduler { public: BufferQueueScheduler(const sp& surfaceControl, const HSV& color, int id); void startScheduling(); void addEvent(const BufferEvent&); void stopScheduling(); void setSurfaceControl(const sp& surfaceControl, const HSV& color); private: void bufferUpdate(const Dimensions& dimensions); // Lock and fill the surface, block until the event is signaled by the main loop, // then unlock and post the buffer. void fillSurface(const std::shared_ptr& event); sp mSurfaceControl; HSV mColor; const int mSurfaceId; bool mContinueScheduling; std::queue mBufferEvents; std::mutex mMutex; std::condition_variable mCondition; }; } // namespace android #endif cmds/surfacereplayer/replayer/Color.h0100644 0000000 0000000 00000005170 13756501734 017017 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_SURFACEREPLAYER_COLOR_H #define ANDROID_SURFACEREPLAYER_COLOR_H #include #include namespace android { constexpr double modulateFactor = .0001; constexpr double modulateLimit = .80; struct RGB { RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {} uint8_t r = 0; uint8_t g = 0; uint8_t b = 0; }; struct HSV { HSV() = default; HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {} double h = 0; double s = 0; double v = 0; RGB getRGB() const; bool modulateUp = false; void modulate(); }; void inline HSV::modulate() { if(modulateUp) { v += modulateFactor; } else { v -= modulateFactor; } if(v <= modulateLimit || v >= 1) { modulateUp = !modulateUp; } } inline RGB HSV::getRGB() const { using namespace std; double r = 0, g = 0, b = 0; if (s == 0) { r = v; g = v; b = v; } else { auto tempHue = static_cast(h) % 360; tempHue = tempHue / 60; int i = static_cast(trunc(tempHue)); double f = h - i; double x = v * (1.0 - s); double y = v * (1.0 - (s * f)); double z = v * (1.0 - (s * (1.0 - f))); switch (i) { case 0: r = v; g = z; b = x; break; case 1: r = y; g = v; b = x; break; case 2: r = x; g = v; b = z; break; case 3: r = x; g = y; b = v; break; case 4: r = z; g = x; b = v; break; default: r = v; g = x; b = y; break; } } return RGB(round(r * 255), round(g * 255), round(b * 255)); } } #endif cmds/surfacereplayer/replayer/Event.cpp0100644 0000000 0000000 00000002701 13756501734 017352 0ustar000000000 0000000 /* * Copyright 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. */ #include "Event.h" using namespace android; Event::Event(Increment::IncrementCase type) : mIncrementType(type) {} void Event::readyToExecute() { changeState(Event::EventState::Waiting); waitUntil(Event::EventState::Signaled); changeState(Event::EventState::Running); } void Event::complete() { waitUntil(Event::EventState::Waiting); changeState(Event::EventState::Signaled); waitUntil(Event::EventState::Running); } void Event::waitUntil(Event::EventState state) { std::unique_lock lock(mLock); mCond.wait(lock, [this, state] { return (mState == state); }); } void Event::changeState(Event::EventState state) { std::unique_lock lock(mLock); mState = state; lock.unlock(); mCond.notify_one(); } Increment::IncrementCase Event::getIncrementType() { return mIncrementType; } cmds/surfacereplayer/replayer/Event.h0100644 0000000 0000000 00000003066 13756501734 017024 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_SURFACEREPLAYER_EVENT_H #define ANDROID_SURFACEREPLAYER_EVENT_H #include #include #include namespace android { class Event { public: Event(Increment::IncrementCase); enum class EventState { SettingUp, // Completing as much time-independent work as possible Waiting, // Waiting for signal from main thread to finish execution Signaled, // Signaled by main thread, about to immediately switch to Running Running // Finishing execution of rest of work }; void readyToExecute(); void complete(); Increment::IncrementCase getIncrementType(); private: void waitUntil(EventState state); void changeState(EventState state); std::mutex mLock; std::condition_variable mCond; EventState mState = EventState::SettingUp; Increment::IncrementCase mIncrementType; }; } #endif cmds/surfacereplayer/replayer/Main.cpp0100644 0000000 0000000 00000006547 13756501734 017171 0ustar000000000 0000000 /* * Copyright 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. */ /* * Replayer - Main.cpp * * 1. Get flags from command line * 2. Commit actions or settings based on the flags * 3. Initalize a replayer object with the filename passed in * 4. Replay * 5. Exit successfully or print error statement */ #include #include #include #include #include using namespace android; void printHelpMenu() { std::cout << "SurfaceReplayer options:\n"; std::cout << "Usage: surfacereplayer [OPTIONS...] \n"; std::cout << " File path must be absolute" << std::endl << std::endl; std::cout << " -m Stops the replayer at the start of the trace and switches "; "to manual replay\n"; std::cout << "\n -t [Number of Threads] Specifies the number of threads to be used while " "replaying (default is " << android::DEFAULT_THREADS << ")\n"; std::cout << "\n -s [Timestamp] Specify at what timestamp should the replayer switch " "to manual replay\n"; std::cout << " -n Ignore timestamps and run through trace as fast as possible\n"; std::cout << " -l Indefinitely loop the replayer\n"; std::cout << " -h Display help menu\n"; std::cout << std::endl; } int main(int argc, char** argv) { std::string filename; bool loop = false; bool wait = true; bool pauseBeginning = false; int numThreads = DEFAULT_THREADS; long stopHere = -1; int opt = 0; while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) { switch (opt) { case 'm': pauseBeginning = true; break; case 't': numThreads = atoi(optarg); break; case 's': stopHere = atol(optarg); break; case 'n': wait = false; break; case 'l': loop = true; break; case 'h': case '?': printHelpMenu(); exit(0); default: std::cerr << "Invalid argument...exiting" << std::endl; printHelpMenu(); exit(0); } } char** input = argv + optind; if (input[0] == nullptr) { std::cerr << "No trace file provided...exiting" << std::endl; abort(); } filename.assign(input[0]); status_t status = NO_ERROR; do { android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere); status = r.replay(); } while(loop); if (status == NO_ERROR) { std::cout << "Successfully finished replaying trace" << std::endl; } else { std::cerr << "Trace replayer returned error: " << status << std::endl; } return 0; } cmds/surfacereplayer/replayer/README.md0100644 0000000 0000000 00000023726 13756501734 017056 0ustar000000000 0000000 SurfaceReplayer Documentation =================== [go/SurfaceReplayer](go/SurfaceReplayer) SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by [SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays * Creation and deletion of surfaces/displays * Alterations to the surfaces/displays called Transactions * Buffer Updates to surfaces * VSync events At their specified times to be as close to the original trace. Usage -------- ###Creating a trace SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to utilize it. To allow it to write to the device, run `setenforce 0` To start recording a trace, run `service call SurfaceFlinger 1020 i32 1` To stop recording, run `service call SurfaceFlinger 1020 i32 0` The default location for the trace is `/data/SurfaceTrace.dat` ###Executable To replay a specific trace, execute `/data/local/tmp/surfacereplayer /absolute/path/to/trace` inside the android shell. This will replay the full trace and then exit. Running this command outside of the shell by prepending `adb shell` will not allow for manual control and will not turn off VSync injections if it interrupted in any way other than fully replaying the trace The replay will not fill surfaces with their contents during the capture. Rather they are given a random color which will be the same every time the trace is replayed. Surfaces modulate their color at buffer updates. **Options:** - -m pause the replayer at the start of the trace for manual replay - -t [Number of Threads] uses specified number of threads to queue up actions (default is 3) - -s [Timestamp] switches to manual replay at specified timestamp - -n Ignore timestamps and run through trace as fast as possible - -l Indefinitely loop the replayer - -h displays help menu **Manual Replay:** When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled by the user. Pressing CTRL-C again will exit the replayer. Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter without inputting a command repeats the previous command. - n - steps the replayer to the next VSync event - ni - steps the replayer to the next increment - c - continues normal replaying - c [milliseconds] - continue until specified number of milliseconds have passed - s [timestamp] - continue and stop at specified timestamp - l - list out timestamp of current increment - h - displays help menu ###Shared Library To use the shared library include these shared libraries `libsurfacereplayer` `libprotobuf-cpp-full` `libutils` And the static library `libtrace_proto` Include the replayer header at the top of your file `#include ` There are two constructors for the replayer `Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)` `Replayer(Trace& trace, ... ditto ...)` The first constructor takes in the filepath where the trace is located and loads in the trace object internally. - replayManually - **True**: if the replayer will immediately switch to manual replay at the start - numThreads - Number of worker threads the replayer will use. - wait - **False**: Replayer ignores waits in between increments - stopHere - Time stamp of where the replayer should run to then switch to manual replay The second constructor includes all of the same parameters but takes in a preloaded trace object. To use add `#include ` To your file After initializing the Replayer call replayer.replay(); And the trace will start replaying. Once the trace is finished replaying, the function will return. The layers that are visible at the end of the trace will remain on screen until the program terminates. **If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was never executed. This can be fixed by executing `service call SurfaceFlinger 23 i32 0` in the android shell Code Breakdown ------------- The Replayer is composed of 5 components. - The data format of the trace (Trace.proto) - The Replayer object (Replayer.cpp) - The synchronization mechanism to signal threads within the Replayer (Event.cpp) - The scheduler for buffer updates per surface (BufferQueueScheduler.cpp) - The Main executable (Main.cpp) ### Traces Traces are represented as a protobuf message located in surfacereplayer/proto/src. **Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger). **Increments** contain the time stamp of when it occurred and a *oneof* which can be a - Transaction - SurfaceCreation - SurfaceDeletion - DisplayCreation - DisplayDeleteion - BufferUpdate - VSyncEvent - PowerModeUpdate **Transactions** contain whether the transaction was synchronous or animated and *repeated* **SurfaceChanges** and **DisplayChanges** - **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as position, alpha, hidden, size, etc. - **DisplayChanges** contain the id of the display being manipulated and can be changes such as size, layer stack, projection, etc. **Surface/Display Creation** contain the id of the surface/display and the name of the surface/display **Surface/Display Deletion** contain the id of the surface/display to be deleted **Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the buffer, and the frame number. **VSyncEvents** contain when the VSync event has occurred. **PowerModeUpdates** contain the id of the display being updated and what mode it is being changed to. To output the contents of a trace in a readable format, execute `**aprotoc** --decode=Trace \ -I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \ $ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \ < **YourTraceFile.dat** > **YourOutputName.txt**` ###Replayer Fundamentally the replayer loads a trace and iterates through each increment, waiting the required amount of time until the increment should be executed, then executing the increment. The first increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and goes from there. Increments from the trace are played asynchronously rather than one by one, being dispatched by the main thread, queued up in a thread pool and completed when the main thread deems they are ready to finish execution. When an increment is dispatched, it completes as much work as it can before it has to be synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action (e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event object. The main thread holds a queue of these Event objects and completes the corresponding Event base on its time stamp. After completing an increment, the main thread will dispatch another increment and continue. The main thread's execution flow is outlined below initReplay() //queue up the initial increments while(!pendingIncrements.empty()) { //while increments remaining event = pendingIncrement.pop(); wait(event.time_stamp(); //waitUntil it is time to complete this increment event.complete() //signal to let event finish if(increments remaing()) { dispatchEvent() //queue up another increment } } A worker thread's flow looks like so //dispatched! Execute non-time sensitive work here ... event.readyToExecute() //time sensitive point...waiting for Main Thread ... Finish execution ### Event An Event is a simple synchronization mechanism used to facilitate communication between the main and worker threads. Every time an increment is dispatched, an Event object is also created. An Event can be in 4 different states: - **SettingUp** - The worker is in the process of completing all non-time sensitive work - **Waiting** - The worker is waiting on the main thread to signal it. - **Signaled** - The worker has just been signaled by the main thread - **Running** - The worker is running again and finishing the rest of its work. When the main thread wants to finish the execution of a worker, the worker can either still be **SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the main thread will **Signal** it to complete. The worker thread changes itself to the **Running** state once **Signaled**. This last step exists in order to communicate back to the main thread that the worker thread has actually started completing its execution, rather than being preempted right after signalling. Once this happens, the main thread schedules the next worker. This makes sure there is a constant amount of workers running at one time. This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the worker and main thread respectively. ### BufferQueueScheduler During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential **BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it, which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by handling when **BufferUpdates** should be scheduled, making sure that they don't overlap. When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a **BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one every time an Event is completed. ### Main The main exectuable reads in the command line arguments. Creates the Replayer using those arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit gracefully, if there are then it will report the error and then exit. cmds/surfacereplayer/replayer/Replayer.cpp0100644 0000000 0000000 00000057133 13756501734 020065 0ustar000000000 0000000 /* Copyright 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "SurfaceReplayer" #include "Replayer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace android; std::atomic_bool Replayer::sReplayingManually(false); Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere) : mTrace(), mLoaded(false), mIncrementIndex(0), mCurrentTime(0), mNumThreads(numThreads), mWaitForTimeStamps(wait), mStopTimeStamp(stopHere) { srand(RAND_COLOR_SEED); std::string input; if (!android::base::ReadFileToString(filename, &input, true)) { std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl; abort(); } mLoaded = mTrace.ParseFromString(input); if (!mLoaded) { std::cerr << "Trace did not load." << std::endl; abort(); } mCurrentTime = mTrace.increment(0).time_stamp(); sReplayingManually.store(replayManually); if (stopHere < 0) { mHasStopped = true; } } Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere) : mTrace(t), mLoaded(true), mIncrementIndex(0), mCurrentTime(0), mNumThreads(numThreads), mWaitForTimeStamps(wait), mStopTimeStamp(stopHere) { srand(RAND_COLOR_SEED); mCurrentTime = mTrace.increment(0).time_stamp(); sReplayingManually.store(replayManually); if (stopHere < 0) { mHasStopped = true; } } status_t Replayer::replay() { signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control ALOGV("There are %d increments.", mTrace.increment_size()); status_t status = loadSurfaceComposerClient(); if (status != NO_ERROR) { ALOGE("Couldn't create SurfaceComposerClient (%d)", status); return status; } SurfaceComposerClient::enableVSyncInjections(true); initReplay(); ALOGV("Starting actual Replay!"); while (!mPendingIncrements.empty()) { mCurrentIncrement = mTrace.increment(mIncrementIndex); if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) { mHasStopped = true; sReplayingManually.store(true); } waitForConsoleCommmand(); if (mWaitForTimeStamps) { waitUntilTimestamp(mCurrentIncrement.time_stamp()); } auto event = mPendingIncrements.front(); mPendingIncrements.pop(); event->complete(); if (event->getIncrementType() == Increment::kVsyncEvent) { mWaitingForNextVSync = false; } if (mIncrementIndex + mNumThreads < mTrace.increment_size()) { status = dispatchEvent(mIncrementIndex + mNumThreads); if (status != NO_ERROR) { SurfaceComposerClient::enableVSyncInjections(false); return status; } } mIncrementIndex++; mCurrentTime = mCurrentIncrement.time_stamp(); } SurfaceComposerClient::enableVSyncInjections(false); return status; } status_t Replayer::initReplay() { for (int i = 0; i < mNumThreads && i < mTrace.increment_size(); i++) { status_t status = dispatchEvent(i); if (status != NO_ERROR) { ALOGE("Unable to dispatch event (%d)", status); return status; } } return NO_ERROR; } void Replayer::stopAutoReplayHandler(int /*signal*/) { if (sReplayingManually) { SurfaceComposerClient::enableVSyncInjections(false); exit(0); } sReplayingManually.store(true); } std::vector split(const std::string& s, const char delim) { std::vector elems; std::stringstream ss(s); std::string item; while (getline(ss, item, delim)) { elems.push_back(item); } return elems; } bool isNumber(const std::string& s) { return !s.empty() && std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end(); } void Replayer::waitForConsoleCommmand() { if (!sReplayingManually || mWaitingForNextVSync) { return; } while (true) { std::string input = ""; std::cout << "> "; getline(std::cin, input); if (input.empty()) { input = mLastInput; } else { mLastInput = input; } if (mLastInput.empty()) { continue; } std::vector inputs = split(input, ' '); if (inputs[0] == "n") { // next vsync mWaitingForNextVSync = true; break; } else if (inputs[0] == "ni") { // next increment break; } else if (inputs[0] == "c") { // continue if (inputs.size() > 1 && isNumber(inputs[1])) { long milliseconds = stoi(inputs[1]); std::thread([&] { std::cout << "Started!" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); sReplayingManually.store(true); std::cout << "Should have stopped!" << std::endl; }).detach(); } sReplayingManually.store(false); mWaitingForNextVSync = false; break; } else if (inputs[0] == "s") { // stop at this timestamp if (inputs.size() < 1) { std::cout << "No time stamp given" << std::endl; continue; } sReplayingManually.store(false); mStopTimeStamp = stol(inputs[1]); mHasStopped = false; break; } else if (inputs[0] == "l") { // list std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n"; continue; } else if (inputs[0] == "q") { // quit SurfaceComposerClient::enableVSyncInjections(false); exit(0); } else if (inputs[0] == "h") { // help // add help menu std::cout << "Manual Replay options:\n"; std::cout << " n - Go to next VSync\n"; std::cout << " ni - Go to next increment\n"; std::cout << " c - Continue\n"; std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n"; std::cout << " s [timestamp] - Continue and stop at specified timestamp\n"; std::cout << " l - List out timestamp of current increment\n"; std::cout << " h - Display help menu\n"; std::cout << std::endl; continue; } std::cout << "Invalid Command" << std::endl; } } status_t Replayer::dispatchEvent(int index) { auto increment = mTrace.increment(index); std::shared_ptr event = std::make_shared(increment.increment_case()); mPendingIncrements.push(event); status_t status = NO_ERROR; switch (increment.increment_case()) { case increment.kTransaction: { std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach(); } break; case increment.kSurfaceCreation: { std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event) .detach(); } break; case increment.kBufferUpdate: { std::lock_guard lock1(mLayerLock); std::lock_guard lock2(mBufferQueueSchedulerLock); Dimensions dimensions(increment.buffer_update().w(), increment.buffer_update().h()); BufferEvent bufferEvent(event, dimensions); auto layerId = increment.buffer_update().id(); if (mBufferQueueSchedulers.count(layerId) == 0) { mBufferQueueSchedulers[layerId] = std::make_shared( mLayers[layerId], mColors[layerId], layerId); mBufferQueueSchedulers[layerId]->addEvent(bufferEvent); std::thread(&BufferQueueScheduler::startScheduling, mBufferQueueSchedulers[increment.buffer_update().id()].get()) .detach(); } else { auto bqs = mBufferQueueSchedulers[increment.buffer_update().id()]; bqs->addEvent(bufferEvent); } } break; case increment.kVsyncEvent: { std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach(); } break; case increment.kDisplayCreation: { std::thread(&Replayer::createDisplay, this, increment.display_creation(), event) .detach(); } break; case increment.kDisplayDeletion: { std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event) .detach(); } break; case increment.kPowerModeUpdate: { std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event) .detach(); } break; default: ALOGE("Unknown Increment Type: %d", increment.increment_case()); status = BAD_VALUE; break; } return status; } status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr& event) { ALOGV("Started Transaction"); SurfaceComposerClient::Transaction liveTransaction; status_t status = NO_ERROR; status = doSurfaceTransaction(liveTransaction, t.surface_change()); doDisplayTransaction(liveTransaction, t.display_change()); if (t.animation()) { liveTransaction.setAnimationTransaction(); } event->readyToExecute(); liveTransaction.apply(t.synchronous()); ALOGV("Ended Transaction"); return status; } status_t Replayer::doSurfaceTransaction( SurfaceComposerClient::Transaction& transaction, const SurfaceChanges& surfaceChanges) { status_t status = NO_ERROR; for (const SurfaceChange& change : surfaceChanges) { std::unique_lock lock(mLayerLock); if (mLayers[change.id()] == nullptr) { mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); }); } switch (change.SurfaceChange_case()) { case SurfaceChange::SurfaceChangeCase::kPosition: setPosition(transaction, change.id(), change.position()); break; case SurfaceChange::SurfaceChangeCase::kSize: setSize(transaction, change.id(), change.size()); break; case SurfaceChange::SurfaceChangeCase::kAlpha: setAlpha(transaction, change.id(), change.alpha()); break; case SurfaceChange::SurfaceChangeCase::kLayer: setLayer(transaction, change.id(), change.layer()); break; case SurfaceChange::SurfaceChangeCase::kCrop: setCrop(transaction, change.id(), change.crop()); break; case SurfaceChange::SurfaceChangeCase::kCornerRadius: setCornerRadius(transaction, change.id(), change.corner_radius()); break; case SurfaceChange::SurfaceChangeCase::kMatrix: setMatrix(transaction, change.id(), change.matrix()); break; case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: setOverrideScalingMode(transaction, change.id(), change.override_scaling_mode()); break; case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint: setTransparentRegionHint(transaction, change.id(), change.transparent_region_hint()); break; case SurfaceChange::SurfaceChangeCase::kLayerStack: setLayerStack(transaction, change.id(), change.layer_stack()); break; case SurfaceChange::SurfaceChangeCase::kHiddenFlag: setHiddenFlag(transaction, change.id(), change.hidden_flag()); break; case SurfaceChange::SurfaceChangeCase::kOpaqueFlag: setOpaqueFlag(transaction, change.id(), change.opaque_flag()); break; case SurfaceChange::SurfaceChangeCase::kSecureFlag: setSecureFlag(transaction, change.id(), change.secure_flag()); break; case SurfaceChange::SurfaceChangeCase::kDeferredTransaction: waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock); setDeferredTransaction(transaction, change.id(), change.deferred_transaction()); break; default: status = 1; break; } if (status != NO_ERROR) { ALOGE("Unknown Transaction Code"); return status; } } return status; } void Replayer::doDisplayTransaction(SurfaceComposerClient::Transaction& t, const DisplayChanges& displayChanges) { for (const DisplayChange& change : displayChanges) { ALOGV("Doing display transaction"); std::unique_lock lock(mDisplayLock); if (mDisplays[change.id()] == nullptr) { mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); }); } switch (change.DisplayChange_case()) { case DisplayChange::DisplayChangeCase::kSurface: setDisplaySurface(t, change.id(), change.surface()); break; case DisplayChange::DisplayChangeCase::kLayerStack: setDisplayLayerStack(t, change.id(), change.layer_stack()); break; case DisplayChange::DisplayChangeCase::kSize: setDisplaySize(t, change.id(), change.size()); break; case DisplayChange::DisplayChangeCase::kProjection: setDisplayProjection(t, change.id(), change.projection()); break; default: break; } } } void Replayer::setPosition(SurfaceComposerClient::Transaction& t, layer_id id, const PositionChange& pc) { ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y()); t.setPosition(mLayers[id], pc.x(), pc.y()); } void Replayer::setSize(SurfaceComposerClient::Transaction& t, layer_id id, const SizeChange& sc) { ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h()); t.setSize(mLayers[id], sc.w(), sc.h()); } void Replayer::setLayer(SurfaceComposerClient::Transaction& t, layer_id id, const LayerChange& lc) { ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer()); t.setLayer(mLayers[id], lc.layer()); } void Replayer::setAlpha(SurfaceComposerClient::Transaction& t, layer_id id, const AlphaChange& ac) { ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha()); t.setAlpha(mLayers[id], ac.alpha()); } void Replayer::setCrop(SurfaceComposerClient::Transaction& t, layer_id id, const CropChange& cc) { ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id, cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(), cc.rectangle().bottom()); Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(), cc.rectangle().bottom()); t.setCrop_legacy(mLayers[id], r); } void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t, layer_id id, const CornerRadiusChange& cc) { ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius()); t.setCornerRadius(mLayers[id], cc.corner_radius()); } void Replayer::setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc) { ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); } void Replayer::setOverrideScalingMode(SurfaceComposerClient::Transaction& t, layer_id id, const OverrideScalingModeChange& osmc) { ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode()); t.setOverrideScalingMode(mLayers[id], osmc.override_scaling_mode()); } void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t, layer_id id, const TransparentRegionHintChange& trhc) { ALOGV("Setting Transparent Region Hint"); Region re = Region(); for (const auto& r : trhc.region()) { Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom()); re.merge(rect); } t.setTransparentRegionHint(mLayers[id], re); } void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t, layer_id id, const LayerStackChange& lsc) { ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack()); t.setLayerStack(mLayers[id], lsc.layer_stack()); } void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t, layer_id id, const HiddenFlagChange& hfc) { ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag()); layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0; t.setFlags(mLayers[id], flag, layer_state_t::eLayerHidden); } void Replayer::setOpaqueFlag(SurfaceComposerClient::Transaction& t, layer_id id, const OpaqueFlagChange& ofc) { ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag()); layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0; t.setFlags(mLayers[id], flag, layer_state_t::eLayerOpaque); } void Replayer::setSecureFlag(SurfaceComposerClient::Transaction& t, layer_id id, const SecureFlagChange& sfc) { ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag()); layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0; t.setFlags(mLayers[id], flag, layer_state_t::eLayerSecure); } void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t, layer_id id, const DeferredTransactionChange& dtc) { ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, " "frame_number=%llu", id, dtc.layer_id(), dtc.frame_number()); if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) { ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id()); return; } auto handle = mLayers[dtc.layer_id()]->getHandle(); t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number()); } void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, display_id id, const DispSurfaceChange& /*dsc*/) { sp outProducer; sp outConsumer; BufferQueue::createBufferQueue(&outProducer, &outConsumer); t.setDisplaySurface(mDisplays[id], outProducer); } void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t, display_id id, const LayerStackChange& lsc) { t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack()); } void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t, display_id id, const SizeChange& sc) { t.setDisplaySize(mDisplays[id], sc.w(), sc.h()); } void Replayer::setDisplayProjection(SurfaceComposerClient::Transaction& t, display_id id, const ProjectionChange& pc) { Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(), pc.viewport().bottom()); Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom()); t.setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame); } status_t Replayer::createSurfaceControl( const SurfaceCreation& create, const std::shared_ptr& event) { event->readyToExecute(); ALOGV("Creating Surface Control: ID: %d", create.id()); sp surfaceControl = mComposerClient->createSurface( String8(create.name().c_str()), create.w(), create.h(), PIXEL_FORMAT_RGBA_8888, 0); if (surfaceControl == nullptr) { ALOGE("CreateSurfaceControl: unable to create surface control"); return BAD_VALUE; } std::lock_guard lock1(mLayerLock); auto& layer = mLayers[create.id()]; layer = surfaceControl; mColors[create.id()] = HSV(rand() % 360, 1, 1); mLayerCond.notify_all(); std::lock_guard lock2(mBufferQueueSchedulerLock); if (mBufferQueueSchedulers.count(create.id()) != 0) { mBufferQueueSchedulers[create.id()]->setSurfaceControl( mLayers[create.id()], mColors[create.id()]); } return NO_ERROR; } status_t Replayer::injectVSyncEvent( const VSyncEvent& vSyncEvent, const std::shared_ptr& event) { ALOGV("Injecting VSync Event"); event->readyToExecute(); SurfaceComposerClient::injectVSync(vSyncEvent.when()); return NO_ERROR; } void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr& event) { ALOGV("Creating display"); event->readyToExecute(); std::lock_guard lock(mDisplayLock); sp display = SurfaceComposerClient::createDisplay( String8(create.name().c_str()), create.is_secure()); mDisplays[create.id()] = display; mDisplayCond.notify_all(); ALOGV("Done creating display"); } void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr& event) { ALOGV("Delete display"); event->readyToExecute(); std::lock_guard lock(mDisplayLock); SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]); mDisplays.erase(delete_.id()); } void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr& event) { ALOGV("Updating power mode"); event->readyToExecute(); SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode()); } void Replayer::waitUntilTimestamp(int64_t timestamp) { ALOGV("Waiting for %lld nanoseconds...", static_cast(timestamp - mCurrentTime)); std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime)); } void Replayer::waitUntilDeferredTransactionLayerExists( const DeferredTransactionChange& dtc, std::unique_lock& lock) { if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) { mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); }); } } status_t Replayer::loadSurfaceComposerClient() { mComposerClient = new SurfaceComposerClient; return mComposerClient->initCheck(); } cmds/surfacereplayer/replayer/Replayer.h0100644 0000000 0000000 00000014167 13756501734 017532 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_SURFACEREPLAYER_H #define ANDROID_SURFACEREPLAYER_H #include "BufferQueueScheduler.h" #include "Color.h" #include "Event.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat"; const auto RAND_COLOR_SEED = 700; const auto DEFAULT_THREADS = 3; typedef int32_t layer_id; typedef int32_t display_id; typedef google::protobuf::RepeatedPtrField SurfaceChanges; typedef google::protobuf::RepeatedPtrField DisplayChanges; class Replayer { public: Replayer(const std::string& filename, bool replayManually = false, int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1); Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1); status_t replay(); private: status_t initReplay(); void waitForConsoleCommmand(); static void stopAutoReplayHandler(int signal); status_t dispatchEvent(int index); status_t doTransaction(const Transaction& transaction, const std::shared_ptr& event); status_t createSurfaceControl(const SurfaceCreation& create, const std::shared_ptr& event); status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr& event); void createDisplay(const DisplayCreation& create, const std::shared_ptr& event); void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr& event); void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr& event); status_t doSurfaceTransaction(SurfaceComposerClient::Transaction& transaction, const SurfaceChanges& surfaceChange); void doDisplayTransaction(SurfaceComposerClient::Transaction& transaction, const DisplayChanges& displayChange); void setPosition(SurfaceComposerClient::Transaction& t, layer_id id, const PositionChange& pc); void setSize(SurfaceComposerClient::Transaction& t, layer_id id, const SizeChange& sc); void setAlpha(SurfaceComposerClient::Transaction& t, layer_id id, const AlphaChange& ac); void setLayer(SurfaceComposerClient::Transaction& t, layer_id id, const LayerChange& lc); void setCrop(SurfaceComposerClient::Transaction& t, layer_id id, const CropChange& cc); void setCornerRadius(SurfaceComposerClient::Transaction& t, layer_id id, const CornerRadiusChange& cc); void setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc); void setOverrideScalingMode(SurfaceComposerClient::Transaction& t, layer_id id, const OverrideScalingModeChange& osmc); void setTransparentRegionHint(SurfaceComposerClient::Transaction& t, layer_id id, const TransparentRegionHintChange& trgc); void setLayerStack(SurfaceComposerClient::Transaction& t, layer_id id, const LayerStackChange& lsc); void setHiddenFlag(SurfaceComposerClient::Transaction& t, layer_id id, const HiddenFlagChange& hfc); void setOpaqueFlag(SurfaceComposerClient::Transaction& t, layer_id id, const OpaqueFlagChange& ofc); void setSecureFlag(SurfaceComposerClient::Transaction& t, layer_id id, const SecureFlagChange& sfc); void setDeferredTransaction(SurfaceComposerClient::Transaction& t, layer_id id, const DeferredTransactionChange& dtc); void setDisplaySurface(SurfaceComposerClient::Transaction& t, display_id id, const DispSurfaceChange& dsc); void setDisplayLayerStack(SurfaceComposerClient::Transaction& t, display_id id, const LayerStackChange& lsc); void setDisplaySize(SurfaceComposerClient::Transaction& t, display_id id, const SizeChange& sc); void setDisplayProjection(SurfaceComposerClient::Transaction& t, display_id id, const ProjectionChange& pc); void waitUntilTimestamp(int64_t timestamp); void waitUntilDeferredTransactionLayerExists( const DeferredTransactionChange& dtc, std::unique_lock& lock); status_t loadSurfaceComposerClient(); Trace mTrace; bool mLoaded = false; int32_t mIncrementIndex = 0; int64_t mCurrentTime = 0; int32_t mNumThreads = DEFAULT_THREADS; Increment mCurrentIncrement; std::string mLastInput; static atomic_bool sReplayingManually; bool mWaitingForNextVSync; bool mWaitForTimeStamps; nsecs_t mStopTimeStamp; bool mHasStopped; std::mutex mLayerLock; std::condition_variable mLayerCond; std::unordered_map> mLayers; std::unordered_map mColors; std::mutex mPendingLayersLock; std::vector mLayersPendingRemoval; std::mutex mBufferQueueSchedulerLock; std::unordered_map> mBufferQueueSchedulers; std::mutex mDisplayLock; std::condition_variable mDisplayCond; std::unordered_map> mDisplays; sp mComposerClient; std::queue> mPendingIncrements; }; } // namespace android #endif cmds/surfacereplayer/replayer/trace_creator/0040755 0000000 0000000 00000000000 13756501734 020405 5ustar000000000 0000000 cmds/surfacereplayer/replayer/trace_creator/trace_creator.py0100644 0000000 0000000 00000021202 13756501734 023566 0ustar000000000 0000000 #!/usr/bin/python from subprocess import call import os proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/" call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"]) from trace_pb2 import * trace = Trace() def main(): global trace while(1): option = main_menu() if option == 0: break increment = trace.increment.add() increment.time_stamp = int(input("Time stamp of action: ")) if option == 1: transaction(increment) elif option == 2: surface_create(increment) elif option == 3: surface_delete(increment) elif option == 4: display_create(increment) elif option == 5: display_delete(increment) elif option == 6: buffer_update(increment) elif option == 7: vsync_event(increment) elif option == 8: power_mode_update(increment) seralizeTrace() def seralizeTrace(): with open("trace.dat", 'wb') as f: f.write(trace.SerializeToString()) def main_menu(): print ("") print ("What would you like to do?") print ("1. Add transaction") print ("2. Add surface creation") print ("3. Add surface deletion") print ("4. Add display creation") print ("5. Add display deletion") print ("6. Add buffer update") print ("7. Add VSync event") print ("8. Add power mode update") print ("0. Finish and serialize") print ("") return int(input("> ")) def transaction_menu(): print ("") print ("What kind of transaction?") print ("1. Position Change") print ("2. Size Change") print ("3. Alpha Change") print ("4. Layer Change") print ("5. Crop Change") print ("6. Final Crop Change") print ("7. Matrix Change") print ("8. Override Scaling Mode Change") print ("9. Transparent Region Hint Change") print ("10. Layer Stack Change") print ("11. Hidden Flag Change") print ("12. Opaque Flag Change") print ("13. Secure Flag Change") print ("14. Deferred Transaction Change") print ("15. Display - Surface Change") print ("16. Display - Layer Stack Change") print ("17. Display - Size Change") print ("18. Display - Projection Change") print ("0. Finished adding Changes to this transaction") print ("") return int(input("> ")) def transaction(increment): global trace increment.transaction.synchronous \ = bool(input("Is transaction synchronous (True/False): ")) increment.transaction.animation \ = bool(input("Is transaction animated (True/False): ")) while(1): option = transaction_menu() if option == 0: break change = None if option <= 14: change = increment.transaction.surface_change.add() elif option >= 15 and option <= 18: change = increment.transaction.display_change.add() change.id = int(input("ID of layer/display to undergo a change: ")) if option == 1: change.position.x, change.position.y = position() elif option == 2: change.size.w, change.size.h = size() elif option == 3: change.alpha.alpha = alpha() elif option == 4: change.layer.layer = layer() elif option == 5: change.crop.rectangle.left, change.crop.rectangle.top, \ change.crop.rectangle.right, change.crop.rectangle.bottom = crop() elif option == 6: change.final_crop.rectangle.left, \ change.final_crop.rectangle.top, \ change.final_crop.rectangle.right,\ change.final_crop.rectangle.bottom = final_crop() elif option == 7: change.matrix.dsdx,\ change.matrix.dtdx,\ change.matrix.dsdy,\ change.matrix.dtdy = layer() elif option == 8: change.override_scaling_mode.override_scaling_mode \ = override_scaling_mode() elif option == 9: for rect in transparent_region_hint(): new = increment.transparent_region_hint.region.add() new.left = rect[0] new.top = rect[1] new.right = rect[2] new.bottom = rect[3] elif option == 10: change.layer_stack.layer_stack = layer_stack() elif option == 11: change.hidden_flag.hidden_flag = hidden_flag() elif option == 12: change.opaque_flag.opaque_flag = opaque_flag() elif option == 13: change.secure_flag.secure_flag = secure_flag() elif option == 14: change.deferred_transaction.layer_id, \ change.deferred_transaction.frame_number = deferred_transaction() elif option == 15: change.surface.buffer_queue_id, \ change.surface.buffer_queue_name = surface() elif option == 16: change.layer_stack.layer_stack = layer_stack() elif option == 17: change.size.w, change.size.h = size() elif option == 18: projection(change) def surface_create(increment): increment.surface_creation.id = int(input("Enter id: ")) n = str(raw_input("Enter name: ")) increment.surface_creation.name = n increment.surface_creation.w = input("Enter w: ") increment.surface_creation.h = input("Enter h: ") def surface_delete(increment): increment.surface_deletion.id = int(input("Enter id: ")) def display_create(increment): increment.display_creation.id = int(input("Enter id: ")) increment.display_creation.name = str(raw_input("Enter name: ")) increment.display_creation.display_id = int(input("Enter display ID: ")) increment.display_creation.is_secure = bool(input("Enter if secure: ")) def display_delete(increment): increment.surface_deletion.id = int(input("Enter id: ")) def buffer_update(increment): increment.buffer_update.id = int(input("Enter id: ")) increment.buffer_update.w = int(input("Enter w: ")) increment.buffer_update.h = int(input("Enter h: ")) increment.buffer_update.frame_number = int(input("Enter frame_number: ")) def vsync_event(increment): increment.vsync_event.when = int(input("Enter when: ")) def power_mode_update(increment): increment.power_mode_update.id = int(input("Enter id: ")) increment.power_mode_update.mode = int(input("Enter mode: ")) def position(): x = input("Enter x: ") y = input("Enter y: ") return float(x), float(y) def size(): w = input("Enter w: ") h = input("Enter h: ") return int(w), int(h) def alpha(): alpha = input("Enter alpha: ") return float(alpha) def layer(): layer = input("Enter layer: ") return int(layer) def crop(): return rectangle() def final_crop(): return rectangle() def matrix(): dsdx = input("Enter dsdx: ") dtdx = input("Enter dtdx: ") dsdy = input("Enter dsdy: ") dtdy = input("Enter dtdy: ") return float(dsdx) def override_scaling_mode(): mode = input("Enter override scaling mode: ") return int(mode) def transparent_region_hint(): num = input("Enter number of rectangles in region: ") return [rectangle() in range(x)] def layer_stack(): layer_stack = input("Enter layer stack: ") return int(layer_stack) def hidden_flag(): flag = input("Enter hidden flag state (True/False): ") return bool(flag) def opaque_flag(): flag = input("Enter opaque flag state (True/False): ") return bool(flag) def secure_flag(): flag = input("Enter secure flag state (True/False): ") return bool(flag) def deferred_transaction(): layer_id = input("Enter layer_id: ") frame_number = input("Enter frame_number: ") return int(layer_id), int(frame_number) def surface(): id = input("Enter id: ") name = raw_input("Enter name: ") return int(id), str(name) def projection(change): change.projection.orientation = input("Enter orientation: ") print("Enter rectangle for viewport") change.projection.viewport.left, \ change.projection.viewport.top, \ change.projection.viewport.right,\ change.projection.viewport.bottom = rectangle() print("Enter rectangle for frame") change.projection.frame.left, \ change.projection.frame.top, \ change.projection.frame.right,\ change.projection.frame.bottom = rectangle() def rectangle(): left = input("Enter left: ") top = input("Enter top: ") right = input("Enter right: ") bottom = input("Enter bottom: ") return int(left), int(top), int(right), int(bottom) if __name__ == "__main__": main() cmds/vr/0040755 0000000 0000000 00000000000 13756501734 011200 5ustar000000000 0000000 cmds/vr/.clang-format0100644 0000000 0000000 00000000231 13756501734 013544 0ustar000000000 0000000 BasedOnStyle: Google DerivePointerAlignment: false PointerAlignment: Left AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false data/0040755 0000000 0000000 00000000000 13756501734 010534 5ustar000000000 0000000 data/etc/0040755 0000000 0000000 00000000000 13756501734 011307 5ustar000000000 0000000 data/etc/android.hardware.audio.low_latency.xml0100644 0000000 0000000 00000001634 13756501734 020665 0ustar000000000 0000000 data/etc/android.hardware.audio.output.xml0100644 0000000 0000000 00000001661 13756501734 017705 0ustar000000000 0000000 data/etc/android.hardware.audio.pro.xml0100644 0000000 0000000 00000001625 13756501734 017145 0ustar000000000 0000000 data/etc/android.hardware.biometrics.face.xml0100644 0000000 0000000 00000001516 13756501734 020301 0ustar000000000 0000000 data/etc/android.hardware.bluetooth.xml0100644 0000000 0000000 00000001464 13756501734 017253 0ustar000000000 0000000 data/etc/android.hardware.bluetooth_le.xml0100644 0000000 0000000 00000001476 13756501734 017736 0ustar000000000 0000000 data/etc/android.hardware.broadcastradio.xml0100644 0000000 0000000 00000001470 13756501734 020224 0ustar000000000 0000000 data/etc/android.hardware.camera.ar.xml0100644 0000000 0000000 00000001623 13756501734 017074 0ustar000000000 0000000 data/etc/android.hardware.camera.autofocus.xml0100644 0000000 0000000 00000001643 13756501734 020504 0ustar000000000 0000000 data/etc/android.hardware.camera.external.xml0100644 0000000 0000000 00000001645 13756501734 020320 0ustar000000000 0000000 data/etc/android.hardware.camera.flash-autofocus.xml0100644 0000000 0000000 00000002034 13756501734 021572 0ustar000000000 0000000 data/etc/android.hardware.camera.front.xml0100644 0000000 0000000 00000001555 13756501734 017626 0ustar000000000 0000000 data/etc/android.hardware.camera.full.xml0100644 0000000 0000000 00000002025 13756501734 017431 0ustar000000000 0000000 data/etc/android.hardware.camera.manual_postprocessing.xml0100644 0000000 0000000 00000001662 13756501734 023114 0ustar000000000 0000000 data/etc/android.hardware.camera.manual_sensor.xml0100644 0000000 0000000 00000001640 13756501734 021337 0ustar000000000 0000000 data/etc/android.hardware.camera.raw.xml0100644 0000000 0000000 00000001620 13756501734 017260 0ustar000000000 0000000 data/etc/android.hardware.camera.xml0100644 0000000 0000000 00000001555 13756501734 016477 0ustar000000000 0000000 data/etc/android.hardware.consumerir.xml0100644 0000000 0000000 00000001560 13756501734 017431 0ustar000000000 0000000 data/etc/android.hardware.ethernet.xml0100644 0000000 0000000 00000001502 13756501734 017055 0ustar000000000 0000000 data/etc/android.hardware.faketouch.multitouch.distinct.xml0100644 0000000 0000000 00000002014 13756501734 023223 0ustar000000000 0000000 data/etc/android.hardware.faketouch.multitouch.jazzhand.xml0100644 0000000 0000000 00000002117 13756501734 023217 0ustar000000000 0000000 data/etc/android.hardware.faketouch.xml0100644 0000000 0000000 00000001637 13756501734 017221 0ustar000000000 0000000 data/etc/android.hardware.fingerprint.xml0100644 0000000 0000000 00000001502 13756501734 017566 0ustar000000000 0000000 data/etc/android.hardware.gamepad.xml0100644 0000000 0000000 00000001547 13756501734 016646 0ustar000000000 0000000 data/etc/android.hardware.hdmi.cec.xml0100644 0000000 0000000 00000001502 13756501734 016711 0ustar000000000 0000000 data/etc/android.hardware.location.gps.xml0100644 0000000 0000000 00000001656 13756501734 017651 0ustar000000000 0000000 data/etc/android.hardware.location.xml0100644 0000000 0000000 00000001665 13756501734 017061 0ustar000000000 0000000 data/etc/android.hardware.nfc.ese.xml0100644 0000000 0000000 00000001507 13756501734 016565 0ustar000000000 0000000 data/etc/android.hardware.nfc.hce.xml0100644 0000000 0000000 00000001570 13756501734 016550 0ustar000000000 0000000 data/etc/android.hardware.nfc.hcef.xml0100644 0000000 0000000 00000001573 13756501734 016721 0ustar000000000 0000000 data/etc/android.hardware.nfc.uicc.xml0100644 0000000 0000000 00000001511 13756501734 016727 0ustar000000000 0000000 data/etc/android.hardware.nfc.xml0100644 0000000 0000000 00000001631 13756501734 016010 0ustar000000000 0000000 data/etc/android.hardware.opengles.aep.xml0100644 0000000 0000000 00000001546 13756501734 017627 0ustar000000000 0000000 data/etc/android.hardware.screen.landscape.xml0100644 0000000 0000000 00000001503 13756501734 020450 0ustar000000000 0000000 data/etc/android.hardware.screen.portrait.xml0100644 0000000 0000000 00000001501 13756501734 020360 0ustar000000000 0000000 data/etc/android.hardware.sensor.accelerometer.xml0100644 0000000 0000000 00000001470 13756501734 021365 0ustar000000000 0000000 data/etc/android.hardware.sensor.ambient_temperature.xml0100644 0000000 0000000 00000001504 13756501734 022605 0ustar000000000 0000000 data/etc/android.hardware.sensor.assist.xml0100644 0000000 0000000 00000001456 13756501734 020065 0ustar000000000 0000000 data/etc/android.hardware.sensor.barometer.xml0100644 0000000 0000000 00000001446 13756501734 020536 0ustar000000000 0000000 data/etc/android.hardware.sensor.compass.xml0100644 0000000 0000000 00000001444 13756501734 020221 0ustar000000000 0000000 data/etc/android.hardware.sensor.gyroscope.xml0100644 0000000 0000000 00000001446 13756501734 020570 0ustar000000000 0000000 data/etc/android.hardware.sensor.heartrate.ecg.xml0100644 0000000 0000000 00000001507 13756501734 021270 0ustar000000000 0000000 data/etc/android.hardware.sensor.heartrate.fitness.xml0100644 0000000 0000000 00000001502 13756501734 022200 0ustar000000000 0000000 data/etc/android.hardware.sensor.heartrate.xml0100644 0000000 0000000 00000001466 13756501734 020537 0ustar000000000 0000000 data/etc/android.hardware.sensor.hifi_sensors.xml0100644 0000000 0000000 00000001456 13756501734 021252 0ustar000000000 0000000 data/etc/android.hardware.sensor.light.xml0100644 0000000 0000000 00000001460 13756501734 017661 0ustar000000000 0000000 data/etc/android.hardware.sensor.proximity.xml0100644 0000000 0000000 00000001457 13756501734 020624 0ustar000000000 0000000 data/etc/android.hardware.sensor.relative_humidity.xml0100644 0000000 0000000 00000001477 13756501734 022311 0ustar000000000 0000000 data/etc/android.hardware.sensor.stepcounter.xml0100644 0000000 0000000 00000001462 13756501734 021127 0ustar000000000 0000000 data/etc/android.hardware.sensor.stepdetector.xml0100644 0000000 0000000 00000001453 13756501734 021261 0ustar000000000 0000000 data/etc/android.hardware.strongbox_keystore.xml0100644 0000000 0000000 00000001461 13756501734 021215 0ustar000000000 0000000 data/etc/android.hardware.telephony.carrierlock.xml0100644 0000000 0000000 00000001505 13756501734 021550 0ustar000000000 0000000 data/etc/android.hardware.telephony.cdma.xml0100644 0000000 0000000 00000001563 13756501734 020160 0ustar000000000 0000000 data/etc/android.hardware.telephony.euicc.xml0100644 0000000 0000000 00000001440 13756501734 020336 0ustar000000000 0000000 data/etc/android.hardware.telephony.gsm.xml0100644 0000000 0000000 00000001561 13756501734 020040 0ustar000000000 0000000 data/etc/android.hardware.telephony.ims.xml0100644 0000000 0000000 00000001465 13756501734 020045 0ustar000000000 0000000 data/etc/android.hardware.telephony.mbms.xml0100644 0000000 0000000 00000001443 13756501734 020207 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.multitouch.distinct.xml0100644 0000000 0000000 00000002064 13756501734 023601 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml0100644 0000000 0000000 00000002170 13756501734 023567 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.multitouch.xml0100644 0000000 0000000 00000002013 13756501734 021753 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.xml0100644 0000000 0000000 00000001615 13756501734 017566 0ustar000000000 0000000 data/etc/android.hardware.type.automotive.xml0100644 0000000 0000000 00000001500 13756501734 020411 0ustar000000000 0000000 data/etc/android.hardware.usb.accessory.xml0100644 0000000 0000000 00000001515 13756501734 020026 0ustar000000000 0000000 data/etc/android.hardware.usb.host.xml0100644 0000000 0000000 00000001544 13756501734 017012 0ustar000000000 0000000 data/etc/android.hardware.vr.headtracking-0.xml0100644 0000000 0000000 00000001536 13756501734 020455 0ustar000000000 0000000 data/etc/android.hardware.vr.headtracking-1.xml0100644 0000000 0000000 00000001536 13756501734 020456 0ustar000000000 0000000 data/etc/android.hardware.vr.high_performance.xml0100644 0000000 0000000 00000001561 13756501734 021172 0ustar000000000 0000000 data/etc/android.hardware.vulkan.compute-0.xml0100644 0000000 0000000 00000001542 13756501734 020353 0ustar000000000 0000000 data/etc/android.hardware.vulkan.level-0.xml0100644 0000000 0000000 00000001541 13756501734 020005 0ustar000000000 0000000 data/etc/android.hardware.vulkan.level-1.xml0100644 0000000 0000000 00000001541 13756501734 020006 0ustar000000000 0000000 data/etc/android.hardware.vulkan.version-1_0_3.xml0100644 0000000 0000000 00000001610 13756501734 021022 0ustar000000000 0000000 data/etc/android.hardware.vulkan.version-1_1.xml0100644 0000000 0000000 00000001606 13756501734 020606 0ustar000000000 0000000 data/etc/android.hardware.wifi.aware.xml0100644 0000000 0000000 00000001505 13756501734 017276 0ustar000000000 0000000 data/etc/android.hardware.wifi.direct.xml0100644 0000000 0000000 00000001513 13756501734 017450 0ustar000000000 0000000 data/etc/android.hardware.wifi.passpoint.xml0100644 0000000 0000000 00000001515 13756501734 020220 0ustar000000000 0000000 data/etc/android.hardware.wifi.rtt.xml0100644 0000000 0000000 00000001521 13756501734 017006 0ustar000000000 0000000 data/etc/android.hardware.wifi.xml0100644 0000000 0000000 00000001475 13756501734 016206 0ustar000000000 0000000 data/etc/android.software.activities_on_secondary_displays.xml0100644 0000000 0000000 00000001405 13756501734 024115 0ustar000000000 0000000 data/etc/android.software.app_widgets.xml0100644 0000000 0000000 00000001360 13756501734 017604 0ustar000000000 0000000 data/etc/android.software.autofill.xml0100644 0000000 0000000 00000001355 13756501734 017121 0ustar000000000 0000000 data/etc/android.software.backup.xml0100644 0000000 0000000 00000001353 13756501734 016545 0ustar000000000 0000000 data/etc/android.software.companion_device_setup.xml0100644 0000000 0000000 00000001373 13756501734 022024 0ustar000000000 0000000 data/etc/android.software.connectionservice.xml0100644 0000000 0000000 00000001530 13756501734 021015 0ustar000000000 0000000 data/etc/android.software.cts.xml0100644 0000000 0000000 00000001510 13756501734 016064 0ustar000000000 0000000 data/etc/android.software.device_admin.xml0100644 0000000 0000000 00000001361 13756501734 017706 0ustar000000000 0000000 data/etc/android.software.device_id_attestation.xml0100644 0000000 0000000 00000001512 13756501734 021627 0ustar000000000 0000000 data/etc/android.software.freeform_window_management.xml0100644 0000000 0000000 00000001377 13756501734 022676 0ustar000000000 0000000 data/etc/android.software.ipsec_tunnels.xml0100644 0000000 0000000 00000001553 13756501734 020155 0ustar000000000 0000000 data/etc/android.software.live_tv.xml0100644 0000000 0000000 00000001354 13756501734 016751 0ustar000000000 0000000 data/etc/android.software.managed_users.xml0100644 0000000 0000000 00000001513 13756501734 020113 0ustar000000000 0000000 data/etc/android.software.midi.xml0100644 0000000 0000000 00000001351 13756501734 016220 0ustar000000000 0000000 data/etc/android.software.picture_in_picture.xml0100644 0000000 0000000 00000001367 13756501734 021201 0ustar000000000 0000000 data/etc/android.software.preview_sdk.xml0100644 0000000 0000000 00000001474 13756501734 017626 0ustar000000000 0000000 data/etc/android.software.print.xml0100644 0000000 0000000 00000001352 13756501734 016433 0ustar000000000 0000000 data/etc/android.software.secure_lock_screen.xml0100644 0000000 0000000 00000001367 13756501734 021142 0ustar000000000 0000000 data/etc/android.software.securely_removes_users.xml0100644 0000000 0000000 00000001372 13756501734 022115 0ustar000000000 0000000 data/etc/android.software.sip.voip.xml0100644 0000000 0000000 00000001560 13756501734 017047 0ustar000000000 0000000 data/etc/android.software.sip.xml0100644 0000000 0000000 00000001474 13756501734 016077 0ustar000000000 0000000 data/etc/android.software.verified_boot.xml0100644 0000000 0000000 00000001361 13756501734 020117 0ustar000000000 0000000 data/etc/android.software.voice_recognizers.xml0100644 0000000 0000000 00000001407 13756501734 021017 0ustar000000000 0000000 data/etc/android.software.vr.xml0100644 0000000 0000000 00000001465 13756501734 015733 0ustar000000000 0000000 data/etc/android.software.webview.xml0100644 0000000 0000000 00000001354 13756501734 016751 0ustar000000000 0000000 data/etc/aosp_excluded_hardware.xml0100644 0000000 0000000 00000001734 13756501734 016527 0ustar000000000 0000000 data/etc/car_core_hardware.xml0100644 0000000 0000000 00000007177 13756501734 015474 0ustar000000000 0000000 data/etc/com.android.nfc_extras.xml0100644 0000000 0000000 00000001452 13756501734 016360 0ustar000000000 0000000 data/etc/com.nxp.mifare.xml0100644 0000000 0000000 00000001456 13756501734 014660 0ustar000000000 0000000 data/etc/go_handheld_core_hardware.xml0100644 0000000 0000000 00000010175 13756501734 017153 0ustar000000000 0000000 data/etc/handheld_core_hardware.xml0100644 0000000 0000000 00000011424 13756501734 016464 0ustar000000000 0000000 data/etc/tablet_core_hardware.xml0100644 0000000 0000000 00000007471 13756501734 016177 0ustar000000000 0000000 data/etc/wearable_core_hardware.xml0100644 0000000 0000000 00000006533 13756501734 016504 0ustar000000000 0000000 docs/0040755 0000000 0000000 00000000000 13756501734 010553 5ustar000000000 0000000 docs/Doxyfile0100644 0000000 0000000 00000237066 13756501734 012274 0ustar000000000 0000000 # Doxyfile 1.8.3.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "NDK API" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = logo.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented classes, # or namespaces to their corresponding documentation. Such a link can be # prevented in individual cases by by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 26 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../include/android ../../av/media/ndk/include ../../av/camera/ndk/include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.for \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page (index.html). # This can be useful if you have a project on for instance GitHub and want reuse # the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = $(HTML_OUTPUT) # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = $(HTML_HEADER) # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = $(HTML_FOOTER) # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefor more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 0 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = YES # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search engine # library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = __attribute__(x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES docs/Makefile0100644 0000000 0000000 00000000427 13756501734 012213 0ustar000000000 0000000 HEADERS := $(wildcard ../include/android/*.h) all: html website html: $(HEADERS) Doxyfile mkdir -p html doxygen website: $(HEADERS) Doxyfile header.html mkdir -p website HTML_HEADER=header.html HTML_FOOTER=footer.html HTML_OUTPUT=website doxygen rm -f website/index.html docs/footer.html0100644 0000000 0000000 00000000020 13756501734 012724 0ustar000000000 0000000 docs/header.html0100644 0000000 0000000 00000000553 13756501734 012671 0ustar000000000 0000000 $title

docs/images/0040755 0000000 0000000 00000000000 13756501734 012020 5ustar000000000 0000000 docs/images/camera2/0040755 0000000 0000000 00000000000 13756501734 013332 5ustar000000000 0000000 docs/images/camera2/metadata/0040755 0000000 0000000 00000000000 13756501734 015112 5ustar000000000 0000000 docs/images/camera2/metadata/android.colorCorrection.mode/0040755 0000000 0000000 00000000000 13756501734 022622 5ustar000000000 0000000 docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png0100644 0000000 0000000 00000062003 13756501735 027370 0ustar000000000 0000000 PNG  IHDRRcIDATx {.pS }xXPH ㊊ׅ%jTxH58F]/G`lȑ/x4koI$. կᝢg:9ϙ}7ϓJIho}jjj>R$I$)7fΜ]wueggqT$I$IywZ (J$I$ $I$IB$I$IP$I$IJ$I$IBI$I$I@(I$I$ %I*m\=ɲ HBI*W[_ {Se\H,ӧ AC??}$I@( h׿8ҙx_:o2YVv >;^4Ӽwb]} JP@(I-Qޅ_-{i[.~vwCap[P$PJfW9Z[[)tc?.X~ozg?G#IBd &F1ʒ`\F05!*/I  ©IVoer-w²'ya{"z$I@( ve8d};XI aC<G?Cx{}o )[$ RD6{E!++O!bt$PJ:R1zW iЭHB)I@j㷜dvI z_8K.dj$7V$IB _nUƬ:>.ammmm;r5V:K#m$IBԫ@.1ݾ+W;(+{ $ JR߿+ۼc;jKP@(I=sigNR쒹W~$ JRbы*ȡRjI 2$~^Hux5Q]G 4w'4k~ p1$L}+F?I8_}ퟖ=νhv[If#O{Cgۈ>{]}CAJ\${ ǜ4ѫPI $hXFߣ!vHGNz%^T dųr ~{[t8IBpN%{ ;,_9W\\;B<PcKrw;3L!I ԐKu~_;p>~2ܧ@86}!мQ}i1ެytIH|7=qwŶ͎eсW,>~.!78h,|K7i>` hNvwxӨg/v/b'=s?r3a& 48646cOֻ`uσZp !$}jBx@B(phcZ7U04!Q+ "PHL@7@sC 2m̷\,^k1m!6S( $I@(I3ٷS]Y%H!i B}8R8XYX a Ec~s~v^}1g︱hjvKtfxN(d\g`wsievYtPBI۽2qCNzaY X6]hN"L@(Ir‘ghFvBYW I gTjkkkL,6T#uuuZMMuqg5L*p?BY%lLH8.5PH6mjٸq]* ,Zjkk3ƛ3gwM7yeE0 oȑ ˟WSHx*$:;"U@(XJRap6@   ~1w/ao۶mڵkg^<\zW^_S@( "^6V`7_<`?eʔ ̒|%B.6D^[~,㷜ڃYgoIrX& *ݸRIk>:l+C.!$O_\μLE mMq˪bԬ)Y‰y[jr6XbRCuއ[cɞ祼P@<1oԩC O ?s[>>LN+B`3pn5 #rO'_XA[@h!1HDժHNmZ0Bd:PJ,~AͿ P@_ ڻeWmŊ5 [.@H1'MaP o?f%ۀ%^|[@( ?`Vnv޽u=>) K,\PUv'oûR9&Sj NQ B`<j<@s ?/ #\^¸931y@?=̼^`X|LUSMz  \ g% `vܹlP\8׀$rbxStCvByFqcg^4΃ki¬dW|njs=14oaڹe\ʸxfx+ V'kqj9 9LR|뭟AaP[[)s[c(BiBp a A1 lK*1-xapcV:<>`xlA faG 1NFV̀m?mi33W\_<9567ׁV+\CwM 6=o<y:n, l@sm#l =@~,̌~\01A?\WrM책y r9kG<}\#fZO:2V,Wo %Cn`P@ZJ  ?'s='TL.|S۰rP5 x g̕3qoП8U4.-0x|l}2lPR{4 Zc|(1|ۜN`*1$U˖- +W\) eeob4<J`6l7c L@( %2gPa]$ gyr7ydObwPV@H )1nS@( %)1jkkk- ;vlF#, ޘY*ǵx{TJ7M1k׮-g BTT1![BL@(IЦMZbj +tqI';\ٰa7~g\`n&/#L0v{  RҤ&ϖ %C7{ VWSOn6a=zFc܅ v1z_-Z=ٷwRHB:`queBzFnV`7_EBV3%SL/zMMMy̙n&M]dI9}.aWʶmڴiAx9xu8xTe_Ø1cP5/&pxemb@@?-] 2|>. LY,'`F>>"bW ^orFhD$vK뙑grX! *ݸGu3s8ۛ g$t_曃/d6rћ3gnݺ`z |Gz/`lŊ\&7x|wyW__\oqnO<mܼQ ĆZkY$ $|<:2P@y,Yr.9HZ\ !6YU.$}⼈#qt(eB)PtVqy):(, {0s>ڻ+ /^:jx3!s2 gdwU/x/mE,`e/H}z8>:pW 'b㰕̇_y, x&1q+1X*e, ,#e/KB{ɸv}P@(I=3ᣏ>3~衇6Ƌ 7r%FebAIpJ#w\}@&1$xIKeVO@IMg\@8zP׀35c Yeqَ'Yt~#s--!NBI*sE.-5Ya;voÊ2m#P!,>y\%!V.`P&0cg!bK (9rPs-eʩ.G\]rZlѕx )vXHC ׈Fx k6t+n>oB&(,>#^KKK$Ζ-[D Է1?BBSÞBbUH(/V ,dɒ 3QؒE"$f[$&!0ߓt7TIal(v91p^#1!~؟Um>0{Ll΅}~Q@^?0e ̻s*墐1oq-hX-^!JHW# nܣ:|۹sۏ¢!?O,\  r֘[8伀X9߀<*TS\8#lFр qJ%F<p6 qIJY8N0Pi3-` ̀M+KйI>G9`j۾@a4 H>:n,q/ Y<9Vs.BXR00(5<+bBB"!uB)k  $gv ~ Kϖ [sՖG~Tw>~˷|R1l^90s7 spd1&uav׀ͅ9[b/<{ /Gci xڽfox~-v5|?'iccak ?]x{0 ]qV&@-d?uq1ُ݅R ]nT4 YVΝ7\7 %// ksw8?S,(X: @,<:jͣ1&Xr ]yMhv(@\bakk d9/3tX$ 9;֘&e=Ц-v6C .O̯3ƣ|bUSJhd/Cy5$Oa\<khgJ q1><@JBYm޼OZdn믿>TspH3s饗g eL i<n' x*kB >µx F07} N- ܷvQ8l{48幀0nߞNOAS@|8}zꗐ5X V>Ǭ{ m +YDyviW* ex(4  hc2rlI' 2֦Mftx  6!kvBI<1'!B󷮮.x]@EC2a!ee 2ll,xPo$ i`1@- 0!׊܏VW@(I¼Ĵ裏 m; ' =ۋw O /@ mIbsB.Zrv -:';7،9Zq1`!Fx؀ck' $auNj/sE):1fY 9j~`Sm+VܰiУULF121Ph9 ygy&8bk?/2`rМW%-yYb}Sw>s\8 AXz<'$BB(rҟcu/k)_ؓrQX-̱= UwĀ$U{Jخ]G0^,Ї455c_tE\BQ( yn@ `@ظ~/^\цA'pI<@[Q ȾqX)6w@>uWJ t2πǜƗ+xrAz>@a 9{s CHbں뮻x SRu!nA@t!P8aIsrrBP uO_ <,0yk!rQc@#F蒨Z@H(!@<.?׆0/_;K ö1cۘhZc0g:cL@HL:i_@( ,1w 'y\BB!1 {@"+{{1;oѮP*+5?Te͛7OJQ $ 5mO=``t~yW\q0 4 !c@ 9xƶ0x ‹/8$Vc lof21^BE8o~)U&X*D6m/aÆ~<  $2/~~/,TXJ7U rݔ3n]`b!atۢYl3y .FgVM 0_Kj%*+ @<]}C@P*Dk֬'A5(Ԣ<>x`pdU*I$$meC<B!aNοozp熂_45!ae7 eBI*GÄXEL—n);b`tN,(wsE朣J0W $5)Xs4RtCc7dՄ 33fkɥIC;I" $aN*UnrB¨* W>Z@X=@hsHCHdЌ_F Q DyIT}BI ,N? ,F(>VQ:έO*u͒Kg d.^8OcB7_L@(I?s2D@XֿӻVW,.-M1r B&.6@,yWVj`P@(IyI@XP]rɒozN}{ɫV d19=y⌄ѹˠLFs Rg@ 1 -*ӷ!Hmy<BI2jӦM-1yjРA%H~F +߮g01 /15)5횗cΟ%z]!"^'m J}d@.`|K uzIR`iP@'ɕ\7I4BxY:g^=Džxpx>{JU+vl=[< kd]].ʭ ߫{nˆQE[7@heqP[>ܪ;N{oh:(SafW2J(ƍ[SS ׀'¯*0EBT@ǟ}^6&L~&mP&JU ۼy?kg&\!,fԼo߰a5m * eBIBmmm> ͖m\ok[7o<)ݏ0oXO޹`uW\)+=SA}bĉwuשj2)P& $)$/aÆ~<SoӁYqm{x[$Ef͚zTaܵq!̖Ś}cqTBPx%I@( %I@( $L& $P2PBIdBI %?|lBVq;tϗ$Pz|' df_-v$ ^N\ymYٹͶ{0}%I@( :̿.igxaPIBIFzGYٱ'm$ JRzqO~mYؚWxu^}>+$PR*7\}%I@( L] !;,=%$ JRցÆ"UקoE}%I@( ++*\,IB$e!m:|6oT~ c׷;g^_mI %);]꛿%\OWZBI^ mظk .degsA~$ Z0ڙ/ȐSx^}:J2J$ JR|yUeU cO KP@(Iу|W.{'(%X3 WWBI*O<ߗS)|,KjxIz*Q'IB$ O!s A,1jSJ1#IB$U7ФqvR(O7m\qmPnD_QI %4j}CoWEYoACx߾ ')IB$VCGz^,ߴDV#y;AJ;-RXBIEQn~泼 <ϻۂ?|le}v LbPj#,f}鋇IBIJ e:Ց(}$I@( a*-j}ŷ*6=🌑*IP@(IRp2ƽE=moRI$ J$$I %IJ$ $3"$I@( $^.$IPa@Iy(,79sPKM}$IPs[)I@I$)?(,ħSJP$I$I<dNr$I$IRu CX$Rg$I$I$I$I%<虊 %I$I@I$I$$I$ITP$I$po}K%I$I$$I$IP|Ʒ}&$I$I %I$I$I$IR%n~+$I$I.o56@J$I$I(I$I$}ۍJ$I$UnJ$I$ $I$IU;$I$Ik|[Tzy#S Vd &F$I$`qD|׷Td2YҌ94OmF$IJBVDog+_[}wyZ_Ż+W~2q)iS绵9ՑzI*$I^5!%MCBJ0Îh?^sOYv,쟟yΛW7hp-zݚ$I$`f1Cp!+g{iVֻaiWS_%I$I۱`BVi`omsu $I ̳ca DX(Rna;9>#UxWpnk$IR5yM}w+[ _{;$IRXHgT<_t2k`AV5Bިc(~+|,I$ ]\z+s&Upȁ}~ߏ-Q$IX ;=@V«ٓ"I$ JGq=KP vۿĦLmS$O:ӎK)9\= :^WW_yF%I*}Ƶ3n⻂8|hrn$I%,Kf O2Y-{iܪ[$IRqE_#s+g$aShzi AX9eL8ULVV-IT<5^=`091lo1 dvq555Z$8"DkaSpH ?R-?$ًX&ky˟˶h;xtsA]nv]bamK$Nc S{R=0 oנؚ@]s#կrֿ;ıԹ5nymބ4*x2' ڝsŵ_:d9k5x0t].Xr!#ktc7x)t~vmjBreS'q^'3I_([1}!)^(F*Rv4lP"fkT^1m)vR_ehT5J%}ǸbQBQ45~nXP(!~:f@%X8νhvp TrKb}!=T+WK' Dͩ}Gs:* H*AN{RPv8Id$Exx 0`e׮HL97bu]T氒lv)Dq,*cv f|HlIha靐Hߌs9(Pٵ`lO|?bU/rNlg#Q5ql9Ͷq{x]0WK\GqheWh0me@}xgܴi^^!QǓkc|x3~5?cP|$ Nm3ps>nJ*4y,ت[*lrK?0wl ‰@ `?>vp$}<@P2>hxб>`Ī06!گkg5]ׂb,>Σ=rlsA?l )gdHeUWxJH?,o787,׀s1<Ud\|8/cxާ|=BPz^۔Tǂldɐ!4gn?PKaI@;jE€ɼO6`d薬Y}⸲u@go^ ˼x0Vg"4@<ݱq=]Az4o`Ix yͶs< qmyl?3'{@qcdtc煇`O@(JBY9Hmk,^ 9g'mxi} x_ xB΁] !Tn_(10A.xhcZڷc8g|=`.ÀǼ7\g>.A/x?{7n_ ڳSL R@(IBa}B'(DK.;~i\Dh fj[E,A <H 1F/ᦜ1Xy|8%&kT.wA alh 1?0b?7ozHA-=|sS0VŹڢh};k'[D{c׃qu3h{' L;# Ĺ %I@( b ɔ;W|`g>Z:c;裃OtQFyCxm;k,/ j!aq G& e2a!cXt s -3P㎝&L'\!ϯ*[./X9 wki* j©e +e2a!cx/,ivO.]N>`r6g+d, eTGё e2a)B=BnZ0 sh@3RfMRg$ >3|~P&x`rw=& =_%I7a~jmQ^cAHgF ʙQJTp X6@ j%=(uT|"RɃoG5>3!F2$ RƷ=)lZ.Ʊkquq3!0E6ڢ^qO}(=gBJQލR|@+{ hmA3' eBI@(v |,0޲1 kq0[H=cc|V؞\g& 2u %I7a+ |!; f2M9W\u7\@{PzsXhQތ2AK, R P@XL eQ aa·vl6c(Bkp)~mAǞ86S6}'Wye[g 뀗>iK@|~oFGe/ɢ|#0]{ 2wjh]YzRBmp}oT>@枇 ]:c%2/ڳ~1V:ob.}]cFn[)m|ǚW'}}xW}{߷} D@omD^J[n{Q7)3G$qnt<VO6|%a@whadF@zn6t/:97L^!/= ^@d7t3s39IJeG~*4XJ?|le'Pb@vh?bB7kr/#Hn v_cA?T`@6p'Hshn`>"%P sި1C}x-~0 נT@ $ e#X@X{ǷK Ϲgr?9P.0s~=̢>B~g#c!am7 >BI@X( @|iݛۆM,B+JBմK/#7w7^:S&Y|:û25fpzx9Umą $7 zǽ FOk& %a_Be_n!BдNܖ:7'`c37 !QVǸhqnAaqL>R>Y@S+)1u~os|ܫ9~A sSO78.uw@={6c{*Hc4gat[tI^.\B̌kW6r4}.eg}L9h;I, er3P߸ 1Hœ 2 ΝV}Y}uꞯZƒP@ث1uy'ZdeB =]څUTxlU@2[RpC=]5'9 {$f3)|(g+ %I7Wcc'lCƀY4awĦr&8e=¸>ۙ\T/ $׳BB ҏ JHSOP|$ Ŏ=ql<9n!ٞz F@E` hӷ!bCh79WB֌)P& $‚0Hؓ'9Z)k/R9Ju>\XuN[Rby)yCa!dq{ؓcN͹`?=>k$.ei$ +Gnls5Z4/l\~@ mtō5Q@( e*Bb{X@%KH;` J'EZiU95Jb6.U@( eBaZuZO"m>=f>_:<ӵE_` %2ۧ(̧OBQw}W\g sBjᑣVq\}c (ֽ ^4 ͙3'؏w̙v9m!]7dB{쎕c FrPBL@!@5[Ȗs?8(s!m@kx3 n ^BE:ujlll Q3 SO=% 2P&2pű@xGw͘Gpz:BFXwݺu׏9~WC ƀ1ZO1iѶP`N[TfsJV'|m ZɛCh@J7 , gs-m+/?yp<ӆ#dq,VX0W}9@c=dLD@( eBa.Wc]\!'׻PzBmyOt @<<}vshvg]C藰c>6 cl$lMx4[@( eBa' .zD-n%r& LX}\̺BXKw@9+qNa! >,L%WX s 1ɷ0&G =@眀{+dϋ2vB+/IEt>dedۍ7F 5ВQS֭ڒ1&Tn!`BեB$G6SclR5Y5+ɦO=9 &ˢʬ't.\V2!^%|*pfs7Ȣ*6p ')9v_:r}{X*u/C;swbkHmmm{4,6lذ{N%M[(w d j[M4<syr.f׍tãLlax' f%8OBOB '׈\Kz3V5 ggehÇRRտߒ)ݒ~Y\ba VF96νhv`x¬/^5@ Ps~0V(Z'=)4Jߘ&f<)}⩤OSKRf9eɶSN9V<Z(Sm!t@b\cg  جOO?>U =SO[a|)cn> xO^2&/ΛfaMBsCW, |zR@xʴ3O+*Q@س{~>}{+E˒6߿ޚ0ayaK.S{cL8lbPnQ}ч\XB?Wr^dR$ =_|IJv}e=<ձlJ}*eaO>tO=߫{T&6QdHjmm1ߛ e^KR/A u(qI;jԨǠ_~oUgN@(=uO=KrS U$2K LYa2R\ۅ z5wn]5L{@V=_*Goa}}tЫ$2 oJ"[]]ރ>5pK2*e|2d*Ke.>< o-5 2Y2P|%Iz+UP$IJ*<!U;B-0$I*ZL+[[ _:‘UH$I&~ιpl}!J$IRk}L?n-B$ISz2Yk֙w^"$Ij\~n{LaW^4"$IjaM>e2Ywr{MMGwAI$Z_ Ȫޖ=y I$Uȳ?jawq }RI$ X*(c3%\]*ժ[$IT:ʷ}/׽!8U1eoC=TG=aI$IJ]wÏ|[ 5l>3]wJ%I$)(? aN}%I$)SȜB-4Um DW^$IŜKy e`?/'M8 nM$I$u+V)iH^MEܩ,2sG!Nr J$IR4G\8C꾒퇏?o2YF<~x̜' AI$IrYʷLTc 1L_a)6( GAIENDB`docs/images/camera2/metadata/android.statistics.lensShadingMap/0040755 0000000 0000000 00000000000 13756501735 023620 5ustar000000000 0000000 docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png0100644 0000000 0000000 00000031267 13756501735 026760 0ustar000000000 0000000 PNG  IHDRRM7_sBIT|d pHYsaa?i IDATx|ud[K6$G̏\2\_-I~)问~~]]T~$nRMlLaˏ&bEcc~usy]p9Xm pxz%!.` q cKC\01%P͛7OC==J8; >\C{?s99m;pvRpJ*jذ~ZU4dZix`YV @Uei>z6lؠx}GZvZl q6۶==B*0(ebccl{5}tM:UBYq 7xzʀݻK\Phhh]Z 6{QZtUW^zzᇵ,ӣ>oQUTQM6 p8_?Tv@w}_W#FPPP|||M֑#G\?W^Tn]]uUYڵkWmk̙jѢ|||TV-9RG-rߔ9RM6UժU͛k„ -zuuWZ]sy ׵^+???uY_}UڿxլY|ξ>SN5jh_%I[lQxxU&EDD(==6oެ'xB-[T``|||Ըqco+<Ο]} Pץ5r +W$k}.t]q͝;W#G飺u꧟~Ҝ9stR_^۲ez_Uwu:tH/Vǎ~o,YjoV+W$edd]vVxx^j׮]?q\pe˖W^V:uȑ#[o9]Pgѧ~>}(,,LW֜9sO??wkQ!!!ݻrrrvZM8Q)))Zz SNر2224j(u|oڴIBBB4rH룏>R׮]7ߨI&=xڷo={s #eR\dջwo5J֭S||vܩɓ'{;m۶iҥڵk[s͞=[/wܡ=zԩSڸqjٲeڸq"㏵|rW]t7|>H)))ꫯԸqKz<P*Xe[eO0?~=~x{̘1vǎmaGFFǏwwޱ-˲9Vhhh6lmY^o>pWZe{yyw}K젠 ^n322:uصjղsss ?޶,ˮZ}v} d[eۦMf[eO6L'Nsrr.:{ddmYu">|? {-ܞo-˲7lϮ]=?o[e/\i{ttmYSO9mߴim[e9ֹsgp8mKII)\_N͜9Ӷ,5j(۲,{رN۷nj_uUŞ| քf͚Own[e_s5{ȑ#m˲O>i{zz}")x,'O.eIIIN]X\J8M8Q'NԩSn:_~u9|MkԩnҥzK*;;]vSHHm_yeffjժUE}'ԬY3mђ_9Tr9 ^+{366Vu-x"IRÆ =SO=%I ik4aiFȣ)SNEEEi?C ,е^^xrz疤Acǎ,pVZiN2d$i֭NիW쫦w9k׮ٳӶ4V^~-"eԩS5vX/?_-5ׯ/ruiOjݺe?--H(I?,I3uM;vTӦM]sȐ!JHHmݦO;wV\'IǏ״iӔ~IN8?:~A999j۶sΗYZR%]wuN-\:tМ9s.ҙ(>_=//O3g… wرc:}t]+pcǎڵkRSSU^=b@)㣶m?Vݺuk`\ÇK,KǏ/Z蒏_j"*U:󟮳C^z &(99Y~3~g5zDM2Eoz-Iglҥeϗ.]hƍjѢ5j[m+..N'O,}%@Z.x\`޳gعϷr_myyyN ŋ+((Ho@m[SNu92wϲ7at%PTZU77|-[nߊ=eY:zhoRd 4iD .ԩSuV\R׿cOÇ1z쩞={*''GׯWbb|My˥OqF >\sumEy]3,x9\q5\^6mڤŋ[nZ|l֫z}/<_|4\s ]jժi޽E:uJEWk߾l֗_~ifso^vsyyyuzg`Iŋ/>>> Ք)SɓZ|eTq7+r_|Qd[pp|||cǎw*Uo-:۵k׺Ssp KGi#fNڵkeYZjetV".2`ڽ{*W\ 1Źە^ /boZxG1c^x?Ck֬#""3f7о+f-[k͂W\yCϗ_~sQ}\UfիW;mߵk{"T cǎNuӦM?er1޺ow֭w۹/瘒aJJJr6}tڵK|=`Jk ^<~;-_\e_V5.z~Z+VPDD jժ髯ݻuwyoܹsf͚),,L7tgYF]wz\*U;S Q˖-뫽{jƍJKKӁ.;5k,uQ7xU;wjҥRx≋WFF:trڼyRRRT~}w}5$[5?Om߾]_g%%%W^Zpa}^~eZJSNզMԡC߿_|õdɒbʫ3yd^Z6lؠkZhõx"^ڶm:?VԡCeff*99YM4QڵHEFF*((HJNNV``x~$@E\eYNyyyf͚У>]qŋ+..N .W_ݻkѢE-ve˖2eRRR駟ꫯVڵuj.?ߢE mݺU?X)kV6m4i$^83h ꫯyfnݺ4hq)!!A6mʕ+p8T~}7NO>˞WWرck͚5 RllƌSl\jݺuztRmڴIM4[o:W>Y+=Zl6lؠ&M7ߔ/^538-YD/-[_[5n85m۷F_|QIII\W^yE522ɲMPa7NVXQm=ټyy]gtp\Qd۶m믫z~n$kq@nVtMj֬?!.^w8q (֨Qd-ZHGյ^={駟VN<=L^ veVVVX \wrrr{ny睪^W.WX!C(a|-. >Vj ^sd c͡?!(%}sVߝ ,])lۋJk%5g߇~l[R\DʷQD01%!.Kjk%5O=ⲔO 5ƚ'XVOP01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` qYJhǕ<]+Q.۷o-\8Y;w.QvZܹDTScݡPhJNwbcsi߮][jŊڻwNH^TXX7O]T*::Rޕ"Imۥ}~zTIf+,QļVnНccݡPҪWFwK _K\\r ׶m3fzP^^c5hPg7N]T(^z~JjĈ]޷O'Ӷի7)-m|rzE`ݡP*0p38bD]h-Z-1qv횣;{_2K/UnMSccݡP.qԩ:zO_ Ⲃhذի;vyzT ;4JeYr4~ 5n\[S~J ~-^xyyc'4u|O uƚCIIJ=ZIN8!C()i*=.l=䓪S|||ԪU+ X9sbRÆ*#㐧GBCIc͡$=Lk""^TR&xCY.˾}jӦMzWոqc͟?_ӧ5p@w͈+0k <. 6^k<=*Jk%iJL_7\lV\ hΝ;+==]<  K8Kٳc4|xo=D-XqPAPXsVXXkըQU8r &$$_w(##C6l0>.߬Y/hz街qPAPXs( :wn_VV1OR*\-ZHvءۛ }%IM_=ٳc4th"o_$iڴgs?;um ydRS,2uƚCI k#?#Ijڴ $%%mRn=1 EAAڷ/K0Ni4>jаa]׿6oqya5jԨۋOuζjWO]a͘1V_/7VMwmۺ>ڻIm+**BQQNܽ;C9oƺCIc͘͡?Yg\57B{f,\;A>koƍ?WJNGcm")㜭y.o.fvƍըQ#-[iUN+z oٲEmڴD@U==*>*i6oެ֭[_._sX쫓G)qy-_om6IR͋ ql}N͛:un3>ݻkԨQ:v옂`}駚?E.z>c7N:r䈂pB{e%ťNSka|_#!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\0O% 3'\'\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\0,|5y8%'/Pf6SlS.[?┒^4th7OO'}TVoNVޤzzZVe`YW RwG~(|ӧOkwծ];ժU(gXs(i KI:p KrPu45VG\0yol߶m$Yf%=9J ^ZڱcG)5KFȑ#El ,Pαi^^^zX M:zgգGըQCCqqq T,Ks($ UF!OTj\R\feeiSdd$pÇPd{ÇH(XsY^w&(1q)U. 4Я*i̙㖡=۶m-Rd{-$I۷o/Pα)ghzZ :}%l'I&j۶m6/// a}dY>?u*??__-Ijڴ $I9MIDAT%%RnnfϞCQPP{ۗQon$['r$I}TeLXX/Ijڴ'IJJJsСCt'I;wyd>`:tFzD7twǃҏ5"??J6 R~]%IIIk{RghpEh߾3Q6EEEhO}Nv[c<Km9[O eUYOS͘?Rm߿l֍7ަ{3pXr8E> gm=zF.۶}CI> 13f̺j޽r8_xyyy֭^}5oF={ޥkz `͘͡1V_/`uS\s}wb\^d۶"tݻ3Ԩʲ$9_l'$e~;++K5kԄ -[M6(a==֞DA\n޼Y[_x901%7,_\Ǐ.Iڱc>CIRxx||x8@Euq#(==]ҙoYh-Z$˲ze%eZZ;@9501%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\J?E? P(@ PaKKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\Ⲕɓ+9y23U~'ϥ}vm+&jy:q#>{Qaam<5:??M+3s7*66ڥ} 'k%^;(>~yje94_ݏ,WFwK _Kl۵}m[ƌ=b3X ٍS^ZEGGۻR$I 駇Jʚ4iULjfm<_ 960JϯgٮmٲEmڴIU0u໊[I^1ڵkv: OXdxzr! \4iE_i[ZՕTIz5* 4_]gG%͛պu ޗW.˺c:uZGP~+?*޹Ot@~֭ij,c94_݃,,˒C_qښ:O a:WvQPAPx~J4^=z$8qRCLQRFOKo?S{zT9_/\j__~Qjt뭷*66{gC=:wn_VV1Orj֬4|xo=KO8Xs( x~uvEo9zl٢nݺg k#?#Ijڴ $%%mRn=1 EAAڷ/K0Ni4>jаa]׿6oŨB#_IRӦAׯ$))irsOj ۗ)I6EEEhO}Nv[c<KL`͡^WGVNNƍw{m{,ΕB1aկc۶տAmGh,99gu[G 5ߎkƟիD%'oУAY1cXկuMws޽\w%իlVTT"{w5r`͡za~!I_7襗^#o =* %^zI/ra 2..cǺc&Q&MR\\bbb㮙PF)Sh SϞ=~zovlq9.eY!eԩSƇ@r\snq8%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\0,lO ƶxzT09xϯG\Zίk01%oٲmw<˓me OPFmCIb]9'.ϯ'[s{-.ou `@@Ú2.ϯkСcٶ{^WҊ+ԠA(JKKӝwީի_nKT<01%!.Kl=䓪S|||ԪU+ Xvv}YC5jԐP\\B9j* 6L7֭[[lh(RSSW ==ZE\"}U||&LdmVԂ <=ʩ,͞=[yyy$YP͜9S{ј1c|rM6M߮OrѣW^y-_\jР~K\⣈Je˖W^Z` P;Ԏ;g9]saըQC&LPllA9uAլYiըQ#5o\}&CEӾ}{edd(==ӣ;J) i< mذC( 熥$)88XDU۾B#.K۷+88ȫ-Z$رc=zT[lQf<= 1۶C7Њ+O{zrd/%>FPx;GGVNNƍQP5Jf͒$yyy5j(OU>{ァӧUVظq4rHl Y|?]ҙO&(KNxx|||<9ʙ)Sh SϞ=~zovMjȑZڶm뮻NYYYZh>=\v|z)rq7N|9`׽P5lذC-*K˲zyr<3/\U˲t)Ll޼yzw~W_/1b %p%!.` q cKC\01%?RLamIENDB`docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png0100644 0000000 0000000 00000036123 13756501735 027431 0ustar000000000 0000000 PNG  IHDRRM7_sBIT|d pHYsaa?i IDATxy|Mw%=]V-%E-UUK#ҩ4*5cvJfhQRec)Eb-ZϕDɕ|<<|r?O}su1F.Op kp kp kp kp xXTT|||tAOtx_~%C͚5K.K}Q&Uvg=_\.x\Y4xĎ;K/{U…7o^q1bvÇo t9HDž =zm-8|A:;d\"O3f|||dɒ4$(QBSLQDDx9SF *(_|ӧO< zU\9(88X;vԆ |$jР ( ծ][N/R>}o>= ֭Ee?Μ9nݺtuGUŕ/_>*UJ<>6_J/_>8q"3F.KxFQFriʕ?ׯ/9ן_ p;'ԩS6l*V(___M6ꫯֵk.\.mիݦ :T.K_uط~nݺt)E͚55p@%%%-뮻̙3ӽͷٯO?~)444幬Ry+&???ժU糦ugn޼YڵSBf͚o:|飢E>>[nfȐ!7{\x1]^^^CfС3sISR%8iРyWM~L2J(aBBB{=8q\fԨQnԩcRoM|޽>|yM6mL|̹sRmC.]8f>m<_… oooөS'3l03p@ӡC?~}߭=&&8cMDD1b0`i֬)^ۼ{6㘮] 'x¼+vqӬYT 3e˖5={4C 1/Uq&))mٔ+W.eMDD5?qDEE-ӬY3r8}ϴj <}ԩS}3=#y.]FqL&Mիy衇MXXqTV\rm}O?)V֭yW̠AL&M8RJ_M;t`Mn̈#L۶m8&((ڵ+]\p,Ӽys8okpYfMs i.]2?qv!b/nSƓߤ 䶓|y'8?OO7x1.]j1=PL2& 1f׮]s=ȑ#nӖ/_nLNR[nuY8c}є?|,X0U ѣq|75///tTo1=]LL޼y޽{SÍ8O{G]wejԨa6m2~IrLVR d1_|EZN:koq̙33ׇˬx&Md1&MJUͅ ~Ν;q̖-[RMw19\)S8p e<)))%[m={Ç7㘹s纍'/6ap8vq̀k1Æ s߲eɗ/mKooojժ+W|8c (`>z45>>>^Mޖǧ㘘i}Ӳetm p=>GIN5mΝ5jۿ?0 tў={ /QFn. )[lx%I˖-KtfϞ hԨQnשSG={IiӦz'+///Z7͙3G JQ3ؒԣG5i$gqRUw6) [lq/UTڿ=kٲڶm6|Ɗ+ C-)ߝ6~%;w{9sFW\I~5kjrI&ڳg6oެRJ.e+;wԩSN0>>^e˖=n<'X7oMq;wm1UMkxoFo%yMnْ\o}u\__Ν?XƍҢEt) 4b>_Kk乺+|MחŋK#i׏_aH%T|y\RƘ#}-[TB'O-_\mڴU`AիW/eujժU3f͛hIW/5j{Tu$މY:*UJ4j(Ʀdɒ2d{߭sZx&L3f݂zFtZh׫z޽)"oooc/̟n7rkǾ<~sui.]r֭>ss)c4qD1=uYI&ZrVXnn%ypBo>?#f``ǹ镺Ş>}ZO:}}}յkW͜9S˖-S6mRB'_JY fɵWۢE M6M7nU|ԯ__˖-Çk.=éAZh.] 6(66VSLI ;͛7w?+RmlfPR%͝;W/_֖-[W_iʔ)z ii۶ڶm .hڵZx{=kN~*Ut[}Z~"""R]}T,$?gGMs}y o66lؠ?\ZҒ%K2iFZ~ﹼYns.e"""~ Jt_re*THk֬IuRJȑ#ilذ1ޞd!>?%Kf͚QFKRJ-[5RNH֪U+IҥKzjs*[lNsMfհaCEEEi2/H5_rep _V^^^] 9sH> W͛7ׄ 4|p]xQK,횒owӥKTV\jr͛u̙Tӯm+Ww-RرcG`)]n$]|YW8UZ.eBCCSތz衛^qԩ cǎ ;s75k֤\^xA֋/ÙM6Յ uTӞyy{{kРA)]~ӪU\gr_._Q>_Y[zҙ3g6mݚ6ٞ={4tTɓ'?nSpaӺuklٲ.?8cTڴic\.ySMmcTb ,hMJK/vdG;vlט[osf֭3 05k4TP7]7P7Ƙ>tTPϟ(PT^ݼknFDDeS'y>0={4!!!TVͼ&))) ntӷo_SHkjժe>#_i~ެdeʔ1e˖M5~Aӻwoǎ67ɓ'DV=qnvIϚ2eS|y3bs4{Ę Spa裏1]1ŸrnˠA4yd޽Cz''h߾}}0r#FhܸqZt|AOf͚}j֬Y}N -s xаa秷zӥ <ٳgg%XRJ5uVMiF7>_tzY@Ӽ2-\&Ue$=zqI%<]rzY'w'QҾ-dZLK5exސ9d5z@gQ[!\%!\ީ {:=O2r1=FG5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXC5KXCCk;5VG:Id}#;~v/ܭj݊\5;YCV2\(X;wo-[ I2ƤkW_O^>Zaχ)Hպ6ިe+gf;d5zY|y<]8^A̓$IկSt/a`?umlņڻhZswj=8r 8ol|I:r;EK* 9}F!spK )RKi.}F!s.s///9CUgOt9%;d5zYK[ٳg5p@Wjҧ~YqAjTzC?tI;d5zY ]#hÆ z7UbE͞=[ݻwו+WԽ{̪צC=ZjA.A!sjͥ;\~ꫯ4guMԬY3k֭\.>eL#OjNO\CV[Kw\`ԵkW>}СCZnp6MG1O+zqA.A!sjKm۶rʩNV^]}v5lnu\X0+/@T%"IYċ9]1T G$IORߎ}5ڶ{v_:/^ͻ6g ۠9d5z.s;\8qB˗O52=M %'0cʍJUZoڪ*cu(Grrݫ}2ƨoǾ۱:ڧS@2YCVn͜4/7 ^NIwUXQ˗ח_~6~ahܸq:thMTN$+sH;7vڷ7\ytɓ);\֨QC;vЕ+WƷn*IV;\vYgϞg͚w}7YE/ Ӄ>̙3 ՜9s[g 7_҈#4rH?Wg{nEVhLٝƏ9:4]]!k_8qRI I #7+vi;I92}='IEg豣Zj޼y&V_#+XHs-k7::}XLau2py .;-$cҵ+'FOnjSKgoT岕3ldsZ-Xߕ/S^=:PD,вȽտy{{k\޼yԼysKء=%KԴi,Xp`o_^ZZ)}=[5h lgyF.iѬGftH[x5$ TNҽltqVhsVkE*pH߭\Rk$ծ^[;uϔ+(Z_ܓO>UQF'I믵yf֛j԰QԌ-X_빀@kz歞6xb`OV~b#ـ8`)IGcUh [e!ss[6= HN;iΝ)R\VUxq%"#d\t%BE92(RV==] XQZ5}wƷn*IZjV\qytW]zxwEMb˺cx.#gjl@'OO ꒐KļֵZK_<^z)f}sdٳg5dnZERTTTf QF=|% r}usGl zpyqM>].]RΝ%qn՝nk3Zr'N(000x؉'$֦ݛ>Fꮯ|OOtYw 2e_~Q\\ƍY5p9s<]XuVըQ#x%I۶m꒐Kq RE<]/uwiMSzzӊ^r|J*^z)c^^^٫֮]Gz:&ͪ5/g3֒e= !@>N=aIR*Ҳ$)fu/&jzt WhP%M$MfNiU:7?EjԨQi~MTNY찧 @D{rƍ]-&p k&,YDΝӯ*Iھ}ϟ/Ij׮|}}Vl#gU||3o<͛7Oh޽*U"=d8\ݻ73@9p kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp kp k& tE~t5?+G.` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .PxNѯ*i}Fei掟 wڽpGG+Dh&W__:樒v$is뻐b!*q:v$)Sx^I- ;'VGURBFΞ+D;N*)!I]9 GC V[ H1ZWGVa|7R7rʙY6ص[+_zċ:wݽ>ߠF [  VznI{Ly|~ m!^AU\@둁[Rk\սӭ{e\fdh$9~DT%lQmtY?r9F22(RV==] I -.7^zCtY`Mnx.#gj쉞.Ъ47fv)__=tCoi-<]ay?p|rEGGk͚5:x .ujȑ]vfֈ?q}lG?C?tI^KW-Ux=muhA W,Pe`Gnϥc_נAdM4IǎS 54|ūr}}cIR=\ dܲK˩ShѢncaaa*_Ǝ͛[/x8BOt9-qA.}`)I\?nkpG{w[vZW#=O֦MԪU+[:aIR*Ҳ$)fu/&jzt WhP%' j3m6Wu^tQwmA4L~ wKk}3]*T Gʕ,'I['$s?%IM4ѰKWaZfqFnZsKk=a WhP%W_s1@6~.s9|A^4o<]Vjri&թSG/ KN`84G^[*-7$N7ƨ\r:pf>CS~N^TRi~whw,u g=]@{n&}ײ>cg(Sxɒv$zWtuRJiܸqZdUL=3fˑ/T5gu-eM6ھ}/yN8"EhԨQ9rAu1-Zmܹs*_Ue˖y26 6ԡCRrb P׮]Ciݺu g"+,%_+WVBB*Bnx```tȉ{9]pA#Ft) iӦIE pU91O4uTժU 1bz);vL ./D :ӥ8;DPPPG'O<2r(3Fcǎճ>rÕ,YR%K$IM߾}UHOpFڱc\6uVIRj 3g(44Tsѿo͞=[#,YDΝӯ* i׮|}}=Yr &_WXXڶmk׺MoР*CNSO`W+Ǐk޼y4dN;Dr91B}N<ʕ+W_c=Ґ-[6&ŽqݻWJdya7oo&:˗/{*df҇~;vԩSʟ?^O=zty9p%!\%!\%!\%!\%?S:U*Y3IENDB`docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png0100644 0000000 0000000 00000036737 13756501735 027456 0ustar000000000 0000000 PNG  IHDRRM7_sBIT|d pHYsaa?i IDATx}|lcq\͐o3%eEuҕ~uٗo*7JR+"S.RDRҌ C v~f3gǶv-?>g|Xm pxKC1KC1KC1KC1K&5i$ǀp8~VBCC=V~лロm"""ѣG+!!ARݻk׮]֭K -_oH҄ 4eU^]}4imxM>]^0>}ZSDD*Wr VZ)44Toz)o7(QB%Jv@ƕKxo)S_+W1XJRJohYN9;wNǏW͚5UhQ 8еN||}QU^] V׮]?丯t_߮%Jtqƚ9sf+}o>UppԴiS}gz>/^'NwW]磏>R6mTdIAzt_zڴi#өҥK\˛oz) @*Uң>z]>Ä 8P*To{=-[hѢ^nժ e|׮]r8߿k,55U&MRzTD /^\z޽{_Ն r}Wt_={V5j J*ҥV^VSO=[oUŊSɒ%UfM_{l۶M:uRRt:նm[}KHHɓժU+/_^EUHH_~5veO֨QTJf͚>}ze۶f̘uc)555g$%%iРA*_Nõ~zIqV^_9q^z%u]T-e˪k׮ڴi5zHe˖U``6m };r {^zf͚O=z֭[u*88Xʕ$ĨC:v:v_Gѧ~֭[kɒ%رqΟ?O_|ԩ|PZv{1m޼Y~ũE S>Hݺuӗ_~˭/$|z饗TlY=Cr:ZbƎh^Z~~~/^޽{_{V 7(<<\_rO<7xC+V԰a뫥KUhQ%SJJUD =m}7ߨC'Oᅲ$Y&~3t1,DFFSxx:v(___8p@_}6oެƍgyVZI n:W}諨(-ZH 4Pjƍ/\՜>}Zڷo:t蠮]ʶm۷O˗/W޽Uz,lٲEӧOWxx~aO>Qv?vڮukM>]wu7n,ө={hZl6nܘ'sС:N:WK,ѳ>3ghĉY1b+$$DÆ -[^*R5+?~\ZR%Էo_h…Ԇ 4|p8qB]vչspB[~Zhz_~Eնm[w}[o>-]T-[wZcǎu*Ucǎ?V~tA=ӹ:l"""l˲w}o۶mYNIIɲvXXhoܸ1˲;$$._0amY=rH;##5~{eY~-˲-˲'OeV-˲;vTV.VX}gڰamYj9r5nwɶ,˞2ekɓvPP]H{֭YkȑeY\7n-˲k֬i;v5flҵwe>7/\pCdzvZ۲,gq-_ܶ,СmYw^ײnݺٖemO?d[ew=Z/?LIIIeYv۶msuY|x$ԩS;&&v:vdddw}׶,~WZն,ԩS$TRvɒ%ϻƿk۲,vvjjkܹswq?W>'mY]D K.ٳg]˾5555g\\]|yvW޽ƺ~Gnp-Kx]:ul˲UVe[k.{„ Y̙eptl駶eYѣskf[eX¶' ;$$$cla5W6UT˔)s't۲,;,,,!wɶcW^5ؖe ȶ~jj]dlrȐ!eYܹsmW_]I?KxIKK&MFi-Z޴imY=k,۶/~/K*ez뭮ug۲,o߾no۶gW\ yW'N-˲[nvWZlmYs麙;ȶ󶯯ݬY3ݹsgNOOw]O/^+\:TTTmYsNXfo 9(VXp}v8vlllBCC G}Զ,>p@˶ĉm˲I&ZxY7ݻwkYZj~JK^l)l/yIүϽޫ={رc }ڽ{wn-O*W.BJJ$)(((?,qZj)$$DӉ'TD =m۶Ͷ~%tm믿2#˲r&ܪVox--[jJMMUɒ%vZ~ULYFCULLRSSէOc֫WO5҂ tuEZRӦMJp 9r$ǝ)/xgQFٳZn͛+00Э:NhڴiձcGjJvUM66rرcٖXB?(%%Eee)999--&{TR9'ƹ͜nhѢUoϹZjtfs8*WΜ9jժeۦbŊڲeK7jƌڴi9seY~AUT)X*UTjluwjҤIڶm[ ^W\9޽;»u릌 I= u'eE]ue?Ȳ-J,c3f_ez71/_>*TЁ%J鹹\k__C⟹ZO||xO[N֭S֭}vZn߲]vs8Zf&Oŋz*Q S222nhļ꫏>HӧOׇ~KZx]ŵyfM0A˖-Stt$L21bƎ-T] .d1cF o^UTQ``,Ғ%KO?ey^V{}tҹZ.d T&)áui9j2 L;Kx]֭~z]6]-p]K/e˖snߣĢ$˲tgwСlo,˵^s|Ç_uv'==]ɪRGtՂ;sz\o`YzΝ;'۶]]vO?i͚59~RNR+W^޽{~z[zuq͝;7[ǎSŊsu9_&LЄ Zsռyo>}W!!!z%]|ڵk5sLM8Q9tM8Q*TPLLLqlxݕLNpRRRܾ"lڸq~AzketJ{j-" 0@>>>Zxc[l)I^:uTRڴiSyGUTÇsٸqcٶcHP׉1sZOMMնm۲&MȶOP6lp*;r{<ŗaK(kjڵr:["2C7aÆW@4h _^NS~iuu…ïW*UR߾}j*믿suգ> ]JMMUxxx`y)K^Rfo4͛]K֭-Xfdd\sʬ+...x_F։‹p s=gϪcǎW{2'FIڵ4sL\2u6m7G=:\iii?tG?M6:s挶oߞm٠A$I?p႞~iٶƻv[nE~nݚ&N'NdGܼߡC}ڰaC9ZlgyQ]\R;wT6m4j(NSfR޽զM[˗׆ sNqٮꆇogϞҥKUtiUPGFx2kN˗/ב#GSwqr۶mS=Լysծ][+Vԑ#GtR]pAGζ̗k۷oC龊WƍՠA5h@+W։'|r%&&SbŮx_|FpլYSe˖U||.]*r8z5m45h@]tѹsn:?~<tw֬YT^=C~~~t-bŊq19RԸqcW]7nԮ]\oIÆ ߫I&j߾??X'NЋ/Ik뷧/؏?ݰaCdɒl>k׮ly睶&%%cƌׯoŊkժe˞?~O2vv\sUTnӦ /M2f9۝.wiTRv6m… ֭[ŋSN2GKuv``dwk0 TD|MN:vѢE퐐GSSSjժz +|پ}km۶eY`۲,H"٦}{9UVvEڕ+W^;:::~222,1{?nO<پ뮻쐐hѢvŊ툈{…nսk.6mj)S.Zj޴iSu3"59Izz+uֵ *QQQܹsmÑm*kĉma_>xFFꫯڵkζbŊٍ7zZ]ܹsnv:v2e=z;vdСC>h-[5ׂ >m{>mȑz׵w'AoԶm[9rM_խު>}h.-Cwy֮]RPp%p3f/zBm ý] 111ۛN>'|RԳgOo +WNF /c*$$%:?>s͙3ȂW_Ղ Zfx,\*/R%I*"Ps7;K"2. )ȇNIQn͇pdi:yCn2l[V =wC8\lY%ܲu.JwTD0p c0py*PskxJ]@GIY%yC^9=+4%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\%!\MԴ)h%nJTt1ޭmCʅ^Ѻ薣JߕnQ9vZJ|r|C^s85i~3Z%]ㇺs=ꡅjﲽ:.۫ySX0WԴ=9Z$*}iսkvZ5y=ӟ֡y?TdHW.oRkC%_.$ٶֶ5P}v6M+ZݿW-hkԑ7~w;RRϭ]=+H85ߪ:u_mz| gqTyiIRP 5m^Kk>\s3gh-_, y-PJG\깒Ad%O2,VO{R1h(Tϥ+!EiцEYƖoY]Ch+%|y|\;}s+$N>IUl%Se3{BNUzF %V%LG6g=d-Re !Rvۥ,K>U }'VZzmk.7[ P;:!Ϩuֺ+.o'zm>'zxΊ +ԡQI鳧jŖ^摫py)עEtQծ][cƌQ޽=Up@%$$3^6{&ǬҀhؔa|A!ն~[;uL'`P}rǎSN 4$ܹS-[4[]!&R@;K֬$jΦiي8޵mz+W$5mTNK>Y$u3l>Y'W"^Cf-_KK})gS/\X]lw6\q)Ja]xfA]i9ڍ@uIDATwZoz̳j _l)gQ\*u3RBi4ي+JaC|疌]msJ9Aտ]Qn@AvLIIQ5(I}xKV ^33'TՊU%]Ċ^+lVvu9,G?z#׿mֈ~#4'IJHJPFF&>6Q˶mٷGgワw }6sLUpYKs]r8\6e۶uA]ey} Tks I36SU^szWK=7$_9ˑe.̍lTV=hGU"q\[~ݢΓ;+zk8-]oonZjFϳ:tH!!!z4zhxLL4i"U,$ @TSmi֭jܸ5uҥKxuѣ( 6Ԯ]e|풤뛭 {:u/^e|ܹ Q-% k:q´`}?>s wGcǎuQթSG .<*\:Nkz.` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` z|{|/P(wί+*W.` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` .` & pjڰi~)Z&*}mֶ=衅j:}J{ռmݵUl_˧-7}HNM;M =Q{}R!DLzE>Y*=>]Q<\1;өiӧ)zU~!]ǻsTLywuImظAW#\ޤKkhӒoHlvkۧWH{ghN:ߛW-ik#` wVjT*lVYmQxkСӒ%"EhՊ?]*11Q+WM6,WvY8$)Dt].Y؅zדaY?sbnoqq*]R!}֫| O>K\\J]깠 ~ =h*Zկ__?s۷Kի%/>>>zg;:y^[ZeYw+h:gZ7hK ѣGg.]:KB!5g Nɓm6JIIф 4aOՇdY+!9! s~V}JqԥU-۸,/ _zuzY<99Ygս{wI~bӳԯ}? 6P77_~ Ij^'Klc)))y] ίW+ժUӱc$]}=R̘=jDkno ۷aÆ4h IڱcG^B]z6k, a/ӼUrm{K6O|ڵkYf1{6oެD/VzJAd-FD63%IuU϶=%I+6Pڹ45[QD)OKf<>C:Ҝs#vZmz̳jo$I֘~cO?i(Ev51kܾ̏%2"R@wgK}f4ي?Ja-6sUKTϜ$}⓼< #r:*^Rի=/܊K=lEEE)z/ܜ9sȈGgu dj֬w$[Jb0=.$8Az9SUUtJq;{םd۶Ip8Y.wnYmkPAqPwxj!IJHIP'*dl֞=?g^;PTf0SU+]wR.]:p@!Ñm.̏o۶5b0Bm˯_U/^ԫץ .sd]wynMq^uԽՆ u88^U֒fORvrrʖ-'9111jҤԘp<P:P(v(,2֭[ոqk$0p c0&oYrMѫ ?޽2ehλsNj `8z&\*XC["ImۭmzZEhkH[oU:,\pPw+sw5P}v6M+֬ն(5tPiɒ\"EzjEDDǟP.]+զMO|z,PJG$ ҐnC޶˓]|<9*X=I 0S麗 ~߭ߴ^$5nX}H(XT:Ri{nW[ᅲ$}W6Mq[{fo_=+eYZƗɇu0*d,prwYeR[nڽ{+XJRFF0_͛7WMg. АPU)_E;R6}vIRz$B_#\>>>zg;:y^#tlcKPp~͙r͚5߿jժ%өJ*[nd}Aeqo+/?$x$=ίWv|뭷~9R+WԌ3o]֭d>K:.IIIQPPP̱. ׫soʖ-e,22R5jԩSW&4{l oO ]}v5l0x $I;vPHp~6\^,֩SGFYp 2Lv9`ܧK>UڵլY3ט=O7oVbbCAEijԨQe111jҤTL"YÃg.۫Jqٶ]zg;]Fywpo{7UJWu yErt׿֯_wm{UUz.8w漣(g*S8];wV``~G7 ȟ8^}Ԗ]1xA)i֭jڍwrܸq2e|M=#ٖem $H"q7w%}ږv.kI&iʔ):ujSI\_cƌDMȧr.yM4IƍӸqv]{|*Cs[nioq~>bݙ3~ell .N +'u۷oZjuu,۶=ߓj*UVM@ZZbccu=(88z,\ =0p c0p c7SN'THHԨQ#}G. ةS3ϨC*S&MPYFWZt:UR%uM111. ԶmԩS'UZU*]5|oV`.o"=zмy4qDEGGYfӧ,XP@%''k:w.I,U {뭷~9R+WԌ3o]֭vy(RSSUJ Zr͛jժ顇Ҕ)S]^TD7?\;wւ Իwo=ܣ;wjr8[2ehĉ?~Aef?TF կ___~*CaӲeK%$$(..ۥ8Ē%KTxq+KL+$9NթSG^Uҥ*,7;vN:ٮN6h@sNoիWۥm[:riժUz駽]VDdIFƃ\ 1bΜ9cz`Ç׬Y$I>>>?Ç{p q?ԛoFy`cǎ?$-[LO=4zhoV.oKѣG] 4iLSGv9(*Wʕ+K"##%]fРA*S7K+p&ѰaCڵKYƷo.I_74ik̘1.Pf͔XoR.oݻwשSx,sUHHZh^&MҸq4n8oBjݺuQXXK)pxY&k:q´`}?>[cV\?C'Otqf?r:u꤀o_ք {W7oβoRe(~a,YR͚5Sr唜E?3ѽ{wntfϞ{quN;턖-[bwƙg_]C?~o~M6ᅬ/={D˖-;`xrG}tt <'F=+;v,;DrJ~~Ѻukxxg8=3\s >9~79眃o}[Q{3o&g#<35g<8蠃жmۨ'Nām[ƍ0}tuQС:ws9k֬#D]]A]]ڶmu֑8\wމ+K\lƎ]w-[N;턳: o˗/G]]>7|39DW\}:k,?>샎;M6w}q5`ƍ$_l0aAN;K -[{UVѣ~%Cf0`CE]]7 8SG p:B6mڄ {Zl_[opt6mڠG81}tNkq}aРA֭ZnwCţ>*_~=.rZ{nA(;a|}ѦMt~8z!?/ۣcǎ:t(.\(mۢk׮8{ u-ZÆ K~=e+W;@~ЦMO: k;#.Oƞ{vڡ]vw z-|kкukO~/R駟[b„ rnk(#9cʕ8C>[o'x?>FΝ;35k0qDwqx7뮻FzΝns  m7?~ ,;??ТE Dߎg}FB}}=LO>I'>'p.",X?0V^oz2d`̙]1c`ƍXp!:(ڵkK/ᨣB˖-V\\v`ܸqLs=ѣGc֭8ЧOZ _0yd̚5 p};-23Ƣi[ơ]~˗Cȑ#a̟?]wf͚3gYfJ_l0m4aРAR=pg`8ѡCL<7t>h ԩStR .fΜ<Ot~i'x"ve{3Oo!F^z <~1cc8sѺuk{X`N]O7x#v}w~ر#},Z9Bi& 6 |F͛'slذ!Z⋱裏F'`8sl27Iǐ!Cyf=}ŋQ__/BO?~??OxWdɒ0w\ 6 A`ݻ7,YOS̞='x"=XӦMÝwމ-[஻Jf͚aРAyc7nڿ0od ~_G|nk֬ :uiK. ڵkp23&`ztSN w~G[WW 2$cŊBݿ/X0iҤuY16nl2{ef c^~QG >W_}'h֬YD1?)gcw][|yТEo߾~1cFЬYओNo;Xzu"?cAgB}Ν;Ka.O?M|mX0bĈ`ƍ?8ԩSбcoI:Zh[FcO?3~hu1Xti"3 c>]۲eKЧO.xw}Զ ܫWߏon:~ܦM}7hٲe'q֭g0Ƃ^h[n;3s'W;hժ7?\f}ݹwމD0n޼9nc9lNZjl޼9vࡇ,X $вet֟UVEl¥7>'K4nݺ%y)c "J/*W_}5vus|Aa]pygAꫯ{  z衑%\0Ƃ F|֯\vmc, LXkioaڵkpg?O.X~}A?Y`O? c\\X"`̙3<_'Omذ!رcн{w~{c-[&^X>ЯN:)h֬YbqSOq_|E ?p8|];HnA]]脋 gϟ0Ƃ>?m|UWq>sGLB+-C8.\矯 h^|E!Q !VX4kL: 2$`I>lK(smKh۶m"Yf֭}]Nɓqw_իy(1O?4FZx1c֯_W_}ݺu-",vme˖i˼xbGcxᇱxbs9Z}Y@.뮻^p_vc xbq*u;ᮅI&O2&L3gb}̙34ٲe0~x|Wh۶-fΜ _9ŋsۍu˖-Kl׊n͜9?яꫯb͚5[_}Ə'xo֭[ =O>ХK㼌1xA>,֪U+z꩸{1ewq-[ GNͬYs6>clݺoF,'cL۷o#GgO>~8>`Ih⬳ߎ~SOőGC9;vwIx=)0 7܀3f`ժUذaC"=|`^~eQWW; Ix&Kc4hYVXzjx+V$ =Q] ~m|܃]vpm(e DE ռys|7kǏǸqХK :=z@6mOŭފw?z\h޼9F[n#>$tŊ8#ѱcG4k oxx TEWmw>stAcǢK.h޼9>3?^zrժUXv-G.ñ|E\ 6oތkݻw/ ,7Ň~ [lիf0`4)B]~0=|ZT6"#0L^p)ݻc=Ĝ9si&̝;7zp#@-0mڴnm"%Kpc=܃^z 3f@>}iӦ>1rD;=SXhƎ/>rBBE} ;0O?4 j &MBnp'd;v֮]vyߪU+\}ոbܹ0a|A\2z gå^K/| ϟI&믿KEVoY&L1ci'N~*,G}$'k| Kdeދ+Wk!.\K~Gc=a]^֭M嫈j~)֮]C=#֭/̑ 5*svڡ_~Xt)}cpka,.Ν;cժU-[W^ Y 3 C ڵkq]w/m۶!3f$˳Bf̈́@t4':OO2OkXd &M}_OΙ3Nj"nG\1flܸ&MɓfyA!om.3<SLA޽1wh_֭FG}x7kY׿τĢmٲE8B8N͛7Kۺu+gf[{OT+VUгgOw{r%(e bv@6mhѢv7|K/4 #\ 7_|]ߴi~_'?Mp l? Kzu2!^x<#ҥ>Cocڴivs"ѷo_" Fc,cy睇N:kŢE[ 'ߎuq2}hm3+VW\Am>`_%J?f0aqG?B-0n8;]7m$$*2|?]_n֭[͛G[ŦM;B| ֬Yz Ap}fʔ){fCE߾}1w\bX"t5k{D=܃7|j;.{oŸyǏ#n݊/Ap,_o[G-^ÏcEq'bӦM5k>ssQ#<?p=_~=z4Zhgy;wƷ-y睇^z wuzc=+֬Yz ϽgLv.rL:wj*h# [<N; wѷo_,_O>$:tMEDH>c?4x`\s5O}J?~ _|w?plvq 'K.x1j(r!cvȑ#ѧOzXt)sN8( c1b9MmpQGah޼9o?ݺ@ûC>ׯ=| y̛7;#uO. u]_+>#\r%~~zqӧ v 7nĴiӰl29{qqSO)ݻcҥ2e N=TKC'kW^̙31|pc {/O<1z%K0}twqxggƘ1cp7bܸq={67|'O'88СCL2K,g|СC3+O-\D_WѺuks=x'qz!?r ڵk?4i Sbڵ3%wqy 4ӧOǭފ/ ]v z‹/ /˗/7ߌ)Sǂ 0rHO>$I&ᡇEav3^Q3Ƹ$z'r|vn@>}pBǤIЩS'}܎ۿ̚5 zhu'^(s衇b޼y2d}Yqؼy3̙M;ꨣ0g}ј~݀%LpaaѢEXv-Zn]mw;vl X=Hy֯_v xꩧ26QF.!|Gk_֯_EEK(Q[ayvDņ z'L b5A,mq]w᫯Ro[Gyr­ފ';>C̘1FѣGW%J83f nfG?^,^+~mp6lz͛7cX`t邛o.f㦛n9#S4o}Ÿqp饗V۽%JxB%JN;1c`޼y3g6l؀ݻ .gϞv 3mڴ=MO1eٳIK(QD%:6l؀+Wc~A"SLg%J(QD9pYg)e2#݋+>ⲟ7}.5͓eRu`HoĥLcյϟg&@>mew]ԛ=})?i*)bWMlJ{ ~;\;C.e#UVO׺bZ>j ,\';!uاrmwMv&?q/~BZek;Ǜph/"Sz4O5 #UV/xl߾=O%/$?fZ#H^:o}eԤ%eg@. 0'}*$kseE/)/Ю};wߌȥM."Y6`e ʯQܫ&-lE6KɰT?e!A[b2?Kl5`j{9MY筙5C.YrKUMM[2|" )2OVp`t|`Cw.4isP̝l~?K[5C.ؿ5% ʫkM7Ȓ=9HāDr7?5TzNPxajm#aϣCk>%ɦVm*oWè^8 ^=_T32t0tٕ3 ngo~Jr|cp?^xqjFe xD~GZSSI;|l@ǭ~k\v~Z 56-b_i2?bhspa> fr:"v,ʭ+ q DOO©5s8v AV3|#56TKW>K{j(D?&ԫc=ٟΑu45dP>Ϋ\KK;wŲBM"m/Y3^ʑ A~ QMkVV|w1闚]ٽjȬ3kyӚ$E}k`Qr,~JAkE>,̴qɫ@i(@v(TWw]j7e"1IQ䲜L1XNk4*cz)S”j,=V"#N2&ȥʗ0E,7iJm"y( [uBYE72C!񯼄%U'#CQ_dmgjQnfS4a]&`E/(JvmVxr ]֐u{e?-b=k9iEȤ) a"F_& t!\qj I.Sl%wNjKq̈%b=`^g&_}4\KUK5RHrY܉"_R߯} (z2~Du 3Vi?O,gP bIJo  ?+3+f_SfaEy""7%>Mf9gx鬉YH5=N.%*w 9Q֦KP7O'k՞>Q7s@Ž0T'r'",?SZʉqbITW2dmdKEAIhQIҹV~qS(o;3*CнJ:C1KUpUVK(ϵ1ݕkUVpC-.-sg^e0C&h?hߜWi5CSM{u5eG:)Џjl>Z$Ĺ`[YUN*f(aʙ?ջ}+˺tD^=g.k膵tRju_7T[MYSS}W.)Ҝ,i ƧPj$(;F%5fyj=kj\Dk<׆m&T)gSyj{V@I.-Է$%8o9#c#;Ndˤ$}yl3WߠE;]N5M<߇2$-l|mcbje'vr.Wʗ!$ R[fe5x' IzuC,@2~;*ɥ!X%B4njGsCwAeXKzT yn'V{K.n76i?hL(:PQb|,<Ϭ6>>yr!T* "Ůb{'F-U38QPaIaYXK1h_Dj[v OT\Ӝy׃͆ U&dAP|R0GDf KǶ&\7d}Y$ič6;zpI mRfEE2^'V2(JLZu0z4ėD"CX `Z~UcdBa&2r1X|pGJh4wHW/7:X9m!Ʈ5UGչCp7Z-VvE}t|Z(*S< \Z\p\U/\ORT<HdV>amW)KOhR Zk"r~MD.C:ؐ5>R@I<0= B!(Fzbє:/LQ=qSF2k#bdUHRɒ"J#TdOu^>_uA_:w$i 2/#"L2 ͑fnmʡ!:HHߗӒLYpV[2$b[b[M9X`]]Ϻtbb35Ve΄ZInI zQUb * E DB5t!^cP,*$RWT(QSYW<Ӌ$ܠu2YYnzŨr8?[l2Ҟ &r19ӑYD2ZdnuVVATOȵr̽dJfԱ>_AH(dMnSSW)3G$(C/tBjyLtUFi%TlM\2+D+(|x8ac&ȷ)2%>8cU/$$MJlؒJS/r</u|zW+?D 5}2M >>:^:yWj)N')cyեa@Pe!-h TJ2tq05Ҩ$H1HJsĠ@,\V"dCa'z4&6DMF.cğpLRe_YN%Gg#j]ٔI7O&V `Ol`̋ݣ̰am'4q ?jeI s}S|?H_}c͐tU X6 :|E@|1Oeɜ%6'nX+ R LwiUQäҢv䲸ݲ٬{f52:UVh^˜E"EEKf>R,h#Z~K\W VTR͊y*Lhro3צ}HmWf t Cxڹ6g vdg.8%&֨_]QVpxHX6$@\~z>7S>ri/d+9+Y LT 1a\ SSM!@(1Y} .ٴV/1Lzeѡ=GG6e{$5ӶɋQE\:jш#mRbDy#m1U(*cB'!YGtao Mfz`:z9ߏY֢EBKQ0+/48$AֹZփTHG wꪃ-k-2UP7ykt[ko"acF_(rm2uR "<Ŕ|.Iex,UvS^zJyg=X?mlاvʾI 2EKUDeŬg #t39O.՟17{#o_|2Oh1X}g@KA:&4vTO2_Nmlx^}4뷯 g]nl fD. c\|SN'N$}Ib}*$w%IW ?Om5ӨU/5,3[Kn/(2V zK6za}0ឋ5-B-N`R^dM,sMsE>}j}ueq)yФUr"{ֶ$5e&zeՠ!7)Cؐ?y-6uq&Et(# l<_N@$ \QyOJޞea_w#fMaJ= τZGWme>`&-$V]Jg.F&tW7k!!!6gUL]1Ũ#WR\6. W3?>\d~AU,y5z9alJzgL2en6F kk>37h0pܭ lZ&6bSIQ(k5_F-)N;TKe16G'(6D[D;Mx!Y"Ua܈1r2/eHa꽖9MNJ ],.ٽn6,̛reO,>fB3KU8'[ݫJMx]P^wq4H1 Ʉ8!9#(ڹ_-=+QVLJ|1r]b2%<$~.K6%. %q^\B8)IK#ܶ8CS!,AJ4d5ZQB)&>Ց Y;A'K2 H;giM8ie:@-8m~D"(B5\jMr^upeawF֭qG7o'Ni8ꨣo/iTWqEv2zNF~ aTLJW6#AHpS2@j'g90J㉸|G#CHxwN񝡿_kuĠo{O<{^y睇/w(N0՟VOQLj>g`N/(ΕgԒ%M qdϴ?=D+*R*v-r6`̇5n[_2'UvWؗ4E"\N|d6*gC}gELG)&yM ?=&C 19m^:$d6\ܴrJ~,פWsS\mիWO>.]D"vh׾}#0&~Frf}N@'`jmYMY->v1r1.oS(/a] DWüh 3rL5lnLA6#z]hGZBlEЙtET ̣)StQ}sbG*p `Z$,ay7&rO2SLMrYKﹼ?G~X39"eY'X*Gz٧BZ -J f(WH<101})u.ŔʸV# .'Te/L<:ڤIR=mjzر0ak˖-سǒŻv*NY&JwbŒ?"łE\4P6Q "O/Qb[^S.2iZ$1 %022˔_ -aWG9z V}ؾH,j@&غuk?O+dgL',)ۇ^I7Me)yk6UO9{]2ԟ$ykMw琢q0R~i1j\S)`/q:5$X#2$%ka*CdrIIsj-kD&Fºu'O0;3ݹ""f){EѠvUg6rrHsK_5Y#I"$NgʽJ&&ӴdRhS Y+L~2&PiDyLN7!iӖVznȒh\>CE]/{ĉ1uT< hȚXVM!cL1OumPt&OBBGpp|8H_xqbZ=2@EeS_,;[mE>#3Kt0z/ W]u֬Y&M©(:-# Jvɧ~MάI el^QeWdZD9%33joLcd{j6I{j@H\O8)U݃i_a,pFž44F"EO[K) !=M'3DIj*M:x_G n鹯Ȉ\mvn6sK Rl7'gY@C\|ͫz;vp<jic{p*  =45!-iAYR?uNƝ4m᪘`FUSܓY$>/Ni-\4dUDdfH'Ǵ'~>GB;7yc9& e`»=J)|,Hn{$F@N)%/1E4vG9A=ƹyF1ѱläȖ2`%qy` QFJ hYiݢAZtn:Z?jK[*@wx#Lz/E;4&(+DyoR)ȊXz#ScҤA@6%\_ B"hhBԄ:$ `,CSHT`DOt> SԿP:q(@#fmOb| Ozd+oŧkqA^Rn=2iEBߢm z\$7/z׶)@YG˦:ێTitIfwM G1mBJy=rϘƼFBڏ\ ^glwpե̯o&ۆS)#S|b387(eHW& 9SY<{!x>YD3N"FɒH4CUD+Ԙcg hZMDÄh F4j/U84JҮקC"kZw(\&[Zɾ}*iK H7诟XV#<範~'w3r%*,-#1K~1eh1,\7~ɔp&4')!Zِ$ ܖ~WF)mzGٵ?*5ߍICOUzumw KI&%y7O߻Rmls䒛@"2״#6ZK.,`3`gF1(9hV. H3YN: xԐ%!SoHdTD/RHrǻՄҌ =`]IZ2#'V^,B'N. f<%|g}ĩi)Vl>ngsogrv5m}EL4ͮDzE`5EUfBhTݢl,]!4ԁ#.>-Jgj K7^zƏL2HsʹD>mIؐӣ#K35E>w1zYƬaE*vOiK{LKeCG#d@-S֡u)6)6ӾGO|#HgZv>@.ȫiR'{.Kz 9Kc[ZJʗGDN@lUMBm)~5MI|u^7E2cj'fH)KXJXv%1aJB6%"|lBCR6#h\Qs˪KAI셳o(FE‡?ےZWQ p:+/N OJ 94oPIsOr~{oˊ/~k3iVP2>D"?L/z$R[eN^yYSw'm1Oz4djKY0?*WnN2@A""3<h>ť1ClJ0^'Ejeevd;hF*{If)DA@BE/HהzdnG ƪ3v{i) {PD>yZDI2 @4*|It)Bw?I`3|nX'0I|6JlZJ"B2Z)0nzr1Rm7VjL fo9:I/o.Ek38$C~Rg`Q?\!U"Ϥx,-#mưDdsybS%or,ԶxadRڱU=# P^ K&_>8 }Aijˬ8JtXF&є*]˓ P2(Dx+c`.S\&+qAEljUͫ|y)6]>E(WȎ X[M=>4&1Q0*ߊOYI\G> )Yz3a$L>6=6J4n}PD˔ĺЌHDi+b rfɩ-F[kwQ%V^v#5\*`ޜ%SrR_ᅪM<&STf?Js :]t֎ȝiP JJXj%xW\ _gyلDrC o/!P<蔈NEJeJ2ds9H kOP*r XUj=R_xcJ+%L6i1:D cIJߨIt2&;Бn0o6eIQPd-KE$SON2yb#D!~[lq Y^X `ZQˆ&^Rfll-+R~6[}p>}fkZ/(15U~k_᧸hT ԙb|OQ fʴ̴ב VQwQdΘ-bh٩\Υh.&uڄC. PMbiI҄bgTKɸ6Vڝ ?Z`֋} ڼh9Rf[(}fj?hsA43&^$D3YGR꟒Iw\`r!"PRHXNE44IIc~KL*AlJ̭+D7՞q"6 3JǕt`.x iI$,L'Ap顠*XY2je\.E=u8#Z豖!Keq5Ʒ]籛ܖ<)E'@Ḩ$ 4$SÌI#P* +DDFDU.%#:B򘷯]:r z/գ :_?hx;QN7[͚Vtvݞ7Am!AZ黯"Mh76x%Vo\Hbm~"1@ZR͉h-3̛]u #V ]\üy^_n L=LeN/I>SY1|H{hzf6Y "l4"\7&:?(؛ҤZ !]ju2,qj4j"E0UZ-#׋CR[08.Ȟ\H钨U+gOL- H14Kv9:Q3D(tH*UJ,dcQ0!HLJ(v1>XB3ig*+m$3IL)&KQ]MȘ5$"R?ٓ r0(3A'ʪP\%,TɊ٦Ǒ= .\k^U~k7DW{+igse4)si#,4)3btBȭ{mȜO]ZS%S\IgRqR諪ńPY,Z jaɫp|o3,_6]4JlxD2NJ˜GYHD[LSlں?L,GHheH4NJ'y!j]hBUv|T>{ɫd&LfaPF1۷ߍDJCA A\-LEN#0z\\2 Ol;9MhڤiKx3:(՘+n%PGy<"P, ҈^bIiM$RiM,ޖ eVM Rc*#Q@d >rA">B*,|ǣ5!Lt [27u/1,Tk4z VY>CҴ"h;⻭/66Z=[2ٕrdv~}Zͣ_.fhϏ\uWsHpHOۏ(l6k5PE8ю4eqz+%#sA8! 0('ʃBIQU?91$]jEpLOrYAJုmL2,OLi1Em[x)D)F2yBOG &\Lǭb(~.]ѐP `UDu$ cTL盚`6ҙ!U?tBm'NgųAEtiQ('$80H \Q' S ihVn(qscT$}C,V*z*)2 qCF„RQBf|c}hR}VID.OԶߣFh5?Zi(IFpjgmj'fwU&k?|&nBu#B?'mҲXZcWE$aFgI$Uv[{J[8le1ʒJj>_#&Y?mve&'K٨!t?d%7Cۉ<\G=Qidv*CqF2ڑ$JSo,VC_*M/8RǷ"8wH IDATD qqTFȿ6+-ESG~m b@̰LBT( .n } !,?.Z%f- Íc}-=#Y1E,ڴdU쉰c/x_x5d)5ێ|Y/)E'mp1(cHb;F0&c5&SH0gdZRb4_9Y' zVJulk9z}EAd3}Ā )¼Hfk3y&ke/PS Y1*W^T)S2:)FINNye\|&rd[5XHۢh\K WFp7Tmq`?{m؟#K/՗dkhG\ ]d]&gN{U"ZeiOʐb˳5YHkb%8 G+&P,ˠR)VMlɦ; Do:iPv7R-ؿ&~X2*&YrpG6e7iI '*(%`Cc@kvksz~/+mXih&i$9".s6y_J6v!H1 94f bZa8ӠQkJykH@Qȷ5qoLT9_(2LVnzS'˷tWcjD*=r\FҚǢad2z)=R6O6wQSЖ%+ N,qSWOv]fbj|U$1`4rL_fD[N 'Sx!ZdveJJ^r\RVuވJIlRDSBynNV)ga8PY`X¬F2&]ϕDࢀJ*v.3UbFW.8\2)eSjcL? L?} JXDB 6a!cS:_OEA4m"EđBi,ͬmqNe$Ku'yV3Y3'jE bƒ_ ݁Se"9ENxSDQM݉f4*BND\0q'!ڰR{t-%MJ^(lAJ"toq˭dX4F0}T6<3=4d1%@e1Kk:ۉ&9t*S8쨁AIl{IUdZ|JuӳUnTRVP u'?L--R84[jq|ʕmަ"J4,20f͊mY#1-6HZd.}wfmC:c |d ߘS>ii2 7(h"Q$%o|R"#ĸb4f5'ő},F9d~.]t~m~ Oaſ RTFBJ6uNS0>"(qvny\I8{fd,hRxsd'8(܀LJ⏍2$g|~g!+"otdOJPl7P?IxT@SxYH576hU 5lyuWFebC,?XY![Y'6 /,2%' I a?ɬ KYخ|H<V>ƉN,CM꒩A]#vwq<=[zMZrm|ue: 1 fXS|`4)zS=uh:l?: A mP5|9wrנȖtě.LY\" $G[SQj6:&HVg!O RJ2ժ<S." ]VN"] BEXTEI!Gn`/7De5az5J* p6L&C ZێHXiDBcґص7$Ȟ ۸?h2$òMNmu{ $b;HdR !s(HWyb$R:mHp%$uLAnfc6@r ZB$Ѥ@hoa~[\p|,9ވ@so̟X]7IIkja@˞3y5IvW0 ^Φ ןm_]d_LU2@3b劫F9'g${Qň϶xw P??%_O)j%z1!l ^lkd[.nx@YWI3LiuIEY5{ʫ;x 9=7~И8>R/Z[Uf>Zv,ՅۺTGAKSuhJP7&c6*Y:qEF0  slRW\lS`Sٖ6 9>#+<XNu޻^DiD'H@H0`n& N`0L"~6o'Agb(F\v]yZyZ?޲5=[]fbzԚ7%Tr.A+̬x`m!NI? %@K]vk:[ϽXNHf_ÕS⛤:g|6dx6!§ܧ~Bmگv0NΎvQk.?d;-QRUTZjx\myDq[ܤ n@)&& .X3eB@ 'J= >D-)zD,QR`)-6Ҡ3ꨘD u:!7o^|O%v:޸ !rE\z UZRd85gMF^oC^V k|@˙^& 8Py`#9{-KdO,qQDw8ߪoU ȜL>M+eod2IbxE94hMDʀR6d2H^ x%^tO|pUV}Q4xp.C~A @WnRdfR“2$E- #1s$maBի2xaw?Z{o>+:9&mzv["W\&{܅ Z@#Tb[kR tY>bHkKDiN^j+ s P.uSG~b'P4-jc9VCSy mZ{zdWX: !6>7>~j MTQ9 ܺEDbp1o%=&ǽ1DcŗBɮF ~&u,J|;Z⡒&`90wljYx?e΍Xf_Ti_uʪZN[xoȥTZn%?_+YD=3MizzͲn5uh ,+4 Y3 ,TSKjJpmɫA#sdf[@j}MEpHW;w`Q̣pnrthqwwcyb!T,R$ͱfVI_ʿE-G_3^+̠!>ğl .sӼ"?iNi<_,Um\,1Y89 ,)I>d)D9D^l^l0_zBtz)~dQa~kNύR1Os@tWjV,Ыc>AX]N1:Ah;QUm mC[@?b[hQxt4X:P_# p y-2ܺ#],jPX:gqJzc]Rdm:`i(aU]l:Z/6cc߷F,{2/oCAŷ!U&XrLZf `bX.0Va Y=?D4*<@iXf6ڼW8EJtUWUL'9N2#r!aJP)sQ;E m}fgEXoM)DP RX xҲXk?#'pv@p | &p$(R+(B%j|T/* z@˅&0qSYtpu+Y`&'<]Nh.PAIxyEPxA6#NUT9}]6S5yz-n G~8(D/+W- 1m-d#ӱ~y.88ȨE\ MVc2mT*+V{!|m>#d7Gs 7D.%=?@_to y.vCj@5D]w`w1@#h<$CrH5ңkp~ꏈi`Ӱgi1IUݻʍ':Z_[޶̳tY۔e#h'MkvW=~7 .܎qdJ=7|bc?k  ^jDD|&սqVo=ɔn3]M|ŷL3ԻJn :X.ZPo"ل]=@|p )gpB6dfߘҏzdVI ʗlq=Ԫ3pԧ`z¥8\O9jts+T% SH i}~$a^bգ^h|i^RҞfFR-Bgޑ)UP>rڄ\Q+Zm;PBj1}Zj W,eWhoSf8 DM>Wο<\Pkb z 5P2c!@<,S;f+Vɻ,2ꀞULFTB ="I \\fSA$ j4[F+-l6Y(t,FfyHL{}O=Zk,:2X Xu1 ,BtPEcC%W/4E C_. zDV:!"?X+7LK~4U^Na6y6%n[@0p~)J+t0Yv0S[.wSÛ{~楪^|v۹'Ez F񏚾ꎦ5%Gz.-e. Mh^lz-ӣ5B0)u2mrTcOq pD~7$NHUe d !Z9^WSyKr@\Jz|`8/uneɺLw^%$R ;vp"#Zs+E.,;~ϹSjwn!/dyq4=-p7bvNmqnZ9đlc?vYOBC`:@qO[Kqhp<6(WՆ|L '*,AB}α|1xėpc;AviPp*:4Wp Wo8vVI|;akWzw;UFf+A-ki>,Fblq !2SњaWGvfyoyTa|b>kxH.xJRK1y|3WSLFsIۼ j.Y7{Ya[*1zY4F.Y@SDlK]?2T,SUmO+n5cD@gCE<-<s%JE4"N{ٯ\{,Q5\@\wBi Ly`#2B $'mZ.>Ľ{~@z!@HoYp,kupp.}ﻒQ,@yM}oz|0xi`um\p>9سqbnxqKVhAx}Moj&i[9kmT:02qjE#t/,!HRLke3*aօ4aKxx sw03М~<'JsQE2R1PY3D|tˌG[ᆮp`s|2PiXLj7[:UjYe k9yc78鱙{DQ̇̐9%(0nwn.r8$;L'E4>[#|No.5@ Uy6#G7d} R8"@0ON?įz@Nr/Hk2} @xD+>҂iV-zMm`hםL$Ԫ>py S9x*b1R{#=Cv+QJn^_.'.h=ys g Z2Tn^9u?U?+׻$zYOcݽ٧/Z~CK՟1Aw(G'0/G&ўvs}Akۗ"t7X_Z& Ale%I ;y2@!]D]`K}>DZQߜogfcx]a)(e>V_ב[IU "=iw_yH+k% ddZ(5jhx޽Wqy}Mz-VǑPMj|Ck:)d%"ܬlli{J IDATR AFUSTsMMlblrHiY"l!ZN%C@_x4ҽ{{m7M~t܎YhZFA*X-9څDT#kص!_@LcH@qQ\  e1->֏!/)D낌D2\AIv j19 = e=KS]H*[3FZZ&=zq6Rpyk,/=ԉv'36XB4lI'{S +C[meh$#=#).C6,𶧾on6gp)&`Albr͋H֫<fMfutzv5s,p0$ Ai܌R9ra=vV t^u+fk2%ir<P*V{̐;hɁP,Z;5CK|`6quxE,Bxp[<@=LmTHj$C.^;pR6yxչsuٺX\Ɠx= Iqep`E5H^ͨ0P^fvݬw|Pg7Q*&`9Ik9V@=5Xj:J8۸'g)0yPIHA/,ahupBU+NY MZs2j鬚_c_T:$O#`_#1UT *DtOPH-i5a"b"}yM%PnO`8˧jY(Q1E|dS̲.CGBc>JR;0%l o" oF ,K¶eXҥ*co x@Z6 '[ME#lI!|tb*3زD["YѲYTqʐlVsCկzt%vpDS*48ִS1>Rv8v 4,zU&lV:~wۍȳ6}pAK1}8HwKV̋'$ Z&hIJ̼O0x!Xq۔NpTbR":D֐}Ps&KtGT o]5ՎBQTa4-G*gsO(wc͛E'"4ϧo>Zc.kz@ e ه"xu咡KwX-qqwoPaF+pfRH DFY)*t}Fp0LzAF2_Ƴ fu79UKc.뺆:`$ nc9v&g7P46hB.oeW,rP\t.3iE V4~)ps(˃Ku5Aeesu-#䡙rd^uEGʏ.'/VPcҏLKfP$сzDeoȌTC0:|@Sw |C?ɘyW=V<4}\G Fq]rMeM00D1om&~?$=,8Nh>ȥ8~D ?l~: BX. @l"N/4"-^&%10uY"ÕG[ 0X,^?H} %%M_4]MvL?Aj"`?YGj$Zc41Z:$pjo1SI@.g8YTsVa&%E/>C|BcHk/&ZB׌C+R~ L[esM+iWk|類QGq44gޥinXt!xi]4x51OPERgÖ=8#~l҈45@ḞNBG."y4ȋth$V;^UP<lZ3%v! B4 E!]C/BHWcc*OF㗘nQa:HHVj!MJDd;\L[dYpYK? \~U߽SM1;8 NQۦ*bҍQ3;6!NՕF͇'z0EWKiSKJ1xF!?FdJatpkWYT|wH?Q -U1m<6d'J9 ׭h Jy!9įSȐ`V>:U֘pEo٧;O~;NmY H*mvdu1lLA0hLF!s6þ5a`>I&QUƀ]u3y`E9Q{A ]& JOJ`Ef~]a_\~SSZ 2zto]AjӅIofYn#R6mbdiJ46xh஛6-qCQC*>Z==y+{mk3f00\Y9Fz+as3ϣkwJ>>,!{U/1?Xv٨<~Fа- .?(dd Lu(W֑Z9^1`L[6u/ `e\Kġ^Ga19&@(rz|xXr$E&gJ۔M=g.mspEʃ M/q[G * maF*ɩk9{n$cd#3Q}Ai={c:Һګ,_tτ<?}S^sS|jX.xXHl({huW8/[nX+Pxj5`h K!V"{ް뗭D*ネj{ksf^ bN@ 006JrFeF;Ap9>H­pS_n,B[|ֶQޭ?8Ӣl Q-4LaB൐!ѫI*b}#p׎AӠ1=ੵp_9$ pVFmxҭfKF~BM7ѩ|=qx7"J2C!I? LVU?u=󋥣dO%}f'K<<`&a0aA|wghB&ea#|u2y] xW7$$p92X2M[Kh\h`GܨERTrv݄ssxz,&A^Ȝ̴\Dd[ueyrb(ZlAK2*I= b)s4} я\+%fEs;c 2@98RGœ4xvwQC_~09O Ctc*{hwIvD"#U1wu&;iɠ np`чVGף:?K?xUWY$F E%D*?Ñ~죗#| _ z>oCDͧ$"2pTE}%)K~7mY)ŠjseijSؒk߻K3RB$xBh0%i,"O-X ]ɧdrFFY">CSoU379ϛGi),5Cg{lqk"+5C>e,RՕA?BU5`nܮ4rKg⭛/v2n;3mtdxPQo=`UzL_o/^>fr`t~w}.AWF6Dnp bM8tNkZ@w5"6[_ ⛦z_bU#/Yp.,:16؟M$/bwЧ]\W˹umܳTXG?ӡ% ^0"\P[iHhצs0wGK[>ƹyVz$mwK%Mt@oiRfu/4UG.o_k?ğ7J|!|otH5ѕuzœ3Ik/XBw̳CAQ@}da@kGYrʒ2-{G(sO%?O?mjvlC-E62q܈-SZj{R#$UoekBQK dC?ڽp*:z?*ŧ*3)ҿ(aU)ptԐV^@q0]ԝ׼>sy~J:/7i:߹uXLD>\?) ,K"Y" yS)fR%{19:ӧ7Z__\F2\1\3,V z)pW}K0d߷S:jhU"~% t+ d^htQT2v*̃Op/_ Wm62w#=N;N;6[&糫;AD~@r=^$4TmfxAyP3EC; 18xR Lo{?\7ߴm;%+Ĕ;) Mb'O\-xE;X^A4-m[Z7;N\~~0sw/٫|khjo[Ht|WhĵwA*̕[0wGj!s_Y+Z}DZpڍihYgGl+}o9e,;_wF‘\-;ƑVR4p )'/l#ۨ:5= .}8?G.9JET1I[fUjKOOv]~aRFk;xvO`<52IJwUdu h\@ٓgK1|)phpǬIҾ0>طR6ˠ$r(iI&8_#0 *("R?IB3w0p7O$)+PptFy$ 9q &03ɞ϶MqBĭ1~27C@)<8⡦ԕ ;[;u2Fqy0e)xZ+kj Znh49CoDe(}2!?jGظ\$]a"T]"2} oT>5!,dIVP(f/1)7s73-1/7pǀK7;گҔ8EYQZ9vE\#!*]1R:^;lm$~st±D:Eױ88 igF5H (ˬG~|Էa[nȱ eFepgl"-a- 7 :P9g~|Q1}f%w+T|mȿ6 F/#J}o<ʍ<HDO_0,k",o{ 3@8uza9}3Jo|v3MKɭЅH>3,eN85т ~0b-<|[M~(zT= F,Ei@9X 1,ydl(A?\dƷvf-u2!QqwE0c!QGr[l-4[cMlRȞsʥ,ge>h9$rwD:4]ycp߽hd.[_G% r0'>Do`!fw_n8#aǝ/TJa#u#qk`vsLed(ng]z G`-Jދ03cJ6A3 *RjWnㄴMp9-L?om&O52SMDPk9>v&Z" :_K+IW'2ܩ/ D3[ݲk+UI_}ݾRNncG6rg>3lTOZ|0um6D?\ZT "QW;VL(B Uf:@6];!>{hגێ Gn֩'G+s۞q$ԈMm/¢݊\Ky39@~ C5+P(YjMznyL_-1LT1)p4FhX#йԄsY#xNҎّ+FT#Nyݴ@x'TA֍K[+hhfA~~1p9 wwlVI;~WYDQk_WGHoQh,%|Z[&,d9-Z/DaWSB hˉ2Ҷq Ok ;sgxɶt7[W>@2"$|JwOjuy )6ҫf:e涑v%"[{G)K ONO|ӦG.}b 'KhvD4Y.cZV:v{ڄwO@n\,3ʲ2&^X/M :%-5Bc^s|־Oω ,Y);!9[Ȩ>Xʯ{#4U%{4j\G~b[^o \ڴsixw]c:҉O`xz.(;$EFh}Fbu"uU Bw<4|.q3O?`=zclAEg]CnQ#+(n <|pK{|rR9h?5Jt{U8Ayt"QBoA2%ץֆdÜ{MD.iGccHQBe|\̛ 8'=8wX2 +(rRߨN~ NwTkPZȠ tgO`Pӵ[}W4d."|@*8دHOtm&U7gx\-y{@_쇙Av4 o+-vΤ=IVG耿D{\5쬖[)wKAzۄ-{$m&X3"CĪr>ppi6o+Sv%R#-)kkH,/:1~0| r)RnW+qX9y:|^zG1oy|H$xixiy="y;vzj9cPt%rFB夕xG`Ѝs0.}+%ByY#KPBhNࠝ9W ?#R5QZO|`r&8@e@I 3y!B͛Sb%Ct5X8 ȘB 02aA}>DǠ_\jq[[b[;;M6w؛v_U`(򷹭nDv>QFқ`rzT̜ŵzYXe|G!5MiXAk X;ewad=E(hO2, ̿ @riLY2 |Y(0>]ȺU\M/W'^|$+m}k{ˡs/ht[:5NkܶhihHɹyѰKXhmzh@8rXcIė +*{fLhSƕZuLW^cGr{ѱmںd(8YiG.ͷQ6'7a\͵1Y+F) &2/Zgl`}FZw>tn\Dc2}[˛ur6wta13sT d6wYpuJy)7hYi2Q0˼i9raxaSW?qYcK*1\[6*ˁnzMsCuja(@ϒ lΖ}uY4'qi/uEgu;MM!}bs>~Y1?hRXbPJ99Te)[Ӫ=ET~i`[Pmǀ1mXk໇5ʳSHzVD^yS}̲cHgk0MոAew46)M[d#?S9Ղr&ƪ@plNj+0fྀDng |ZHs鶘Ql:o*m (\nf@6^hcJtϫ{W6s=A.H*!+DqG my":@lCn|7ug_rc2O?r,Q]ׯ M\lqL"ھ->w(UAa>.>dm^ۀ5jFŏo{Nk&#fi vR'\zr-(+y_EEJ¦pZ/] -5|؋rEŹ\5ux*sLaC.}yByg[ 1~x6QӞ$hree~.!u2i,_>GF˭֗%QٯiEk- yXz']LWk>EY.J'[ET~H7Q0ONk^o/;ں7"O %~4J r5azSfw/_Qj28" \'K#H~:χHF?.H^R#`8kh9+e#qsW aD~WPrbh(" `#ս֘6{lp'*#Nٍϐm{9\v:xկkX|wG\nByJlI:=tFĒ0jjN7y?Pe#07<3{"!ChiҘ("Ixj: TGK۪ӊ1[h:V9Ə "#7v/W-o<[N-D]l6{KЮ0`ubN5c8790`њhˏ"` Yrp@ٚ4RD;߼;gSSuȢ^mqζlhWxYKΰkWCV*XRYGUmT`o a~҇٥ظ,^L2z(U{Ij+1FAwY'7rl iȲJȅ XFA@Jlsi6m" ycmJ->ezIm~=WQ)ۼ#.0V߾o; 4߂?ɽ;K*cEްe-}%TF6wŘk>]C",3cp| ĊfZ&ye_u-iv׺O t.Շ5B"0̋wp53I `- *_TnMEHb':ZViۖ$\Pr%`y>TdUW_$rўw˖TɨhoT-dX}@%~6h\}8ӆLS|:p%,#{6K~ `.MӔEE) ,D,^,4#@n7 E#6=3򂇯 K 5\긾Eٞ2hʍ׸^7f_/5Pc8[E,bx [C.EfqIrLh-#0μaJB08=S(i<:G{ ÞdKL5M[Gڞq^ M ,:8CIpUD8} 3Qm^"}٤E: H{Ԥoaz,*霴-*'#9c{KK_4@a\>#/*M tD|"$ Tl[:i4qGʥ`(k*0yuΗ@D)ƍ3_[tu{jLV,9vmGd@T&|Zy>JsN{Fĥ,ciD#4%f*۪G^v1SοTt׻e/'v+hF|Hk'۵. H5 '6GGs緜u="%MOJEčxЌI|2hϺt-&Ӗ5yv@tv\O5?,:o㰐29MDA>?TFٞ (V~b,xQ˂g8#D,@7C:qKqpT 2_D崦 ov#RQh:LKu.4ءMmzT^Mx>y`)3?f,\ -~Nrn|$Y|iW >"+W\9ym |myڦQn]ǑU=ߎ+%kzV`0bGxbz5sit#y 2}8 :K"c\,aG@bq DF2 qQ̇~,*޾R%/vSu5t vR9[Pm_ɩC ԏeٱ7"ʞDty|8S q|GtV3_'}ok^;eJm9T~]!̓Y#3e>()=>#(Op=M̮1zf}m"j?O1l;X?l鯘H6F`)ʐ2`M?bCK++S5QZdFDŚD#et?33)\NH/ׁFV"^%~av]D=.cm`\ ^͜|}6N%9AE ͹C*p`E5b)m`eϏ:p$E;1t[}töۯg8Jq⪗<%l6.30Ss-/ڜOfŮEF嶷m7 l}^*n1;1mr}{Dy -F@-k4 Uʶ@%H4{o8ʡ hmԘQM_-~5('id'ȁ>SL'>f{ֺOj2lYtFt0?^L}h~ǀ%K^"һa܍.(jy|l)F@v8gw|ĸz׉iJ~噾޸Ąqh-::" }u+=(:sv8J Xn5>S5{[^JҴ"-ڦ]N'K̤lwb@O&>V]{rq x݆< xiŘ#ŀNp "mhM᷽BKק[hT+6Yv1`  )6,u `X"nӖjQ{' j9;c&H<.wn'!Yׇ)_~O* 2>skNfXk} S!b9Y[ڵ_G,'dp(8[ͤHz J fFǗ #ࡀe%1sKo7{GJiB7xKREl{v8KbVXɞW>*p}F/Rk} Eymh[fҞ٨{ρ-pC'vI/: F Ȗ;Jڡ88$oY ;qMUtk|l|mMiyKqiًn:T7\5B篢~TFq*o߆n}w|g\wNЅ Fkzy9jg=̴1 1p)|@@! <_ @7ad0k9 4 dA`z'v$O ZvK$jywٿd{C~9%itpR鸅%JOevfu9+sVM9U׈~#KlyOnu\4]#Y,edxLʌ+*ԆsCŋI !W]78XzTH fqe`g"~Q1yb*,FO#}G,eQGeV$uϙZ6ty˶Ϲ,q)=Ckb'v5QZeǟKv{~)>̌Q.W0=Iη~L}|P| ccZE}w Y;҃K260!)`yH%!!`4M&B w- 뫤3>VieRP/NGDa#n?"S0:=j| VkRh}?"}OUh՘I2b}J/NVKR6?, %^([>_yCV r*7qԤbDgӱɊm^#2r#F'8#c=c~t?ߗ'QB6X _̺{εܷoz>IIXU`*|@| go!gf5 aFM=CWUJY[NQ Sr6+Fe5sp::}e Q|/r@dz=EHΕSpzX>`{Fs;~e@ '0M# %Yz+A<(e[O" J<~)q(ǵ"{M` hEp6_9tgDc ש,\`y@c]7lK0lK"|)o(7 ;w4 bb<[_Tт/C\I|ų덝E~P=a2.4bAVw׏ :A@Ѷm|nP[UC]p!"ߩ!j@Dب`r`̈I26XJv9%%FQ_QX!iόG!lWh: F2_-ٌ›Sq#r#E;f.yΣ5q{8ׅt?ͶeFf7[8m}t#l1ms2N !U~Av< !j ˥;F^5q^@'$gIl{ee~ ^qP ]j̡2â~{ *T·G||:B~=EK(V[`l1V+f-!pYA"\#a5NFÃ7i>.sv)ҕEPzxIB5;6D{m Inp7K릒gf_!uULNJ C*Pkk 6FjXYnϕHP! 9t m#G* (#ZO=4KOhEǷ^F#j7GTI哜iD :0LJsvM2A|QJEH5a y|Wv}uôIm9ѧ^LlT^/Ehb8t`&~Dr# z) ]Ng)NtZKނʫOb*?1A]LD缒ZZ9\~}Mn(9cbi{vze)>k_y[Bq^'Kumk*[<6 ^ܨ>E**c'}JPD V`>PXW`(?]Krt .| 2˽ ms.}:k ?̮o3l=] N}skhuv1.En,cmO,c;& RYΙ+bWQI<뼼Mi`wӖnlI>f!ҿoo{v? vdjdҷWֈGe]#m /-7% ~:G<Ͽ=^9G!]EeRZpaau4gyѥʁSqs'3݂JڳNE$-S.h1#y؝V8=ʵSW~-~0M at#[Y!T}k1fzW M/#GId({*ԏ]s9% ՂO%Ti6B@} bMnl7̤<~I~X+jFnGb:Erc JUפ(_ T]s\==|g:[ VZoU?O{Gb{*oPR`5.EU{wۑ9&Vs->"}d2%y,V f*ULmDDOfrr-)Kok[yv`аl:I"C1Kcrut$Nz`L%v!] R_yYwG .. ǖ (>BɜΊZ|ϟ+u#\x6ĸ~ՙT>VbXRtKMxe|<4sqHi"wyY;됐vE;n%Z]A9ߥvۘhUR]yW}t.?Vв'8hiBqZ"4{uD P1GjR*iFd@ ◳5i"$0$"Vì!ݔj^QWUO>+IZ.b^ -FֻtanP>?AЍN?_(S?0ۃʔkï9a-Psg.2!gKlAW>R`;MH|W-uq~pC*\Lq}k c{U/\}V!7-;UO^z߼Y?xS>< e6 ]95t\OiXXuer'ϱE'h畒Mϑ Ni4_"1x%""*O[ p{veЪ+ Vۣ7PvZ:~0K%Px_ ]XnO6j7H9G{hmqOj`rHPAP54V=PؚMȐ( bg'ұ>] n@?Kh|ؔlV? YUˆ̏"ͨ9 H:cgCjr`?DkʶOkyɓ⬣OK>5$2}WgoQ;?ŧUJthz7.sQ~ܗf&"Xͫ:e޼Cuk楅V|Ok+㉏,1]5ntKI#a؋L~׌oOXn"t8fgW .;2 /O)61;N+2ak 2@cc HKAt;,]QUXȞGs,u XF$:Gjn U~g 7ѯ.ߏH%bp'9m<䰩w¡К?0r^o'1jt]]W J{ ( IdBr*2|Gtcdxcuo68Qo3vPL}0s?_mum8]_l^%gqT;f"6)@PұW֫qPycxipokyCe D!7'YӯhLL;ߵcwoֵ;ObD:c 2)Lj-&o:KfΗZuFVCİl{(GI6>zsƍZ,A=J`?:w݈$.4 u5 ;m8c [:?mKܖ ̼2<A?NFN-S䐯hm\vޝ2=_v/_Z̛ ؾ=cp=-xYSڛhi9{9{?)? #gZ Dcdn{&@䡷FEѓ!ሧF}w=E"?sat.[ LߛEO HjUv6v5_xOم,i})8DzM\V ;+\?0+IPy<'8C IDAT$@c Fx Dypgf >z}׶9nOB)'Rr7w0)7SMFr~E13$ԷDs[sz}wuO85.?KMat5?$"::'(m ?r9rH]tc{ײa7GZ9OAcƫ ?1 vJ7}Lߖz7cK'6 ٖ<ܶ6B! +';b\|'!JQ Y8IFD𐊴f)9s|9b#!@zl^lky`m&=.Z;f=_r=_K >3 + &G0bPAh}tAeN˳1鯨q+LW:?M IXM\"XF}ө/9t (XbpR&U o9lz@R1eUVts"^1VaZe>Ǣ~k("/6.Wi/#l6ט07=RfBɶ^)I[ @&QD:?r53 +/HrKg1p6Q1&Ѐ@9 0|U&|B ImoSK=ыWE P1;f[j!A8TdI4{s=#Ie+KxZ93l¶ɛ&?O$rwKk<334}՘1~j8]B;'/G={CR{Z\[qՏqyt3vXV l~pD~ d}^Fm+xoEg{EѾ7hn>T0>0'exvE %7z5U7DP{b Fm:m \ PaP\0Y)8+U*:}Bgˏ_9NFГ1f#V#@+Hos[ x}>fL>X*,5 XhleWG7ݿ}C\2ElNUz7T\?]Yn.$~.ߧՆ+Hht\ϯkez[2j##q1\r^tUoֲl,ae# py eD *,D!t)mփ:Ftѱ?GT38˶ 5v!!0ؒ-oUM DmUa5gA1^v;bǃ/iT2&EG-V_hLO"0Hپ%M%(|KAekf!zXX"|6<ҴCar*9z/.-'߽Pɶ!8!| va-nF";[ֈ$sW|щpI2T0 udďH2p3GNqQ@=m#89@/zz'q\oIڌhMxVCVͨ#8([̲6r)P'ϟL˛hm<mwc`nտIp[t_`p$V(9b% 5+5S -}j]n㆒rBPza7DVx8j0݇Ϣ 耢F`2UxQ_VDI|Oz|--z$o4XH'aMusN\S P.C8jT)GUjc}ϔ=+ 馻ť؁VҙR ud<#I3^;"\a 컿wos&]X^>,ֵu*/ 3!]~ҋa`s7۞s) Sj󜂣7לFޒsA/>?5d/}|Mb羈սEx>ګ7U,YW`*X] PYj Cjwxt̬W|>f( I zoA +S:p y:x>,MoϕRi}v_W*S.m"sn: WPBkRR<@ 9~w 'lN3}Ds (He?˧W 1>os,^'byMve>; (?e-\T]Klߊ%quqY;n9;١}U÷, ?0}*2d{ NΗsXrp2F÷x _S=[%:zAbCG r(E"[ *Q`CN! jv4z E7ͯ?g_YkLgfm͵k5^֟=]# zT\٪zN^QRC }r̿eģzr|^(B=>^")`ɐMmñ5wnl6GIx:gF^P)h+=]觺%G SN2^=Z_\<Ҫ絧Q$q4ȝܼs.؊O+YPi4h7<F9;F sif&#/ \<Ϋ6JAx^8Ty1FSrW:@@e HXá%>L9Q9%mJE{lz G[Uw1y =õ؆.;l KnAA\6dx//=pn\_GsEzY`s:"rx[C|/&Ek;L_z 4 >fq3Vk5gXL`eX@jE'$KR(i/l[T{ƌJҁ&A%|~M-huT_XH?J//ΛǏ˲&80c+nR^LXey@3);xA·d>'^,7߸R ̻Wȩ6goR<SÚ͉w8G&5"xroVܥ|BIQk5':Mve2) (U˝tnosw}Y䲂,KM(#6®!G6Q~]^u`E>kyǓ{b[T8@eL$u 8])`]/Jm,_gJFloXKK֦x7eL`SlXP4YrJ;0EuAd3nr9gkŚb+/,s*۲ǜ=j`*VdUNOŸM~#l7\i~HT8{;GF EZCH5$uq/7*S&s{?kT?>!πacgk#I[N蹛ϡꊔܗk7!C=~ 4hq*',g3:k ٰߎh[׉wu@Y^`UDٞ81Ԟ~-"PvF\}pCtG0vMmSF[|IVDalt ,J=RwL$8v|F5mQ_@?|h\Ed25xP D](& .?'{?G_+vHi| r"HA؜j0_sό;R6Fq EtNkqKOXN#P-6/Dy!sD>[9 b+N1:Km>O*6/&J]07 ?gp~__ܹz*l J.ⲷm ]-M-6oS֬OQ^Nہ,y4vƶ~}/k)#-5MW=8>ӬCGҶ4_ u7=1`jZ{n-~ؠ"8r9ZoJ+қi^묞ȠV[u?/N~`N~jxOFlWm6k5VIReݞȣW ,Q|Exa􍺣B{UGG/ C4|>g=≮142r3ݓ8cʇ_O1RvSCiOеc<[kهq 8JCey[}kիc/ IhCcڦAHFĐ/"t7m"˼1sεȦ{͹[c5W)֌J 0 E2z٘(ŽlׇSxmwb;H`ֹ#IX1Si=P(VX?i6bQS^bedOk0*TNXg~L hlr=<ðgܝAfMAc$Ca: ]*ͱ=vKx}s>~i7@sDuرp$3;"!!2dD0jD{&uHz{F.[@]#RuX[[5WQ *J0S;]eIԾQNϹDGq%l8:q~Lbk)Kn;s1!R񄲩{[<5pwqENp@gLK2QT&`|4DӉVV 'JcS'\T4|衇¡C駟}o>RЭk5 = -|^HG!{.βuw5=<{Q.q 2^p].|h;&]bZf9%= Iăkb$,t]M8fvj|X(Ib:O%!XNdT%Qz!DtJ&$rIb? >.pb$q[_(X>^zR4B P4eW#.Em4,`;8$ &lH&HzCR|q6eS_>쵡JKN%ls@"[ huˑJc͇{ ZbV q ηTT"ZX MZV_BodΰDܾ)X%f@O &L*iKjsisQu4QT SI=-lzmF?SF2e'-ZNcf0j}Sr>͝ $ÅVe.)\^<4:mq2~j8e eoak}7@]*^}QAg{CbmɃJޜ2yFv#h4R/}o04R fL.}O?X$YOoStD-Sf<)p_+ S}j=㨱_7}BAp9Y72 T(U2Q\";0fz.$g|+M+ڋ9eq O.&|\ v{;F;ך[{ZNxQ{ݥRQ")@Ω3bSj09ִ_ ۜ\v~d 9 ~StK{pu: Kѣ/IDAT!pY 7x1MxqGA^1oq[z$f!lmbOx y;G)Ih--? dDXvS\Nm_A⭠mK5LB^:q=Ǚur4oⶤF :;Aȍk=Ag4qKK *{XQ xPF8[BΒ T\\"l.sLpד!qyjyHӤ;SDE^DbeK߮,cB^o`.lqhRqO_txyRF`*KsuR`rQm4?fzV/zntQO)Ov*F_iJ}Z?/)~2z_rz{zvvA=Oj|B6X#dTb)/*{g |%|r=%!s9_fCeq3L|;@=? J{ Fk~%B>O\%!֯ IHٖjנJNגn#%ENL,kB;:dDZ!H\ g:J4\JU! Mqo:Call%VmCwy \Rl[΂=fYk>[JNL?ɔ T4cCSZ%FR^wxV5%F(eB<Oasr[]"s$)YKeNժO4uTšJ>FH4'8D&mieZ~MC^5f:HTK*wS%E'<ݶGó#Fpu.Rɓ; e=հ,??r9V3Kھشx:0#=z0@j{AN\b=¥6ΰPK DJQB%0z zID,E&AK%)TشGN{:L7%0(FFhN~#)7S9n_y"Jx{@syx6B<2Ea\V Dɹ{ϱc+Sti (zD2G>ůq<Ԭ>e (T龲Vإ0rҘO%xK82^/cpϭ+9qH\u>{+ƨ2ykSoXm[og!iH"ߒq;ֳ2yKR/$jv$eH`puRRs6mO$fɍmbXqlE ]$5Fe/yLiE:y@޻>~5$IzQN( _̌X*HŔ$ ]ߩ-hAav+Seֻ۰~.3'@ϰR\io襣p9!ZHk[{|ݜ(*dIǾ͟M"jNJ3"HDqNROQ'I|S QR LO'3c:e[.<3%iE9toDs`  ƧC.+<TbX{e( l *Cϖ~?(gJᮬ! =+H(J%.UELF 46`xi`38$r2j!=SL }.Ы(VM^V׬_{ɵD9~Rпdig ;DBa DsCF $D 3Q_`&- ?6BjVrmr`:8Cp F;D`xhuM^he3zq9OY>pGA.g e!*[ իjԀӗEtUj"qy CIuZ7r䏅QjMEƦꑼIM\|HV 2$&>)aŦ,uPÄwgRK"!» ,E631A,ˡXzhsņ8LuOT\`SR h7>7Dl$ z-)UYAd?h3,we$!y%cZYKzkAGjhݨˀA+]+e %W+ʯ@zO1۔+Ѥk.Gc 𺲖I$C)RI,ٗ}͠Y~;%l(-5I/yEK0mYA7'F"-?}pnY8crɅ/}4:y_o'PPˋ[Oܥsrf-g$+ђBL0?4^.uU̙L!Cҙw oaJf)Tq6;7ؕ2ԈHa\à rمw%һSYsPF nV'sf!ʅ 0rL5lv@ -4Ќ3,#k%3Z`@~nf4w4c\vѤe.`zޟ8>$[Dɰ5j4n7nҨ & OuMևDnǤ@,frxN#:l/,hG7BQ6%- D_ "/5`aGQfi'msSa)f.sg,?8gغe^ !G.kTmkd<ɓr"_V y +{74Bq"[af 4\,D~msܛ[DDg@e\vᇕw#"-WX9G\&GD0 S'F!&'LY ṜOfg'v oaz0E^,o(#@> PtJsXNXl$"CD]``ϴ^r9ًbTQjo'un{h"g3@|!jAkPS!pںRY[M/͓ޡe_gSƄڸKCOCHiy'GzѨu@yȋRҥ _x -~^<0~BD_\|]x?/8colzu ~L 815AûTFe\v⑯7܀{),`ldqPۯH^pҥl*-?~h6 `0~3Q#ϟ'xnرc `0 C\|Ξ= =AY5ri0 `~=`0 a`0  #`0 14 `0eE؀;Zرcٳ~0clllw ]vW\2k_|C g>xgJf)?Ç;w'?I8~xf #᳟,(p կ~;V:YŽ"'OOp +W\a=kk.np 3Źs}oﳋ/M77|3<䓅"3l7|W_}^~ҡV*kks\ꫯŸB >ӐCb pUWݻk ;wwC-F.+s=we'??_",`Po< |.ahz-?'oۥÚ%W^{ n~ qwKѣC1GpW}8rH #`(5x={0c=zn68wo}[pe{J6;;wtf'_0'ñcot8뮃뮻<777`׮]%Cle%G? /"}7\",`P{-ab޽[oٳgK2;z+lll/<\{Pd xamm Jcئxꩧ+ot(-W-G7|nF8q9s?n[p)x"\p6O&X>|vQ28<nv;D"\x= ?8{nwKf1>.nfye4pYKg>OG繪Mo] *Ü#OSx;\}} o—F. `0 b=`0 14 `0ȥ`0 A F. `0 b0ri0 `K`0 \ `0``0  #`0 1?ިxIENDB`docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png0100644 0000000 0000000 00000030763 13756501735 026603 0ustar000000000 0000000 PNG  IHDRRM7_sBIT|d pHYsaa?i IDATx{Xu ` \L4lxHM3fAkӶ"(Z7ӲnkPn.b( (ܿ?D~~|\WWq=3OO{fleY {T%!.` q cKC\01%&M$ݮoH9Rv]vdըQ#5nmnWHHg%Pv<==UV-+fs>β:(fĉ%I.\b _^[nջ Y*}c$("##յkW͝;W_ԨQ# 2iӦkqܹ7o.˲m۶zUNUZU 4ГO>Gz{۶mP5=zhӦMFg.Lsv.^_]zU _ \gݺuݻׯUvں;U.\N:)00PުWzO>Ϟ=^xA 4Pjt뭷jh" 8PM4?Q{ys)g_^}NVZ2d~Bok˖-ٳ}o޼ZÇl6yB?00:|uhl6kEz,_eeYҤI!CΝ}{=h̙馛n{U>}_*33Sҥg~guU}q駟VPPu@3o5kaÆ)77W۷o~:oo_9vT"j*fwuz+00P}կi޺J*;vrrr7owgUV=ZuVNHHPHHz?MhMDFFnݺ_o߾رcլY˼4x`?^s¾6Mƍ$Yglz7e۵pb?_eؕ v}G2dknզM@֭[}vt预뚽3j۶mvͦؿ~'Oo>|VX;jȐ!֭t钿oQ 6LZjk׮뮻W5k,4t.o瓤ÇkZvtyrrrR;$I oԩS\ڵ+rl^#G8mOKKӛoJٳg.9}/7nСC_u 7((l6.^(Iw}QFiȑS}$Io5oTԩsu3g^F<=/qu{"IWLLf̘ ߗt)MVI&5uTM:U o] $wy:uꔺv*!???yxx(11Quܹ]s)lfk]v…mNRtAuQ#G<==uI͚5Xԩ$edda%Px{{{Qv_ r~~~lP.SRR رc=sqg*_yfShhvء͛_v]>}Y8qB7nԲe˴|rڳgJ4ot-ZHaaaN-]j.jKuAM4smڴIfͺuSRRt뭷~{y,(9ιʑo]GVRRoԩ,*\uu \vEmܸg-L[!!!1c׿ܹsZjUn#00P'|߿_{-Le4p_\S6lP\}.W}¾PRR5jij @93aUZUoN:%ҋ=4~x߿uΟ? ;wVfjʕNΞ=[,L%:jJB+g lpeTZ׸qcYIZzϟ_`\M.]x9]6o<߿m1yԄ+;vԩSyYf9}}nn^xYGy8P|z'5k,zլY3-\PFRVp8t뭷… :|6lؠnI?c,X@=zpΝop8 Fqd&x#?Yҥ6l*Uh۶mWÆ JgϞw߭`mV 6Tvv֬Y}O>kk3f>C ֭={hHÆ >.o[F>+(N{rꈤKuP$>|pגɔCeqyS-K㼗b,o%tS%\+7eɋ5w]ZBygQSx+"C\e΍oRwzeUQYs(eެ9W#.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\Ⲍ뫑Ӧո8}/rr4$2H}ի()I=G5kp(}}i4;.N_hKNFq;`-[ ژ xxjg> 6MK;%EGrr\\z<>^?HNxbw_],nPѣM+V\hYEn ڽ[+gOyI\Șu:ԅSGKy* {yUVMѓ'iCs#"ԬM-ٶM[p(4,oͭ*kC\v뺨8@:kՒti1|"_wڸ|Ӷ-11zltVTGRH޺XwթTm[F_&&jظqz'ΊȡCjnbM:u$Im~%3bzxɿn] |jݭ2Oԯ0a¼y3r|nAgN-[j箇r9sT7Aꞷ6icyn;ݽ{˲,5JFrկ??I:gf4H}\&M$[ޚ} ?,K#ǎȼ-^^8P~p|_,KW B1oM6Upp+GUz4uTK۷o߮vnI~$JK;uڹ{T mۦm^s"sYVBLOOϿ[oO?TUw$nd(wWff>3-Rz7'r* zz衧zJtRԒ%KN@TO?+22RjѢ-[j>#ŊK___͜9S3gt<(NJ|%{KC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%ttwQwJuwHv4c_1%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` qYFyӦiv\NIі,u0@Ӗ-ʄmʄM^X\<5;__Oqqڝ#99znzzm}u$'G\<1;___M6Mt<%EsrY5'IpBOIQnFpbw_],jR$˲tݰWj=yv847"BڴђmԸE Wr? @ݪbF?tegkmll+ @GVsUREkZ!!!Ϫo~JIIQܪU]961P)V-Ix"_w\߾:m7D 7N=YQq9tH-ݍZuiz^$鶶mdFT,ޚcXs>ZjN;_$iݺuΝzctu%3|zWZbeff^|ES튊rl0f)b|sgE r+.SSS .80o4l&=6ĸ{0&--M_ޖV#zuzxFtII?K3r^}Q-]qݻwo/n$ٳGB%J|%_WM7O}FԔ'P[yСC6 6L7oVJJCE}.H"DoϯWgC޾QCԲ8P16V粳а0 Rʑ#fRQ…JسG;v̿ ?;wApW]Ӗ-*;;[3`X:)9oI߯a&;ڷWٳ?/͇r$IDATrpW5\-50o-歹 S [s .1cO+ĉS[սG=}_-ݲtmq}U§ SSSUvmM4Џھ}ڵk|Y JL[-R&Mt,)I,PhXXחWABχM>xPKAQwPmNHPM(9)Io/XAaa__v$''-^^(J$$U\&M (,,,ݻ|||cEDF*>>-u;kY'i۶mj+DA;(-ʼnK01~Ut>}Zw^}gPy{{Fr̘1:t萤Kγ|r-_\6MjР!P>;.]1*ι1%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\x%N{TJ7{Tq_1%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.˨j9m^G))"'GC"#t?w^]ZٳZ|^[FOW6MuJhtݽhڲeZZɋ~PFyƚCizeuC@z-//mZF*uk݊?^={jΓO*EĨС.]̀=Z^^[wV]ϫJj~\DEi~]̀JMuPLLTz'\5**\9EaY i۶mj۶5@^10*[1ݕyIJ=vLE75*0JW .+0&ЉusӦbLwJ^ƪӠQPIP8^LU=%IΞՌõ%6SP:{.Jk9.׮]ŋkӦM_t7}߽=>xɿn] |ɜ b͡4q|"|'NhjժN83f讻իROb-&$H$mĘ=;,~܄ya4qmq8Xs(M_q9{lծ]iPpp^uؿe: TƉTDtW}TqK{T9WgE~Aϕa)IjѢ9bt(Fnݔy~- S#5'xA%CYu'##C۷oWM̓hpkԐ$5hR$ml={4:(HybwT]:&o}#FU׮zo"A1*}}哷Z}yncleg+":Zaau¬Y7jXP {uǎy9g0(Xs(m_]rرRxxU+o͒9sTaCI>ˠA2h,cM(5)Iv]6齺~;u8PO?-nЙSڻŹ᠜xyͺ>h筻MX޺NKxw޲,KFRQn3A .ǁ5~Hr/'DDDhʔ)={ƌSr>kDT|BJ?'**JSL믿^hXr*v\FEE/b&SŊɓ'+**JpL(3fhĉr8yf.|)r\f)..NqW"fŋƇ@R丌wJjq0%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\01%!.` q cKC\0,~,wJ&5Rƚ;p|u=ⲌJvtN{T:9W#.` q cKΖ$eJ'I|J䬻(.J:˚C)b] wPq|-̼gee.DIWA%P߿ZrT.]Ͳ\響Zz5j$oooWJAvvիW/\s_%*^cKC\eHffƍz[mڴ'|Peff_TϞ=(ݮ(w lڵ1b6m*___կ__kժU5k?KPFF4hSjժUZx5jє)S=^[_}zKꡇޫW/ݻW:iii ԤIqPA?~\kvvuZf&CeөS'%''СC¡Vʈ+VF4hGyD4* peXJZh#Ga"TVjՒ>R#.ˈ={E$I{uXrھ}ZjQPY8qBsիϻ{ d/#\`P;VYYY w(z)͛7OᡷzKO=KDDD?ٳզMw ,<<\??+WSvv^z%wVeDZ }v2===rH4e3fAw-[n$9Ir3j(s s.ˈo]?rss޽[Ժukw./qP uA999JLLt(qYF_Ϝ/ZHSǎ45ydEEE)""BT||<<<Q*~-^F8CO=~Wiҥ?%KpUV̙3:}KLp/9vx`f̘'p͛.뮻4*\~~~Сn&jO/rڙ &eș3gO?TjѢ^y <ݣkܸqllJLLT 9*}fŋ0*E?O?SNz;ciСB".` \01%!.` q cKC\>@iIENDB`docs/images/camera2/metadata/android.tonemap.curveRed/0040755 0000000 0000000 00000000000 13756501735 021753 5ustar000000000 0000000 docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png0100644 0000000 0000000 00000052350 13756501735 025270 0ustar000000000 0000000 PNG  IHDR"sBIT|d pHYsaa?i IDATxyxT>{3Kd2 $a_$I$XM%( nRTV`_q VKiRT0-V$$  CɞL2sf103Ʉ̚ܟ9ϙyr2ssBeDDD2 "" w K"">0,$""Ò K"">0,$""Ò K"">0,ގ_W7oF# ֮]뵽nGJJ ~i~n &MBtt4qWԧvڵkS{ۖ-[P(pPw0%F⋰X,+lgW_}5`8~8NgE}}=?||}@ߵk֭[ǰ ?C:BP(`2`4Cy]vmؽ{7K@}}=RRR\ttt`2e >^?{p1=׶D`6nP8$nF$&&_tBP࣏>* ( ʲwy?ON:ɓ9~ڵ{cƌq~/;v_œ9s(̘1۶m: Ē%KTtMhmmui{\wuHIIVEff&^x~?E||s|6mv܉ٳg/2|MbѢEغuO~'⭷իo`ժU_5=\8pO>$>,\wq֭[z^{-[o~iZ W^y%.\wygƽދ~ؚ 77_~O ovx:~i=z<tvv/5\vJK/w:++ ݨGww7oߎ QQQ. \\̄Bj5ƏΥfã>ɓ'C@VCȑ#p_^ Z3jMo稳{W^ FlEf;wĵ^ S|s{M7Jë V뮻Nu(|M< 矏_1cnfcbbk޽UUUu<1{477Ce9{hhlltg0\j./{=5L._'%%|hN綽e[AA^x{x9s&T*q7 Z`p>}mMoHEȲ;ON ݎ > g}6=\/ ݎ^{ W\qg;ÒAFF#L0w_mccc1}?Otvvw PYYo%'|> .R,\;wbΜ9xxON xg|r: 6l@~~>f͚6moé}Y\tEíފѣG| }Qe]-[`ҤIBii)6no Z|e[ssb 9%%E/"D9sQaXR݋˗Dgw}7 MB̝;7]˰DDD}|>3,ZiiiP(xwH:&,/.>cL>}|I]&$[msg$IBŁC8p( } GG:::P^^rGCQUUqtٲer ; ^~e}!?MyGϠ#.rdrϞ=.{+._[lAmm38GBZ /c'"" I Djᇀ[l62|{QP!AgQQ\)20 mX^|-[m9s&^љFqv/UB#h0uJs":Q먕TP!#-K/å'z[؆% {ʾğ$9YE+Jl^/z} Jdjzg+OŰ$"v JVMZ$ EۑhH1Ž^BވhMLHaXQ3(3L̹y(b'%8p$|>/<.FĨd腨PuEΆ%"%;+<V( 9772K H6A pdIDD~dhnBلnK7lԂkрDcG#ZeCaIDDflhjDKW#$qU-԰6݊ZS 4kg$y:^%"3fD4thS% ΠK΍8OJ "8Y,"5-`3Y#K""7nEل^8}9G`4`rRĬ '"cYP^cMhnscJ2\vshXPS]K/GFiGDD'M[Z}>F/DeĄ csP\)u5 f}P K""/Ovf4imu5H2<ѳ?="Òhn딳";w*b|CR:ZJZ{ pǰ$"¼ۺ|Z5NĨdhպ@w7dDDC@mU@8]TPt?DDCTgn 듐Oz{A%r_Ȳo*@Rc g!%f J#K"!CetZhG붎A.| 4% wjHZlnN3x'a)YaiGUQԴU9\GDDc$ߑdve`* "=[o9z Jn% #~`Ȓ(ɲ ؁zt=J5ddˍu[aID$InsYig !KRQF$" cL=_pqjt>!JH.Rm %Qn_`O>KHRDDah  Iz?0,PqImzB2$LIP oʲ B c0IĨdqdIDFv j!g}Y5Ac1~2DDa@h2ף,kVc2xvB.QMhnu[GDD! Iv4w5Is?GH%4M8TFsǠ/u[GDDA 2ںhMt̹眇cg붆Ò(5%`2htL6hT Xx4r1m ^|laID$,B.2'].&ZTh:}J*yBaID4/t4:e*s6b 1>^Frt*BT{KgaIDt-t^ZV}e}.tS(oJ<y4hp.tPor;V"-n$F$ePF,,tV HNA6 "ݧ~ȒlvBJ )11&it İ$",hnƷz[l 鱣7@[#/ځ:X݀ Z\+H?szau8Nw%Pđ%]D$ں[1,hHev:Oj^P!): $( Ħae7 K Òn u;l zP)UnJ%zD4^D[au[[=2S&x<;Q{­"Hr g {*$aLIv96J$JMyMO؆dnGjjTy:hvɡ3fxlv DJY!˞"I6ԵU3 {* z>}%$l ? ***j*TUU[n,[ k֬q_h6oތ/8v/JYsD&2Zp,-B+l/ŋ؈ףSLAaa!FrtCPlj'`4h"<#,5貹N`EDarJ\;v|VCᡇ BψIY.=A C$gEB(Ò"G5ޟu4,˘=_:WaE g K"0]D}G-:m}3ḆSp?öm`W\4h|>7,ȩҁdOɣz= |[ycG2(iH`X$YBCG&:fVopô{K4X2,%Q^ nZPC-amz=ޤfb8$Z|rQ&<Ȓh;Q~bI"whd Ǡ~73j7yjP$ 5Y fk'N(yZ3#g(.$ y KAN%:O SOB@FZ IDAT.^zDA-% Y KA1(өHMXDTB᳔41,!I$ZzmM"Qo`u^0,&M `XEZd4t6LNEGD>cXE O&ǟ5ٹS{5SGaXl:GDİ$0jMV{5$p8QoIЫ0*q<Dg#KR\ZEr0$Dđ%Q$ Rk[H(^v%%QezXERu}ךA!7_QK6Զ}fK3 {jM&aI| :m5IBCdYFcI4u\$QhOO0c[Pr-(֚$ ,„,h,ym9y|&aI5h.JtL=&aIb]b'j۪aJFձ$Qp0,BDe4dn赝FŰ }zFDcXh6su0 RKDİ$ w@eRBjl:bqAyð$ Im^h ̀ Dg@mB˹-gz6*+@$T8G`4`r)<+ovMDgډo\)_09jX,q ʥ83QȒG, u^L̜QiQ^߹F 'kffPE(%$Yhzmbk+m>…9!İ$fAM[v6JЪumJ*y%Q/:,mkd&b3uDÒYhGv$EYRhcX.PV ᵍR Dkb3" %)m]m(^h:  v%2D݌5!{mM@jl N!J4dI&BR\^ۧ G.' % 95(.\4@7qrgØ^T#-n$BTzKDaICʞ/tiQè8s01ss^#VJP/4dTTVE4h:$p;d$aPG4tlweI97 8KhU8Io~ܰxt !1Ynڴ cƌNCnn.JJJzm҂n ÇNCff& [ W$|Lj? 8F `J 6`ݺunۋzSTT.* )Y9ӳQYq$!=r 3( ʯ< '|8Ap䚙:XVh4 _kZUAxdYFCgץ&fN}UhF 'kfFFZ),鰴{Ś G Ò"RsW#:jGCvLD_ K(,Y.RizǏZɏ7PĐd u谴ymuJD KvɆ*tټ/eKBJp+<)%=nEu^ INE>AID$Iu .{R, HS}#D5 EQjV'[X8tG3YhGsw#OX,I30{YZ-gN4k&ۣ#~;l6z=p98:u*4ފFs=̎{9ӳQYq$!=r 3(J%feh[X=zMMM(//GYYojطoގ"\cA Ɣd/BǬXł~R##-#%!/aP( NSbҥ}ǎ޽{Q^^Az3' ѐ`dMì J" eo)nj1c'??ފ"1u\.S455o믿K/=N5 =?ކ"P{PƤ1((Dرc1vX\s5oٲw}7&NڟoGal4듂#"3zZ?HNNƄ 0rHF I`݌ert*,_~e׿ƭފ3g ӟ7|[QUTd;*ZQR'ClS\C19:IQȿ< 6l؀뮻<nvHMMQ)GHfm8t0*+ L̜AID_.9r-rۮK/aƌX~?ފB%(Z-GAuT$ EPo!*AID_2..^Z ۶m[QlweIegW[#s$(? C4+㗰袋׿ѣGg$IB2s A `AQx@b/Bv4tz^eIP HD4dё{{ŹfAm)JŏJ:DD߀SLC{s2z='@VVY+%;N}I hպ (b}w^b޽8pdYƯ~+g ;;999xyYQ=DTh+"ӯkkZ?яȹME߿%@+|(**bX:bH'GDDi7A@nn.rss_`pA]hjO#56z+ 6j5]֍%vd}TJ5FB3DD=qVԴV,eCFBb-"S1,IPVl?56:!*Ƚ""  !BeԵUb?Q8]B{EDCD65H泱DD0,vK+iTZ W"^0,nEOT? J*Ƚ"", A&PZTiq#QW!""W AQĹcgH(ML{EDXJb8E:8B3"İDkQ\eBE5O08yeg" ˰6m˜1c鐛_B+2= {ʾ>ݥ_@^m _wTVag"3auVu]PVV<̟?UUUwF^^^zZ5?B5)Z-GAuLޑ$ EE@XSO=+VoFff&y17ozn_uaرAmlweIegW[#s$׮DD+lVXzya׮]^[~=F#VX%[ͱʍ(˙L$/Q^qƹjjݶ H&šaX̜A "v~R1ɚ{=SrB7$DXEǢ)ׁ 0 hhG""27io((058&j!}$"1,HT"'bOJsjq|S-l jAiY7AID&m0$#̝?6цDd$u `[pJuBA# JNQ0,K4{ܮSG'DDt9, .BD)$2=NRSOD);Dv]',Q$aXPJ=Ò(0,='Eey_)(U\0(0,.`[ܶ~%QaXHq;WEetsrѠ O{ %4*]zCDD P(B#""eXlݐOY<'krIDATE&ex}R`"Hİ .[:5O'"D tRAT7DD4P K?V$vޯ$"\ K?~aID~,$"d K?TKTCDDaGl]bDDaG\,7mڄ1c@!77%%%^۾KCbb"1w\޽;h} (Sst"HauVu]PVV<̟?UUUرK,Aqq1>s9É'WY= z(a}a[Š+p7#33< F͛7{lcʕIK/A$l߾=}-dmNKDDNxcZQZZիWl7ovkf$m, ,붶6(EvX=n(zH x|'mXL&vlOMME]]Ozjcܹ^lذ֭s^\\(Gǧ !%m{I`8Pw!"<7x7ct'جYί0b̚5 ~~v6/A)yBE!??y ϓox| ۰LNNJrE׻6OOG'|^jZhZ I6&zPs2'6UDD(YanY왈hpaX# "x KR⺍DD %;vvޯ$"|gKDDC y]{4V%j(21,π,O z.ND41,πctm;' N 3erÒhpbXo+8{T{&XȜeol:q̡rS?JPaad^x8fK$hKe C`(P}mssڞ+O?i޹{(!1,JXޠB5DD2y<D\Hq1""bXJ|`QoT""RR..wxoED K BU<KD K …%/!"uz !""1,#rMXi24CDD2W&)\ a!Dȕ{,F+: =W8i,%$*\ aFgW%0,py׃AѪB5DD&eΠ1i2 _iR+W!"$ I\,׭[{VYYYطo_mۆtX,cǎQ=oNjS'OcÆZq1(1tX~'Xx1-[Ӊ<uT444bɒ%ƦMcXvvv3}՛.7r2rܝn| ?8B7n˗a2.'fOҰOҰO{ay%tuuaĈG . .ȚV›o4ƪ҉He/_olXݺ̜ǥ/))ҥKW\AJJ okIGGqy 0@rb$ $ $իWq]waȐ!qc6, W=y睲bb 8p ߐ 0=}}}v2pfYYY6^__z(L0!h]'"""f,`ҥp8Ƅ ~zc…^xFªUEEExᇱf̚5 }vލ2(JKKK." :eeeXv-olڴ ƍ{0xꩧHOOGEEގjL2E L4 FcL/*HIIINkL̞$"" K""DDD0,"`XEɰTDNjjjcE|_lb W:l2m6@رO>H477a2֭[e= .6fEqqqsӧO66m41w^Qmr{t++/6nŌhzEnnذa7o&RnEjjpJ3P<Ɩ.]*&Nk5)a ml\: GԩSGGMv~Gq<m_?Dۧz wq zĘM>sL01b222PVV.%JVE4}8q"=8ڊ:̘1'>Ï9#q4fRȌgV5j&O'hO@mm-(1&DӧV|7xPWWgϢ^oe+.>͝;/^ĉ!+b%J>ý^/.]&q4~}Dk~wyfٳVʋRt5<󨩩aÔ*/fy?|> >ׯ`@VV~7'lXӞ={rJ[9998w`ٰ|r%ʍj' KϢڵkQVVݻwcرY駟~B[[fΜ|шӧOcѽ[ y?l6L& XZZ.\ ܫ5!>-_w}~:^~e,[1^ 7:tT7yȢW_}.Surdqq455~|I<#hjjBrrR+*Snn.Ν;gΜ9͖A Dק7n`yfo}gggd2I Y%ٵY,^XW !p8ݮ>;p0 bբE^Z3_ڣ5k,n*~ϵkz ۧ[ijX}joo-O_~>|xwz ۧ+V͛7Vk.1zh1g^"]&Np:N3bp8_YdhnnTUUU"%%Ef1~xwżyOŘ1cdv]l۶Mን'G)))@ϊ+/\arKB~DNNX,"55U\Rx^V>y<QZZ*F-VHNN믿T\9~mo͛'͞={Dff0ղ$""@S,$""aIDDÒ(%Q K""DDD0,"`XE$J cǎN z93fC.(nq;rп 0vؽ{7L&4qWMS$JdǎEnn.JKKnŇ~Ȱ$%F<ŋ(Q0,ѣG ٳg)))D(D ¿gykX:u >z=y5J#{}zPlcc# ^(0,ɓ')iyVy^rرcG81,✜>ǎ 3f@{{;~G%?0,\AA(//8̙3~z o4h&M۷ÒHCa2ׯٳgcݺu%0,4t"==%=n8(5k.]>}`̘1jJSQܳ$""aIDDÒ(%Q K""DDD0,"`XE$""aIDDÒ(|{"IENDB`docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png0100644 0000000 0000000 00000036226 13756501735 025665 0ustar000000000 0000000 PNG  IHDRsBIT|d pHYsaa?i IDATx{|Soڦ)&P NE, Q."C0eV@T.SmACT>JQpN@ZD@@ʠ\M!iy^_~ $cRT@#ԀXPb @ %5 ԀXPb @ %5 pxbY,_>Cɓ'5c ^ǰk.k͚51c9m]vbhŁ "D:yfΜ6Æ ڵkqYF3g$^kjذa "TLԖ1FNvf͚Yf~|f?y3'OT|||jg U c]s5jРRRRt=Tdռys5GQzXVRR{WڵSllZjɓ'ĉNX,뮻O+--M6M?#3gΔbbo[I^qҥ Oj߾}QUTT8ms˖-7܍͛5x`5lPW]u&O뫤e7|d˖,Y^z~jР  x$_83ƚ4O+WzX,3sLz^zѣN9o<#|cN8a.hrrrʕ+͓O>i7nlJSQQḭ$ӪU+n^yGIfرfڵfڵfǎNwOMBB袋O?m̄ $;۷oIHH0mڴ1/6fԨQm۶FYjUsgswIfٲeqUwa$˼駟6͚53)))km155,[̼曦CiӦ&33\wuw5/IJJ2N?6˗/7yMnLǎMYYcӧ;u}>ݻ;jZjeڴic-Zdrssȑ#$3gzd{9toI&>c̪U>7܍{̘1jmۚl>lڴH2 ,p?f,DzGyX,sw},[ԯ_lٲǃ!pQU,%_iktq/43<^Ϟ=MFFrvvrچ1,]H2eLƍÇ=x`쪊$g9۹sg3dX,_:C 13gmc̶mی$3a}dZآE sqDzzH2\rSΝ͹***n7w6o2bNy嗍$K/9b 4hiԨ9q1X>N0as<{H2wZ/;;ۣXΚ5H2yyyUSXJ2-rK/iY7o6SXXhbbbwޱcL-M7TApX,>|Ӳt޽qk׮s=Xm6}}]uE\r p֕W^&MchѢzYcեKui#F׶WZ%ICŕz쩴4}ᇵkmq_rZZ$iСX,.ց4~x(&&FVU5rH7tbbbP/Vnݜr-*))ƍ]\^{t:uJtYy_uAh}O׿vYvmi͚5o˞{9]veҥ$>PyyF3S}uG,x9-l.'~Zv{N6ӏ?/RV_Æ eqy[g&$$,l駟䲞eQ\\,ciٲڌشiS˱.VTThZl??uIӘ*hrLL\락u9wl6Ә=y.k _Hg+**:ݷocDm籮+mڴI/֘1c= lEEEjժryy]n^+T^^Ç;ӟgf\N:W'U*\U=4i뮻N/~a=ssCgtRǞ=|=Kx]&Mt^лᆱ"C_R}ԣGm۶q;AxKW_i֭N_{5n_ոJIK//жmtUWzޘGOTr|lTy_~믿r]qN˷l٢M69-{W԰aC]z13/٣g9tPm߾]}QT_~|vۧ\Kt82dbbbw߹ѣDسO~Zd.nɓ'7/~ eff*==]*,,Ԋ+t=/v 6Tjj~m]uUjڴ;'O֢E4tP͚5KIIIzWk׮'|RcƌjUǎձcGq( :Tv҃>effi;ԩ.BM2E5mTͲeAi˖-zխ[7[lkV3fPrr^z%{jGsJJJk^TsZd:M2E={O??X/5`hBTvv4iT}ZlY;x`nZ&LPQQ!XLg͚iӦiΝդIU~}͜9E |PUg֯_e3uib$iӦ<`:vhbccMƍM׮]Mff)**r'L8}\tl6#Ɍ31{6_rcƌ1N˾+3p@g6mjƎky#lڴX6uTӲeKtӧc=f:t`VILL4zٳgk3ʳ5~1=c֭[͠ALÆ M&MB3J+6l0Ç7 40 64#F0?vRSSͰaҥK_lbccM۶mMNNq;gasm .ǛAuI'ts?1&M2mڴ1V4o 6|׎uonFӴiSӸqcs뭷׻=FIII1Ov[oe `5jdl6IMM57xYreg1?Iwq^}U;N3fh̙:x`mV]tѻ+#GO?U޽}n̚5K-[Tuqzgꫯj޽ڵn:͙3G/%|Zl)Ţz|p|O?퇑]ĉ֭{kFS~~~o]HX,z7uWΟ'-_3*ǏM6iڵ& L >k׮ 2D .nju{RÇ׏X?c;-[6,**r`䤤$СCU~wvv6vafϞ=^ĕGC:u=6mゕŅ^jW5F3gvVZTS̓g'>|X:tPÆ za-Z|_ TВdՆeEX]I .W=k}7Yv㕐j0Oa<<Վ_F XKӲ+VGuzbYtfԭG~8[/H%aUPPIgRPPBIg=ڱ{neeei۶mZh.\{キN۷% kR2Gv'|tP{ׯ׀+_W3f/^;)IڵSnn233SOe˖__u~5F.KztS0` #hcyW.^eYqFlg<\G0 a@Vr55r{=d rjX, 5!b Xz`@"@0 2Z"yeL,IJ&Dby&Dby&?b XzD,`@!>@0 K!>L&>bB XD,`@!@0 !:eL 2&?b&7b$&/bD&'bd&b&b&b&b& X C,C !`D0e"?2Lb& |X C, a`0D0e"=2Lb&?b&b!& XF G,#!`e#b&ԌX`@ %$L@8 =KIҨQԣGK< 5~xIѣժU+eggKuqԵ^@>bXwJNN椟aA˛oYŚ5k߯.](77WB=x@E<ݫf͚izGZdYNYt: u,%i„ 0aV^t9&&FӧO02TsLa-h_DhJDbF0;b LX«&pD,u@! K .%|`>G0:b BLX¯&PD,w@! K *%`G0;b@03bA0+bB0#bC0bD0bE0 bF0bG0DH X"dLB,R&@ 9K$ %B/!`bG0DX |X"lLB,V&_ ;K% %-a`bG0/b@0bA0DD!X"LE,& X%" bG0ԄX"G,G0TXg!!9&sK lPX $b Ԉ` &و%! D.b "j`*?b ԁ',:5Br޼yj׮┑O>9'*99YqqqJKKSnnFHRs0tNf1@u,Yɓ'k޼yӧhСںuڴi~YY ͛kҥjݺ٣ `ܯ=:^Zr}TtճxCP2''Gcǎոq$Is|+;;eEZfV$)55կcF)FF?٭L Dm,ʴaM2ifY|zꥉ'Vft-OަT%%g~vv/=R9/̏ĸ2F'\;s.hZq1]3Cti%%%9-OJJRQQܹS}F\}8qCMvvfΜ|ժUZu=ԪC%6pYndnnݯ Ȃ'0O;y7m,+Y,e***Լys=3VFFۧ9sT˩S*++qD)))0`@ˆnW^^ 8܍2n0Ԯkk03̓g}rADEGGE8peoRrrV!״4L.ll.˭V+O0GUkim}G 8$kTt|/agjn#p9䐗޽{M>}cUTT8m߾]nC bQd:z>-AKIҳ>Ei۶mTaaƏ/I=zNX5i$m߾]fϞ'! Y,[ݿu`#hJ7߬b͚5KW.]x;HaaXBJOOWV4i$O C,>YS*VRJԱ &h„ n[z˲^ziݺu>๚߇I0`ԇapX~B0E,?"@h"L K &Z%  b Wb?znG0XlR]`+4㍻ ^b[o믿f" }w߭rիWO޽_׮]Mkܹs>+??_=iӦMQ&X^bjU׮]յkW5q7"+uvک]vM`|y5nwbk*++]+ao߮_]GjUΝu饗w>|7o8 dʞG}ÇkǎzuWkϞ=4iRSS裏zc3&^{$o^۷׍7(I*--ŋuCկ~{?l6N͘1C999|/ {UW믿ǦE0J,_{$p,IDAT5}U^_RzhL7mݦ25h@ݻwWFF.R駟~Ҕ)SԧOol @ x >ڲeqF}Z`?.I~^x xWbtk̘1vn~3Y,ol &=^=B;w޽{%Im۶E]D(5L;˿/JLLE]6m())Is u@0X.\PFK_ԭު^xA]v빀#J,Νl͝;W7pn/ў={4l0 4H?76&Pw^~Ç,ӂ Իwo͚5p&P7^eFtԩ*{M8O=IJo߾z׫m۶***Ʀxj+4g-_7oVfͼ)^B0y%z?nA#GԚ5kt v^Z'Ov|mA0x}{褐@}UFT^=]uUj۶yom Lf7j:xRSS5buqW_ZׯW~~vuƇAq,?3_v]Ig>gΜ9E0y|tkźU~}{/|9F~!Y=cAi=z~a[Nzꩧ|9F~D0WDzH]vӲN:i 88ƺ,袋o> @L#qqq*--X Qoƍueq+!8KE,t颯Z˗/eX$IՓ$=JOOWnݔHq,KjӦMڸq6lؠ7ꫯ1Fm޼uK.D>&"Yl6S={t,/yfXByyy#Vtj*##C~'I*//ז-[aÆ B0;n4&FݺusX`"x[GDVHB,D D$ D#`"K^C0%"GLb '< ~P{x̢?^8? yԮ];)##C|G{dXtxSs0tNfA-cdMx`Y͚5K͚5رc=:d[ZZR咒3ߠ`e8V9/O'ZTsI?ԢAk`tg|5?ACJJJrZ"OpBxl͜9eUz!yrU$5Jlh*ܺ_?;7O;y7}m,+Y,et1zZ`=S*++qD)))0`>0fەAjz8Ay1FOwv][yO)..m,yMIk. >ܱBҙ/otff,ZQ>C0t-4+b L#b Lb (Lb hL b Lb L&XB,@ BK!`Ÿ%E0/@H#b L&|XB, _ Ka`›%E0-@X#b L/b "Lb bLQ&X8E,D$ "%F0 b LԄX&G,LTXY&!psKp`l@0QX@5&$b 5" و%x`F.b @0#Z"X@B,f pfd pf#3K%x O`b >@0 !X@,f#3K% M`b @0C !X@yS'<2X@y̢?^8? >Sv }'U`OM4Q&M4p@}~-M4j9= X.YD'OִiӔ~iС*,,tի5bZJk׮U6m4x`ݻ#ګ)QQ*:a@P2''Gcǎոq㔖s*%%Ew/ &K.QN`UTT?n< RVV 6hʔ)N5kxt'OnWӦM\T%%%$.^ya~lԩr\.))QJJ Ei۶mTaaƏ/I=zNX,>YS/-q2$M0A&Lp{ի.ڵ|~!~ԇa#KQ%0B=b a`0A0}X@!A, L#wKS{%1<"3? Cq5u,srr4vX7Niii;wRRR4|ϝ;W ԩSթS'M:UW]uΝ|`eZNul3h_,++ӆ 4e֚5kfڵtZ6dȐjcYZZRGJ>\ס=ݮ'OXV5 Z̓g'0ObNim*+_S>7xw[^7/:tN>$III***r{Z/Iٚ9s:a@u,..VƍvAJ1e?uTeee9.9rD*,,DhϞ=j`<]uY999={ 5| 4VۍW\["#0Oa<0Y,%5 ԀXPb @ "2|Gfj3G ,P~ԤI5iDNmK^{5Y,۞]mȑ#8q4iSy;w:vz)%%E:uꔟF׿4|plREoV㏕8o^O?t7l"kfVY`ٺu4i_ٽ{׬YcٳͶmٳMLLYnG?[nl&OWnn6my7$oVΝ;M||4iٺuY`ZfҥnŲgϞfN:udLvn\}Nˆ b~lV9:Wyyiذy}1Qy*//7}1>3fLDIJ4|Ӿ}{SVVĉ͕W^,++gc 6ԩӲ;?ն"0lwdo(we=$}Gf(kʔ)jժ!ӧ~ C u;w꣏>ȑ#oV'NTyyz! 2OotAWL!~СCJNN~"*|ᠮ׫իW+..W ӱct뭷jJLLFmOj޼yEGG+##CӜ9s6j3OW#ٶmyG##c=fbccҥK;sEٰBӠAs]woƼyCMÆ ͫjviVXa.BsM7!űcL~~7LNNwfʔ)fԨQ+:inj.\[G1cuƍ… xbb a0qFIe]r]~$I똀pA,0aIrJRSS:& \K LTY˯Z#Fbh@5K 髯RzSOI:sϷ~wyG3gwa0~z*m6Zl>}讻_.##CWNNƍ:ֻ;gAð@<裏#cN:;w_t nW_}ˬe˖9o~~>{%*O֭[nٲEeeeK.O:Uo*6k͎HF,0PgI,7nܨzSNM6~;8>g۶m:u{%v6oެ͛+997nܨtEGG;.w]qqqb7nܨ .@۷P@,ezW)yCg.y 77tYD:b ڼ^YQQM69"Xy2Æ SaaKb X!nر2hΜ95}v8qs/_p+l2pr%A6n(ժ.]8.7h@]tcnASII {#@WΝeu릨*t!ǫcǎ*Tj%5 ԀXPb @ %5 ԀXPb @ %5 ԀXPi+.LC0OIENDB`docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png0100644 0000000 0000000 00000035250 13756501735 025460 0ustar000000000 0000000 PNG  IHDRsBIT|d pHYsaa?i IDATxi|TߐLV !!`dS!V R֖E RdQ  VAho  U! ) KPAH@@dBfd$3IfbΜ9ۑš0 CRu=|b %K 8@,pXDdffd2i˖-sAL&effzn`.k.͙3GPdҜ9s= )_\\6oެmz{(5k.͝;WzRV=yf]yb M7aTܹs[T|5Qav3gL&vܩCQFՃ>SN<0 -]T;wVdd7nk6egg뮻ҕW^]}7n m+wnnƍWzԛ_W޽{d2=_|QPTTn޽f;U~}۷OT~X6떔'TMj:~zZwܡ_]tQdd[Ǟzk׮vʷl٢!CUVTV4tP:tnL&5zhEEE^z4h^zCڴin&EFFE5k.\`aaM0AjҤ}w6-..?f͚nݺ曵uVjJ+/)ϛ7O P&MԻwoH])##C&If\~nV5lPuUjj>A,U{ZJKKի5uTuƍ)SO>zwtRܹSݺuo]믿VJJ-[uק~ݻb{W__Znv͟?_dm޼Y7o.IJOOט1c_Bo{9}JII޽{meXtw[oջᆱ|PO[)++]wݥ hذa? (;;[zO?dm۶iƌ>}z-5jHsfϞ^xAתUt)q6?xڵk O?#Gn$3Fuի>Lz?`ѣG5d=zw5x`=䓚kÇHGpeeeb1:dH2}]}?N=$cÆ 6Os={֥cEqI&6%zᅲamڴte~wi֬Yرի2tMv/WFQQQi޼mխ[W6uy^?*Gln?kذa4k,p jذL&X|5k֬ePV叽|݊8zMo="ǏWUk*z 0@qqqz饗ԯ_?|*-\Pƍә3gΝ;;Q0 ۃ2335zh}JNNr^zP;v~TZJÆ 8wyf%%%)22R۶mӂ t5{%ӰaC[N:s挢5`}<pN~t? ͛d2wq>HIIIP6m*OgcyY%&&/S8p@T={Loy@aM&~m򗿬tӧkڵ6i9~xm۶M7o0*`.ټygnʕ+eXd6+|\qqWZĉjҤI Ya̙3.߀ѣG?166V*,,K]ai>|XW^y˶0ȸ sUG3fдiӬO:J{f\dXaݻ#v0Ob<;u㳺`)UXx%ٹ~t\IJYfvp1?^YDEEbQݺuդI9̓ UZV_tLЗ;$m4OI7vT_\2%%EͲu)99 uŃɉڳ{ʴ{ǗڽKg?:?*??_.~4$??_.>9ruСC6mvޭ_|Q+W#<\PJRӘhxbg,l٢޽{[o8j(effȑ#pJ"++KSNՒ%KԼysOҽ\P65nX_Q^^[իhE?ڳgOqTo,kAkɳ6= 8M5SђV^X|VuBN|% _ D,>B)KPJ#|5_D,^롔%!xR"/PJaJX<C)Kk(%b D,n%!IR"7PJbJX\(C)Kj(%b p@D,衔%!PR"PJPMJX!C)K58!C)KJX@(/" ʟKBiXlJ{`E(+F,eU%P:@, JLj%1Bb AP:X@"C, #DeK%BY;=b PE(]X@"E, J#@{K}%B^t?b ~Pz?E(=X"E,J ;U|R'~:V=8?8ҥKպukEDD())I6mr kNԩSuyTI5k]y {t,׬Y)Sh̙S=4`TUٳgkZr֬Y3fxx:'rDe>ŋk̘1;vx-[7oެT 6LZR~4tPmٲ#bh֭JKKYޯ_?TݻW^g}]jҨQ*OqqO>-IX,X,.x&|^1Oa*b77b.᮹X… YG+W֐!Ctqu]aT&L 5w\6lPݺuk$\vv'0O5=ʣ 8w[볱,g2lnaƍSOiҥo>M{d)IӦMӈ#-_\?~$iȑjѢ%I ŋեKiYf;THH7 TQ("c.GɩWX***ҼytuAYYYjٲ$HL&?oUӦM5h =Sz 3onr>KI8q&NX}7nٳgkԞ!Y@ / XJC,"!KB߈%܈Pb nB(7 XC,e`""2pKpB؈% |jPb 5D( X@5C,ep"$B%8P7b JKD,R%TPR.C(q9b ! JTXzKAP@"p%X:E,B DMKAP6%G(Q[@@#pb `J %\X8F,B w KP%G(n_#b oJx %ctRnZJJJҦM\~ФI%$$(++CP]OYCH( |45k4e-]Tz5`ڵKW]u%%%۷bbbo+ÇՠA/#'rD ӱ\xƌcJ222[˖-Szz/N8fIR˖-=:fbeIInݪ4SNNNYvRRR4i$jڴ ӧ+$$zӒ$"gXT9GDEƨsw ^Oql, u,ѣkt(++K{դITZZǤkܹv7lؠulo/0O5=ʣ Tsιe>r&avʕ)&&F˗/WHHwiѢErƌ6mӧ޽{I&{"b(;;[}=ɞ3GxpP~דsܲ]ettBBB";fwY...NfkBB==&<<\vf3/H#0O9'*2Fc<8"jHXXN9dgg[n>&55USYYuٞ={Wa(xy(TP=>KI6m^xڽ{N?^4rH͘1ú TTTɓ'kϞ=?kҤIz @sʨRȃ#jƧc)I'Nĉ+oƍvRRRͣ>} h%"DJ*b %%PkXB`@,D jP"KF(l%j!F.q%l޼?MQ%4 CabS|D=KU" c;/ egNTNԥK럎;*,,Ub~8qBS^^V^SN)44T ڶmv%`%4L2ر:v#FX;prss]p3B sI,zujݺ^W Jb.SF\)^B(ʹӷ~믿^:ubJj.={~lu]믿^ݺuӠApB 8Ӱ~N8}iժU߿>ɓ'e˖Z`+v%}tDڴi6mhbeffGѵ^{ǕP pKcyp7N?/^L,A(wz/=+J\^{M{ӧOb7jP5ӰGVIIׯ.]())I_O?)--M"@͹$?vܩ<>ӊ+?JjwŮj% QNԩS'5ʺX,e2\+D(sհeee:xբE jʕPMp ] *::Z\s*VQQv%:.9\r{1M0A={O?[^}Ueeesή'J\ˌ kڴieÆ SO=w۷vءXW@%z.9 w^ 4nyDDVXnݺi޼yB4>IDAT*J=\ˆ ?uTtŮTPXv]zjJGV?1!{$?-ZHk׮۷iӦJ\˔-\Pw}xٳX,ڸqLb.C(pٗ׵^~Xݻwddaիz)W %IՊenn233uqlRCUbbڲedXȗ.F(r:~z)"0$]֞Eiʔ)6&''+99ٵ# P{O5111JLLTΝ` D=: ]k׮eE_|M@o߮u);;X@(T5JJJRRR~HJKKsNmݺ|˾HfJLLX#osOt9B >b xPX^@(B,#!J?KC%࿈%onR?;U|R'~:V}Yn8rҥjݺ"""M69^{M&I/{OXXN9dgg[nvo^۷oW~~ϝwީ޽{+??_:y(Tw#KI6mFdh*((%I#GT-uW\q$-\Q("c.GɩWt,~i޼y:r:t蠬,lRTPP:u|̙77OR&N'Vxƍ|lff(XN"@"%܈%@,*J*E(#@%KK2%p B "B 2P5bG(8B,%gK-B YAPbC(TDP!jX"hJ5E,% x@mK4B %*Pp%bC(D@!܁X"`JB,%w"{K5B %)~P$b C(x_!XoJB,%o"yK4B K,B WK$B KB KB K B WKB Kx%P^C( b JX£%D,1"B K%܊PnC( b  JX¥%@D,2@"p B K%jPש:ӱJ'Gq%\tZn%%%iӦMb C7VƍէO}gmp8U|RZWBB t,׬Y)Sh̙S=4`Tƍ5tPmذA7oUW]~osQ:>ŋk̘1;vx-[WZ'sj߾VX2}y`bg/)))֭[f_~qjΝbQTTTzӒ$"R&GDEƨsv`N<9yrgcYXX .(66fyllRiiijѢS:;w 6nݺtjר((#_{pD%;;C ̓s;w-X3L6 ð[V W_ƍQz3fдiӬO>x[M43GxpP~b(;;[}lp|9EEEnٮ2::Z!!!vGǎ;ڼ3{d)IӦMӈ#-_\?~$iȑjѢ%]<:k,^ZZ֯__7Bs=JN>_EEE7o9:(++K-[$N.] KI8q&NX}7n}A(99J.]l|=Kx_8# r#APse">^˖-p W3fP5c zȨG%] {%)D[nUZZ~)''l޼YSNYvmUb|!ϩS..?_h/UR\<w\u/x>|X 6p|9̓sN:JQQQ.ݮ2::Z!!!vGǎ;z,׬Yj/I [ިQ#^4lؐ9r9̓s\Ͼ$egg,Vn*|LJJ֭t}G4m41BJIIUPPKF-Z(==]4yd|zu]ww{i\Ȝ9sx{С4igO?_~Y{N/)Iu]ŋk*((вeԷoj7$$DzRhO[«#0Oa<9d2\}}-g߳WK 8@,pX@P[O3G+VP=Ըqc5nX}g}zOu_K^{5L&Ǟ]u~ФI%$$(++CSFFڵkHkԩ:F4h 5o\&I|GJJJRDDڴim^{0Ɗ+]v'O6իg:tsrrcݻ_s;GÆ 3,Ybw6Fm4jo<jofٸq㌛nZ Ӱ忑ٯ_?~#oJw5˝;wNKj:OSӦM5fw'd֮]M4IСϟ .xb^Qy޽nj}c߲e, LodԢE c&'hʕ}BMi(++K{դITZZӐ!Ctqu]aT&LPZZ'7*;Tsj;Ar@PpBڸq"""5<<9sFÇ׊+꼞˗+$$DIIIhѢeƍSOiҥo>M#$$X`{ncAg駟6Œ7x8rϙ3gGTwfϞm4hxW֭3ڶmkw}z q#//3$/6IKK3Fa]#SN5ve\8kɒ%F˖-07>#}={4Fe?]vl6ڷooUgZliH3{lêZT0?O999ƍ7hmڴ1z)ã̓b1̙cmֈ0㍉''O=gÆ }S>7F2zi7]t1ŒVZ˖-~=K, b %K 8@,pXb N:d2YԩSG͛7߮; @?^ 4PÆ NTRR?\ׯlVNNNP* jA{@ ۶mJKK9s7vX\RN"77Wt 7ףGI=:& PK @lݺU*|XYYYy"4,O.X@a0 ?^/lJŢ;vX2o魷޲n^^G%/ILLtΝ;URR믿3;dSiion]fG277Wj߾vTT~oٽ{Ο?ϑ% b =Ţ۷+&&FqqqUNbݥKEDDS+ԦMs;wTqqSG!/=zw}~m@#eeeڶm5y@_|.A,?7fE9\wϞ=:{5߾+ԫW/[%Drsse6աCkswkҥ:}4GK SxxvbbK[ڵk筡>o,pXb %K 8@,pXWտ2)[IENDB`docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png0100644 0000000 0000000 00000054720 13756501735 025222 0ustar000000000 0000000 PNG  IHDRsBIT|d pHYsaa?i IDATx{|S?W.'i{)-[ UP N s^|)NN7 Dʔ`@͖"RzMM$#k$$i mI>9 ys.J!@DDD^DDDaIDD %Q7DDD`XuaIDD %Q7DDD`XuaIY*O"997p:;v̥9sK{Y|rddd@cԨQx=>ヒ?!!!M7݄'N_< >s~[n ԥ7|;vƍqwcݺu]Crr2v\s5./^+V`ɒ%q5{3<_󑗗> eL dȃ7|S~˗ bԳM:U& J%ywq uuuB!:::DTT3gK۷ G Xpṿl6nP783v[vZL0QXX?̙шdffI>[Gdds~ !nVz+W_ߏ&̞=ۥ݄ o]Jl[o<t{͍7bڴiDXX&MM6?T*ݻ]wKf9s&"""g}eDnns &>nk1bz=F>]z뭈`9spv;#,!''_|[>3dggCcС'=W_aڴiBXXb S>72dz=>Ct:c矋+Rh4qFge˖ bȑ⩧w-F%^z%QPP nVqcc[;#W_x@j[o"--M=Zbݺub̙_lKKKv.^u DCC… Ezzddd /P|bbԩBՊÇ;jZL:U|'袋DFFWgRԩS{'6n(V\)/^l3e1emOѣG*G}$6l =*&vsE!$$}ꫯpI,Zużyo^C}}=݋yAo7`ڴiHKKsY~- ;vpY~UW<?~}vc….]Q̜9~-fs\y.R0k,2VaÆ9__WL4 j$ VBqq{6m5 Ν.m͛xĉHOO͛ݞtp>NLLDBBf;wW_ Nl9st۷oGss3/^C=qUWA$efBRR|M粯ۜ˾ #%%7ܺuI?v߄~YYYhiiڵkkowg\p ZAzV$IF-7ͰZu.{W ŋq]wAVcHLL_ xVCrr|*^hoo~kf}}= t:~t:477;ǸquWj+`nuY]]g['z޹ p N>>Z ,/FDGGc͚5HNN_lW]]?-l;L&ԥ,E=>%裏md 555/pUW!!!mرcy߾}1c8 ;x饗p ..Fĉ]Fgh4myEEcOucBO/!CvZbؾ[aÆK111PT/Hg6$$MMMn˽Qꭷފ{|Νusl<yg'x.rhZ>|yyy#իW{=[o6dYz?1T*ەk֬Ahh6;;qqqXn<{キ۾:9մi78Ծy SXVVW͞nĉ« !v(--u\ag߬,\tEx7{b]}Wb7gX,G>{GFF|INx嗱pBkEBBjkkgW^9+ŋkʼn'SO!99NqqqK`0`ʕ())q}v܉o]wN8{ XxOO+_{v=Q__xqc;{G ka;PWWg}pw'Nȑ#OAA&N{#GDGG;W_SZ?_`D~ۭ#B6O?THEzzk]naq{ŬYDTT"33SW.[G}z'貝j˖-:N1BKn>+  yyybժU]^ y"1i$&\%o>1g%t:o,JoݺU\q"66VH$RSSW\}հ..\P 0eqy,o+222^YYY7p>%K+WL!I5jxw]u~6l ,X :tȭ]d[ݮFOرcNo=#bbbܶdbʔ)`001zh}⭷YYY"$$D=Z]հ=jjjx7<s2d$IƊNj{L=љQ ű"3Rd˛5k[oŷ~}m L< KDAoѢE1cQUUW_}]$҂x$ ƍ1}%"":sAJJ T*UJmݺǏGHHW_}zJDDol6#''ۋ:=zgɓQXXG}sOU"aU*>\}^9qF<.]|ԄԭB9Ȳ͛7#??눝zg~BA(e^y v^nÊ)]JBЄeRR[h.K*zgSƞu)`'2`4ڞ~IMoM&;olJO!_t>Ig#hr„ ]mذyyyED ~Zkܼ((_%uEEE΂GEQQ8ZDZtRcXjx~?9!qjZ+ JO+g:/Z]?[~;ܹs';+.\k֬Aee38`Ȑ!X~=~OBJJ ^z%?љBm5 Nzm(a ><74}|; mXN:bk֬q[6e޽{EDDBQ6VZv3 `E8MJsoKgO~6 K"" ؼm*[t80шYsO!y:_[y*%❵oAQ2lEaQ!݃aDp4!0EH6%rK0 EQP~b1OsV/$#}F5% ʬ10edHYu6/((a"" l(ܷ cD9u%%I”铝|_zZ4 C>oȒ|]nCMFȲcj#kHc6$F>S;%fk @+i6j*@ɲ S tI~wO0,݂83(FdpCܺqdq/e9wrm%.M^ۜ>wCGa7TkrZFKgc] K J1weg8G.A`B J]%^vt^R#&4g!gxl v=/DD E(hjG}[-m{T5"6,#f:nw|aÒx伩m5)=GTH,aj<Vv`X p(,|m( ӅgGDDEpn=<%r0ҢDD]dbs]Mr9yJf5%"Pрzs  IGDDALK5|J4y_Ȓ(HY0ak>X&9 Q,8tMG%|i?y_Ȓ(Hu4u4}d3;삀/0,QnBC[ׄ .<]/0,TDuEk G{$P'9 K"( vEfk jUnUԈ GtjGgaIDN]1v'u *D:Jfu֕ýGD`oMY&޷ @*3 }0,cc(;'A´M6%"gvŎ4*HZpdIDOhlRgP8$"fk j[`[i4=UA% xr=U>dHE;UA]797,Q% XvņZs5;m+itHOAqZ^YyÒ!׆rƤ * T#ƁaIDAYV{[+aw].I4>%3%-83 < K" JgS>ktjrS@DAlg";a JrÑ%ofϙC2 zr%󉈂JkG3 гYiQC-%bGUI;|Fo!xfԴV.l,EG+WĐ!Cc۶m]1rH"-- ?::Šl -'`$gQoڵkq}aʕ4i^{5̚5 }w?իWcĉ(---q!^S[5IY>|ͯR Xhnvdee_DZZ^yw؁I&ᦛnBFF.2xعsgzցGPk|֌Y>|oGVv?.=ns/ /đ#G~z,\X,X,f5nթ;~RƎ:4Y}P^tdڂ=rISP~z߆dnGbbDTUUyn@mm-.b!`-pOb ,_m͛vno"wSt"C]hsڬ6TCs]sYTX &_0bZJž=Sg}K$*++1f_2kT*׿ɓ'9s駟@Dx*e(Sz,*gHDthkz_%,^/n˖-.Z--[e˖Aψlx*`Dmc€Y1]“!i"7}XQpټmSg͘=  )Gr4Io/!( |BFpDD1(_1,ϴ7*;|M!6$5QcXQB\J g@v:gbXQ:Pxum5,E7W!nB?L Y|jD.A tϚYP8$"qe5CQ9N!7{'_ʠ$ð$"ڭn)Gu$FTćaLf|+=5  K":'B4u4 JmcB`4$@ ZF<Dtd-'&l'itHP)01, !шZs%h2:Ԉ8Ch(0,2[+`tNZD%EQ dh5ZԴVB(sTH IPYGÒ<:8$I1y9.NUk .{KԻDSqfYQr Jy,F|x24MRw"r8sjZ tzG̵5 5*-R"#)rGDL3䚟 !Q cX0EQ dh5Z4Y`2W^K#"]'Ò(WcM([l$I1y9OMdH4“V$љbXߺg8C1{Fd wSHOAdHtt((:qR^Qzi)ue oBm2H61D#Ky&gPf)'C$gE%PE;`7x|#K( QN~ $QOR$L>zcåGϠ$%QBY[2.,I$0e;ZN쵍 *$FaXEQ`۠( 6 *`[4:D^҇$`XXyE96oۄVs?0lP >!6]"Aqo'D>[-gf1y8X|3fOÈ.ړDS~HyEKPz@p-X 52ZjQ0D~#K"ټm3(ƌ”!I|V({pUW %r0${MD=%(#ʩ/$9$ SO^pFf0(Òd (`Vr=h#IFG[Y}2"? Ks$@:ȲkYauI:HZCHD@ Zn°n ssDZQ>DgɮPTv[ 7/Ň( ȡ0a59RV#m": yKt6 80c4b%(]A)Dt8$:CmV3*ˠx2"k8b188lN!7{'_ʠ$ P K3шꖓ׫Uj?<Λ ł/W\q" L~vʕ2dBBB0~xl۶Xd ,_zKJ:s ZʽV-!-z(t]5-/! ~=\v->\&MkYf[V̘1 裏0h 8q{ B(n@km(RҡU)":K~?^Ep^|E|xWb WF}}=o=%==OLŮQ\vM.IVqI6,V+vڅ~ee]۷{fݺu0a,Y> 馛CA\bb`s0,n7é3nE$dM>1!Sp?Lo KݎD剉͑#G7`޼yX~=:%Kf'͊+|r7oFXXع VPP]5z  GUGL(>s~%~Z[[[΂߆eK !3R xסh0~xTTT<.]|܌4h4YQPP3f8wVk3LmU^/QA ? +O=3uuu~qqqh4nȚfdHr5++ UUUZܫx:54 Xe+cں#AI`7:6N)FD?`XRB HSM,Tk$$-o!0,) !PkBcG<m.Soݸ Vc䙛=5(/9+Ody9(->EQPG1Sj/ng JBT9G.A`B J/}&"ő%!T6nDpcpp8p`?:Iqȟ|)㰫^gPQ|-Z_<Y+vl<6q jFàQ0aX!\Uʛbk^R#5*RXYR@)67qZ H畈ΞN|())lQxkPjTEaP9l6BCC?oرtz)"v+BV䳫a2d x):vl:{\1(:Z5+o$,?sDEE⩈f5dq/A EjT4jM eii)>C466B$=ƍĉ1g$$$eh霷ժXPrJFAID'a7p\{nڵ v½ދ˖-?싗 W^Q6ho!ZIa#28aR8RCܮDԻ|zgС:t(Zzk֬<#F'?/_oڷ(?n68C1{Fd tHLcPQի!z=N^`XW.A`Dm V`&c04-IRD4PɥӦMÊ+(@m޶YcFaɐ$ ,cm(_EQ]a1& $>cX|:u}s{zNho!LjrK I{%%I”铡;+)>ddy뭷j"<<>Əq!++ x1i$_!&*[q Fh%׏$I0QQ^llqW";> V8pؽ{77@kk+`ذax}RJ@+iam0AedYI:HZCD}'ahl,\йرceÆ 9&ȦP\a#2Qr , nvjq _|+VҥKn& &g5\ya0͐e[l},EցMǽoNOA>{EDtf|6)=܃#F%.bT*T*!0uT<Ӿz) ݊MǠa^3 ݻwc͚5Ezz:nF8Ϝ93gΝ;QXXYI bɦ)6Bb` K^L2,;K$=xp}C^^o{JC N6n>\LD,z)XVvmXf }Q <o{@l>[R"1((xdYXX3f7p.?>rrr? k֬Qlm^#%2j' o*\p.F9s;FdF:Z  hԚ>ѹqX !ӹϮ2|pTTTSxLhh7y\QiiX9 B;QkFNNKq9EQ`nmCuI KJQ\- n sVc䙛=j5?RD|z um5;<(((_#o JZɗsozUmOÌӜFł \0w! ,#"xDpcPs/= lN!7{'_ʠ$ư$7BT6.l.;G]M_oW\m(1,M][ eu$Ih4Z^CDȅ)=1"j?˕+WbȐ! m۶m@Rꫯ]FU j Iʎ$˵kc=BL<fBYYY?~<&OG= |BT.'GAQ{":,_x,Z~;/"-- mv;͛˗cС}yP){DD?v`Zk.<./2l߾vO>$hѢX,X,Ӝ>[j^SiaFSg!︟zgz߆dnGbkmDTUUy_VZΊ+|r7oFXXCrAs(X;dܻmm z{Agzkmmm~ND"̟?oz<.]|܌4h +?2@LlrYQPP3f8u;~۰FqEԸ68v̙\YXtN{^@^2ސ.zg[o/t?~!L8ѭQo>9]uUGQQSߎ,`ҥX`0a(++]w曑+V $$cƸ2$":3~sE]]|ITVVb̘1X~=eeer 9<)΀+.^/n˖-]nfw(9|$"ò)r< $"a9t}R>Q`X]LyJ"n0,S;eyJ""`X)$"ϒΜ(j:6qDDgaD+ʱy& Y6"y9OpTbyJ"3ǰ ; ;krVZlCɁ(->ayy,e((w J^Դ:ò7A5 İcfk V*gH L K?%:5BGDDO5u4j-ר4  \ K?dW2ѐZ=""~Z:Q!#"agd m' 3V%Q`XC0kU ibi.>J"°B*6{DDD~ڌv[rԈ cJ"İP`jV2Z=""S1,@S{=d\"&,zDDDbX3bC][uqD֪$"&gumP<ԪkCjxkU e?5{UĠ@ǽ!""o xCR°BP#tZ}°-FXln*5UIDw}fBOn KF^QWG+ʱy& Y6"y9O!:*ò,} 6نQZ|3fOÔ.%~;rHMKN(djnQ8emre֘Q2}2$I,غq@Qlf,{s?<Ȳ)#ʩ/$9*H)'Cw&RwaZ"" ޽GY{$.i xH4IKRDMƢ3Pt@$P 'Δ!fQNƚ(hB\"cXe efngɛwsgy/NArJ"Hİ "Z, s5KeP( [ZN>@sKݙ}ΠTXZ`+%""ox(dƻXCΣFł%(m/em2Ȇ{'q6HyyXZ (Ee}}= رc^mllDaa!RSS"|!ݠet;MŲ¦ҧ)YuDDQ غu+oɄBZ fmmmx'p?~w}7/__!|mJJC" RBXj"""EtX֢7nDNNꐙۿؼy3>dgg~+5b]Ɠt)PTa,V+:::P^^2|r+dYƔ)WX,X,sʲH]qܠI毿w222OĆe__v;]qeE(//GFFnS]]g}mȑ#HLL[.cG?p_2ڴ>)>)>PPa9ӕBE0_|E8pmmm0 ^@YY233tR\aå/Ɠ)XrU,˖-suK'e'e'e߈ ˩SBѸE^rhV{AUUZ[[wmzs2I47uɆ1Fo t{M鐟vʡ߫?w}.ӫVaK q%DD4^{d eee(..FAA.\}l6cӦM###FOرf̘<*MNNFrrrvnzEu֡=梹YYY2x}}=V+~ӟgΝ Y צL󨒈(EtX͛yf?kkksyu"1<KD"eB8FAY{rY/&r"Ű`N&,QbXN0oa ݸDD4cXvj)AJF CEDD4w&J"İ =p:H]*""° #a8<-WFr Z Q01,0,!n<KDWF 2RR#AJ C5DDl K?YVXDg!"U K?y8`bOgDD1acXt7hUkPzQ%Q,cXg!"K KڣUkpg"XưTbasOҥ+#DD1ag!"_ K< *$3QcX*`s0bvO%Ab b?=DDaY{x(>0,}!۸Nх""" 5zQ%Q`Xu%Q`Xyg 0TCDD nuOBDDqaymy^Y{ o ='hBDDqaŰ[ҏp8+Z(zEtX֢7nDNNꐙaٲe@vv6***C..y9FȱZ(zE*V(//w_|9=DZm6+V6,- ,kFG_0ݧ?b'WA/BBe įx>)>)>)pIs"DlXn#==e<==/_;/_k{Ƴ>6"҉(1y _Ć[Bv,@YYw}, mt,@ff&.]I&>)>)>)s5}ݘ2eʄ7brԩh4nGW\q;zs]w=z :O)>)>)3ї"NC~~>ZZZ\[ZZp{ m{yݞH=2 .ľ}`6i&둑j@ii)~^5k?2(i*+++]7HKKCUUكa߿sVţ> Ĝ9sP[[*f444`ٲe~=F<6+HIII`I%&Z"","Ò% K"""DDD>eXkhOQXXT~i Kc^uT*מb}eF 䠹9DՆ}ٳLl۶ ###!6O8Y'L ,ݪ(*r_4Vҧ>MMM E!>>~3477… زe l6~߆ O?8^ŋC͆_W(//EQgfC__FUX  /hkk`VyCi_hllԩSCU^p80m4۷QSSa9Ɵ>a׮]ǂ pEh4bǎ(7jxꫧۉ ,ٳgڊ`v/@oo/yhZ;wf nah4B$h4XNN._ NԚ!>رظq#>_۷opV4nrLǻヒ`v);;NBWWO~,]]]] U!iѢEx 8*^OfY$''gyF;wNӦMB>ܹS{Yfk׆%ׅd&Id29bS^^.ۏ}ud۶mٳ_Qj޽"++Kt:1|qQϖ,Y"6lo!fϞ-$IСC!8QVVoΝ/<}/,^R.,X z9sصkl!:,ˢR̚5K )6o,9rϛlذA,YwļyN3f ~?/׳$""!YaIDDÒ% K"""DDD>0,|`X$""aICRj5OիW?wyDQň`ҤI Z IMZϒ(} ػw/~.\/ly\)QtݰD1ĉn*z=OEg ?ǒ%KP[[|ى9{q%477E(%c`w !AOO,Σ̱Fo~M&F$ c7̝;gΜj]WTT&͆SN9#g K0vd$,;;;l)S੧snȒ K'2N:iӦh4ܾyyyh4̓`ʕ+b;;;qw`̙A(0,ܙ3g`XU!o>zu{ v'w K(JÁ'O:CpXx^f?D7aXE!PSSscpp>;୷BWWo!%Q$Iu>NNNƽC}}=xdI K8b20gzsB5k֠={vJ%(Y$""aIDDÒ% K"""DDD>0,|`X$""aIDDb gnIENDB`docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png0100644 0000000 0000000 00000053107 13756501735 025144 0ustar000000000 0000000 PNG  IHDRsBIT|d pHYsaa?i IDATxy|T?,w$3@Ab!AXbmRŅJUD TEkVJkmV\%lYBHB6'3wcF$LfI>׋?ι3gn|r={TB"""j: "" u K""0,:$""Ò K""0,:$""Ò K o*OaxGPWWu: ѣGHDL6 _`Z]mO<* 111HOOիp8:ƍ??`֭PTغukBtѴuOF[l/}O?uk{QL6 O ..OpwСC/ߏ9scÆ x衇pioƍXf ӏF۷+ vW.ÒBV?ipqĉ߿?n㦛nBuu5v؁T׹[O#??=_Ò/&&455!"""ݠNӰW^łcӦM;j(ٳg]oO<틛nɧBv~Xf =y$˗/GЫW/,^555nӯ_?p 1rHF :o{aѢEݻ7t:+Vnڴ^~饗/_~08q"9Yl2$''#66?Q^^>ׯԩSшT,[  ** ɓ ł_hjjrkR_ ^+y; >ǎÌ3øq;7t EQ<ꫯȑ#]Xv-Fш8r-8~x#KyaϞ=x0d`Ϟ=p'N@bm999o(+ljkk1{v{ꩧ؈۷owmOJJ7t6oތ˗#333<۷c}݋~˖-Cbb"?஻ App1cVOcؾ};}Y}  2Nrm,$''nM>m6ל1c붞ڵn/**rm;~8̙={B@$L0p!{m=n0}n#!FYfرc(..xT*̜9m[ZZ1ڶm1m4v,?#deey*RaҤIn۴Z-Ν󟨭8;я~3WT;wϼgϞHOOİ$Z~=,X?;v,1|3 0qD /֮5h_ڭ|{ƨQ0j(L8˗/SO=O.UUUAbmWTٳ/z477={|[KJtv>,n݊;w' p~_={PDD 6^wkx;-ʊ ۧJJJ;DKK'|Rq6gϞ?gNkWfWիQTT 6`ٲe(//jV]z ;;Xbn6ذayW$$$=&%-- Zu]wџd2n-0(++ѣ/5f3U'''_kze`֭$Iv; C?k{)L&v{tbt8m  .JuW^1cƢEo#99SNu1PTs6 .,/={Vc͚5hii>W^y%y^v mM<?m?4667p8FWX~_Ekϻ/ lm޼m1p@&Lz|Gn/qۖӧ#77nM~s~)_q> h\p B̙3^Çő%Mmm-0g :عs'>c'?p &`ƌxl2qua̘1'"..555믱w^עW_p^#ݾ};VZ}v؟/_|ӧOFAZZquC]]Ə {UWa޼y}V\7< ɓظq#~%pƍ8s=xg I}]ݻk{N_~ =zk6q5׸5͘4iz)lBì# ,o~̝;>, >u:kެ\}Z<>|8jjjcҥ:t(F+<v; EwXt)fϞ q{qw`׮]kR|>|8ދ~_DA`D]HKK{DZZFQ\qgvaٿP;p^[[+y1zh#ZHHHb͚5nm6`C K,~*.\(,PTn$c=&+$IIII{Νs{}믿'L &LතB<B$/222OK/olͿn[gwܹӵ/cǎb ={x(m۷OL8QF/^WZ/k׮$Cw}k?/ wg\TT$~򓟈(-nfqF@?x >}Zy睢gϞB$,nVqYW#GSaX/>Ca~sl[oZDFF ((ϟ/vR !D2Bߎ UTXx1/=s'DQQ'a(,СC!2lقw;w.:ÒBDD~ɓZӧ{1<u< KDDԁu>̙3 Jm6ddd`0`QWa؈t' 8q3f@ff&?N)uuaqVR_W0=cذa[}{{u[9bu >۷ow+']w|MȲVխ(dj&!둜ae²̣rbb"v;*++,zj*X"]$"9}_o)2a x6n=qXtqmm-#Gx@N,#77YYYm؉W2 @Kerرܶ}5jT"" ZfkDö#FȡPЁ_țu%N8ת˗/]:u K.šC[o7t[B :9o} J97妛nrMYmҿYڵ YYYǭ,Xu֡пlܸ=֬Ydw7w@ːj5+!;|:ICфCS1Pm;;V ˉ'[@׭[m„ .2LDDW\Rܼ(ؗlN!T *'S͈E]fǴIӱeWo ٰ$"aWNPŵ&۰w^߿3&cHv_#Z8 )v| K""4%nAaN06 gfę`I0VkC F0,S!֏]A:l(&LɄ$Ie6ЁB(]{=c2@hB!&%dgQxBAm9>p('Nu+$I0%u#B1gtos%%"m>s͕+v6v0'#G$,&.37 zC0!%]bǹ*4WCvV.QY^屨,˨$tRpKaJp0*܂p4d O6A][Y6fGDDtQT7UZa ?z& Tʜݾ,DDRF=S+nO]FՊ7kz'o9%,E8ohգ-&_˨@\(3{Dn3"m$2'|P K"n[)ï˜>F kEaQtZp$"*EcUӧRt3oP<u K"nrJ *:Ò|ѥ@Ҡw!Qw DD76ي}% })E'V%'mȒ[6T7UU Rt-PaID 8C-54JљoP< KDԥVDԶ tRt%Q,IWzkmmJ)@*u1V{ Jꊾ+IvP%þ] pdIDͨj*G}=0u 4:KÒ( (Vi&T7kz >ykH %Qn0"*8bh}zT1!>fp-E K"f_cWou[%g@H1,B?0#. !yDD!R붪Tj# m7*Gx((ru[*5#,?樞 J?$" !rJϝ1RbQknaIDοOR@\TdxtjoAImju`Ñ%QVT5Yu[C !" aCY]1N;nVm -YIZ،H0۾ƺAư$"Dv 6n-F\ qF4j-LYqMmaID!w(vT7UǐlczZFd aX] $RLŕWlpT5!>-k,%%j޽{ d[P`X])tדǰ$"]|h},L iW.u5س7 GJ7I1) 0Ű$"Q܄8]}vB`0G^kxx6g`h%E:8$"j!lvo?~kޘ7k>n\ڰԵ0,˻9Xͨh8&׈]j5:CU1,V}:ǣヒe˖᭷¸qp~7=Y,P6Ҩ0G&"F$Qg + .Djj*V^^o߾Ǐǜ9sЯ_?L:gƮ]s"j˥,bkcPRfaXlS/5\?رcƌǏcƍX`AcZaZ]8m-E/Fmqn֜Z>+BBIîevE}Mg J8$&&mOLLDYY}~\s {=|VŠ+<""ۓ.iY.u XV;-\zk{/}MMޫ:] VnBy f֭xvZ\}8v|A$%%ᩧ˱tR㺺: ++ & e999v[́8}O jը; ;*).nǸo:uC6,f34(cꩧ¼ypB؈O<z޵$I/dx|ݏS %6h$E-a`ŝ tߧtֱ BN S9997n}PTx'qX,̜9=\>Q,7V{ۅ@Zٸj(tX}݇s[nu{j3g @ψCq̧ZΩ IDATzSNÈAgQX$%@ epGm*5̑5CRwrơ54 ~_\ ^'%]Y%h;-Z KTjϯZ F<DtQ+;l]TzFyD.EQ eHZ MrJaW/) MPqIDUqI1r6`_>l $a1*sEꢑĥKaX*59HNF 2].,„(Z)=+0,€5Ů@|YkR("$D!f8쵮l]k|\k0,B-pܷڛ$QqQr(m8{:5Iӓ(4q1$QpdI"j*GuSE +_pIaXÆh7.KTj 3kMÒ(ZjPPE(mQ4Hh}v5I K q(7Zn;̽Y(DA,74n;sd"fV! 2%Q !PTvIS`aI ÆҺb؛mc聄$U:°$$CQ'񨿛^/D~V\Rܼ(ؗl?!0bT:, f&["Òȏvt[pm-0Z"ngop6BDaIړE+6HIj54-'u Kv4W6= DJP$D^(BAyC)ZεFk `ψ(Dp.|-6hc 3" %yl (; p&BDRL 4j!.BjPXn8D.E0,R]FAEc)ꭵmU BÒbmv JZ 2#FÒ`h/itHQ(`XR+'YG"`(rk!It$a”LWoQQaXRdmMv.eN0A+T$ & `mkQavZk}MM /^$ bƍ-"g]YY^YvCYQYVtQa~z,YO<󑙙ӧk{͆l>7xz p)T4q8ŽACp.ʼmS+0[Y.<"m$W !"7!=W^]w݅ V^O>*VZBuu5K}:ꭵ(+v2bT:: EQp@!=ńʊJWPjdeN f(lXl6޽˖-s>uT|^ٰaƎŋ?, ̙{ VVsq:r:DjP\͒`FkZQR\z^Vc-shIg  x|:>!p8HLLt۞2;?-[nƍqQ,^vO?}VZ+VxlEDD.,''']*oǏDZc~9rIII^!t͉v QI\Z.IȆ%,][oСCx衇PTT{0|,_{EUU|A9r~!y,^8X:ނ5asBѽṚB4,̚5 UUUXr%JKK1l0lܸu;HQQ۵|x衇^zc=@ֈSPyJ>EgDՄtX}݇s[n6vX|W+ VE~\"ÒB皪PX:bB:5҆gq6F)1}%҂"?zkmm1jUH[#0İ缇$"%:P۹5y%o !İR\Rܼ(ؗlVbА1* QѽcQ+'YG LvَƒqQdϘ!J" ,΄P\RzR;oP97Z)=0((`8Æb”LHYmS((ؿ6PQ@qdIA( p('Nֵr$I0%ӵ:ǠQo<" ,ٳg2Mv60'P$,εEm9K(ɨKQ7dSZ\YY^YvCYQYڣt\ˏ/a) Zրb 2`ZmS+0[YڬΑ爴n+/P4ˍ8S{ #FȡP0YLtZFV #aO&WP%]FՊ7kz'ZnhDZZ*׿ÇCTrjqǢCR#o Qx6ÈʜĠ$[X?~(((@~~>jHMM޽{v{ VRbܕ l!i%^$KXT*HÇc7o'N`Ϟ=((([Q٭(9E8>ߺĖJJ^pKXw߿?nf)Êӵ'h#(#uHN[Dr~>@ll?^ aCqI8#tQHaPQ>3g0rHq28;R$c@uI" ]~ #GCMM $I•W^#Gbܸq9s&6fŵ'!F)ɱ}D-eTWWرcxw1m4>}> ^xoCaĮ؝Ay}ޠ5W ƒ_+ 0 -ycu#`Ȑ!O~ϷP(9yր^Vk3"Kөz=-Z+ n8P\{͠i %ѻG?h,ILD/a Gm: 0E(8Sw -fKz-_l6DEE᪫BFFFT477cٲe?~?ފB"=fZBЪ%'eCC<|ٳ;voA?oE!F Jj,7z}^лGH%/FAZZҰ`'OBe 4YEQpp8PT&k;Z=AҰH7^@R'O^G^Я_?<YqI1r6`_>l  Q$jTZNbD6_lѧO$&&FUUނhWNcװB,a8r ^j{Nˠ$/aowܹs? Ç\a#Ź^Gdӫ gfTWBRwl?赆`vȯrvXj.]6g߯_?($6YY^YvCYQYQ I:HZOID]_^z 6laXV`* \EfۦdYFzz:gϸI8S vű#.ۡt6YDԥ_5&LY!K/aɒ%nmGQF0Ŏ"3Θy܎Mlm+4/Kl6yXnqDFFGΝ;;@B֝]~ Hbt/uhCD݆#|dgg7pm;w.ӱf[3GVxMm,hB#DD]C2=mСC1sL|~^}K-5Wz}(E3="" >:纄FII_;EgsZI1)\˾d0`Z @IPA>Ъ)QXoŞ={0zhx"(? Yy}>!:)""" ->aPXX 6`Æ SrFs9_HKKCzz: #UMh5x}.XC\{DDz|}jb޽سgvލ={BGuhBB1b y< :T7Ux}Π@B'yVc̘13fk,طo[߿~)rrr! =1)Px%B$!##8x v}$s(AgУR[DD:eVEzz[X BCv1'* FN!"rlLuSm^1qB nZrF$D% nf4* cR"" ( =@RLHRDDİ(;{IBKd"tQxaXvq+`\>=QaXvaT6^k@bt/N!"EA 'U$""1,bmF|d ĈQ$]bR8"0,];?BQj( * 7tS'0K݂RףWJ2tzQQl܌ƚfy#cɒ%Xv-Ə^{ ӧO7|>}ߩS# 333 ܼͮL6dB$Ȳmp@!E=["#W^ywu.\T^)))xWpnÊ+0`68EA|)B+H S2NoBvdiٰ{n,[mԩS_ʕ+aXp]wtjj:=,CK}XmVd "T$,&&NYz\ox|:>!p8HLLt۞2|x7QPPZ +V؞_JZ@eydYv,/NeE%@`S&[x|555ulX]˾soI,˗/ҥK]ꐒ,LKx/`ZmS5K9H_'2rrr7Ġ$"3~Rc{H]t;DD1,LY3Q'`X!FzMk Q #皫 ;l5* L AQ v ϙ"Qk#"a&*BZb qAQ r5^K"Nǰ q_(EGDD2ĝ_|*`u? k $ !" ekk\ \aX(%"  QJD:!_B 2İ+QaX%" = bWdT+QaXʆPX(0,C.e`W"ư JDADDadJDADDADDa$JD>ADDaJD^f[qBx^dW"Ф vbmF=eZIACbĨtX̬JD+'YG(EȡȞ1N"Ӱ-(z=z$CEAͨ8nQ;8dy]A:l(&LɄ$Ie6ЁB(ܼ-7k~{KDDpdىEA|)B^%I„)}{NQ`Xv".&;o1'$d1l ]xc N$i%$*Ȳ{ʲʊJNɍzsIDATAr"PİDj#ҮXVl۔ k6s9"m$j8B'tؕЁB?z& TʜQ[8d{c \FՊ7kz'f7Yv2E(E?w vű#.!IJ@V$%QcXv`I0#{dL> vَd;""OvfJDDaɚ&m*f%QaXv"!ZaiV ~cw" Ex3HA ]*e'ze+`Xv"o+("" N-,u=4jޱCDNDv`W!p8HLLt۞2^cٲeիLfUVaŊsssӨ&yl?V-vuINNNx|55yvB6,[]X"NSٸ_W_[`h&˱tR㺺: ++ &~V7WZ}Ԉi>N8e999Ά$s[x|'TUUulXfh4QdyyhBcӦMHKKk^^f$]/sJJ}d {Ǩq oxY&d't:dddxrq綾^{lSuz;eX&`ScM "b@A dYf"a.,t TX&,Fr е]m&%%!)))bu<CLqE,[ gϞEff&jkk p88s=u.]l2WAL%̟?X]]]@LӉ fY !D,%DDj_;joWx%z0,P=x%Z0,'O'"R? eiԛ`SDD$xp{]~KD. ;px%O!"Rzr'# "R=zQ0,|teHa%#DD '# "ee`1X"a!.ӉTahkw+~㼾HaDD° τ%"L7o|oܤ@+PER& 7KFԋa)$"L K""aXxAgAgT"" v9y:1,e%#DDİF)$"R; Wt09y:1,% fC$"R9DNo`ԏa)$".D<H}:l69͆iӦᅲFzQ,//ٳ1g 2HIIAUUU+**0n8#==x'QQQ!m_t7۽g!"eP`\.>NǏG}}}8p-46a„ۆe[[|˗/_9t!kp46&>t"*n7_/h4*]Nbaa믿says|ιsd+W[o7>RJ'""]x=zkaizN(,,-_t p8wjr̙3޽,III˗/C޽lXzo?zpZf3fx= {444wY3L®]:ڵ >hsΠI[PXX͆l`ݺup87o`̙HNNʕ+xǰzjL29vލ})1(KJJJ."LXf nܸ 6`ذaw}>, %%(//Gii)0n8Y1f 1#i'i'i'i"ѧqϯ%""R=fIDD+DDD!0,B`X$"" Aa=2㉜UWW#//zB^0vXQV9rK6mڄeOj'O.]B~~>V+, (U},ZN3J*ɓ1`$$$`!gdeeb -- lO}Vw^ʣKn>#a6,~W_ *.\ʣV,^XlٲE۶m---K.@455ja4͛e4.?m41qNc&L3f̈XJۣ[y<ѭ[7~H3>fDXSUUHKK.+ }OXV,Y$ƍ@} 4~; h#3ӣk֬Aii)vލCFL/VL<7z'NA"[>YVFzؐ!Cp9\.L֬pdl6̙3ڵkxWx~?x7`0O>_GS=2C GPVV˗cǎΎtۧt=zgy?8ң*Snn.N>gN< ժʠQCJ+gIIIbĉ⫯oR!*iҥ[nbƍEܹS 4HL6MW^v]v@ ĦHl6,ZH455^:"ڵkEjj0LbĈbϞ=F-f͚i>L nQRR" $,HIIGϷ~7fG:1|pa2EUU,B1K""p0,B`X$"" aIDDÒ(%Q K""DDD!0,TdСHHHt: 0&M.(nq;"p:֭w_r?b0]a6MϒH͎9ǃ\tzlΜ9~Ȱ$ wDCCࡇ{,//pD K"8|0r\Jφ%RC40`rss`9XVVFrrCCvڅc֛;w.Μ9|ݰD*б vժUB@ӉlذSPn;v̷ٱlZuNk۹5J%*t3lذ?~. #F\\\۷cgѣG}iÒH:,eCC[ݻ7^y\r7Oss3N',${nGE~`ZCЀCBł'v644gϞHKKhDaI?6I[޵SNŶmƉaIz8r/;;sҤIp8駟D°$sgφeee!=y$] [{1c`֭hll=Dð$ҐFdff֙:u**++qnYÒHCv;222`6}Æ NO)SptVT|B%Q K""DDD!0,B`X$"" aIDDÒ(%Q K""DDD!Kdg~IENDB`headers/0040755 0000000 0000000 00000000000 13756501735 011237 5ustar000000000 0000000 headers/Android.bp0100644 0000000 0000000 00000001007 13756501735 013135 0ustar000000000 0000000 cc_library_headers { name: "media_plugin_headers", vendor_available: true, export_include_dirs: [ "media_plugin", "media_plugin/media/openmax", ], header_libs: [ "libstagefright_headers", "libcutils_headers", "libutils_headers", "libstagefright_foundation_headers", ], export_header_lib_headers: [ "libstagefright_headers", "libcutils_headers", "libutils_headers", "libstagefright_foundation_headers", ], } headers/media_plugin/0040755 0000000 0000000 00000000000 13756501735 013674 5ustar000000000 0000000 headers/media_plugin/media/0040755 0000000 0000000 00000000000 13756501735 014753 5ustar000000000 0000000 headers/media_plugin/media/cas/0040755 0000000 0000000 00000000000 13756501735 015521 5ustar000000000 0000000 headers/media_plugin/media/cas/CasAPI.h0100644 0000000 0000000 00000011310 13756501735 016723 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CAS_API_H_ #define CAS_API_H_ #include #include // Loadable CasPlugin shared libraries should define the entry points // as shown below: // // extern "C" { // extern android::CasFactory *createCasFactory(); // extern android::DescramblerFactory *createDescramblerFactory(); // } namespace android { struct CasPlugin; struct CasPluginDescriptor { int32_t CA_system_id; String8 name; }; typedef std::vector CasData; typedef std::vector CasSessionId; typedef std::vector CasEmm; typedef std::vector CasEcm; typedef void (*CasPluginCallback)( void *appData, int32_t event, int32_t arg, uint8_t *data, size_t size); typedef void (*CasPluginCallbackExt)( void *appData, int32_t event, int32_t arg, uint8_t *data, size_t size, const CasSessionId *sessionId); struct CasFactory { CasFactory() {} virtual ~CasFactory() {} // Determine if the plugin can handle the CA scheme identified by CA_system_id. virtual bool isSystemIdSupported( int32_t CA_system_id) const = 0; // Get a list of the CA schemes supported by the plugin. virtual status_t queryPlugins( std::vector *descriptors) const = 0; // Construct a new instance of a CasPlugin given a CA_system_id virtual status_t createPlugin( int32_t CA_system_id, void *appData, CasPluginCallback callback, CasPlugin **plugin) = 0; // Construct a new extend instance of a CasPlugin given a CA_system_id virtual status_t createPlugin( int32_t CA_system_id, void *appData, CasPluginCallbackExt callback, CasPlugin **plugin) = 0; private: CasFactory(const CasFactory &); CasFactory &operator=(const CasFactory &); /* NOLINT */ }; struct CasPlugin { CasPlugin() {} virtual ~CasPlugin() {} // Provide the CA private data from a CA_descriptor in the conditional // access table to a CasPlugin. virtual status_t setPrivateData( const CasData &privateData) = 0; // Open a session for descrambling a program, or one or more elementary // streams. virtual status_t openSession(CasSessionId *sessionId) = 0; // Close a previously opened session. virtual status_t closeSession(const CasSessionId &sessionId) = 0; // Provide the CA private data from a CA_descriptor in the program map // table to a CasPlugin. virtual status_t setSessionPrivateData( const CasSessionId &sessionId, const CasData &privateData) = 0; // Process an ECM from the ECM stream for this session’s elementary stream. virtual status_t processEcm( const CasSessionId &sessionId, const CasEcm &ecm) = 0; // Process an in-band EMM from the EMM stream. virtual status_t processEmm( const CasEmm &emm) = 0; // Deliver an event to the CasPlugin. The format of the event is specific // to the CA scheme and is opaque to the framework. virtual status_t sendEvent( int32_t event, int32_t arg, const CasData &eventData) = 0; // Deliver an session event to the CasPlugin. The format of the event is // specific to the CA scheme and is opaque to the framework. virtual status_t sendSessionEvent( const CasSessionId &sessionId, int32_t event, int32_t arg, const CasData &eventData) = 0; // Native implementation of the MediaCas Java API provision method. virtual status_t provision( const String8 &provisionString) = 0; // Native implementation of the MediaCas Java API refreshEntitlements method virtual status_t refreshEntitlements( int32_t refreshType, const CasData &refreshData) = 0; private: CasPlugin(const CasPlugin &); CasPlugin &operator=(const CasPlugin &); /* NOLINT */ }; extern "C" { extern android::CasFactory *createCasFactory(); } } // namespace android #endif // CAS_API_H_ headers/media_plugin/media/cas/DescramblerAPI.h0100644 0000000 0000000 00000007434 13756501735 020454 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DESCRAMBLER_API_H_ #define DESCRAMBLER_API_H_ #include #include namespace android { struct AString; struct DescramblerPlugin; struct DescramblerFactory { DescramblerFactory() {} virtual ~DescramblerFactory() {} // Determine if the plugin can handle the CA scheme identified by CA_system_id. virtual bool isSystemIdSupported( int32_t CA_system_id) const = 0; // Construct a new instance of a DescramblerPlugin given a CA_system_id virtual status_t createPlugin( int32_t CA_system_id, DescramblerPlugin **plugin) = 0; private: DescramblerFactory(const DescramblerFactory &); DescramblerFactory &operator=(const DescramblerFactory &); }; struct DescramblerPlugin { enum ScramblingControl { kScrambling_Unscrambled = 0, kScrambling_Reserved = 1, kScrambling_EvenKey = 2, kScrambling_OddKey = 3, kScrambling_Mask_Key = 0x3, // Hint that the descrambling request is for a PES header only kScrambling_Flag_PesHeader = (1 << 31), }; struct SubSample { uint32_t mNumBytesOfClearData; uint32_t mNumBytesOfEncryptedData; }; DescramblerPlugin() {} virtual ~DescramblerPlugin() {} // If this method returns false, a non-secure decoder will be used to // decode the data after decryption. The decrypt API below will have // to support insecure decryption of the data (secure = false) for // media data of the given mime type. virtual bool requiresSecureDecoderComponent(const char *mime) const = 0; // A MediaCas session may be associated with a MediaCrypto session. The // associated MediaCas session is used to load decryption keys // into the crypto/cas plugin. The keys are then referenced by key-id // in the 'key' parameter to the decrypt() method. // Should return NO_ERROR on success, ERROR_CAS_SESSION_NOT_OPENED if // the session is not opened and a code from MediaErrors.h otherwise. virtual status_t setMediaCasSession(const CasSessionId& sessionId) = 0; // If the error returned falls into the range // ERROR_CAS_VENDOR_MIN..ERROR_CAS_VENDOR_MAX, errorDetailMsg should be // filled in with an appropriate string. // At the java level these special errors will then trigger a // MediaCodec.CryptoException that gives clients access to both // the error code and the errorDetailMsg. // Returns a non-negative result to indicate the number of bytes written // to the dstPtr, or a negative result to indicate an error. virtual ssize_t descramble( bool secure, ScramblingControl scramblingControl, size_t numSubSamples, const SubSample *subSamples, const void *srcPtr, int32_t srcOffset, void *dstPtr, int32_t dstOffset, AString *errorDetailMsg) = 0; private: DescramblerPlugin(const DescramblerPlugin &); DescramblerPlugin &operator=(const DescramblerPlugin &); }; } // namespace android extern "C" { extern android::DescramblerFactory *createDescramblerFactory(); } #endif // DESCRAMBLER_API_H_ headers/media_plugin/media/drm/0040755 0000000 0000000 00000000000 13756501735 015535 5ustar000000000 0000000 headers/media_plugin/media/drm/DrmAPI.h0100644 0000000 0000000 00000054357 13756501735 016775 0ustar000000000 0000000 /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DRM_API_H_ #define DRM_API_H_ #include #include #include #include #include #include #include // Loadable DrmEngine shared libraries should define the entry points // createDrmFactory and createCryptoFactory as shown below: // // extern "C" { // extern android::DrmFactory *createDrmFactory(); // extern android::CryptoFactory *createCryptoFactory(); // } namespace android { class DrmPlugin; class DrmPluginListener; // DRMs are implemented in DrmEngine plugins, which are dynamically // loadable shared libraries that implement the entry points // createDrmFactory and createCryptoFactory. createDrmFactory // constructs and returns an instance of a DrmFactory object. Similarly, // createCryptoFactory creates an instance of a CryptoFactory object. // When a MediaCrypto or MediaDrm object needs to be constructed, all // available DrmEngines present in the plugins directory on the device // are scanned for a matching DrmEngine that can support the crypto // scheme. When a match is found, the DrmEngine's createCryptoPlugin and // createDrmPlugin methods are used to create CryptoPlugin or // DrmPlugin instances to support that DRM scheme. class DrmFactory { public: DrmFactory() {} virtual ~DrmFactory() {} // DrmFactory::isCryptoSchemeSupported can be called to determine // if the plugin factory is able to construct plugins that support a // given crypto scheme, which is specified by a UUID. virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) = 0; // DrmFactory::isContentTypeSupported can be called to determine // if the plugin factory is able to construct plugins that support a // given media container format specified by mimeType virtual bool isContentTypeSupported(const String8 &mimeType) = 0; // Construct a DrmPlugin for the crypto scheme specified by UUID. virtual status_t createDrmPlugin( const uint8_t uuid[16], DrmPlugin **plugin) = 0; private: DrmFactory(const DrmFactory &); DrmFactory &operator=(const DrmFactory &); }; class DrmPlugin { public: enum EventType { kDrmPluginEventProvisionRequired = 1, kDrmPluginEventKeyNeeded, kDrmPluginEventKeyExpired, kDrmPluginEventVendorDefined, kDrmPluginEventSessionReclaimed, kDrmPluginEventExpirationUpdate, kDrmPluginEventKeysChange, kDrmPluginEventSessionLostState, }; // Drm keys can be for offline content or for online streaming. // Offline keys are persisted on the device and may be used when the device // is disconnected from the network. The Release type is used to request // that offline keys be no longer restricted to offline use. enum KeyType { kKeyType_Offline, kKeyType_Streaming, kKeyType_Release }; // Enumerate KeyRequestTypes to allow an app to determine the // type of a key request returned from getKeyRequest. enum KeyRequestType { kKeyRequestType_Unknown, kKeyRequestType_Initial, kKeyRequestType_Renewal, kKeyRequestType_Release, kKeyRequestType_None, kKeyRequestType_Update, }; // Enumerate KeyStatusTypes which indicate the state of a key enum KeyStatusType { kKeyStatusType_Usable, kKeyStatusType_Expired, kKeyStatusType_OutputNotAllowed, kKeyStatusType_StatusPending, kKeyStatusType_InternalError, kKeyStatusType_UsableInFuture }; // Used by sendKeysChange to report the usability status of each // key to the app. struct KeyStatus { Vector mKeyId; KeyStatusType mType; }; // Enumerate HDCP output protection levels enum HdcpLevel { // Failure to access HDCP level, an error occurred kHdcpLevelUnknown, // HDCP is not supported on this device, content is unprotected kHdcpNone, // HDCP version 1.0 kHdcpV1, // HDCP version 2.0 Type 1. kHdcpV2, // HDCP version 2.1 Type 1. kHdcpV2_1, // HDCP version 2.2 Type 1. kHdcpV2_2, // HDCP version 2.3 Type 1. kHdcpV2_3, // No digital output, implicitly secure kHdcpNoOutput = 0x7fff }; // SecurityLevel indicates the level of robustness of the DRM // implementation on the device enum SecurityLevel { // Failure to access security level, an error occurred kSecurityLevelUnknown, // The maximum security level of the device. This is the default when // a session is opened if no security level is specified kSecurityLevelMax, // Software-based whitebox crypto kSecurityLevelSwSecureCrypto, // Software-based whitebox crypto and an obfuscated decoder kSecurityLevelSwSecureDecode, // DRM key management and crypto operations are performed within a // hardware backed trusted execution environment kSecurityLevelHwSecureCrypto, // DRM key management, crypto operations and decoding of content // are performed within a hardware backed trusted execution environment kSecurityLevelHwSecureDecode, // DRM key management, crypto operations, decoding of content and all // handling of the media (compressed and uncompressed) is handled within // a hardware backed trusted execution environment. kSecurityLevelHwSecureAll }; // An offline license may be usable or inactive. The keys in a // usable offline license are available for decryption. When // the offline license state is inactive, the keys have been // marked for release using getKeyRequest with // kKeyType_Release but the key response has not been // received. The keys in an inactive offline license are not // usable for decryption. enum OfflineLicenseState { // The offline license state is unknown due to an error kOfflineLicenseStateUnknown, // Offline license state is usable, the keys may be used for decryption. kOfflineLicenseStateUsable, // Offline license state is released, the keys have been marked for // release using getKeyRequest() with kKeyType_Release but the // key response has not been received. kOfflineLicenseStateReleased }; DrmPlugin() {} virtual ~DrmPlugin() {} // Open a new session with the DrmPlugin object. A session ID is returned // in the sessionId parameter. virtual status_t openSession(Vector &sessionId) = 0; // Close a session on the DrmPlugin object. virtual status_t closeSession(Vector const &sessionId) = 0; // A key request/response exchange occurs between the app and a License // Server to obtain the keys required to decrypt the content. getKeyRequest() // is used to obtain an opaque key request blob that is delivered to the // license server. // // The scope parameter may be a sessionId or a keySetId, depending on the // specified keyType. When the keyType is kKeyType_Offline or // kKeyType_Streaming, scope should be set to the sessionId the keys will be // provided to. When the keyType is kKeyType_Release, scope should be set to // the keySetId of the keys being released. Releasing keys from a device // invalidates them for all sessions. // // The init data passed to getKeyRequest is container-specific and its // meaning is interpreted based on the mime type provided in the mimeType // parameter to getKeyRequest. It could contain, for example, the content // ID, key ID or other data obtained from the content metadata that is required // in generating the key request. Init may be null when keyType is // kKeyType_Release. // // mimeType identifies the mime type of the content // // keyType specifies if the keys are to be used for streaming or offline content // // optionalParameters are included in the key request message to allow a // client application to provide additional message parameters to the server. // // If successful, the opaque key request blob is returned to the caller. virtual status_t getKeyRequest(Vector const &scope, Vector const &initData, String8 const &mimeType, KeyType keyType, KeyedVector const &optionalParameters, Vector &request, String8 &defaultUrl, KeyRequestType *keyRequestType) = 0; // // After a key response is received by the app, it is provided to the // Drm plugin using provideKeyResponse. // // scope may be a sessionId or a keySetId depending on the type of the // response. Scope should be set to the sessionId when the response is // for either streaming or offline key requests. Scope should be set to the // keySetId when the response is for a release request. // // When the response is for an offline key request, a keySetId is returned // in the keySetId vector parameter that can be used to later restore the // keys to a new session with the method restoreKeys. When the response is // for a streaming or release request, no keySetId is returned. // virtual status_t provideKeyResponse(Vector const &scope, Vector const &response, Vector &keySetId) = 0; // Remove the current keys from a session virtual status_t removeKeys(Vector const &sessionId) = 0; // Restore persisted offline keys into a new session. keySetId identifies // the keys to load, obtained from a prior call to provideKeyResponse(). virtual status_t restoreKeys(Vector const &sessionId, Vector const &keySetId) = 0; // Request an informative description of the license for the session. The status // is in the form of {name, value} pairs. Since DRM license policies vary by // vendor, the specific status field names are determined by each DRM vendor. // Refer to your DRM provider documentation for definitions of the field names // for a particular DrmEngine. virtual status_t queryKeyStatus(Vector const &sessionId, KeyedVector &infoMap) const = 0; // A provision request/response exchange occurs between the app and a // provisioning server to retrieve a device certificate. getProvisionRequest // is used to obtain an opaque key request blob that is delivered to the // provisioning server. // // If successful, the opaque provision request blob is returned to the caller. virtual status_t getProvisionRequest(String8 const &cert_type, String8 const &cert_authority, Vector &request, String8 &defaultUrl) = 0; // After a provision response is received by the app, it is provided to the // Drm plugin using provideProvisionResponse. virtual status_t provideProvisionResponse(Vector const &response, Vector &certificate, Vector &wrapped_key) = 0; // A means of enforcing the contractual requirement for a concurrent stream // limit per subscriber across devices is provided via SecureStop. SecureStop // is a means of securely monitoring the lifetime of sessions. Since playback // on a device can be interrupted due to reboot, power failure, etc. a means // of persisting the lifetime information on the device is needed. // // A signed version of the sessionID is written to persistent storage on the // device when each MediaCrypto object is created. The sessionID is signed by // the device private key to prevent tampering. // // In the normal case, playback will be completed, the session destroyed and // the Secure Stops will be queried. The App queries secure stops and forwards // the secure stop message to the server which verifies the signature and // notifies the server side database that the session destruction has been // confirmed. The persisted record on the client is only removed after positive // confirmation that the server received the message using releaseSecureStops(). virtual status_t getSecureStops(List > &secureStops) = 0; virtual status_t getSecureStop(Vector const &ssid, Vector &secureStop) = 0; virtual status_t releaseSecureStops(Vector const &ssRelease) = 0; virtual status_t releaseAllSecureStops() = 0; // Read a property value given the device property string. There are a few forms // of property access methods, depending on the data type returned. // Since DRM plugin properties may vary, additional field names may be defined // by each DRM vendor. Refer to your DRM provider documentation for definitions // of its additional field names. // // Standard values are: // "vendor" [string] identifies the maker of the plugin // "version" [string] identifies the version of the plugin // "description" [string] describes the plugin // 'deviceUniqueId' [byte array] The device unique identifier is established // during device provisioning and provides a means of uniquely identifying // each device. virtual status_t getPropertyString(String8 const &name, String8 &value ) const = 0; virtual status_t getPropertyByteArray(String8 const &name, Vector &value ) const = 0; // Write a property value given the device property string. There are a few forms // of property setting methods, depending on the data type. // Since DRM plugin properties may vary, additional field names may be defined // by each DRM vendor. Refer to your DRM provider documentation for definitions // of its field names. virtual status_t setPropertyString(String8 const &name, String8 const &value ) = 0; virtual status_t setPropertyByteArray(String8 const &name, Vector const &value ) = 0; // The following methods implement operations on a CryptoSession to support // encrypt, decrypt, sign verify operations on operator-provided // session keys. // // The algorithm string conforms to JCA Standard Names for Cipher // Transforms and is case insensitive. For example "AES/CBC/PKCS5Padding". // // Return OK if the algorithm is supported, otherwise return BAD_VALUE // virtual status_t setCipherAlgorithm(Vector const &sessionId, String8 const &algorithm) = 0; // // The algorithm string conforms to JCA Standard Names for Mac // Algorithms and is case insensitive. For example "HmacSHA256". // // Return OK if the algorithm is supported, otherwise return BAD_VALUE // virtual status_t setMacAlgorithm(Vector const &sessionId, String8 const &algorithm) = 0; // Encrypt the provided input buffer with the cipher algorithm // specified by setCipherAlgorithm and the key selected by keyId, // and return the encrypted data. virtual status_t encrypt(Vector const &sessionId, Vector const &keyId, Vector const &input, Vector const &iv, Vector &output) = 0; // Decrypt the provided input buffer with the cipher algorithm // specified by setCipherAlgorithm and the key selected by keyId, // and return the decrypted data. virtual status_t decrypt(Vector const &sessionId, Vector const &keyId, Vector const &input, Vector const &iv, Vector &output) = 0; // Compute a signature on the provided message using the mac algorithm // specified by setMacAlgorithm and the key selected by keyId, // and return the signature. virtual status_t sign(Vector const &sessionId, Vector const &keyId, Vector const &message, Vector &signature) = 0; // Compute a signature on the provided message using the mac algorithm // specified by setMacAlgorithm and the key selected by keyId, // and compare with the expected result. Set result to true or // false depending on the outcome. virtual status_t verify(Vector const &sessionId, Vector const &keyId, Vector const &message, Vector const &signature, bool &match) = 0; // Compute an RSA signature on the provided message using the algorithm // specified by algorithm. virtual status_t signRSA(Vector const &sessionId, String8 const &algorithm, Vector const &message, Vector const &wrapped_key, Vector &signature) = 0; status_t setListener(const sp& listener) { Mutex::Autolock lock(mEventLock); mListener = listener; return OK; } protected: // Plugins call these methods to deliver events to the java app void sendEvent(EventType eventType, int extra, Vector const *sessionId, Vector const *data); void sendExpirationUpdate(Vector const *sessionId, int64_t expiryTimeInMS); void sendKeysChange(Vector const *sessionId, Vector const *keyStatusList, bool hasNewUsableKey); private: Mutex mEventLock; sp mListener; DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin); }; class DrmPluginListener: virtual public RefBase { public: virtual void sendEvent(DrmPlugin::EventType eventType, int extra, Vector const *sessionId, Vector const *data) = 0; virtual void sendExpirationUpdate(Vector const *sessionId, int64_t expiryTimeInMS) = 0; virtual void sendKeysChange(Vector const *sessionId, Vector const *keyStatusList, bool hasNewUsableKey) = 0; }; inline void DrmPlugin::sendEvent(EventType eventType, int extra, Vector const *sessionId, Vector const *data) { mEventLock.lock(); sp listener = mListener; mEventLock.unlock(); if (listener != NULL) { listener->sendEvent(eventType, extra, sessionId, data); } } inline void DrmPlugin::sendExpirationUpdate(Vector const *sessionId, int64_t expiryTimeInMS) { mEventLock.lock(); sp listener = mListener; mEventLock.unlock(); if (listener != NULL) { listener->sendExpirationUpdate(sessionId, expiryTimeInMS); } } inline void DrmPlugin::sendKeysChange(Vector const *sessionId, Vector const *keyStatusList, bool hasNewUsableKey) { mEventLock.lock(); sp listener = mListener; mEventLock.unlock(); if (listener != NULL) { listener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); } } } // namespace android #endif // DRM_API_H_ headers/media_plugin/media/editor/0040755 0000000 0000000 00000000000 13756501735 016241 5ustar000000000 0000000 headers/media_plugin/media/editor/II420ColorConverter.h0100644 0000000 0000000 00000012504 13756501735 022027 0ustar000000000 0000000 /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef II420_COLOR_CONVERTER_H #define II420_COLOR_CONVERTER_H #include #include #ifdef __cplusplus extern "C" { #endif typedef struct II420ColorConverter { /* * getDecoderOutputFormat * Returns the color format (OMX_COLOR_FORMATTYPE) of the decoder output. * If it is I420 (OMX_COLOR_FormatYUV420Planar), no conversion is needed, * and convertDecoderOutputToI420() can be a no-op. */ int (*getDecoderOutputFormat)(); /* * convertDecoderOutputToI420 * @Desc Converts from the decoder output format to I420 format. * @note Caller (e.g. VideoEditor) owns the buffers * @param decoderBits (IN) Pointer to the buffer contains decoder output * @param decoderWidth (IN) Buffer width, as reported by the decoder * metadata (kKeyWidth) * @param decoderHeight (IN) Buffer height, as reported by the decoder * metadata (kKeyHeight) * @param decoderRect (IN) The rectangle of the actual frame, as * reported by decoder metadata (kKeyCropRect) * @param dstBits (OUT) Pointer to the output I420 buffer * @return -1 Any error * @return 0 No Error */ int (*convertDecoderOutputToI420)( void* decoderBits, int decoderWidth, int decoderHeight, ARect decoderRect, void* dstBits); /* * getEncoderIntputFormat * Returns the color format (OMX_COLOR_FORMATTYPE) of the encoder input. * If it is I420 (OMX_COLOR_FormatYUV420Planar), no conversion is needed, * and convertI420ToEncoderInput() and getEncoderInputBufferInfo() can * be no-ops. */ int (*getEncoderInputFormat)(); /* convertI420ToEncoderInput * @Desc This function converts from I420 to the encoder input format * @note Caller (e.g. VideoEditor) owns the buffers * @param srcBits (IN) Pointer to the input I420 buffer * @param srcWidth (IN) Width of the I420 frame * @param srcHeight (IN) Height of the I420 frame * @param encoderWidth (IN) Encoder buffer width, as calculated by * getEncoderBufferInfo() * @param encoderHeight (IN) Encoder buffer height, as calculated by * getEncoderBufferInfo() * @param encoderRect (IN) Rect coordinates of the actual frame inside * the encoder buffer, as calculated by * getEncoderBufferInfo(). * @param encoderBits (OUT) Pointer to the output buffer. The size of * this buffer is calculated by * getEncoderBufferInfo() * @return -1 Any error * @return 0 No Error */ int (*convertI420ToEncoderInput)( void* srcBits, int srcWidth, int srcHeight, int encoderWidth, int encoderHeight, ARect encoderRect, void* encoderBits); /* getEncoderInputBufferInfo * @Desc This function returns metadata for the encoder input buffer * based on the actual I420 frame width and height. * @note This API should be be used to obtain the necessary information * before calling convertI420ToEncoderInput(). * VideoEditor knows only the width and height of the I420 buffer, * but it also needs know the width, height, and size of the * encoder input buffer. The encoder input buffer width and height * are used to set the metadata for the encoder. * @param srcWidth (IN) Width of the I420 frame * @param srcHeight (IN) Height of the I420 frame * @param encoderWidth (OUT) Encoder buffer width needed * @param encoderHeight (OUT) Encoder buffer height needed * @param encoderRect (OUT) Rect coordinates of the actual frame inside * the encoder buffer * @param encoderBufferSize (OUT) The size of the buffer that need to be * allocated by the caller before invoking * convertI420ToEncoderInput(). * @return -1 Any error * @return 0 No Error */ int (*getEncoderInputBufferInfo)( int srcWidth, int srcHeight, int* encoderWidth, int* encoderHeight, ARect* encoderRect, int* encoderBufferSize); } II420ColorConverter; /* The only function that the shared library needs to expose: It fills the function pointers in II420ColorConverter */ void getI420ColorConverter(II420ColorConverter *converter); #if defined(__cplusplus) } #endif #endif // II420_COLOR_CONVERTER_H headers/media_plugin/media/hardware/0040755 0000000 0000000 00000000000 13756501735 016550 5ustar000000000 0000000 headers/media_plugin/media/hardware/CryptoAPI.h0100644 0000000 0000000 00000010141 13756501735 020525 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #include #include #include #ifndef CRYPTO_API_H_ #define CRYPTO_API_H_ namespace android { struct AString; struct CryptoPlugin; struct CryptoFactory { CryptoFactory() {} virtual ~CryptoFactory() {} virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const = 0; virtual status_t createPlugin( const uint8_t uuid[16], const void *data, size_t size, CryptoPlugin **plugin) = 0; private: CryptoFactory(const CryptoFactory &); CryptoFactory &operator=(const CryptoFactory &); }; struct CryptoPlugin { enum Mode { kMode_Unencrypted = 0, kMode_AES_CTR = 1, kMode_AES_WV = 2, kMode_AES_CBC = 3, }; struct SubSample { uint32_t mNumBytesOfClearData; uint32_t mNumBytesOfEncryptedData; }; struct Pattern { // Number of blocks to be encrypted in the pattern. If zero, pattern // encryption is inoperative. uint32_t mEncryptBlocks; // Number of blocks to be skipped (left clear) in the pattern. If zero, // pattern encryption is inoperative. uint32_t mSkipBlocks; }; CryptoPlugin() {} virtual ~CryptoPlugin() {} // If this method returns false, a non-secure decoder will be used to // decode the data after decryption. The decrypt API below will have // to support insecure decryption of the data (secure = false) for // media data of the given mime type. virtual bool requiresSecureDecoderComponent(const char *mime) const = 0; // To implement resolution constraints, the crypto plugin needs to know // the resolution of the video being decrypted. The media player should // call this method when the resolution is determined and any time it // is subsequently changed. virtual void notifyResolution(uint32_t /* width */, uint32_t /* height */) {} // A MediaDrm session may be associated with a MediaCrypto session. The // associated MediaDrm session is used to load decryption keys // into the crypto/drm plugin. The keys are then referenced by key-id // in the 'key' parameter to the decrypt() method. // Should return NO_ERROR on success, ERROR_DRM_SESSION_NOT_OPENED if // the session is not opened and a code from MediaErrors.h otherwise. virtual status_t setMediaDrmSession(const Vector & /*sessionId */) { return ERROR_UNSUPPORTED; } // If the error returned falls into the range // ERROR_DRM_VENDOR_MIN..ERROR_DRM_VENDOR_MAX, errorDetailMsg should be // filled in with an appropriate string. // At the java level these special errors will then trigger a // MediaCodec.CryptoException that gives clients access to both // the error code and the errorDetailMsg. // Returns a non-negative result to indicate the number of bytes written // to the dstPtr, or a negative result to indicate an error. virtual ssize_t decrypt( bool secure, const uint8_t key[16], const uint8_t iv[16], Mode mode, const Pattern &pattern, const void *srcPtr, const SubSample *subSamples, size_t numSubSamples, void *dstPtr, AString *errorDetailMsg) = 0; private: CryptoPlugin(const CryptoPlugin &); CryptoPlugin &operator=(const CryptoPlugin &); }; } // namespace android extern "C" { extern android::CryptoFactory *createCryptoFactory(); } #endif // CRYPTO_API_H_ headers/media_plugin/media/hardware/HDCPAPI.h0100644 0000000 0000000 00000015665 13756501735 020003 0ustar000000000 0000000 /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HDCP_API_H_ #define HDCP_API_H_ #include #include namespace android { // Two different kinds of modules are covered under the same HDCPModule // structure below, a module either implements decryption or encryption. struct HDCPModule { typedef void (*ObserverFunc)(void *cookie, int msg, int ext1, int ext2); // The msg argument in calls to the observer notification function. enum { // Sent in response to a call to "HDCPModule::initAsync" once // initialization has either been successfully completed, // i.e. the HDCP session is now fully setup (AKE, Locality Check, // SKE and any authentication with repeaters completed) or failed. // ext1 should be a suitable error code (status_t), ext2 is // unused for ENCRYPTION and in the case of HDCP_INITIALIZATION_COMPLETE // holds the local TCP port the module is listening on. HDCP_INITIALIZATION_COMPLETE, HDCP_INITIALIZATION_FAILED, // Sent upon completion of a call to "HDCPModule::shutdownAsync". // ext1 should be a suitable error code, ext2 is unused. HDCP_SHUTDOWN_COMPLETE, HDCP_SHUTDOWN_FAILED, HDCP_UNAUTHENTICATED_CONNECTION, HDCP_UNAUTHORIZED_CONNECTION, HDCP_REVOKED_CONNECTION, HDCP_TOPOLOGY_EXECEEDED, HDCP_UNKNOWN_ERROR, // DECRYPTION only: Indicates that a client has successfully connected, // a secure session established and the module is ready to accept // future calls to "decrypt". HDCP_SESSION_ESTABLISHED, }; // HDCPModule capability bit masks enum { // HDCP_CAPS_ENCRYPT: mandatory, meaning the HDCP module can encrypt // from an input byte-array buffer to an output byte-array buffer HDCP_CAPS_ENCRYPT = (1 << 0), // HDCP_CAPS_ENCRYPT_NATIVE: the HDCP module supports encryption from // a native buffer to an output byte-array buffer. The format of the // input native buffer is specific to vendor's encoder implementation. // It is the same format as that used by the encoder when // "storeMetaDataInBuffers" extension is enabled on its output port. HDCP_CAPS_ENCRYPT_NATIVE = (1 << 1), }; // Module can call the notification function to signal completion/failure // of asynchronous operations (such as initialization) or out of band // events. HDCPModule(void * /*cookie*/, ObserverFunc /*observerNotify*/) {}; virtual ~HDCPModule() {}; // ENCRYPTION: Request to setup an HDCP session with the host specified // by addr and listening on the specified port. // DECRYPTION: Request to setup an HDCP session, addr is the interface // address the module should bind its socket to. port will be 0. // The module will pick the port to listen on itself and report its choice // in the "ext2" argument of the HDCP_INITIALIZATION_COMPLETE callback. virtual status_t initAsync(const char *addr, unsigned port) = 0; // Request to shutdown the active HDCP session. virtual status_t shutdownAsync() = 0; // Returns the capability bitmask of this HDCP session. virtual uint32_t getCaps() { return HDCP_CAPS_ENCRYPT; } // ENCRYPTION only: // Encrypt data according to the HDCP spec. "size" bytes of data are // available at "inData" (virtual address), "size" may not be a multiple // of 128 bits (16 bytes). An equal number of encrypted bytes should be // written to the buffer at "outData" (virtual address). // This operation is to be synchronous, i.e. this call does not return // until outData contains size bytes of encrypted data. // streamCTR will be assigned by the caller (to 0 for the first PES stream, // 1 for the second and so on) // inputCTR _will_be_maintained_by_the_callee_ for each PES stream. virtual status_t encrypt( const void * /*inData*/, size_t /*size*/, uint32_t /*streamCTR*/, uint64_t * /*outInputCTR*/, void * /*outData*/) { return INVALID_OPERATION; } // Encrypt data according to the HDCP spec. "size" bytes of data starting // at location "offset" are available in "buffer" (buffer handle). "size" // may not be a multiple of 128 bits (16 bytes). An equal number of // encrypted bytes should be written to the buffer at "outData" (virtual // address). This operation is to be synchronous, i.e. this call does not // return until outData contains size bytes of encrypted data. // streamCTR will be assigned by the caller (to 0 for the first PES stream, // 1 for the second and so on) // inputCTR _will_be_maintained_by_the_callee_ for each PES stream. virtual status_t encryptNative( buffer_handle_t /*buffer*/, size_t /*offset*/, size_t /*size*/, uint32_t /*streamCTR*/, uint64_t * /*outInputCTR*/, void * /*outData*/) { return INVALID_OPERATION; } // DECRYPTION only: // Decrypt data according to the HDCP spec. // "size" bytes of encrypted data are available at "inData" // (virtual address), "size" may not be a multiple of 128 bits (16 bytes). // An equal number of decrypted bytes should be written to the buffer // at "outData" (virtual address). // This operation is to be synchronous, i.e. this call does not return // until outData contains size bytes of decrypted data. // Both streamCTR and inputCTR will be provided by the caller. virtual status_t decrypt( const void * /*inData*/, size_t /*size*/, uint32_t /*streamCTR*/, uint64_t /*inputCTR*/, void * /*outData*/) { return INVALID_OPERATION; } private: HDCPModule(const HDCPModule &); HDCPModule &operator=(const HDCPModule &); }; } // namespace android // A shared library exporting the following methods should be included to // support HDCP functionality. The shared library must be called // "libstagefright_hdcp.so", it will be dynamically loaded into the // mediaserver process. extern "C" { // Create a module for ENCRYPTION. extern android::HDCPModule *createHDCPModule( void *cookie, android::HDCPModule::ObserverFunc); // Create a module for DECRYPTION. extern android::HDCPModule *createHDCPModuleForDecryption( void *cookie, android::HDCPModule::ObserverFunc); } #endif // HDCP_API_H_ headers/media_plugin/media/hardware/HardwareAPI.h0100644 0000000 0000000 00000071670 13756501735 021020 0ustar000000000 0000000 /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HARDWARE_API_H_ #define HARDWARE_API_H_ #include #include #include #include #include "VideoAPI.h" #include struct ANativeWindowBuffer; namespace android { // This structure is used to enable Android native buffer use for either // graphic buffers or secure buffers. // // TO CONTROL ANDROID GRAPHIC BUFFER USAGE: // // A pointer to this struct is passed to the OMX_SetParameter when the extension // index for the 'OMX.google.android.index.enableAndroidNativeBuffers' extension // is given. // // When Android native buffer use is disabled for a port (the default state), // the OMX node should operate as normal, and expect UseBuffer calls to set its // buffers. This is the mode that will be used when CPU access to the buffer is // required. // // When Android native buffer use has been enabled for a given port, the video // color format for the port is to be interpreted as an Android pixel format // rather than an OMX color format. Enabling Android native buffers may also // change how the component receives the native buffers. If store-metadata-mode // is enabled on the port, the component will receive the buffers as specified // in the section below. Otherwise, unless the node supports the // 'OMX.google.android.index.useAndroidNativeBuffer2' extension, it should // expect to receive UseAndroidNativeBuffer calls (via OMX_SetParameter) rather // than UseBuffer calls for that port. // // TO CONTROL ANDROID SECURE BUFFER USAGE: // // A pointer to this struct is passed to the OMX_SetParameter when the extension // index for the 'OMX.google.android.index.allocateNativeHandle' extension // is given. // // When native handle use is disabled for a port (the default state), // the OMX node should operate as normal, and expect AllocateBuffer calls to // return buffer pointers. This is the mode that will be used for non-secure // buffers if component requires allocate buffers instead of use buffers. // // When native handle use has been enabled for a given port, the component // shall allocate native_buffer_t objects containing that can be passed between // processes using binder. This is the mode that will be used for secure buffers. // When an OMX component allocates native handle for buffers, it must close and // delete that handle when it frees those buffers. Even though pBuffer will point // to a native handle, nFilledLength, nAllocLength and nOffset will correspond // to the data inside the opaque buffer. struct EnableAndroidNativeBuffersParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL enable; }; typedef struct EnableAndroidNativeBuffersParams AllocateNativeHandleParams; // A pointer to this struct is passed to OMX_SetParameter() when the extension index // "OMX.google.android.index.storeMetaDataInBuffers" or // "OMX.google.android.index.storeANWBufferInMetadata" is given. // // When meta data is stored in the video buffers passed between OMX clients // and OMX components, interpretation of the buffer data is up to the // buffer receiver, and the data may or may not be the actual video data, but // some information helpful for the receiver to locate the actual data. // The buffer receiver thus needs to know how to interpret what is stored // in these buffers, with mechanisms pre-determined externally. How to // interpret the meta data is outside of the scope of this parameter. // // Currently, this is used to pass meta data from video source (camera component, for instance) to // video encoder to avoid memcpying of input video frame data, as well as to pass dynamic output // buffer to video decoder. To do this, bStoreMetaData is set to OMX_TRUE. // // If bStoreMetaData is set to false, real YUV frame data will be stored in input buffers, and // the output buffers contain either real YUV frame data, or are themselves native handles as // directed by enable/use-android-native-buffer parameter settings. // In addition, if no OMX_SetParameter() call is made on a port with the corresponding extension // index, the component should not assume that the client is not using metadata mode for the port. // // If the component supports this using the "OMX.google.android.index.storeANWBufferInMetadata" // extension and bStoreMetaData is set to OMX_TRUE, data is passed using the VideoNativeMetadata // layout as defined below. Each buffer will be accompanied by a fence. The fence must signal // before the buffer can be used (e.g. read from or written into). When returning such buffer to // the client, component must provide a new fence that must signal before the returned buffer can // be used (e.g. read from or written into). The component owns the incoming fenceFd, and must close // it when fence has signaled. The client will own and close the returned fence file descriptor. // // If the component supports this using the "OMX.google.android.index.storeMetaDataInBuffers" // extension and bStoreMetaData is set to OMX_TRUE, data is passed using VideoGrallocMetadata // (the layout of which is the VideoGrallocMetadata defined below). Camera input can be also passed // as "CameraSource", the layout of which is vendor dependent. // // Metadata buffers are registered with the component using UseBuffer calls, or can be allocated // by the component for encoder-metadata-output buffers. struct StoreMetaDataInBuffersParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bStoreMetaData; }; // Meta data buffer layout used to transport output frames to the decoder for // dynamic buffer handling. struct VideoGrallocMetadata { MetadataBufferType eType; // must be kMetadataBufferTypeGrallocSource #ifdef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS OMX_PTR pHandle; #else buffer_handle_t pHandle; #endif }; // Legacy name for VideoGrallocMetadata struct. struct VideoDecoderOutputMetaData : public VideoGrallocMetadata {}; struct VideoNativeMetadata { MetadataBufferType eType; // must be kMetadataBufferTypeANWBuffer #ifdef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS OMX_PTR pBuffer; #else struct ANativeWindowBuffer* pBuffer; #endif int nFenceFd; // -1 if unused }; // Meta data buffer layout for passing a native_handle to codec struct VideoNativeHandleMetadata { MetadataBufferType eType; // must be kMetadataBufferTypeNativeHandleSource #ifdef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS OMX_PTR pHandle; #else native_handle_t *pHandle; #endif }; // A pointer to this struct is passed to OMX_SetParameter() when the extension // index "OMX.google.android.index.prepareForAdaptivePlayback" is given. // // This method is used to signal a video decoder, that the user has requested // seamless resolution change support (if bEnable is set to OMX_TRUE). // nMaxFrameWidth and nMaxFrameHeight are the dimensions of the largest // anticipated frames in the video. If bEnable is OMX_FALSE, no resolution // change is expected, and the nMaxFrameWidth/Height fields are unused. // // If the decoder supports dynamic output buffers, it may ignore this // request. Otherwise, it shall request resources in such a way so that it // avoids full port-reconfiguration (due to output port-definition change) // during resolution changes. // // DO NOT USE THIS STRUCTURE AS IT WILL BE REMOVED. INSTEAD, IMPLEMENT // METADATA SUPPORT FOR VIDEO DECODERS. struct PrepareForAdaptivePlaybackParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bEnable; OMX_U32 nMaxFrameWidth; OMX_U32 nMaxFrameHeight; }; // A pointer to this struct is passed to OMX_SetParameter when the extension // index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is // given. This call will only be performed if a prior call was made with the // 'OMX.google.android.index.enableAndroidNativeBuffers' extension index, // enabling use of Android native buffers. struct UseAndroidNativeBufferParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_PTR pAppPrivate; OMX_BUFFERHEADERTYPE **bufferHeader; const sp& nativeBuffer; }; // A pointer to this struct is passed to OMX_GetParameter when the extension // index for the 'OMX.google.android.index.getAndroidNativeBufferUsage' // extension is given. The usage bits returned from this query will be used to // allocate the Gralloc buffers that get passed to the useAndroidNativeBuffer // command. struct GetAndroidNativeBufferUsageParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_U32 nUsage; // OUT }; // An enum OMX_COLOR_FormatAndroidOpaque to indicate an opaque colorformat // is declared in media/stagefright/openmax/OMX_IVCommon.h // This will inform the encoder that the actual // colorformat will be relayed by the GRalloc Buffers. // OMX_COLOR_FormatAndroidOpaque = 0x7F000001, // A pointer to this struct is passed to OMX_SetParameter when the extension // index for the 'OMX.google.android.index.prependSPSPPSToIDRFrames' extension // is given. // A successful result indicates that future IDR frames will be prefixed by // SPS/PPS. struct PrependSPSPPSToIDRFramesParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_BOOL bEnable; }; // A pointer to this struct is passed to OMX_GetParameter when the extension // index for the 'OMX.google.android.index.describeColorFormat' // extension is given. This method can be called from any component state // other than invalid. The color-format, frame width/height, and stride/ // slice-height parameters are ones that are associated with a raw video // port (input or output), but the stride/slice height parameters may be // incorrect. bUsingNativeBuffers is OMX_TRUE if native android buffers will // be used (while specifying this color format). // // The component shall fill out the MediaImage structure that // corresponds to the described raw video format, and the potentially corrected // stride and slice-height info. // // The behavior is slightly different if bUsingNativeBuffers is OMX_TRUE, // though most implementations can ignore this difference. When using native buffers, // the component may change the configured color format to an optimized format. // Additionally, when allocating these buffers for flexible usecase, the framework // will set the SW_READ/WRITE_OFTEN usage flags. In this case (if bUsingNativeBuffers // is OMX_TRUE), the component shall fill out the MediaImage information for the // scenario when these SW-readable/writable buffers are locked using gralloc_lock. // Note, that these buffers may also be locked using gralloc_lock_ycbcr, which must // be supported for vendor-specific formats. // // For non-YUV packed planar/semiplanar image formats, or if bUsingNativeBuffers // is OMX_TRUE and the component does not support this color format with native // buffers, the component shall set mNumPlanes to 0, and mType to MEDIA_IMAGE_TYPE_UNKNOWN. // @deprecated: use DescribeColorFormat2Params struct DescribeColorFormat2Params; struct DescribeColorFormatParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; // input: parameters from OMX_VIDEO_PORTDEFINITIONTYPE OMX_COLOR_FORMATTYPE eColorFormat; OMX_U32 nFrameWidth; OMX_U32 nFrameHeight; OMX_U32 nStride; OMX_U32 nSliceHeight; OMX_BOOL bUsingNativeBuffers; // output: fill out the MediaImage fields MediaImage sMediaImage; explicit DescribeColorFormatParams(const DescribeColorFormat2Params&); // for internal use only }; // A pointer to this struct is passed to OMX_GetParameter when the extension // index for the 'OMX.google.android.index.describeColorFormat2' // extension is given. This is operationally the same as DescribeColorFormatParams // but can be used for HDR and RGBA/YUVA formats. struct DescribeColorFormat2Params { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; // input: parameters from OMX_VIDEO_PORTDEFINITIONTYPE OMX_COLOR_FORMATTYPE eColorFormat; OMX_U32 nFrameWidth; OMX_U32 nFrameHeight; OMX_U32 nStride; OMX_U32 nSliceHeight; OMX_BOOL bUsingNativeBuffers; // output: fill out the MediaImage2 fields MediaImage2 sMediaImage; void initFromV1(const DescribeColorFormatParams&); // for internal use only }; // A pointer to this struct is passed to OMX_SetParameter or OMX_GetParameter // when the extension index for the // 'OMX.google.android.index.configureVideoTunnelMode' extension is given. // If the extension is supported then tunneled playback mode should be supported // by the codec. If bTunneled is set to OMX_TRUE then the video decoder should // operate in "tunneled" mode and output its decoded frames directly to the // sink. In this case nAudioHwSync is the HW SYNC ID of the audio HAL Output // stream to sync the video with. If bTunneled is set to OMX_FALSE, "tunneled" // mode should be disabled and nAudioHwSync should be ignored. // OMX_GetParameter is used to query tunneling configuration. bTunneled should // return whether decoder is operating in tunneled mode, and if it is, // pSidebandWindow should contain the codec allocated sideband window handle. struct ConfigureVideoTunnelModeParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_BOOL bTunneled; // IN/OUT OMX_U32 nAudioHwSync; // IN OMX_PTR pSidebandWindow; // OUT }; // Color space description (aspects) parameters. // This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the // 'OMX.google.android.index.describeColorAspects' extension is given. Component SHALL behave // as described below if it supports this extension. // // bDataSpaceChanged and bRequestingDataSpace is assumed to be OMX_FALSE unless noted otherwise. // // VIDEO ENCODERS: the framework uses OMX_SetConfig to specify color aspects of the coded video. // This may happen: // a) before the component transitions to idle state // b) before the input frame is sent via OMX_EmptyThisBuffer in executing state // c) during execution, just before an input frame with a different color aspect information // is sent. // // The framework also uses OMX_GetConfig to // d) verify the color aspects that will be written to the stream // e) (optional) verify the color aspects that should be reported to the container for a // given dataspace/pixelformat received // // 1. Encoders SHOULD maintain an internal color aspect state, initialized to Unspecified values. // This represents the values that will be written into the bitstream. // 2. Upon OMX_SetConfig, they SHOULD update their internal state to the aspects received // (including Unspecified values). For specific aspect values that are not supported by the // codec standard, encoders SHOULD substitute Unspecified values; or they MAY use a suitable // alternative (e.g. to suggest the use of BT.709 EOTF instead of SMPTE 240M.) // 3. OMX_GetConfig SHALL return the internal state (values that will be written). // 4. OMX_SetConfig SHALL always succeed before receiving the first frame. It MAY fail afterwards, // but only if the configured values would change AND the component does not support updating the // color information to those values mid-stream. If component supports updating a portion of // the color information, those values should be updated in the internal state, and OMX_SetConfig // SHALL succeed. Otherwise, the internal state SHALL remain intact and OMX_SetConfig SHALL fail // with OMX_ErrorUnsupportedSettings. // 5. When the framework receives an input frame with an unexpected dataspace, it will query // encoders for the color aspects that should be reported to the container using OMX_GetConfig // with bDataSpaceChanged set to OMX_TRUE, and nPixelFormat/nDataSpace containing the new // format/dataspace values. This allows vendors to use extended dataspace during capture and // composition (e.g. screenrecord) - while performing color-space conversion inside the encoder - // and encode and report a different color-space information in the bitstream/container. // sColorAspects contains the requested color aspects by the client for reference, which may // include aspects not supported by the encoding. This is used together with guidance for // dataspace selection; see 6. below. // // VIDEO DECODERS: the framework uses OMX_SetConfig to specify the default color aspects to use // for the video. // This may happen: // a) before the component transitions to idle state // b) during execution, when the resolution or the default color aspects change. // // The framework also uses OMX_GetConfig to // c) get the final color aspects reported by the coded bitstream after taking the default values // into account. // // 1. Decoders should maintain two color aspect states - the default state as reported by the // framework, and the coded state as reported by the bitstream - as each state can change // independently from the other. // 2. Upon OMX_SetConfig, it SHALL update its default state regardless of whether such aspects // could be supplied by the component bitstream. (E.g. it should blindly support all enumeration // values, even unknown ones, and the Other value). This SHALL always succeed. // 3. Upon OMX_GetConfig, the component SHALL return the final color aspects by replacing // Unspecified coded values with the default values. This SHALL always succeed. // 4. Whenever the component processes color aspect information in the bitstream even with an // Unspecified value, it SHOULD update its internal coded state with that information just before // the frame with the new information would be outputted, and the component SHALL signal an // OMX_EventPortSettingsChanged event with data2 set to the extension index. // NOTE: Component SHOULD NOT signal a separate event purely for color aspect change, if it occurs // together with a port definition (e.g. size) or crop change. // 5. If the aspects a component encounters in the bitstream cannot be represented with enumeration // values as defined below, the component SHALL set those aspects to Other. Restricted values in // the bitstream SHALL be treated as defined by the relevant bitstream specifications/standards, // or as Unspecified, if not defined. // // BOTH DECODERS AND ENCODERS: the framework uses OMX_GetConfig during idle and executing state to // f) (optional) get guidance for the dataspace to set for given color aspects, by setting // bRequestingDataSpace to OMX_TRUE. The component SHALL return OMX_ErrorUnsupportedSettings // IF it does not support this request. // // 6. This is an information request that can happen at any time, independent of the normal // configuration process. This allows vendors to use extended dataspace during capture, playback // and composition - while performing color-space conversion inside the component. Component // SHALL set the desired dataspace into nDataSpace. Otherwise, it SHALL return // OMX_ErrorUnsupportedSettings to let the framework choose a nearby standard dataspace. // // 6.a. For encoders, this query happens before the first frame is received using surface encoding. // This allows the encoder to use a specific dataspace for the color aspects (e.g. because the // device supports additional dataspaces, or because it wants to perform color-space extension // to facilitate a more optimal rendering/capture pipeline.). // // 6.b. For decoders, this query happens before the first frame, and every time the color aspects // change, while using surface buffers. This allows the decoder to use a specific dataspace for // the color aspects (e.g. because the device supports additional dataspaces, or because it wants // to perform color-space extension by inline color-space conversion to facilitate a more optimal // rendering pipeline.). // // Note: the size of sAspects may increase in the future by additional fields. // Implementations SHOULD NOT require a certain size. struct DescribeColorAspectsParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_BOOL bRequestingDataSpace; // IN OMX_BOOL bDataSpaceChanged; // IN OMX_U32 nPixelFormat; // IN OMX_U32 nDataSpace; // OUT ColorAspects sAspects; // IN/OUT }; // HDR color description parameters. // This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the // 'OMX.google.android.index.describeHDRStaticInfo' extension is given and an HDR stream // is detected. Component SHALL behave as described below if it supports this extension. // // Currently, only Static Metadata Descriptor Type 1 support is required. // // VIDEO ENCODERS: the framework uses OMX_SetConfig to specify the HDR static information of the // coded video. // This may happen: // a) before the component transitions to idle state // b) before the input frame is sent via OMX_EmptyThisBuffer in executing state // c) during execution, just before an input frame with a different HDR static // information is sent. // // The framework also uses OMX_GetConfig to // d) verify the HDR static information that will be written to the stream. // // 1. Encoders SHOULD maintain an internal HDR static info data, initialized to Unspecified values. // This represents the values that will be written into the bitstream. // 2. Upon OMX_SetConfig, they SHOULD update their internal state to the info received // (including Unspecified values). For specific parameters that are not supported by the // codec standard, encoders SHOULD substitute Unspecified values. NOTE: no other substitution // is allowed. // 3. OMX_GetConfig SHALL return the internal state (values that will be written). // 4. OMX_SetConfig SHALL always succeed before receiving the first frame if the encoder is // configured into an HDR compatible profile. It MAY fail with OMX_ErrorUnsupportedSettings error // code if it is not configured into such a profile, OR if the configured values would change // AND the component does not support updating the HDR static information mid-stream. If the // component supports updating a portion of the information, those values should be updated in // the internal state, and OMX_SetConfig SHALL succeed. Otherwise, the internal state SHALL // remain intact. // // VIDEO DECODERS: the framework uses OMX_SetConfig to specify the default HDR static information // to use for the video. // a) This only happens if the client supplies this information, in which case it occurs before // the component transitions to idle state. // b) This may also happen subsequently if the default HDR static information changes. // // The framework also uses OMX_GetConfig to // c) get the final HDR static information reported by the coded bitstream after taking the // default values into account. // // 1. Decoders should maintain two HDR static information structures - the default values as // reported by the framework, and the coded values as reported by the bitstream - as each // structure can change independently from the other. // 2. Upon OMX_SetConfig, it SHALL update its default structure regardless of whether such static // parameters could be supplied by the component bitstream. (E.g. it should blindly support all // parameter values, even seemingly illegal ones). This SHALL always succeed. // Note: The descriptor ID used in sInfo may change in subsequent calls. (although for now only // Type 1 support is required.) // 3. Upon OMX_GetConfig, the component SHALL return the final HDR static information by replacing // Unspecified coded values with the default values. This SHALL always succeed. This may be // provided using any supported descriptor ID (currently only Type 1) with the goal of expressing // the most of the available static information. // 4. Whenever the component processes HDR static information in the bitstream even ones with // Unspecified parameters, it SHOULD update its internal coded structure with that information // just before the frame with the new information would be outputted, and the component SHALL // signal an OMX_EventPortSettingsChanged event with data2 set to the extension index. // NOTE: Component SHOULD NOT signal a separate event purely for HDR static info change, if it // occurs together with a port definition (e.g. size), color aspect or crop change. // 5. If certain parameters of the HDR static information encountered in the bitstream cannot be // represented using sInfo, the component SHALL use the closest representation. // // Note: the size of sInfo may increase in the future by supporting additional descriptor types. // Implementations SHOULD NOT require a certain size. struct DescribeHDRStaticInfoParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN HDRStaticInfo sInfo; // IN/OUT }; // HDR10+ metadata configuration. // // nParamSize: size of the storage starting at nValue (must be at least 1 and at most // MAX_HDR10PLUSINFO_SIZE). This field must not be modified by the component. // nParamSizeUsed: size of the actual HDR10+ metadata starting at nValue. For OMX_SetConfig, // it must not be modified by the component. For OMX_GetConfig, the component // should put the actual size of the retrieved config in this field (and in // case where nParamSize is smaller than nParamSizeUsed, the component should // still update nParamSizeUsed without actually copying the metadata to nValue). // nValue: storage of the HDR10+ metadata conforming to the user_data_registered_itu_t_t35() // syntax of SEI message for ST 2094-40. // // This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the // 'OMX.google.android.index.describeHDR10PlusInfo' extension is given. In general, this config // is associated with a particular frame. A typical sequence of usage is as follows: // // a) OMX_SetConfig associates the config with the next input buffer sent in OMX_EmptyThisBuffer // (input A); // b) The component sends OMX_EventConfigUpdate to notify the client that there is a config // update on the output port that is associated with the next output buffer that's about to // be sent via FillBufferDone callback (output A); // c) The client, upon receiving the OMX_EventConfigUpdate, calls OMX_GetConfig to retrieve // the config and associates it with output A. // // All config updates will be retrieved in the order reported, and the client is required to // call OMX_GetConfig for each OMX_EventConfigUpdate for this config. Note that the order of // OMX_EventConfigUpdate relative to FillBufferDone callback determines which output frame // the config should be associated with, the actual OMX_GetConfig for the config could happen // before or after the component calls the FillBufferDone callback. // // Depending on the video codec type (in particular, whether the codec uses in-band or out-of- // band HDR10+ metadata), the component shall behave as detailed below: // // VIDEO DECODERS: // 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence // a) ~ c) outlined above; // 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config should be // ignored (as the metadata is embedded in the input buffer), while the notification and // retrieval of the config on the output as outlined in b) & c) must be supported. // // VIDEO ENCODERS: // 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence // a) ~ c) outlined above; // 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config outlined in // a) must be supported. The notification as outlined in b) must not be sent, and the // retrieval of the config via OMX_GetConfig should be ignored (as the metadata is embedded // in the output buffer). #define MAX_HDR10PLUSINFO_SIZE 1024 struct DescribeHDR10PlusInfoParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_U32 nParamSize; // IN OMX_U32 nParamSizeUsed; // IN/OUT OMX_U8 nValue[1]; // IN/OUT }; } // namespace android extern android::OMXPluginBase *createOMXPlugin(); #endif // HARDWARE_API_H_ headers/media_plugin/media/hardware/MetadataBufferType.h0100644 0000000 0000000 00000014347 13756501735 022443 0ustar000000000 0000000 /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef METADATA_BUFFER_TYPE_H #define METADATA_BUFFER_TYPE_H #ifdef __cplusplus extern "C" { namespace android { #endif /* * MetadataBufferType defines the type of the metadata buffers that * can be passed to video encoder component for encoding, via Stagefright * media recording framework. To see how to work with the metadata buffers * in media recording framework, please consult HardwareAPI.h * * The creator of metadata buffers and video encoder share common knowledge * on what is actually being stored in these metadata buffers, and * how the information can be used by the video encoder component * to locate the actual pixel data as the source input for video * encoder, plus whatever other information that is necessary. Stagefright * media recording framework does not need to know anything specific about the * metadata buffers, except for receving each individual metadata buffer * as the source input, making a copy of the metadata buffer, and passing the * copy via OpenMAX API to the video encoder component. * * The creator of the metadata buffers must ensure that the first * 4 bytes in every metadata buffer indicates its buffer type, * and the rest of the metadata buffer contains the * actual metadata information. When a video encoder component receives * a metadata buffer, it uses the first 4 bytes in that buffer to find * out the type of the metadata buffer, and takes action appropriate * to that type of metadata buffers (for instance, locate the actual * pixel data input and then encoding the input data to produce a * compressed output buffer). * * The following shows the layout of a metadata buffer, * where buffer type is a 4-byte field of MetadataBufferType, * and the payload is the metadata information. * * -------------------------------------------------------------- * | buffer type | payload | * -------------------------------------------------------------- * */ typedef enum { /* * kMetadataBufferTypeCameraSource is used to indicate that * the source of the metadata buffer is the camera component. */ kMetadataBufferTypeCameraSource = 0, /* * kMetadataBufferTypeGrallocSource is used to indicate that * the payload of the metadata buffers can be interpreted as * a buffer_handle_t. * So in this case,the metadata that the encoder receives * will have a byte stream that consists of two parts: * 1. First, there is an integer indicating that it is a GRAlloc * source (kMetadataBufferTypeGrallocSource) * 2. This is followed by the buffer_handle_t that is a handle to the * GRalloc buffer. The encoder needs to interpret this GRalloc handle * and encode the frames. * -------------------------------------------------------------- * | kMetadataBufferTypeGrallocSource | buffer_handle_t buffer | * -------------------------------------------------------------- * * See the VideoGrallocMetadata structure. */ kMetadataBufferTypeGrallocSource = 1, /* * kMetadataBufferTypeGraphicBuffer is used to indicate that * the payload of the metadata buffers can be interpreted as * an ANativeWindowBuffer, and that a fence is provided. * * In this case, the metadata will have a byte stream that consists of three parts: * 1. First, there is an integer indicating that the metadata * contains an ANativeWindowBuffer (kMetadataBufferTypeANWBuffer) * 2. This is followed by the pointer to the ANativeWindowBuffer. * Codec must not free this buffer as it does not actually own this buffer. * 3. Finally, there is an integer containing a fence file descriptor. * The codec must wait on the fence before encoding or decoding into this * buffer. When the buffer is returned, codec must replace this file descriptor * with a new fence, that will be waited on before the buffer is replaced * (encoder) or read (decoder). * --------------------------------- * | kMetadataBufferTypeANWBuffer | * --------------------------------- * | ANativeWindowBuffer *buffer | * --------------------------------- * | int fenceFd | * --------------------------------- * * See the VideoNativeMetadata structure. */ kMetadataBufferTypeANWBuffer = 2, /* * kMetadataBufferTypeNativeHandleSource is used to indicate that * the payload of the metadata buffers can be interpreted as * a native_handle_t. * * In this case, the metadata that the encoder receives * will have a byte stream that consists of two parts: * 1. First, there is an integer indicating that the metadata contains a * native handle (kMetadataBufferTypeNativeHandleSource). * 2. This is followed by a pointer to native_handle_t. The encoder needs * to interpret this native handle and encode the frame. The encoder must * not free this native handle as it does not actually own this native * handle. The handle will be freed after the encoder releases the buffer * back to camera. * ---------------------------------------------------------------- * | kMetadataBufferTypeNativeHandleSource | native_handle_t* nh | * ---------------------------------------------------------------- * * See the VideoNativeHandleMetadata structure. */ kMetadataBufferTypeNativeHandleSource = 3, /* This value is used by framework, but is never used inside a metadata buffer */ kMetadataBufferTypeInvalid = -1, // Add more here... } MetadataBufferType; #ifdef __cplusplus } // namespace android } #endif #endif // METADATA_BUFFER_TYPE_H headers/media_plugin/media/hardware/OMXPluginBase.h0100644 0000000 0000000 00000003103 13756501735 021330 0ustar000000000 0000000 /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OMX_PLUGIN_BASE_H_ #define OMX_PLUGIN_BASE_H_ #include #include #include #include namespace android { struct OMXPluginBase { OMXPluginBase() {} virtual ~OMXPluginBase() {} virtual OMX_ERRORTYPE makeComponentInstance( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) = 0; virtual OMX_ERRORTYPE destroyComponentInstance( OMX_COMPONENTTYPE *component) = 0; virtual OMX_ERRORTYPE enumerateComponents( OMX_STRING name, size_t size, OMX_U32 index) = 0; virtual OMX_ERRORTYPE getRolesOfComponent( const char *name, Vector *roles) = 0; private: OMXPluginBase(const OMXPluginBase &); OMXPluginBase &operator=(const OMXPluginBase &); }; } // namespace android #endif // OMX_PLUGIN_BASE_H_ headers/media_plugin/media/hardware/VideoAPI.h0100644 0000000 0000000 00000033167 13756501735 020330 0ustar000000000 0000000 /* * 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. */ #ifndef VIDEO_API_H_ #define VIDEO_API_H_ namespace android { /** * Structure describing a media image (frame) * Currently only supporting YUV * @deprecated. Use MediaImage2 instead */ struct MediaImage { enum Type { MEDIA_IMAGE_TYPE_UNKNOWN = 0, MEDIA_IMAGE_TYPE_YUV, }; enum PlaneIndex { Y = 0, U, V, MAX_NUM_PLANES }; Type mType; uint32_t mNumPlanes; // number of planes uint32_t mWidth; // width of largest plane (unpadded, as in nFrameWidth) uint32_t mHeight; // height of largest plane (unpadded, as in nFrameHeight) uint32_t mBitDepth; // useable bit depth struct PlaneInfo { uint32_t mOffset; // offset of first pixel of the plane in bytes // from buffer offset uint32_t mColInc; // column increment in bytes uint32_t mRowInc; // row increment in bytes uint32_t mHorizSubsampling; // subsampling compared to the largest plane uint32_t mVertSubsampling; // subsampling compared to the largest plane }; PlaneInfo mPlane[MAX_NUM_PLANES]; }; /** * Structure describing a media image (frame) */ struct __attribute__ ((__packed__)) MediaImage2 { enum Type : uint32_t { MEDIA_IMAGE_TYPE_UNKNOWN = 0, MEDIA_IMAGE_TYPE_YUV, MEDIA_IMAGE_TYPE_YUVA, MEDIA_IMAGE_TYPE_RGB, MEDIA_IMAGE_TYPE_RGBA, MEDIA_IMAGE_TYPE_Y, }; enum PlaneIndex : uint32_t { Y = 0, U = 1, V = 2, R = 0, G = 1, B = 2, A = 3, MAX_NUM_PLANES = 4, }; Type mType; uint32_t mNumPlanes; // number of planes uint32_t mWidth; // width of largest plane (unpadded, as in nFrameWidth) uint32_t mHeight; // height of largest plane (unpadded, as in nFrameHeight) uint32_t mBitDepth; // useable bit depth (always MSB) uint32_t mBitDepthAllocated; // bits per component (must be 8 or 16) struct __attribute__ ((__packed__)) PlaneInfo { uint32_t mOffset; // offset of first pixel of the plane in bytes // from buffer offset int32_t mColInc; // column increment in bytes int32_t mRowInc; // row increment in bytes uint32_t mHorizSubsampling; // subsampling compared to the largest plane uint32_t mVertSubsampling; // subsampling compared to the largest plane }; PlaneInfo mPlane[MAX_NUM_PLANES]; void initFromV1(const MediaImage&); // for internal use only }; static_assert(sizeof(MediaImage2::PlaneInfo) == 20, "wrong struct size"); static_assert(sizeof(MediaImage2) == 104, "wrong struct size"); /** * Aspects of color. */ // NOTE: this structure is expected to grow in the future if new color aspects are // added to codec bitstreams. OMX component should not require a specific nSize // though could verify that nSize is at least the size of the structure at the // time of implementation. All new fields will be added at the end of the structure // ensuring backward compatibility. struct __attribute__ ((__packed__, aligned(alignof(uint32_t)))) ColorAspects { // this is in sync with the range values in graphics.h enum Range : uint32_t { RangeUnspecified, RangeFull, RangeLimited, RangeOther = 0xff, }; enum Primaries : uint32_t { PrimariesUnspecified, PrimariesBT709_5, // Rec.ITU-R BT.709-5 or equivalent PrimariesBT470_6M, // Rec.ITU-R BT.470-6 System M or equivalent PrimariesBT601_6_625, // Rec.ITU-R BT.601-6 625 or equivalent PrimariesBT601_6_525, // Rec.ITU-R BT.601-6 525 or equivalent PrimariesGenericFilm, // Generic Film PrimariesBT2020, // Rec.ITU-R BT.2020 or equivalent PrimariesOther = 0xff, }; // this partially in sync with the transfer values in graphics.h prior to the transfers // unlikely to be required by Android section enum Transfer : uint32_t { TransferUnspecified, TransferLinear, // Linear transfer characteristics TransferSRGB, // sRGB or equivalent TransferSMPTE170M, // SMPTE 170M or equivalent (e.g. BT.601/709/2020) TransferGamma22, // Assumed display gamma 2.2 TransferGamma28, // Assumed display gamma 2.8 TransferST2084, // SMPTE ST 2084 for 10/12/14/16 bit systems TransferHLG, // ARIB STD-B67 hybrid-log-gamma // transfers unlikely to be required by Android TransferSMPTE240M = 0x40, // SMPTE 240M TransferXvYCC, // IEC 61966-2-4 TransferBT1361, // Rec.ITU-R BT.1361 extended gamut TransferST428, // SMPTE ST 428-1 TransferOther = 0xff, }; enum MatrixCoeffs : uint32_t { MatrixUnspecified, MatrixBT709_5, // Rec.ITU-R BT.709-5 or equivalent MatrixBT470_6M, // KR=0.30, KB=0.11 or equivalent MatrixBT601_6, // Rec.ITU-R BT.601-6 625 or equivalent MatrixSMPTE240M, // SMPTE 240M or equivalent MatrixBT2020, // Rec.ITU-R BT.2020 non-constant luminance MatrixBT2020Constant, // Rec.ITU-R BT.2020 constant luminance MatrixOther = 0xff, }; // this is in sync with the standard values in graphics.h enum Standard : uint32_t { StandardUnspecified, StandardBT709, // PrimariesBT709_5 and MatrixBT709_5 StandardBT601_625, // PrimariesBT601_6_625 and MatrixBT601_6 StandardBT601_625_Unadjusted, // PrimariesBT601_6_625 and KR=0.222, KB=0.071 StandardBT601_525, // PrimariesBT601_6_525 and MatrixBT601_6 StandardBT601_525_Unadjusted, // PrimariesBT601_6_525 and MatrixSMPTE240M StandardBT2020, // PrimariesBT2020 and MatrixBT2020 StandardBT2020Constant, // PrimariesBT2020 and MatrixBT2020Constant StandardBT470M, // PrimariesBT470_6M and MatrixBT470_6M StandardFilm, // PrimariesGenericFilm and KR=0.253, KB=0.068 StandardOther = 0xff, }; Range mRange; // IN/OUT Primaries mPrimaries; // IN/OUT Transfer mTransfer; // IN/OUT MatrixCoeffs mMatrixCoeffs; // IN/OUT }; static_assert(sizeof(ColorAspects) == 16, "wrong struct size"); /** * HDR Metadata. */ // HDR Static Metadata Descriptor as defined by CTA-861-3. struct __attribute__ ((__packed__)) HDRStaticInfo { // Static_Metadata_Descriptor_ID enum ID : uint8_t { kType1 = 0, // Static Metadata Type 1 } mID; struct __attribute__ ((__packed__)) Primaries1 { // values are in units of 0.00002 uint16_t x; uint16_t y; }; // Static Metadata Descriptor Type 1 struct __attribute__ ((__packed__)) Type1 { Primaries1 mR; // display primary 0 Primaries1 mG; // display primary 1 Primaries1 mB; // display primary 2 Primaries1 mW; // white point uint16_t mMaxDisplayLuminance; // in cd/m^2 uint16_t mMinDisplayLuminance; // in 0.0001 cd/m^2 uint16_t mMaxContentLightLevel; // in cd/m^2 uint16_t mMaxFrameAverageLightLevel; // in cd/m^2 }; union { Type1 sType1; }; }; static_assert(sizeof(HDRStaticInfo::Primaries1) == 4, "wrong struct size"); static_assert(sizeof(HDRStaticInfo::Type1) == 24, "wrong struct size"); static_assert(sizeof(HDRStaticInfo) == 25, "wrong struct size"); #ifdef STRINGIFY_ENUMS inline static const char *asString(MediaImage::Type i, const char *def = "??") { switch (i) { case MediaImage::MEDIA_IMAGE_TYPE_UNKNOWN: return "Unknown"; case MediaImage::MEDIA_IMAGE_TYPE_YUV: return "YUV"; default: return def; } } inline static const char *asString(MediaImage::PlaneIndex i, const char *def = "??") { switch (i) { case MediaImage::Y: return "Y"; case MediaImage::U: return "U"; case MediaImage::V: return "V"; default: return def; } } inline static const char *asString(MediaImage2::Type i, const char *def = "??") { switch (i) { case MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN: return "Unknown"; case MediaImage2::MEDIA_IMAGE_TYPE_YUV: return "YUV"; case MediaImage2::MEDIA_IMAGE_TYPE_YUVA: return "YUVA"; case MediaImage2::MEDIA_IMAGE_TYPE_RGB: return "RGB"; case MediaImage2::MEDIA_IMAGE_TYPE_RGBA: return "RGBA"; case MediaImage2::MEDIA_IMAGE_TYPE_Y: return "Y"; default: return def; } } inline static char asChar2( MediaImage2::PlaneIndex i, MediaImage2::Type j, char def = '?') { const char *planes = asString(j, NULL); // handle unknown values if (j == MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN || planes == NULL || i >= strlen(planes)) { return def; } return planes[i]; } inline static const char *asString(ColorAspects::Range i, const char *def = "??") { switch (i) { case ColorAspects::RangeUnspecified: return "Unspecified"; case ColorAspects::RangeFull: return "Full"; case ColorAspects::RangeLimited: return "Limited"; case ColorAspects::RangeOther: return "Other"; default: return def; } } inline static const char *asString(ColorAspects::Primaries i, const char *def = "??") { switch (i) { case ColorAspects::PrimariesUnspecified: return "Unspecified"; case ColorAspects::PrimariesBT709_5: return "BT709_5"; case ColorAspects::PrimariesBT470_6M: return "BT470_6M"; case ColorAspects::PrimariesBT601_6_625: return "BT601_6_625"; case ColorAspects::PrimariesBT601_6_525: return "BT601_6_525"; case ColorAspects::PrimariesGenericFilm: return "GenericFilm"; case ColorAspects::PrimariesBT2020: return "BT2020"; case ColorAspects::PrimariesOther: return "Other"; default: return def; } } inline static const char *asString(ColorAspects::Transfer i, const char *def = "??") { switch (i) { case ColorAspects::TransferUnspecified: return "Unspecified"; case ColorAspects::TransferLinear: return "Linear"; case ColorAspects::TransferSRGB: return "SRGB"; case ColorAspects::TransferSMPTE170M: return "SMPTE170M"; case ColorAspects::TransferGamma22: return "Gamma22"; case ColorAspects::TransferGamma28: return "Gamma28"; case ColorAspects::TransferST2084: return "ST2084"; case ColorAspects::TransferHLG: return "HLG"; case ColorAspects::TransferSMPTE240M: return "SMPTE240M"; case ColorAspects::TransferXvYCC: return "XvYCC"; case ColorAspects::TransferBT1361: return "BT1361"; case ColorAspects::TransferST428: return "ST428"; case ColorAspects::TransferOther: return "Other"; default: return def; } } inline static const char *asString(ColorAspects::MatrixCoeffs i, const char *def = "??") { switch (i) { case ColorAspects::MatrixUnspecified: return "Unspecified"; case ColorAspects::MatrixBT709_5: return "BT709_5"; case ColorAspects::MatrixBT470_6M: return "BT470_6M"; case ColorAspects::MatrixBT601_6: return "BT601_6"; case ColorAspects::MatrixSMPTE240M: return "SMPTE240M"; case ColorAspects::MatrixBT2020: return "BT2020"; case ColorAspects::MatrixBT2020Constant: return "BT2020Constant"; case ColorAspects::MatrixOther: return "Other"; default: return def; } } inline static const char *asString(ColorAspects::Standard i, const char *def = "??") { switch (i) { case ColorAspects::StandardUnspecified: return "Unspecified"; case ColorAspects::StandardBT709: return "BT709"; case ColorAspects::StandardBT601_625: return "BT601_625"; case ColorAspects::StandardBT601_625_Unadjusted: return "BT601_625_Unadjusted"; case ColorAspects::StandardBT601_525: return "BT601_525"; case ColorAspects::StandardBT601_525_Unadjusted: return "BT601_525_Unadjusted"; case ColorAspects::StandardBT2020: return "BT2020"; case ColorAspects::StandardBT2020Constant: return "BT2020Constant"; case ColorAspects::StandardBT470M: return "BT470M"; case ColorAspects::StandardFilm: return "Film"; case ColorAspects::StandardOther: return "Other"; default: return def; } } #endif } // namespace android #endif // VIDEO_API_H_ headers/media_plugin/media/openmax/0040755 0000000 0000000 00000000000 13756501735 016422 5ustar000000000 0000000 headers/media_plugin/media/openmax/OMX_AsString.h0100644 0000000 0000000 00000155632 13756501735 021061 0ustar000000000 0000000 /* * Copyright 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. */ /* NOTE: This file contains several sections for individual OMX include files. Each section has its own include guard. This file should be included AFTER the OMX include files. */ #ifdef ANDROID namespace android { #endif #ifdef OMX_Audio_h /* asString definitions if media/openmax/OMX_Audio.h was included */ #ifndef AS_STRING_FOR_OMX_AUDIO_H #define AS_STRING_FOR_OMX_AUDIO_H inline static const char *asString(OMX_AUDIO_CODINGTYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_CodingUnused: return "Unused"; // unused case OMX_AUDIO_CodingAutoDetect: return "AutoDetect"; // unused case OMX_AUDIO_CodingPCM: return "PCM"; case OMX_AUDIO_CodingADPCM: return "ADPCM"; // unused case OMX_AUDIO_CodingAMR: return "AMR"; case OMX_AUDIO_CodingGSMFR: return "GSMFR"; case OMX_AUDIO_CodingGSMEFR: return "GSMEFR"; // unused case OMX_AUDIO_CodingGSMHR: return "GSMHR"; // unused case OMX_AUDIO_CodingPDCFR: return "PDCFR"; // unused case OMX_AUDIO_CodingPDCEFR: return "PDCEFR"; // unused case OMX_AUDIO_CodingPDCHR: return "PDCHR"; // unused case OMX_AUDIO_CodingTDMAFR: return "TDMAFR"; // unused case OMX_AUDIO_CodingTDMAEFR: return "TDMAEFR"; // unused case OMX_AUDIO_CodingQCELP8: return "QCELP8"; // unused case OMX_AUDIO_CodingQCELP13: return "QCELP13"; // unused case OMX_AUDIO_CodingEVRC: return "EVRC"; // unused case OMX_AUDIO_CodingSMV: return "SMV"; // unused case OMX_AUDIO_CodingG711: return "G711"; case OMX_AUDIO_CodingG723: return "G723"; // unused case OMX_AUDIO_CodingG726: return "G726"; // unused case OMX_AUDIO_CodingG729: return "G729"; // unused case OMX_AUDIO_CodingAAC: return "AAC"; case OMX_AUDIO_CodingMP3: return "MP3"; case OMX_AUDIO_CodingSBC: return "SBC"; // unused case OMX_AUDIO_CodingVORBIS: return "VORBIS"; case OMX_AUDIO_CodingWMA: return "WMA"; // unused case OMX_AUDIO_CodingRA: return "RA"; // unused case OMX_AUDIO_CodingMIDI: return "MIDI"; // unused case OMX_AUDIO_CodingFLAC: return "FLAC"; default: return def; } } inline static const char *asString(OMX_AUDIO_PCMMODETYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_PCMModeLinear: return "Linear"; case OMX_AUDIO_PCMModeALaw: return "ALaw"; case OMX_AUDIO_PCMModeMULaw: return "MULaw"; default: return def; } } inline static const char *asString(OMX_AUDIO_CHANNELTYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_ChannelNone: return "None"; // unused case OMX_AUDIO_ChannelLF: return "LF"; case OMX_AUDIO_ChannelRF: return "RF"; case OMX_AUDIO_ChannelCF: return "CF"; case OMX_AUDIO_ChannelLS: return "LS"; case OMX_AUDIO_ChannelRS: return "RS"; case OMX_AUDIO_ChannelLFE: return "LFE"; case OMX_AUDIO_ChannelCS: return "CS"; case OMX_AUDIO_ChannelLR: return "LR"; case OMX_AUDIO_ChannelRR: return "RR"; default: return def; } } inline static const char *asString(OMX_AUDIO_CHANNELMODETYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_ChannelModeStereo: return "Stereo"; // case OMX_AUDIO_ChannelModeJointStereo: return "JointStereo"; // case OMX_AUDIO_ChannelModeDual: return "Dual"; case OMX_AUDIO_ChannelModeMono: return "Mono"; default: return def; } } inline static const char *asString(OMX_AUDIO_AACPROFILETYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_AACObjectNull: return "Null"; case OMX_AUDIO_AACObjectMain: return "Main"; case OMX_AUDIO_AACObjectLC: return "LC"; case OMX_AUDIO_AACObjectSSR: return "SSR"; case OMX_AUDIO_AACObjectLTP: return "LTP"; case OMX_AUDIO_AACObjectHE: return "HE"; case OMX_AUDIO_AACObjectScalable: return "Scalable"; case OMX_AUDIO_AACObjectER_Scalable: return "ER_Scalable"; case OMX_AUDIO_AACObjectERLC: return "ERLC"; case OMX_AUDIO_AACObjectLD: return "LD"; case OMX_AUDIO_AACObjectHE_PS: return "HE_PS"; default: return def; } } inline static const char *asString(OMX_AUDIO_AACSTREAMFORMATTYPE i, const char *def = "??") { switch (i) { // case OMX_AUDIO_AACStreamFormatMP2ADTS: return "MP2ADTS"; case OMX_AUDIO_AACStreamFormatMP4ADTS: return "MP4ADTS"; // case OMX_AUDIO_AACStreamFormatMP4LOAS: return "MP4LOAS"; // case OMX_AUDIO_AACStreamFormatMP4LATM: return "MP4LATM"; // case OMX_AUDIO_AACStreamFormatADIF: return "ADIF"; case OMX_AUDIO_AACStreamFormatMP4FF: return "MP4FF"; // case OMX_AUDIO_AACStreamFormatRAW: return "RAW"; default: return def; } } inline static const char *asString(OMX_AUDIO_AMRFRAMEFORMATTYPE i, const char *def = "??") { switch (i) { // case OMX_AUDIO_AMRFrameFormatConformance: return "Conformance"; // case OMX_AUDIO_AMRFrameFormatIF1: return "IF1"; // case OMX_AUDIO_AMRFrameFormatIF2: return "IF2"; case OMX_AUDIO_AMRFrameFormatFSF: return "FSF"; // case OMX_AUDIO_AMRFrameFormatRTPPayload: return "RTPPayload"; // case OMX_AUDIO_AMRFrameFormatITU: return "ITU"; default: return def; } } inline static const char *asString(OMX_AUDIO_AMRBANDMODETYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_AMRBandModeUnused: return "Unused"; case OMX_AUDIO_AMRBandModeNB0: return "NB0"; case OMX_AUDIO_AMRBandModeNB1: return "NB1"; case OMX_AUDIO_AMRBandModeNB2: return "NB2"; case OMX_AUDIO_AMRBandModeNB3: return "NB3"; case OMX_AUDIO_AMRBandModeNB4: return "NB4"; case OMX_AUDIO_AMRBandModeNB5: return "NB5"; case OMX_AUDIO_AMRBandModeNB6: return "NB6"; case OMX_AUDIO_AMRBandModeNB7: return "NB7"; case OMX_AUDIO_AMRBandModeWB0: return "WB0"; case OMX_AUDIO_AMRBandModeWB1: return "WB1"; case OMX_AUDIO_AMRBandModeWB2: return "WB2"; case OMX_AUDIO_AMRBandModeWB3: return "WB3"; case OMX_AUDIO_AMRBandModeWB4: return "WB4"; case OMX_AUDIO_AMRBandModeWB5: return "WB5"; case OMX_AUDIO_AMRBandModeWB6: return "WB6"; case OMX_AUDIO_AMRBandModeWB7: return "WB7"; case OMX_AUDIO_AMRBandModeWB8: return "WB8"; default: return def; } } inline static const char *asString(OMX_AUDIO_AMRDTXMODETYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_AMRDTXModeOff: return "ModeOff"; // case OMX_AUDIO_AMRDTXModeOnVAD1: return "ModeOnVAD1"; // case OMX_AUDIO_AMRDTXModeOnVAD2: return "ModeOnVAD2"; // case OMX_AUDIO_AMRDTXModeOnAuto: return "ModeOnAuto"; // case OMX_AUDIO_AMRDTXasEFR: return "asEFR"; default: return def; } } #endif // AS_STRING_FOR_OMX_AUDIO_H #endif // OMX_Audio_h #ifdef OMX_AudioExt_h /* asString definitions if media/openmax/OMX_AudioExt.h was included */ #ifndef AS_STRING_FOR_OMX_AUDIOEXT_H #define AS_STRING_FOR_OMX_AUDIOEXT_H inline static const char *asString(OMX_AUDIO_CODINGEXTTYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_CodingAndroidAC3: return "AndroidAC3"; case OMX_AUDIO_CodingAndroidEAC3: return "AndroidEAC3"; case OMX_AUDIO_CodingAndroidOPUS: return "AndroidOPUS"; case OMX_AUDIO_CodingAndroidAC4: return "AndroidAC4"; default: return asString((OMX_AUDIO_CODINGTYPE)i, def); } } #endif // AS_STRING_FOR_OMX_AUDIOEXT_H #endif // OMX_AudioExt_h #ifdef OMX_Component_h /* asString definitions if media/openmax/OMX_Component.h was included */ #ifndef AS_STRING_FOR_OMX_COMPONENT_H #define AS_STRING_FOR_OMX_COMPONENT_H inline static const char *asString(OMX_PORTDOMAINTYPE i, const char *def = "??") { switch (i) { case OMX_PortDomainAudio: return "Audio"; case OMX_PortDomainVideo: return "Video"; case OMX_PortDomainImage: return "Image"; // case OMX_PortDomainOther: return "Other"; default: return def; } } #endif // AS_STRING_FOR_OMX_COMPONENT_H #endif // OMX_Component_h #ifdef OMX_Core_h /* asString definitions if media/openmax/OMX_Core.h was included */ #ifndef AS_STRING_FOR_OMX_CORE_H #define AS_STRING_FOR_OMX_CORE_H inline static const char *asString(OMX_COMMANDTYPE i, const char *def = "??") { switch (i) { case OMX_CommandStateSet: return "StateSet"; case OMX_CommandFlush: return "Flush"; case OMX_CommandPortDisable: return "PortDisable"; case OMX_CommandPortEnable: return "PortEnable"; // case OMX_CommandMarkBuffer: return "MarkBuffer"; default: return def; } } inline static const char *asString(OMX_STATETYPE i, const char *def = "??") { switch (i) { case OMX_StateInvalid: return "Invalid"; case OMX_StateLoaded: return "Loaded"; case OMX_StateIdle: return "Idle"; case OMX_StateExecuting: return "Executing"; // case OMX_StatePause: return "Pause"; // case OMX_StateWaitForResources: return "WaitForResources"; default: return def; } } inline static const char *asString(OMX_ERRORTYPE i, const char *def = "??") { switch (i) { case OMX_ErrorNone: return "None"; case OMX_ErrorInsufficientResources: return "InsufficientResources"; case OMX_ErrorUndefined: return "Undefined"; case OMX_ErrorInvalidComponentName: return "InvalidComponentName"; case OMX_ErrorComponentNotFound: return "ComponentNotFound"; case OMX_ErrorInvalidComponent: return "InvalidComponent"; // unused case OMX_ErrorBadParameter: return "BadParameter"; case OMX_ErrorNotImplemented: return "NotImplemented"; case OMX_ErrorUnderflow: return "Underflow"; // unused case OMX_ErrorOverflow: return "Overflow"; // unused case OMX_ErrorHardware: return "Hardware"; // unused case OMX_ErrorInvalidState: return "InvalidState"; case OMX_ErrorStreamCorrupt: return "StreamCorrupt"; case OMX_ErrorPortsNotCompatible: return "PortsNotCompatible"; // unused case OMX_ErrorResourcesLost: return "ResourcesLost"; case OMX_ErrorNoMore: return "NoMore"; case OMX_ErrorVersionMismatch: return "VersionMismatch"; // unused case OMX_ErrorNotReady: return "NotReady"; // unused case OMX_ErrorTimeout: return "Timeout"; // unused case OMX_ErrorSameState: return "SameState"; // unused case OMX_ErrorResourcesPreempted: return "ResourcesPreempted"; // unused case OMX_ErrorPortUnresponsiveDuringAllocation: return "PortUnresponsiveDuringAllocation"; // unused case OMX_ErrorPortUnresponsiveDuringDeallocation: return "PortUnresponsiveDuringDeallocation"; // unused case OMX_ErrorPortUnresponsiveDuringStop: return "PortUnresponsiveDuringStop"; // unused case OMX_ErrorIncorrectStateTransition: return "IncorrectStateTransition"; // unused case OMX_ErrorIncorrectStateOperation: return "IncorrectStateOperation"; // unused case OMX_ErrorUnsupportedSetting: return "UnsupportedSetting"; case OMX_ErrorUnsupportedIndex: return "UnsupportedIndex"; case OMX_ErrorBadPortIndex: return "BadPortIndex"; case OMX_ErrorPortUnpopulated: return "PortUnpopulated"; // unused case OMX_ErrorComponentSuspended: return "ComponentSuspended"; // unused case OMX_ErrorDynamicResourcesUnavailable: return "DynamicResourcesUnavailable"; // unused case OMX_ErrorMbErrorsInFrame: return "MbErrorsInFrame"; // unused case OMX_ErrorFormatNotDetected: return "FormatNotDetected"; // unused case OMX_ErrorContentPipeOpenFailed: return "ContentPipeOpenFailed"; // unused case OMX_ErrorContentPipeCreationFailed: return "ContentPipeCreationFailed"; // unused case OMX_ErrorSeperateTablesUsed: return "SeperateTablesUsed"; // unused case OMX_ErrorTunnelingUnsupported: return "TunnelingUnsupported"; // unused default: return def; } } inline static const char *asString(OMX_EVENTTYPE i, const char *def = "??") { switch (i) { case OMX_EventCmdComplete: return "CmdComplete"; case OMX_EventError: return "Error"; // case OMX_EventMark: return "Mark"; case OMX_EventPortSettingsChanged: return "PortSettingsChanged"; case OMX_EventBufferFlag: return "BufferFlag"; // case OMX_EventResourcesAcquired: return "ResourcesAcquired"; // case OMX_EventComponentResumed: return "ComponentResumed"; // case OMX_EventDynamicResourcesAvailable: return "DynamicResourcesAvailable"; // case OMX_EventPortFormatDetected: return "PortFormatDetected"; case OMX_EventOutputRendered: return "OutputRendered"; case OMX_EventDataSpaceChanged: return "DataSpaceChanged"; default: return def; } } #endif // AS_STRING_FOR_OMX_CORE_H #endif // OMX_Core_h #ifdef OMX_Image_h /* asString definitions if media/openmax/OMX_Image.h was included */ #ifndef AS_STRING_FOR_OMX_IMAGE_H #define AS_STRING_FOR_OMX_IMAGE_H inline static const char *asString(OMX_IMAGE_CODINGTYPE i, const char *def = "??") { switch (i) { case OMX_IMAGE_CodingUnused: return "Unused"; case OMX_IMAGE_CodingAutoDetect: return "AutoDetect"; // unused case OMX_IMAGE_CodingJPEG: return "JPEG"; case OMX_IMAGE_CodingJPEG2K: return "JPEG2K"; // unused case OMX_IMAGE_CodingEXIF: return "EXIF"; // unused case OMX_IMAGE_CodingTIFF: return "TIFF"; // unused case OMX_IMAGE_CodingGIF: return "GIF"; // unused case OMX_IMAGE_CodingPNG: return "PNG"; // unused case OMX_IMAGE_CodingLZW: return "LZW"; // unused case OMX_IMAGE_CodingBMP: return "BMP"; // unused default: return def; } } #endif // AS_STRING_FOR_OMX_IMAGE_H #endif // OMX_Image_h #ifdef OMX_Index_h /* asString definitions if media/openmax/OMX_Index.h was included */ #ifndef AS_STRING_FOR_OMX_INDEX_H #define AS_STRING_FOR_OMX_INDEX_H inline static const char *asString(OMX_INDEXTYPE i, const char *def = "??") { switch (i) { // case OMX_IndexParamPriorityMgmt: return "ParamPriorityMgmt"; // case OMX_IndexParamAudioInit: return "ParamAudioInit"; // case OMX_IndexParamImageInit: return "ParamImageInit"; // case OMX_IndexParamVideoInit: return "ParamVideoInit"; // case OMX_IndexParamOtherInit: return "ParamOtherInit"; // case OMX_IndexParamNumAvailableStreams: return "ParamNumAvailableStreams"; // case OMX_IndexParamActiveStream: return "ParamActiveStream"; // case OMX_IndexParamSuspensionPolicy: return "ParamSuspensionPolicy"; // case OMX_IndexParamComponentSuspended: return "ParamComponentSuspended"; // case OMX_IndexConfigCapturing: return "ConfigCapturing"; // case OMX_IndexConfigCaptureMode: return "ConfigCaptureMode"; // case OMX_IndexAutoPauseAfterCapture: return "AutoPauseAfterCapture"; // case OMX_IndexParamContentURI: return "ParamContentURI"; // case OMX_IndexParamCustomContentPipe: return "ParamCustomContentPipe"; // case OMX_IndexParamDisableResourceConcealment: // return "ParamDisableResourceConcealment"; // case OMX_IndexConfigMetadataItemCount: return "ConfigMetadataItemCount"; // case OMX_IndexConfigContainerNodeCount: return "ConfigContainerNodeCount"; // case OMX_IndexConfigMetadataItem: return "ConfigMetadataItem"; // case OMX_IndexConfigCounterNodeID: return "ConfigCounterNodeID"; // case OMX_IndexParamMetadataFilterType: return "ParamMetadataFilterType"; // case OMX_IndexParamMetadataKeyFilter: return "ParamMetadataKeyFilter"; // case OMX_IndexConfigPriorityMgmt: return "ConfigPriorityMgmt"; case OMX_IndexParamStandardComponentRole: return "ParamStandardComponentRole"; case OMX_IndexParamPortDefinition: return "ParamPortDefinition"; // case OMX_IndexParamCompBufferSupplier: return "ParamCompBufferSupplier"; case OMX_IndexParamAudioPortFormat: return "ParamAudioPortFormat"; case OMX_IndexParamAudioPcm: return "ParamAudioPcm"; case OMX_IndexParamAudioAac: return "ParamAudioAac"; // case OMX_IndexParamAudioRa: return "ParamAudioRa"; case OMX_IndexParamAudioMp3: return "ParamAudioMp3"; // case OMX_IndexParamAudioAdpcm: return "ParamAudioAdpcm"; // case OMX_IndexParamAudioG723: return "ParamAudioG723"; // case OMX_IndexParamAudioG729: return "ParamAudioG729"; case OMX_IndexParamAudioAmr: return "ParamAudioAmr"; // case OMX_IndexParamAudioWma: return "ParamAudioWma"; // case OMX_IndexParamAudioSbc: return "ParamAudioSbc"; // case OMX_IndexParamAudioMidi: return "ParamAudioMidi"; // case OMX_IndexParamAudioGsm_FR: return "ParamAudioGsm_FR"; // case OMX_IndexParamAudioMidiLoadUserSound: return "ParamAudioMidiLoadUserSound"; // case OMX_IndexParamAudioG726: return "ParamAudioG726"; // case OMX_IndexParamAudioGsm_EFR: return "ParamAudioGsm_EFR"; // case OMX_IndexParamAudioGsm_HR: return "ParamAudioGsm_HR"; // case OMX_IndexParamAudioPdc_FR: return "ParamAudioPdc_FR"; // case OMX_IndexParamAudioPdc_EFR: return "ParamAudioPdc_EFR"; // case OMX_IndexParamAudioPdc_HR: return "ParamAudioPdc_HR"; // case OMX_IndexParamAudioTdma_FR: return "ParamAudioTdma_FR"; // case OMX_IndexParamAudioTdma_EFR: return "ParamAudioTdma_EFR"; // case OMX_IndexParamAudioQcelp8: return "ParamAudioQcelp8"; // case OMX_IndexParamAudioQcelp13: return "ParamAudioQcelp13"; // case OMX_IndexParamAudioEvrc: return "ParamAudioEvrc"; // case OMX_IndexParamAudioSmv: return "ParamAudioSmv"; case OMX_IndexParamAudioVorbis: return "ParamAudioVorbis"; case OMX_IndexParamAudioFlac: return "ParamAudioFlac"; // case OMX_IndexConfigAudioMidiImmediateEvent: return "ConfigAudioMidiImmediateEvent"; // case OMX_IndexConfigAudioMidiControl: return "ConfigAudioMidiControl"; // case OMX_IndexConfigAudioMidiSoundBankProgram: // return "ConfigAudioMidiSoundBankProgram"; // case OMX_IndexConfigAudioMidiStatus: return "ConfigAudioMidiStatus"; // case OMX_IndexConfigAudioMidiMetaEvent: return "ConfigAudioMidiMetaEvent"; // case OMX_IndexConfigAudioMidiMetaEventData: return "ConfigAudioMidiMetaEventData"; // case OMX_IndexConfigAudioVolume: return "ConfigAudioVolume"; // case OMX_IndexConfigAudioBalance: return "ConfigAudioBalance"; // case OMX_IndexConfigAudioChannelMute: return "ConfigAudioChannelMute"; // case OMX_IndexConfigAudioMute: return "ConfigAudioMute"; // case OMX_IndexConfigAudioLoudness: return "ConfigAudioLoudness"; // case OMX_IndexConfigAudioEchoCancelation: return "ConfigAudioEchoCancelation"; // case OMX_IndexConfigAudioNoiseReduction: return "ConfigAudioNoiseReduction"; // case OMX_IndexConfigAudioBass: return "ConfigAudioBass"; // case OMX_IndexConfigAudioTreble: return "ConfigAudioTreble"; // case OMX_IndexConfigAudioStereoWidening: return "ConfigAudioStereoWidening"; // case OMX_IndexConfigAudioChorus: return "ConfigAudioChorus"; // case OMX_IndexConfigAudioEqualizer: return "ConfigAudioEqualizer"; // case OMX_IndexConfigAudioReverberation: return "ConfigAudioReverberation"; // case OMX_IndexConfigAudioChannelVolume: return "ConfigAudioChannelVolume"; // case OMX_IndexParamImagePortFormat: return "ParamImagePortFormat"; // case OMX_IndexParamFlashControl: return "ParamFlashControl"; // case OMX_IndexConfigFocusControl: return "ConfigFocusControl"; // case OMX_IndexParamQFactor: return "ParamQFactor"; // case OMX_IndexParamQuantizationTable: return "ParamQuantizationTable"; // case OMX_IndexParamHuffmanTable: return "ParamHuffmanTable"; // case OMX_IndexConfigFlashControl: return "ConfigFlashControl"; case OMX_IndexParamVideoPortFormat: return "ParamVideoPortFormat"; // case OMX_IndexParamVideoQuantization: return "ParamVideoQuantization"; // case OMX_IndexParamVideoFastUpdate: return "ParamVideoFastUpdate"; case OMX_IndexParamVideoBitrate: return "ParamVideoBitrate"; // case OMX_IndexParamVideoMotionVector: return "ParamVideoMotionVector"; case OMX_IndexParamVideoIntraRefresh: return "ParamVideoIntraRefresh"; case OMX_IndexParamVideoErrorCorrection: return "ParamVideoErrorCorrection"; // case OMX_IndexParamVideoVBSMC: return "ParamVideoVBSMC"; // case OMX_IndexParamVideoMpeg2: return "ParamVideoMpeg2"; case OMX_IndexParamVideoMpeg4: return "ParamVideoMpeg4"; // case OMX_IndexParamVideoWmv: return "ParamVideoWmv"; // case OMX_IndexParamVideoRv: return "ParamVideoRv"; case OMX_IndexParamVideoAvc: return "ParamVideoAvc"; case OMX_IndexParamVideoH263: return "ParamVideoH263"; case OMX_IndexParamVideoProfileLevelQuerySupported: return "ParamVideoProfileLevelQuerySupported"; case OMX_IndexParamVideoProfileLevelCurrent: return "ParamVideoProfileLevelCurrent"; case OMX_IndexConfigVideoBitrate: return "ConfigVideoBitrate"; // case OMX_IndexConfigVideoFramerate: return "ConfigVideoFramerate"; case OMX_IndexConfigVideoIntraVOPRefresh: return "ConfigVideoIntraVOPRefresh"; // case OMX_IndexConfigVideoIntraMBRefresh: return "ConfigVideoIntraMBRefresh"; // case OMX_IndexConfigVideoMBErrorReporting: return "ConfigVideoMBErrorReporting"; // case OMX_IndexParamVideoMacroblocksPerFrame: return "ParamVideoMacroblocksPerFrame"; // case OMX_IndexConfigVideoMacroBlockErrorMap: return "ConfigVideoMacroBlockErrorMap"; // case OMX_IndexParamVideoSliceFMO: return "ParamVideoSliceFMO"; // case OMX_IndexConfigVideoAVCIntraPeriod: return "ConfigVideoAVCIntraPeriod"; // case OMX_IndexConfigVideoNalSize: return "ConfigVideoNalSize"; // case OMX_IndexParamCommonDeblocking: return "ParamCommonDeblocking"; // case OMX_IndexParamCommonSensorMode: return "ParamCommonSensorMode"; // case OMX_IndexParamCommonInterleave: return "ParamCommonInterleave"; // case OMX_IndexConfigCommonColorFormatConversion: // return "ConfigCommonColorFormatConversion"; case OMX_IndexConfigCommonScale: return "ConfigCommonScale"; // case OMX_IndexConfigCommonImageFilter: return "ConfigCommonImageFilter"; // case OMX_IndexConfigCommonColorEnhancement: return "ConfigCommonColorEnhancement"; // case OMX_IndexConfigCommonColorKey: return "ConfigCommonColorKey"; // case OMX_IndexConfigCommonColorBlend: return "ConfigCommonColorBlend"; // case OMX_IndexConfigCommonFrameStabilisation: return "ConfigCommonFrameStabilisation"; // case OMX_IndexConfigCommonRotate: return "ConfigCommonRotate"; // case OMX_IndexConfigCommonMirror: return "ConfigCommonMirror"; // case OMX_IndexConfigCommonOutputPosition: return "ConfigCommonOutputPosition"; case OMX_IndexConfigCommonInputCrop: return "ConfigCommonInputCrop"; case OMX_IndexConfigCommonOutputCrop: return "ConfigCommonOutputCrop"; // case OMX_IndexConfigCommonDigitalZoom: return "ConfigCommonDigitalZoom"; // case OMX_IndexConfigCommonOpticalZoom: return "ConfigCommonOpticalZoom"; // case OMX_IndexConfigCommonWhiteBalance: return "ConfigCommonWhiteBalance"; // case OMX_IndexConfigCommonExposure: return "ConfigCommonExposure"; // case OMX_IndexConfigCommonContrast: return "ConfigCommonContrast"; // case OMX_IndexConfigCommonBrightness: return "ConfigCommonBrightness"; // case OMX_IndexConfigCommonBacklight: return "ConfigCommonBacklight"; // case OMX_IndexConfigCommonGamma: return "ConfigCommonGamma"; // case OMX_IndexConfigCommonSaturation: return "ConfigCommonSaturation"; // case OMX_IndexConfigCommonLightness: return "ConfigCommonLightness"; // case OMX_IndexConfigCommonExclusionRect: return "ConfigCommonExclusionRect"; // case OMX_IndexConfigCommonDithering: return "ConfigCommonDithering"; // case OMX_IndexConfigCommonPlaneBlend: return "ConfigCommonPlaneBlend"; // case OMX_IndexConfigCommonExposureValue: return "ConfigCommonExposureValue"; // case OMX_IndexConfigCommonOutputSize: return "ConfigCommonOutputSize"; // case OMX_IndexParamCommonExtraQuantData: return "ParamCommonExtraQuantData"; // case OMX_IndexConfigCommonFocusRegion: return "ConfigCommonFocusRegion"; // case OMX_IndexConfigCommonFocusStatus: return "ConfigCommonFocusStatus"; // case OMX_IndexConfigCommonTransitionEffect: return "ConfigCommonTransitionEffect"; // case OMX_IndexParamOtherPortFormat: return "ParamOtherPortFormat"; // case OMX_IndexConfigOtherPower: return "ConfigOtherPower"; // case OMX_IndexConfigOtherStats: return "ConfigOtherStats"; // case OMX_IndexConfigTimeScale: return "ConfigTimeScale"; // case OMX_IndexConfigTimeClockState: return "ConfigTimeClockState"; // case OMX_IndexConfigTimeActiveRefClock: return "ConfigTimeActiveRefClock"; // case OMX_IndexConfigTimeCurrentMediaTime: return "ConfigTimeCurrentMediaTime"; // case OMX_IndexConfigTimeCurrentWallTime: return "ConfigTimeCurrentWallTime"; // case OMX_IndexConfigTimeCurrentAudioReference: // return "ConfigTimeCurrentAudioReference"; // case OMX_IndexConfigTimeCurrentVideoReference: // return "ConfigTimeCurrentVideoReference"; // case OMX_IndexConfigTimeMediaTimeRequest: return "ConfigTimeMediaTimeRequest"; // case OMX_IndexConfigTimeClientStartTime: return "ConfigTimeClientStartTime"; // case OMX_IndexConfigTimePosition: return "ConfigTimePosition"; // case OMX_IndexConfigTimeSeekMode: return "ConfigTimeSeekMode"; default: return def; } } #endif // AS_STRING_FOR_OMX_INDEX_H #endif // OMX_Index_h #ifdef OMX_IndexExt_h /* asString definitions if media/openmax/OMX_IndexExt.h was included */ #ifndef AS_STRING_FOR_OMX_INDEXEXT_H #define AS_STRING_FOR_OMX_INDEXEXT_H inline static const char *asString(OMX_INDEXEXTTYPE i, const char *def = "??") { switch (i) { // case OMX_IndexConfigCallbackRequest: return "ConfigCallbackRequest"; // case OMX_IndexConfigCommitMode: return "ConfigCommitMode"; // case OMX_IndexConfigCommit: return "ConfigCommit"; case OMX_IndexConfigAndroidVendorExtension: return "ConfigAndroidVendorExtension"; case OMX_IndexParamAudioAndroidAc3: return "ParamAudioAndroidAc3"; case OMX_IndexConfigAudioPresentation: return "ConfigAudioPresentation"; case OMX_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus"; case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation"; case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3"; case OMX_IndexParamAudioAndroidAc4: return "ParamAudioAndroidAc4"; case OMX_IndexParamAudioProfileQuerySupported: return "ParamAudioProfileQuerySupported"; // case OMX_IndexParamNalStreamFormatSupported: return "ParamNalStreamFormatSupported"; // case OMX_IndexParamNalStreamFormat: return "ParamNalStreamFormat"; // case OMX_IndexParamNalStreamFormatSelect: return "ParamNalStreamFormatSelect"; case OMX_IndexParamVideoVp8: return "ParamVideoVp8"; // case OMX_IndexConfigVideoVp8ReferenceFrame: return "ConfigVideoVp8ReferenceFrame"; // case OMX_IndexConfigVideoVp8ReferenceFrameType: return "ConfigVideoVp8ReferenceFrameType"; case OMX_IndexParamVideoAndroidVp8Encoder: return "ParamVideoAndroidVp8Encoder"; case OMX_IndexParamVideoHevc: return "ParamVideoHevc"; // case OMX_IndexParamSliceSegments: return "ParamSliceSegments"; case OMX_IndexConfigAndroidIntraRefresh: return "ConfigAndroidIntraRefresh"; case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering"; case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering"; case OMX_IndexParamMaxFrameDurationForBitrateControl: return "ParamMaxFrameDurationForBitrateControl"; case OMX_IndexParamVideoVp9: return "ParamVideoVp9"; case OMX_IndexParamVideoAndroidVp9Encoder: return "ParamVideoAndroidVp9Encoder"; case OMX_IndexConfigAutoFramerateConversion: return "ConfigAutoFramerateConversion"; case OMX_IndexConfigPriority: return "ConfigPriority"; case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate"; case OMX_IndexParamConsumerUsageBits: return "ParamConsumerUsageBits"; case OMX_IndexConfigLatency: return "ConfigLatency"; default: return asString((OMX_INDEXTYPE)i, def); } } #endif // AS_STRING_FOR_OMX_INDEXEXT_H #endif // OMX_IndexExt_h #ifdef OMX_IVCommon_h /* asString definitions if media/openmax/OMX_IVCommon.h was included */ #ifndef AS_STRING_FOR_OMX_IVCOMMON_H #define AS_STRING_FOR_OMX_IVCOMMON_H inline static const char *asString(OMX_COLOR_FORMATTYPE i, const char *def = "??") { switch (i) { case OMX_COLOR_FormatUnused: return "COLOR_FormatUnused"; case OMX_COLOR_FormatMonochrome: return "COLOR_FormatMonochrome"; case OMX_COLOR_Format8bitRGB332: return "COLOR_Format8bitRGB332"; case OMX_COLOR_Format12bitRGB444: return "COLOR_Format12bitRGB444"; case OMX_COLOR_Format16bitARGB4444: return "COLOR_Format16bitARGB4444"; case OMX_COLOR_Format16bitARGB1555: return "COLOR_Format16bitARGB1555"; case OMX_COLOR_Format16bitRGB565: return "COLOR_Format16bitRGB565"; case OMX_COLOR_Format16bitBGR565: return "COLOR_Format16bitBGR565"; case OMX_COLOR_Format18bitRGB666: return "COLOR_Format18bitRGB666"; case OMX_COLOR_Format18bitARGB1665: return "COLOR_Format18bitARGB1665"; case OMX_COLOR_Format19bitARGB1666: return "COLOR_Format19bitARGB1666"; case OMX_COLOR_Format24bitRGB888: return "COLOR_Format24bitRGB888"; case OMX_COLOR_Format24bitBGR888: return "COLOR_Format24bitBGR888"; case OMX_COLOR_Format24bitARGB1887: return "COLOR_Format24bitARGB1887"; case OMX_COLOR_Format25bitARGB1888: return "COLOR_Format25bitARGB1888"; case OMX_COLOR_Format32bitBGRA8888: return "COLOR_Format32bitBGRA8888"; case OMX_COLOR_Format32bitARGB8888: return "COLOR_Format32bitARGB8888"; case OMX_COLOR_FormatYUV411Planar: return "COLOR_FormatYUV411Planar"; case OMX_COLOR_FormatYUV411PackedPlanar: return "COLOR_FormatYUV411PackedPlanar"; case OMX_COLOR_FormatYUV420Planar: return "COLOR_FormatYUV420Planar"; case OMX_COLOR_FormatYUV420PackedPlanar: return "COLOR_FormatYUV420PackedPlanar"; case OMX_COLOR_FormatYUV420SemiPlanar: return "COLOR_FormatYUV420SemiPlanar"; case OMX_COLOR_FormatYUV422Planar: return "COLOR_FormatYUV422Planar"; case OMX_COLOR_FormatYUV422PackedPlanar: return "COLOR_FormatYUV422PackedPlanar"; case OMX_COLOR_FormatYUV422SemiPlanar: return "COLOR_FormatYUV422SemiPlanar"; case OMX_COLOR_FormatYCbYCr: return "COLOR_FormatYCbYCr"; case OMX_COLOR_FormatYCrYCb: return "COLOR_FormatYCrYCb"; case OMX_COLOR_FormatCbYCrY: return "COLOR_FormatCbYCrY"; case OMX_COLOR_FormatCrYCbY: return "COLOR_FormatCrYCbY"; case OMX_COLOR_FormatYUV444Interleaved: return "COLOR_FormatYUV444Interleaved"; case OMX_COLOR_FormatRawBayer8bit: return "COLOR_FormatRawBayer8bit"; case OMX_COLOR_FormatRawBayer10bit: return "COLOR_FormatRawBayer10bit"; case OMX_COLOR_FormatRawBayer8bitcompressed: return "COLOR_FormatRawBayer8bitcompressed"; case OMX_COLOR_FormatL2: return "COLOR_FormatL2"; case OMX_COLOR_FormatL4: return "COLOR_FormatL4"; case OMX_COLOR_FormatL8: return "COLOR_FormatL8"; case OMX_COLOR_FormatL16: return "COLOR_FormatL16"; case OMX_COLOR_FormatL24: return "COLOR_FormatL24"; case OMX_COLOR_FormatL32: return "COLOR_FormatL32"; case OMX_COLOR_FormatYUV420PackedSemiPlanar: return "COLOR_FormatYUV420PackedSemiPlanar"; case OMX_COLOR_FormatYUV422PackedSemiPlanar: return "COLOR_FormatYUV422PackedSemiPlanar"; case OMX_COLOR_Format18BitBGR666: return "COLOR_Format18BitBGR666"; case OMX_COLOR_Format24BitARGB6666: return "COLOR_Format24BitARGB6666"; case OMX_COLOR_Format24BitABGR6666: return "COLOR_Format24BitABGR6666"; case OMX_COLOR_FormatAndroidOpaque: return "COLOR_FormatAndroidOpaque"; case OMX_COLOR_FormatYUV420Flexible: return "COLOR_FormatYUV420Flexible"; case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return "TI_COLOR_FormatYUV420PackedSemiPlanar"; case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: return "QCOM_COLOR_FormatYVU420SemiPlanar"; // case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka: // return "QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka"; // case OMX_SEC_COLOR_FormatNV12Tiled: // return "SEC_COLOR_FormatNV12Tiled"; // case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m: // return "QCOM_COLOR_FormatYUV420PackedSemiPlanar32m"; default: return def; } } #endif // AS_STRING_FOR_OMX_IVCOMMON_H #endif // OMX_IVCommon_h #ifdef OMX_Types_h /* asString definitions if media/openmax/OMX_Types.h was included */ #ifndef AS_STRING_FOR_OMX_TYPES_H #define AS_STRING_FOR_OMX_TYPES_H inline static const char *asString(OMX_BOOL i, const char *def = "??") { switch (i) { case OMX_FALSE: return "FALSE"; case OMX_TRUE: return "TRUE"; default: return def; } } inline static const char *asString(OMX_DIRTYPE i, const char *def = "??") { switch (i) { case OMX_DirInput: return "Input"; case OMX_DirOutput: return "Output"; default: return def; } } inline static const char *asString(OMX_ENDIANTYPE i, const char *def = "??") { switch (i) { case OMX_EndianBig: return "Big"; // case OMX_EndianLittle: return "Little"; default: return def; } } inline static const char *asString(OMX_NUMERICALDATATYPE i, const char *def = "??") { switch (i) { case OMX_NumericalDataSigned: return "Signed"; // case OMX_NumericalDataUnsigned: return "Unsigned"; default: return def; } } #endif // AS_STRING_FOR_OMX_TYPES_H #endif // OMX_Types_h #ifdef OMX_Video_h /* asString definitions if media/openmax/OMX_Video.h was included */ #ifndef AS_STRING_FOR_OMX_VIDEO_H #define AS_STRING_FOR_OMX_VIDEO_H inline static const char *asString(OMX_VIDEO_CODINGTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_CodingUnused: return "Unused"; case OMX_VIDEO_CodingAutoDetect: return "AutoDetect"; // unused case OMX_VIDEO_CodingMPEG2: return "MPEG2"; case OMX_VIDEO_CodingH263: return "H263"; case OMX_VIDEO_CodingMPEG4: return "MPEG4"; case OMX_VIDEO_CodingWMV: return "WMV"; // unused case OMX_VIDEO_CodingRV: return "RV"; // unused case OMX_VIDEO_CodingAVC: return "AVC"; case OMX_VIDEO_CodingMJPEG: return "MJPEG"; // unused case OMX_VIDEO_CodingVP8: return "VP8"; case OMX_VIDEO_CodingVP9: return "VP9"; case OMX_VIDEO_CodingHEVC: return "HEVC"; case OMX_VIDEO_CodingDolbyVision:return "DolbyVision"; default: return def; } } inline static const char *asString(OMX_VIDEO_CONTROLRATETYPE i, const char *def = "??") { switch (i) { // case OMX_Video_ControlRateDisable: return "Disable"; case OMX_Video_ControlRateVariable: return "Variable"; case OMX_Video_ControlRateConstant: return "Constant"; // case OMX_Video_ControlRateVariableSkipFrames: return "VariableSkipFrames"; // case OMX_Video_ControlRateConstantSkipFrames: return "ConstantSkipFrames"; default: return def; } } inline static const char *asString(OMX_VIDEO_INTRAREFRESHTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_IntraRefreshCyclic: return "Cyclic"; case OMX_VIDEO_IntraRefreshAdaptive: return "Adaptive"; case OMX_VIDEO_IntraRefreshBoth: return "Both"; default: return def; } } inline static const char *asString(OMX_VIDEO_H263PROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_H263ProfileBaseline: return "Baseline"; case OMX_VIDEO_H263ProfileH320Coding: return "H320Coding"; case OMX_VIDEO_H263ProfileBackwardCompatible: return "BackwardCompatible"; case OMX_VIDEO_H263ProfileISWV2: return "ISWV2"; case OMX_VIDEO_H263ProfileISWV3: return "ISWV3"; case OMX_VIDEO_H263ProfileHighCompression: return "HighCompression"; case OMX_VIDEO_H263ProfileInternet: return "Internet"; case OMX_VIDEO_H263ProfileInterlace: return "Interlace"; case OMX_VIDEO_H263ProfileHighLatency: return "HighLatency"; default: return def; } } inline static const char *asString(OMX_VIDEO_H263LEVELTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_H263Level10: return "Level10"; case OMX_VIDEO_H263Level20: return "Level20"; case OMX_VIDEO_H263Level30: return "Level30"; case OMX_VIDEO_H263Level40: return "Level40"; case OMX_VIDEO_H263Level45: return "Level45"; case OMX_VIDEO_H263Level50: return "Level50"; case OMX_VIDEO_H263Level60: return "Level60"; case OMX_VIDEO_H263Level70: return "Level70"; default: return def; } } inline static const char *asString(OMX_VIDEO_PICTURETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_PictureTypeI: return "I"; case OMX_VIDEO_PictureTypeP: return "P"; case OMX_VIDEO_PictureTypeB: return "B"; // case OMX_VIDEO_PictureTypeSI: return "SI"; // case OMX_VIDEO_PictureTypeSP: return "SP"; // case OMX_VIDEO_PictureTypeEI: return "EI"; // case OMX_VIDEO_PictureTypeEP: return "EP"; // case OMX_VIDEO_PictureTypeS: return "S"; default: return def; } } inline static const char *asString(OMX_VIDEO_MPEG4PROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_MPEG4ProfileSimple: return "Simple"; case OMX_VIDEO_MPEG4ProfileSimpleScalable: return "SimpleScalable"; case OMX_VIDEO_MPEG4ProfileCore: return "Core"; case OMX_VIDEO_MPEG4ProfileMain: return "Main"; case OMX_VIDEO_MPEG4ProfileNbit: return "Nbit"; case OMX_VIDEO_MPEG4ProfileScalableTexture: return "ScalableTexture"; case OMX_VIDEO_MPEG4ProfileSimpleFace: return "SimpleFace"; case OMX_VIDEO_MPEG4ProfileSimpleFBA: return "SimpleFBA"; case OMX_VIDEO_MPEG4ProfileBasicAnimated: return "BasicAnimated"; case OMX_VIDEO_MPEG4ProfileHybrid: return "Hybrid"; case OMX_VIDEO_MPEG4ProfileAdvancedRealTime: return "AdvancedRealTime"; case OMX_VIDEO_MPEG4ProfileCoreScalable: return "CoreScalable"; case OMX_VIDEO_MPEG4ProfileAdvancedCoding: return "AdvancedCoding"; case OMX_VIDEO_MPEG4ProfileAdvancedCore: return "AdvancedCore"; case OMX_VIDEO_MPEG4ProfileAdvancedScalable: return "AdvancedScalable"; case OMX_VIDEO_MPEG4ProfileAdvancedSimple: return "AdvancedSimple"; default: return def; } } inline static const char *asString(OMX_VIDEO_MPEG4LEVELTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_MPEG4Level0: return "Level0"; case OMX_VIDEO_MPEG4Level0b: return "Level0b"; case OMX_VIDEO_MPEG4Level1: return "Level1"; case OMX_VIDEO_MPEG4Level2: return "Level2"; case OMX_VIDEO_MPEG4Level3: return "Level3"; case OMX_VIDEO_MPEG4Level3b: return "Level3b"; case OMX_VIDEO_MPEG4Level4: return "Level4"; case OMX_VIDEO_MPEG4Level4a: return "Level4a"; case OMX_VIDEO_MPEG4Level5: return "Level5"; case OMX_VIDEO_MPEG4Level6: return "Level6"; default: return def; } } inline static const char *asString(OMX_VIDEO_MPEG2PROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_MPEG2ProfileSimple: return "Simple"; case OMX_VIDEO_MPEG2ProfileMain: return "Main"; case OMX_VIDEO_MPEG2Profile422: return "4:2:2"; case OMX_VIDEO_MPEG2ProfileSNR: return "SNR"; case OMX_VIDEO_MPEG2ProfileSpatial: return "Spatial"; case OMX_VIDEO_MPEG2ProfileHigh: return "High"; default: return def; } } inline static const char *asString(OMX_VIDEO_MPEG2LEVELTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_MPEG2LevelLL: return "Low"; case OMX_VIDEO_MPEG2LevelML: return "Main"; case OMX_VIDEO_MPEG2LevelH14: return "High1440"; case OMX_VIDEO_MPEG2LevelHL: return "High"; default: return def; } } inline static const char *asString(OMX_VIDEO_AVCPROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_AVCProfileBaseline: return "Baseline"; case OMX_VIDEO_AVCProfileMain: return "Main"; case OMX_VIDEO_AVCProfileExtended: return "Extended"; case OMX_VIDEO_AVCProfileHigh: return "High"; case OMX_VIDEO_AVCProfileHigh10: return "High10"; case OMX_VIDEO_AVCProfileHigh422: return "High422"; case OMX_VIDEO_AVCProfileHigh444: return "High444"; default: return def; } } inline static const char *asString(OMX_VIDEO_AVCLEVELTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_AVCLevel1: return "Level1"; case OMX_VIDEO_AVCLevel1b: return "Level1b"; case OMX_VIDEO_AVCLevel11: return "Level11"; case OMX_VIDEO_AVCLevel12: return "Level12"; case OMX_VIDEO_AVCLevel13: return "Level13"; case OMX_VIDEO_AVCLevel2: return "Level2"; case OMX_VIDEO_AVCLevel21: return "Level21"; case OMX_VIDEO_AVCLevel22: return "Level22"; case OMX_VIDEO_AVCLevel3: return "Level3"; case OMX_VIDEO_AVCLevel31: return "Level31"; case OMX_VIDEO_AVCLevel32: return "Level32"; case OMX_VIDEO_AVCLevel4: return "Level4"; case OMX_VIDEO_AVCLevel41: return "Level41"; case OMX_VIDEO_AVCLevel42: return "Level42"; case OMX_VIDEO_AVCLevel5: return "Level5"; case OMX_VIDEO_AVCLevel51: return "Level51"; case OMX_VIDEO_AVCLevel52: return "Level52"; case OMX_VIDEO_AVCLevel6: return "Level6"; case OMX_VIDEO_AVCLevel61: return "Level61"; case OMX_VIDEO_AVCLevel62: return "Level62"; default: return def; } } inline static const char *asString(OMX_VIDEO_AVCLOOPFILTERTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_AVCLoopFilterEnable: return "Enable"; // case OMX_VIDEO_AVCLoopFilterDisable: return "Disable"; // case OMX_VIDEO_AVCLoopFilterDisableSliceBoundary: return "DisableSliceBoundary"; default: return def; } } #endif // AS_STRING_FOR_OMX_VIDEO_H #endif // OMX_Video_h #ifdef OMX_VideoExt_h /* asString definitions if media/openmax/OMX_VideoExt.h was included */ #ifndef AS_STRING_FOR_OMX_VIDEOEXT_H #define AS_STRING_FOR_OMX_VIDEOEXT_H inline static const char *asString(OMX_VIDEO_AVCPROFILEEXTTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_AVCProfileConstrainedBaseline: return "ConstrainedBaseline"; case OMX_VIDEO_AVCProfileConstrainedHigh: return "ConstrainedHigh"; default: return asString((OMX_VIDEO_AVCPROFILETYPE)i, def); } } inline static const char *asString(OMX_VIDEO_VP8PROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_VP8ProfileMain: return "Main"; case OMX_VIDEO_VP8ProfileUnknown: return "Unknown"; // unused default: return def; } } inline static const char *asString(OMX_VIDEO_VP8LEVELTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_VP8Level_Version0: return "_Version0"; case OMX_VIDEO_VP8Level_Version1: return "_Version1"; case OMX_VIDEO_VP8Level_Version2: return "_Version2"; case OMX_VIDEO_VP8Level_Version3: return "_Version3"; case OMX_VIDEO_VP8LevelUnknown: return "Unknown"; // unused default: return def; } } inline static const char *asString(OMX_VIDEO_VP9PROFILETYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_VP9Profile0: return "Profile0"; case OMX_VIDEO_VP9Profile1: return "Profile1"; case OMX_VIDEO_VP9Profile2: return "Profile2"; case OMX_VIDEO_VP9Profile3: return "Profile3"; case OMX_VIDEO_VP9Profile2HDR: return "Profile2HDR"; case OMX_VIDEO_VP9Profile3HDR: return "Profile3HDR"; default: return def; } } inline static const char *asString(OMX_VIDEO_VP9LEVELTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_VP9Level1: return "Level1"; case OMX_VIDEO_VP9Level11: return "Level11"; case OMX_VIDEO_VP9Level2: return "Level2"; case OMX_VIDEO_VP9Level21: return "Level21"; case OMX_VIDEO_VP9Level3: return "Level3"; case OMX_VIDEO_VP9Level31: return "Level31"; case OMX_VIDEO_VP9Level4: return "Level4"; case OMX_VIDEO_VP9Level41: return "Level41"; case OMX_VIDEO_VP9Level5: return "Level5"; case OMX_VIDEO_VP9Level51: return "Level51"; case OMX_VIDEO_VP9Level52: return "Level52"; case OMX_VIDEO_VP9Level6: return "Level6"; case OMX_VIDEO_VP9Level61: return "Level61"; case OMX_VIDEO_VP9Level62: return "Level62"; default: return def; } } inline static const char *asString( OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_VPXTemporalLayerPatternNone: return "None"; case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "WebRTC"; default: return def; } } inline static const char *asString(OMX_VIDEO_HEVCPROFILETYPE i, const char *def = "!!") { switch (i) { case OMX_VIDEO_HEVCProfileUnknown: return "Unknown"; // unused case OMX_VIDEO_HEVCProfileMain: return "Main"; case OMX_VIDEO_HEVCProfileMain10: return "Main10"; case OMX_VIDEO_HEVCProfileMain10HDR10: return "Main10HDR10"; default: return def; } } inline static const char *asString(OMX_VIDEO_HEVCLEVELTYPE i, const char *def = "!!") { switch (i) { case OMX_VIDEO_HEVCLevelUnknown: return "LevelUnknown"; // unused case OMX_VIDEO_HEVCMainTierLevel1: return "MainTierLevel1"; case OMX_VIDEO_HEVCHighTierLevel1: return "HighTierLevel1"; case OMX_VIDEO_HEVCMainTierLevel2: return "MainTierLevel2"; case OMX_VIDEO_HEVCHighTierLevel2: return "HighTierLevel2"; case OMX_VIDEO_HEVCMainTierLevel21: return "MainTierLevel21"; case OMX_VIDEO_HEVCHighTierLevel21: return "HighTierLevel21"; case OMX_VIDEO_HEVCMainTierLevel3: return "MainTierLevel3"; case OMX_VIDEO_HEVCHighTierLevel3: return "HighTierLevel3"; case OMX_VIDEO_HEVCMainTierLevel31: return "MainTierLevel31"; case OMX_VIDEO_HEVCHighTierLevel31: return "HighTierLevel31"; case OMX_VIDEO_HEVCMainTierLevel4: return "MainTierLevel4"; case OMX_VIDEO_HEVCHighTierLevel4: return "HighTierLevel4"; case OMX_VIDEO_HEVCMainTierLevel41: return "MainTierLevel41"; case OMX_VIDEO_HEVCHighTierLevel41: return "HighTierLevel41"; case OMX_VIDEO_HEVCMainTierLevel5: return "MainTierLevel5"; case OMX_VIDEO_HEVCHighTierLevel5: return "HighTierLevel5"; case OMX_VIDEO_HEVCMainTierLevel51: return "MainTierLevel51"; case OMX_VIDEO_HEVCHighTierLevel51: return "HighTierLevel51"; case OMX_VIDEO_HEVCMainTierLevel52: return "MainTierLevel52"; case OMX_VIDEO_HEVCHighTierLevel52: return "HighTierLevel52"; case OMX_VIDEO_HEVCMainTierLevel6: return "MainTierLevel6"; case OMX_VIDEO_HEVCHighTierLevel6: return "HighTierLevel6"; case OMX_VIDEO_HEVCMainTierLevel61: return "MainTierLevel61"; case OMX_VIDEO_HEVCHighTierLevel61: return "HighTierLevel61"; case OMX_VIDEO_HEVCMainTierLevel62: return "MainTierLevel62"; case OMX_VIDEO_HEVCHighTierLevel62: return "HighTierLevel62"; default: return def; } } inline static const char *asString( OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE i, const char *def = "??") { switch (i) { case OMX_VIDEO_AndroidTemporalLayeringPatternNone: return "None"; case OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC: return "WebRTC"; case OMX_VIDEO_AndroidTemporalLayeringPatternAndroid: return "Android"; default: return def; } } #endif // AS_STRING_FOR_OMX_VIDEOEXT_H #endif // OMX_VideoExt_h #ifdef ANDROID } // namespace android #endif headers/media_plugin/media/openmax/OMX_Audio.h0100644 0000000 0000000 00000231342 13756501735 020361 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** @file OMX_Audio.h - OpenMax IL version 1.1.2 * The structures needed by Audio components to exchange * parameters and configuration data with the componenmilts. */ #ifndef OMX_Audio_h #define OMX_Audio_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header must include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include /** @defgroup midi MIDI * @ingroup audio */ /** @defgroup effects Audio effects * @ingroup audio */ /** @defgroup audio OpenMAX IL Audio Domain * Structures for OpenMAX IL Audio domain * @{ */ /** Enumeration used to define the possible audio codings. * If "OMX_AUDIO_CodingUnused" is selected, the coding selection must * be done in a vendor specific way. Since this is for an audio * processing element this enum is relevant. However, for another * type of component other enums would be in this area. */ typedef enum OMX_AUDIO_CODINGTYPE { OMX_AUDIO_CodingUnused = 0, /**< Placeholder value when coding is N/A */ OMX_AUDIO_CodingAutoDetect, /**< auto detection of audio format */ OMX_AUDIO_CodingPCM, /**< Any variant of PCM coding */ OMX_AUDIO_CodingADPCM, /**< Any variant of ADPCM encoded data */ OMX_AUDIO_CodingAMR, /**< Any variant of AMR encoded data */ OMX_AUDIO_CodingGSMFR, /**< Any variant of GSM fullrate (i.e. GSM610) */ OMX_AUDIO_CodingGSMEFR, /**< Any variant of GSM Enhanced Fullrate encoded data*/ OMX_AUDIO_CodingGSMHR, /**< Any variant of GSM Halfrate encoded data */ OMX_AUDIO_CodingPDCFR, /**< Any variant of PDC Fullrate encoded data */ OMX_AUDIO_CodingPDCEFR, /**< Any variant of PDC Enhanced Fullrate encoded data */ OMX_AUDIO_CodingPDCHR, /**< Any variant of PDC Halfrate encoded data */ OMX_AUDIO_CodingTDMAFR, /**< Any variant of TDMA Fullrate encoded data (TIA/EIA-136-420) */ OMX_AUDIO_CodingTDMAEFR, /**< Any variant of TDMA Enhanced Fullrate encoded data (TIA/EIA-136-410) */ OMX_AUDIO_CodingQCELP8, /**< Any variant of QCELP 8kbps encoded data */ OMX_AUDIO_CodingQCELP13, /**< Any variant of QCELP 13kbps encoded data */ OMX_AUDIO_CodingEVRC, /**< Any variant of EVRC encoded data */ OMX_AUDIO_CodingSMV, /**< Any variant of SMV encoded data */ OMX_AUDIO_CodingG711, /**< Any variant of G.711 encoded data */ OMX_AUDIO_CodingG723, /**< Any variant of G.723 dot 1 encoded data */ OMX_AUDIO_CodingG726, /**< Any variant of G.726 encoded data */ OMX_AUDIO_CodingG729, /**< Any variant of G.729 encoded data */ OMX_AUDIO_CodingAAC, /**< Any variant of AAC encoded data */ OMX_AUDIO_CodingMP3, /**< Any variant of MP3 encoded data */ OMX_AUDIO_CodingSBC, /**< Any variant of SBC encoded data */ OMX_AUDIO_CodingVORBIS, /**< Any variant of VORBIS encoded data */ OMX_AUDIO_CodingWMA, /**< Any variant of WMA encoded data */ OMX_AUDIO_CodingRA, /**< Any variant of RA encoded data */ OMX_AUDIO_CodingMIDI, /**< Any variant of MIDI encoded data */ OMX_AUDIO_CodingFLAC, /**< Any variant of FLAC encoded data */ OMX_AUDIO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_CodingMax = 0x7FFFFFFF } OMX_AUDIO_CODINGTYPE; /** The PortDefinition structure is used to define all of the parameters * necessary for the compliant component to setup an input or an output audio * path. If additional information is needed to define the parameters of the * port (such as frequency), additional structures must be sent such as the * OMX_AUDIO_PARAM_PCMMODETYPE structure to supply the extra parameters for the port. */ typedef struct OMX_AUDIO_PORTDEFINITIONTYPE { OMX_STRING cMIMEType; /**< MIME type of data for the port */ OMX_NATIVE_DEVICETYPE pNativeRender; /** < platform specific reference for an output device, otherwise this field is 0 */ OMX_BOOL bFlagErrorConcealment; /**< Turns on error concealment if it is supported by the OMX component */ OMX_AUDIO_CODINGTYPE eEncoding; /**< Type of data expected for this port (e.g. PCM, AMR, MP3, etc) */ } OMX_AUDIO_PORTDEFINITIONTYPE; /** Port format parameter. This structure is used to enumerate * the various data input/output format supported by the port. */ typedef struct OMX_AUDIO_PARAM_PORTFORMATTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Indicates which port to set */ OMX_U32 nIndex; /**< Indicates the enumeration index for the format from 0x0 to N-1 */ OMX_AUDIO_CODINGTYPE eEncoding; /**< Type of data expected for this port (e.g. PCM, AMR, MP3, etc) */ } OMX_AUDIO_PARAM_PORTFORMATTYPE; /** PCM mode type */ typedef enum OMX_AUDIO_PCMMODETYPE { OMX_AUDIO_PCMModeLinear = 0, /**< Linear PCM encoded data */ OMX_AUDIO_PCMModeALaw, /**< A law PCM encoded data (G.711) */ OMX_AUDIO_PCMModeMULaw, /**< Mu law PCM encoded data (G.711) */ OMX_AUDIO_PCMModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_PCMModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_PCMModeMax = 0x7FFFFFFF } OMX_AUDIO_PCMMODETYPE; typedef enum OMX_AUDIO_CHANNELTYPE { OMX_AUDIO_ChannelNone = 0x0, /**< Unused or empty */ OMX_AUDIO_ChannelLF = 0x1, /**< Left front */ OMX_AUDIO_ChannelRF = 0x2, /**< Right front */ OMX_AUDIO_ChannelCF = 0x3, /**< Center front */ OMX_AUDIO_ChannelLS = 0x4, /**< Left surround */ OMX_AUDIO_ChannelRS = 0x5, /**< Right surround */ OMX_AUDIO_ChannelLFE = 0x6, /**< Low frequency effects */ OMX_AUDIO_ChannelCS = 0x7, /**< Back surround */ OMX_AUDIO_ChannelLR = 0x8, /**< Left rear. */ OMX_AUDIO_ChannelRR = 0x9, /**< Right rear. */ OMX_AUDIO_ChannelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_ChannelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_ChannelMax = 0x7FFFFFFF } OMX_AUDIO_CHANNELTYPE; #define OMX_AUDIO_MAXCHANNELS 16 /**< maximum number distinct audio channels that a buffer may contain */ #define OMX_MIN_PCMPAYLOAD_MSEC 5 /**< Minimum audio buffer payload size for uncompressed (PCM) audio */ /** PCM format description */ typedef struct OMX_AUDIO_PARAM_PCMMODETYPE { OMX_U32 nSize; /**< Size of this structure, in Bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels (e.g. 2 for stereo) */ OMX_NUMERICALDATATYPE eNumData; /**< indicates PCM data as signed, unsigned or floating pt. */ OMX_ENDIANTYPE eEndian; /**< indicates PCM data as little or big endian */ OMX_BOOL bInterleaved; /**< True for normal interleaved data; false for non-interleaved data (e.g. block data) */ OMX_U32 nBitPerSample; /**< Bit per sample */ OMX_U32 nSamplingRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ OMX_AUDIO_PCMMODETYPE ePCMMode; /**< PCM mode enumeration */ OMX_AUDIO_CHANNELTYPE eChannelMapping[OMX_AUDIO_MAXCHANNELS]; /**< Slot i contains channel defined by eChannelMap[i] */ } OMX_AUDIO_PARAM_PCMMODETYPE; /** Audio channel mode. This is used by both AAC and MP3, although the names are more appropriate * for the MP3. For example, JointStereo for MP3 is CouplingChannels for AAC. */ typedef enum OMX_AUDIO_CHANNELMODETYPE { OMX_AUDIO_ChannelModeStereo = 0, /**< 2 channels, the bitrate allocation between those two channels changes accordingly to each channel information */ OMX_AUDIO_ChannelModeJointStereo, /**< mode that takes advantage of what is common between 2 channels for higher compression gain */ OMX_AUDIO_ChannelModeDual, /**< 2 mono-channels, each channel is encoded with half the bitrate of the overall bitrate */ OMX_AUDIO_ChannelModeMono, /**< Mono channel mode */ OMX_AUDIO_ChannelModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_ChannelModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_ChannelModeMax = 0x7FFFFFFF } OMX_AUDIO_CHANNELMODETYPE; typedef enum OMX_AUDIO_MP3STREAMFORMATTYPE { OMX_AUDIO_MP3StreamFormatMP1Layer3 = 0, /**< MP3 Audio MPEG 1 Layer 3 Stream format */ OMX_AUDIO_MP3StreamFormatMP2Layer3, /**< MP3 Audio MPEG 2 Layer 3 Stream format */ OMX_AUDIO_MP3StreamFormatMP2_5Layer3, /**< MP3 Audio MPEG2.5 Layer 3 Stream format */ OMX_AUDIO_MP3StreamFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_MP3StreamFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_MP3StreamFormatMax = 0x7FFFFFFF } OMX_AUDIO_MP3STREAMFORMATTYPE; /** MP3 params */ typedef struct OMX_AUDIO_PARAM_MP3TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable rate or unknown bit rates */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should limit the audio signal. Use 0 to let encoder decide */ OMX_AUDIO_CHANNELMODETYPE eChannelMode; /**< Channel mode enumeration */ OMX_AUDIO_MP3STREAMFORMATTYPE eFormat; /**< MP3 stream format */ } OMX_AUDIO_PARAM_MP3TYPE; typedef enum OMX_AUDIO_AACSTREAMFORMATTYPE { OMX_AUDIO_AACStreamFormatMP2ADTS = 0, /**< AAC Audio Data Transport Stream 2 format */ OMX_AUDIO_AACStreamFormatMP4ADTS, /**< AAC Audio Data Transport Stream 4 format */ OMX_AUDIO_AACStreamFormatMP4LOAS, /**< AAC Low Overhead Audio Stream format */ OMX_AUDIO_AACStreamFormatMP4LATM, /**< AAC Low overhead Audio Transport Multiplex */ OMX_AUDIO_AACStreamFormatADIF, /**< AAC Audio Data Interchange Format */ OMX_AUDIO_AACStreamFormatMP4FF, /**< AAC inside MPEG-4/ISO File Format */ OMX_AUDIO_AACStreamFormatRAW, /**< AAC Raw Format */ OMX_AUDIO_AACStreamFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_AACStreamFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_AACStreamFormatMax = 0x7FFFFFFF } OMX_AUDIO_AACSTREAMFORMATTYPE; /** AAC mode type. Note that the term profile is used with the MPEG-2 * standard and the term object type and profile is used with MPEG-4 */ typedef enum OMX_AUDIO_AACPROFILETYPE{ OMX_AUDIO_AACObjectNull = 0, /**< Null, not used */ OMX_AUDIO_AACObjectMain = 1, /**< AAC Main object */ OMX_AUDIO_AACObjectLC, /**< AAC Low Complexity object (AAC profile) */ OMX_AUDIO_AACObjectSSR, /**< AAC Scalable Sample Rate object */ OMX_AUDIO_AACObjectLTP, /**< AAC Long Term Prediction object */ OMX_AUDIO_AACObjectHE, /**< AAC High Efficiency (object type SBR, HE-AAC profile) */ OMX_AUDIO_AACObjectScalable, /**< AAC Scalable object */ OMX_AUDIO_AACObjectERLC = 17, /**< ER AAC Low Complexity object (Error Resilient AAC-LC) */ OMX_AUDIO_AACObjectER_Scalable = 20, /**< ER AAC scalable object */ OMX_AUDIO_AACObjectLD = 23, /**< AAC Low Delay object (Error Resilient) */ OMX_AUDIO_AACObjectHE_PS = 29, /**< AAC High Efficiency with Parametric Stereo coding (HE-AAC v2, object type PS) */ OMX_AUDIO_AACObjectELD = 39, /** AAC Enhanced Low Delay. NOTE: Pending Khronos standardization **/ OMX_AUDIO_AACObjectXHE = 42, /** extended High Efficiency AAC. NOTE: Pending Khronos standardization */ OMX_AUDIO_AACObjectKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_AACObjectVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_AACObjectMax = 0x7FFFFFFF } OMX_AUDIO_AACPROFILETYPE; /** AAC tool usage (for nAACtools in OMX_AUDIO_PARAM_AACPROFILETYPE). * Required for encoder configuration and optional as decoder info output. * For MP3, OMX_AUDIO_CHANNELMODETYPE is sufficient. */ #define OMX_AUDIO_AACToolNone 0x00000000 /**< no AAC tools allowed (encoder config) or active (decoder info output) */ #define OMX_AUDIO_AACToolMS 0x00000001 /**< MS: Mid/side joint coding tool allowed or active */ #define OMX_AUDIO_AACToolIS 0x00000002 /**< IS: Intensity stereo tool allowed or active */ #define OMX_AUDIO_AACToolTNS 0x00000004 /**< TNS: Temporal Noise Shaping tool allowed or active */ #define OMX_AUDIO_AACToolPNS 0x00000008 /**< PNS: MPEG-4 Perceptual Noise substitution tool allowed or active */ #define OMX_AUDIO_AACToolLTP 0x00000010 /**< LTP: MPEG-4 Long Term Prediction tool allowed or active */ #define OMX_AUDIO_AACToolVendor 0x00010000 /**< NOT A KHRONOS VALUE, offset for vendor-specific additions */ #define OMX_AUDIO_AACToolAll 0x7FFFFFFF /**< all AAC tools allowed or active (*/ /** MPEG-4 AAC error resilience (ER) tool usage (for nAACERtools in OMX_AUDIO_PARAM_AACPROFILETYPE). * Required for ER encoder configuration and optional as decoder info output */ #define OMX_AUDIO_AACERNone 0x00000000 /**< no AAC ER tools allowed/used */ #define OMX_AUDIO_AACERVCB11 0x00000001 /**< VCB11: Virtual Code Books for AAC section data */ #define OMX_AUDIO_AACERRVLC 0x00000002 /**< RVLC: Reversible Variable Length Coding */ #define OMX_AUDIO_AACERHCR 0x00000004 /**< HCR: Huffman Codeword Reordering */ #define OMX_AUDIO_AACERAll 0x7FFFFFFF /**< all AAC ER tools allowed/used */ /** AAC params */ typedef struct OMX_AUDIO_PARAM_AACPROFILETYPE { OMX_U32 nSize; /**< Size of this structure, in Bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable rate or unknown bit rates */ OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should limit the audio signal. Use 0 to let encoder decide */ OMX_U32 nFrameLength; /**< Frame length (in audio samples per channel) of the codec. Can be 1024 or 960 (AAC-LC), 2048 (HE-AAC), 480 or 512 (AAC-LD). Use 0 to let encoder decide */ OMX_U32 nAACtools; /**< AAC tool usage */ OMX_U32 nAACERtools; /**< MPEG-4 AAC error resilience tool usage */ OMX_AUDIO_AACPROFILETYPE eAACProfile; /**< AAC profile enumeration */ OMX_AUDIO_AACSTREAMFORMATTYPE eAACStreamFormat; /**< AAC stream format enumeration */ OMX_AUDIO_CHANNELMODETYPE eChannelMode; /**< Channel mode enumeration */ } OMX_AUDIO_PARAM_AACPROFILETYPE; /** VORBIS params */ typedef struct OMX_AUDIO_PARAM_VORBISTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nBitRate; /**< Bit rate of the encoded data data. Use 0 for variable rate or unknown bit rates. Encoding is set to the bitrate closest to specified value (in bps) */ OMX_U32 nMinBitRate; /**< Sets minimum bitrate (in bps). */ OMX_U32 nMaxBitRate; /**< Sets maximum bitrate (in bps). */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should limit the audio signal. Use 0 to let encoder decide */ OMX_S32 nQuality; /**< Sets encoding quality to n, between -1 (low) and 10 (high). In the default mode of operation, teh quality level is 3. Normal quality range is 0 - 10. */ OMX_BOOL bManaged; /**< Set bitrate management mode. This turns off the normal VBR encoding, but allows hard or soft bitrate constraints to be enforced by the encoder. This mode can be slower, and may also be lower quality. It is primarily useful for streaming. */ OMX_BOOL bDownmix; /**< Downmix input from stereo to mono (has no effect on non-stereo streams). Useful for lower-bitrate encoding. */ } OMX_AUDIO_PARAM_VORBISTYPE; /** FLAC params */ typedef struct OMX_AUDIO_PARAM_FLACTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for unknown sampling rate. */ OMX_U32 nCompressionLevel;/**< FLAC compression level, from 0 (fastest compression) to 8 (highest compression */ } OMX_AUDIO_PARAM_FLACTYPE; /** WMA Version */ typedef enum OMX_AUDIO_WMAFORMATTYPE { OMX_AUDIO_WMAFormatUnused = 0, /**< format unused or unknown */ OMX_AUDIO_WMAFormat7, /**< Windows Media Audio format 7 */ OMX_AUDIO_WMAFormat8, /**< Windows Media Audio format 8 */ OMX_AUDIO_WMAFormat9, /**< Windows Media Audio format 9 */ OMX_AUDIO_WMAFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_WMAFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_WMAFormatMax = 0x7FFFFFFF } OMX_AUDIO_WMAFORMATTYPE; /** WMA Profile */ typedef enum OMX_AUDIO_WMAPROFILETYPE { OMX_AUDIO_WMAProfileUnused = 0, /**< profile unused or unknown */ OMX_AUDIO_WMAProfileL1, /**< Windows Media audio version 9 profile L1 */ OMX_AUDIO_WMAProfileL2, /**< Windows Media audio version 9 profile L2 */ OMX_AUDIO_WMAProfileL3, /**< Windows Media audio version 9 profile L3 */ OMX_AUDIO_WMAProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_WMAProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_WMAProfileMax = 0x7FFFFFFF } OMX_AUDIO_WMAPROFILETYPE; /** WMA params */ typedef struct OMX_AUDIO_PARAM_WMATYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U16 nChannels; /**< Number of channels */ OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable rate or unknown bit rates */ OMX_AUDIO_WMAFORMATTYPE eFormat; /**< Version of WMA stream / data */ OMX_AUDIO_WMAPROFILETYPE eProfile; /**< Profile of WMA stream / data */ OMX_U32 nSamplingRate; /**< Sampling rate of the source data */ OMX_U16 nBlockAlign; /**< is the block alignment, or block size, in bytes of the audio codec */ OMX_U16 nEncodeOptions; /**< WMA Type-specific data */ OMX_U32 nSuperBlockAlign; /**< WMA Type-specific data */ } OMX_AUDIO_PARAM_WMATYPE; /** * RealAudio format */ typedef enum OMX_AUDIO_RAFORMATTYPE { OMX_AUDIO_RAFormatUnused = 0, /**< Format unused or unknown */ OMX_AUDIO_RA8, /**< RealAudio 8 codec */ OMX_AUDIO_RA9, /**< RealAudio 9 codec */ OMX_AUDIO_RA10_AAC, /**< MPEG-4 AAC codec for bitrates of more than 128kbps */ OMX_AUDIO_RA10_CODEC, /**< RealAudio codec for bitrates less than 128 kbps */ OMX_AUDIO_RA10_LOSSLESS, /**< RealAudio Lossless */ OMX_AUDIO_RA10_MULTICHANNEL, /**< RealAudio Multichannel */ OMX_AUDIO_RA10_VOICE, /**< RealAudio Voice for bitrates below 15 kbps */ OMX_AUDIO_RAFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_RAFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_RAFormatMax = 0x7FFFFFFF } OMX_AUDIO_RAFORMATTYPE; /** RA (Real Audio) params */ typedef struct OMX_AUDIO_PARAM_RATYPE { OMX_U32 nSize; /**< Size of this structure, in Bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nSamplingRate; /**< is the sampling rate of the source data */ OMX_U32 nBitsPerFrame; /**< is the value for bits per frame */ OMX_U32 nSamplePerFrame; /**< is the value for samples per frame */ OMX_U32 nCouplingQuantBits; /**< is the number of coupling quantization bits in the stream */ OMX_U32 nCouplingStartRegion; /**< is the coupling start region in the stream */ OMX_U32 nNumRegions; /**< is the number of regions value */ OMX_AUDIO_RAFORMATTYPE eFormat; /**< is the RealAudio audio format */ } OMX_AUDIO_PARAM_RATYPE; /** SBC Allocation Method Type */ typedef enum OMX_AUDIO_SBCALLOCMETHODTYPE { OMX_AUDIO_SBCAllocMethodLoudness, /**< Loudness allocation method */ OMX_AUDIO_SBCAllocMethodSNR, /**< SNR allocation method */ OMX_AUDIO_SBCAllocMethodKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_SBCAllocMethodVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_SBCAllocMethodMax = 0x7FFFFFFF } OMX_AUDIO_SBCALLOCMETHODTYPE; /** SBC params */ typedef struct OMX_AUDIO_PARAM_SBCTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable rate or unknown bit rates */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ OMX_U32 nBlocks; /**< Number of blocks */ OMX_U32 nSubbands; /**< Number of subbands */ OMX_U32 nBitPool; /**< Bitpool value */ OMX_BOOL bEnableBitrate; /**< Use bitrate value instead of bitpool */ OMX_AUDIO_CHANNELMODETYPE eChannelMode; /**< Channel mode enumeration */ OMX_AUDIO_SBCALLOCMETHODTYPE eSBCAllocType; /**< SBC Allocation method type */ } OMX_AUDIO_PARAM_SBCTYPE; /** ADPCM stream format parameters */ typedef struct OMX_AUDIO_PARAM_ADPCMTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_U32 nBitsPerSample; /**< Number of bits in each sample */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ } OMX_AUDIO_PARAM_ADPCMTYPE; /** G723 rate */ typedef enum OMX_AUDIO_G723RATE { OMX_AUDIO_G723ModeUnused = 0, /**< AMRNB Mode unused / unknown */ OMX_AUDIO_G723ModeLow, /**< 5300 bps */ OMX_AUDIO_G723ModeHigh, /**< 6300 bps */ OMX_AUDIO_G723ModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_G723ModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_G723ModeMax = 0x7FFFFFFF } OMX_AUDIO_G723RATE; /** G723 - Sample rate must be 8 KHz */ typedef struct OMX_AUDIO_PARAM_G723TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_AUDIO_G723RATE eBitRate; /**< todo: Should this be moved to a config? */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ OMX_BOOL bPostFilter; /**< Enable Post Filter */ } OMX_AUDIO_PARAM_G723TYPE; /** ITU G726 (ADPCM) rate */ typedef enum OMX_AUDIO_G726MODE { OMX_AUDIO_G726ModeUnused = 0, /**< G726 Mode unused / unknown */ OMX_AUDIO_G726Mode16, /**< 16 kbps */ OMX_AUDIO_G726Mode24, /**< 24 kbps */ OMX_AUDIO_G726Mode32, /**< 32 kbps, most common rate, also G721 */ OMX_AUDIO_G726Mode40, /**< 40 kbps */ OMX_AUDIO_G726ModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_G726ModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_G726ModeMax = 0x7FFFFFFF } OMX_AUDIO_G726MODE; /** G.726 stream format parameters - must be at 8KHz */ typedef struct OMX_AUDIO_PARAM_G726TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_AUDIO_G726MODE eG726Mode; } OMX_AUDIO_PARAM_G726TYPE; /** G729 coder type */ typedef enum OMX_AUDIO_G729TYPE { OMX_AUDIO_G729 = 0, /**< ITU G.729 encoded data */ OMX_AUDIO_G729A, /**< ITU G.729 annex A encoded data */ OMX_AUDIO_G729B, /**< ITU G.729 with annex B encoded data */ OMX_AUDIO_G729AB, /**< ITU G.729 annexes A and B encoded data */ OMX_AUDIO_G729KhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_G729VendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_G729Max = 0x7FFFFFFF } OMX_AUDIO_G729TYPE; /** G729 stream format parameters - fixed 6KHz sample rate */ typedef struct OMX_AUDIO_PARAM_G729TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_AUDIO_G729TYPE eBitType; } OMX_AUDIO_PARAM_G729TYPE; /** AMR Frame format */ typedef enum OMX_AUDIO_AMRFRAMEFORMATTYPE { OMX_AUDIO_AMRFrameFormatConformance = 0, /**< Frame Format is AMR Conformance (Standard) Format */ OMX_AUDIO_AMRFrameFormatIF1, /**< Frame Format is AMR Interface Format 1 */ OMX_AUDIO_AMRFrameFormatIF2, /**< Frame Format is AMR Interface Format 2*/ OMX_AUDIO_AMRFrameFormatFSF, /**< Frame Format is AMR File Storage Format */ OMX_AUDIO_AMRFrameFormatRTPPayload, /**< Frame Format is AMR Real-Time Transport Protocol Payload Format */ OMX_AUDIO_AMRFrameFormatITU, /**< Frame Format is ITU Format (added at Motorola request) */ OMX_AUDIO_AMRFrameFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_AMRFrameFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_AMRFrameFormatMax = 0x7FFFFFFF } OMX_AUDIO_AMRFRAMEFORMATTYPE; /** AMR band mode */ typedef enum OMX_AUDIO_AMRBANDMODETYPE { OMX_AUDIO_AMRBandModeUnused = 0, /**< AMRNB Mode unused / unknown */ OMX_AUDIO_AMRBandModeNB0, /**< AMRNB Mode 0 = 4750 bps */ OMX_AUDIO_AMRBandModeNB1, /**< AMRNB Mode 1 = 5150 bps */ OMX_AUDIO_AMRBandModeNB2, /**< AMRNB Mode 2 = 5900 bps */ OMX_AUDIO_AMRBandModeNB3, /**< AMRNB Mode 3 = 6700 bps */ OMX_AUDIO_AMRBandModeNB4, /**< AMRNB Mode 4 = 7400 bps */ OMX_AUDIO_AMRBandModeNB5, /**< AMRNB Mode 5 = 7950 bps */ OMX_AUDIO_AMRBandModeNB6, /**< AMRNB Mode 6 = 10200 bps */ OMX_AUDIO_AMRBandModeNB7, /**< AMRNB Mode 7 = 12200 bps */ OMX_AUDIO_AMRBandModeWB0, /**< AMRWB Mode 0 = 6600 bps */ OMX_AUDIO_AMRBandModeWB1, /**< AMRWB Mode 1 = 8850 bps */ OMX_AUDIO_AMRBandModeWB2, /**< AMRWB Mode 2 = 12650 bps */ OMX_AUDIO_AMRBandModeWB3, /**< AMRWB Mode 3 = 14250 bps */ OMX_AUDIO_AMRBandModeWB4, /**< AMRWB Mode 4 = 15850 bps */ OMX_AUDIO_AMRBandModeWB5, /**< AMRWB Mode 5 = 18250 bps */ OMX_AUDIO_AMRBandModeWB6, /**< AMRWB Mode 6 = 19850 bps */ OMX_AUDIO_AMRBandModeWB7, /**< AMRWB Mode 7 = 23050 bps */ OMX_AUDIO_AMRBandModeWB8, /**< AMRWB Mode 8 = 23850 bps */ OMX_AUDIO_AMRBandModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_AMRBandModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_AMRBandModeMax = 0x7FFFFFFF } OMX_AUDIO_AMRBANDMODETYPE; /** AMR Discontinuous Transmission mode */ typedef enum OMX_AUDIO_AMRDTXMODETYPE { OMX_AUDIO_AMRDTXModeOff = 0, /**< AMR Discontinuous Transmission Mode is disabled */ OMX_AUDIO_AMRDTXModeOnVAD1, /**< AMR Discontinuous Transmission Mode using Voice Activity Detector 1 (VAD1) is enabled */ OMX_AUDIO_AMRDTXModeOnVAD2, /**< AMR Discontinuous Transmission Mode using Voice Activity Detector 2 (VAD2) is enabled */ OMX_AUDIO_AMRDTXModeOnAuto, /**< The codec will automatically select between Off, VAD1 or VAD2 modes */ OMX_AUDIO_AMRDTXasEFR, /**< DTX as EFR instead of AMR standard (3GPP 26.101, frame type =8,9,10) */ OMX_AUDIO_AMRDTXModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_AMRDTXModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_AMRDTXModeMax = 0x7FFFFFFF } OMX_AUDIO_AMRDTXMODETYPE; /** AMR params */ typedef struct OMX_AUDIO_PARAM_AMRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nBitRate; /**< Bit rate read only field */ OMX_AUDIO_AMRBANDMODETYPE eAMRBandMode; /**< AMR Band Mode enumeration */ OMX_AUDIO_AMRDTXMODETYPE eAMRDTXMode; /**< AMR DTX Mode enumeration */ OMX_AUDIO_AMRFRAMEFORMATTYPE eAMRFrameFormat; /**< AMR frame format enumeration */ } OMX_AUDIO_PARAM_AMRTYPE; /** GSM_FR (ETSI 06.10, 3GPP 46.010) stream format parameters */ typedef struct OMX_AUDIO_PARAM_GSMFRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_GSMFRTYPE; /** GSM-HR (ETSI 06.20, 3GPP 46.020) stream format parameters */ typedef struct OMX_AUDIO_PARAM_GSMHRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_GSMHRTYPE; /** GSM-EFR (ETSI 06.60, 3GPP 46.060) stream format parameters */ typedef struct OMX_AUDIO_PARAM_GSMEFRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_GSMEFRTYPE; /** TDMA FR (TIA/EIA-136-420, VSELP 7.95kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_TDMAFRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_TDMAFRTYPE; /** TDMA EFR (TIA/EIA-136-410, ACELP 7.4kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_TDMAEFRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_TDMAEFRTYPE; /** PDC FR ( RCR-27, VSELP 6.7kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_PDCFRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_PDCFRTYPE; /** PDC EFR ( RCR-27, ACELP 6.7kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_PDCEFRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_PDCEFRTYPE; /** PDC HR ( RCR-27, PSI-CELP 3.45kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_PDCHRTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_BOOL bDTX; /**< Enable Discontinuous Transmisssion */ OMX_BOOL bHiPassFilter; /**< Enable High Pass Filter */ } OMX_AUDIO_PARAM_PDCHRTYPE; /** CDMA Rate types */ typedef enum OMX_AUDIO_CDMARATETYPE { OMX_AUDIO_CDMARateBlank = 0, /**< CDMA encoded frame is blank */ OMX_AUDIO_CDMARateFull, /**< CDMA encoded frame in full rate */ OMX_AUDIO_CDMARateHalf, /**< CDMA encoded frame in half rate */ OMX_AUDIO_CDMARateQuarter, /**< CDMA encoded frame in quarter rate */ OMX_AUDIO_CDMARateEighth, /**< CDMA encoded frame in eighth rate (DTX)*/ OMX_AUDIO_CDMARateErasure, /**< CDMA erasure frame */ OMX_AUDIO_CDMARateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_CDMARateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_CDMARateMax = 0x7FFFFFFF } OMX_AUDIO_CDMARATETYPE; /** QCELP8 (TIA/EIA-96, up to 8kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_QCELP8TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_U32 nBitRate; /**< Bit rate of the input data. Use 0 for variable rate or unknown bit rates */ OMX_AUDIO_CDMARATETYPE eCDMARate; /**< Frame rate */ OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 */ OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 */ } OMX_AUDIO_PARAM_QCELP8TYPE; /** QCELP13 ( CDMA, EIA/TIA-733, 13.3kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_QCELP13TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_AUDIO_CDMARATETYPE eCDMARate; /**< Frame rate */ OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 */ OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 */ } OMX_AUDIO_PARAM_QCELP13TYPE; /** EVRC ( CDMA, EIA/TIA-127, RCELP up to 8.55kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_EVRCTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_AUDIO_CDMARATETYPE eCDMARate; /**< actual Frame rate */ OMX_BOOL bRATE_REDUCon; /**< RATE_REDUCtion is requested for this frame */ OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 */ OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 */ OMX_BOOL bHiPassFilter; /**< Enable encoder's High Pass Filter */ OMX_BOOL bNoiseSuppressor; /**< Enable encoder's noise suppressor pre-processing */ OMX_BOOL bPostFilter; /**< Enable decoder's post Filter */ } OMX_AUDIO_PARAM_EVRCTYPE; /** SMV ( up to 8.55kbps coder) stream format parameters */ typedef struct OMX_AUDIO_PARAM_SMVTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels in the data stream (not necessarily the same as the number of channels to be rendered. */ OMX_AUDIO_CDMARATETYPE eCDMARate; /**< Frame rate */ OMX_BOOL bRATE_REDUCon; /**< RATE_REDUCtion is requested for this frame */ OMX_U32 nMinBitRate; /**< minmal rate for the encoder = 1,2,3,4, default = 1 ??*/ OMX_U32 nMaxBitRate; /**< maximal rate for the encoder = 1,2,3,4, default = 4 ??*/ OMX_BOOL bHiPassFilter; /**< Enable encoder's High Pass Filter ??*/ OMX_BOOL bNoiseSuppressor; /**< Enable encoder's noise suppressor pre-processing */ OMX_BOOL bPostFilter; /**< Enable decoder's post Filter ??*/ } OMX_AUDIO_PARAM_SMVTYPE; /** MIDI Format * @ingroup midi */ typedef enum OMX_AUDIO_MIDIFORMATTYPE { OMX_AUDIO_MIDIFormatUnknown = 0, /**< MIDI Format unknown or don't care */ OMX_AUDIO_MIDIFormatSMF0, /**< Standard MIDI File Type 0 */ OMX_AUDIO_MIDIFormatSMF1, /**< Standard MIDI File Type 1 */ OMX_AUDIO_MIDIFormatSMF2, /**< Standard MIDI File Type 2 */ OMX_AUDIO_MIDIFormatSPMIDI, /**< SP-MIDI */ OMX_AUDIO_MIDIFormatXMF0, /**< eXtensible Music Format type 0 */ OMX_AUDIO_MIDIFormatXMF1, /**< eXtensible Music Format type 1 */ OMX_AUDIO_MIDIFormatMobileXMF, /**< Mobile XMF (eXtensible Music Format type 2) */ OMX_AUDIO_MIDIFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_MIDIFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_MIDIFormatMax = 0x7FFFFFFF } OMX_AUDIO_MIDIFORMATTYPE; /** MIDI params * @ingroup midi */ typedef struct OMX_AUDIO_PARAM_MIDITYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nFileSize; /**< size of the MIDI file in bytes, where the entire MIDI file passed in, otherwise if 0x0, the MIDI data is merged and streamed (instead of passed as an entire MIDI file) */ OMX_BU32 sMaxPolyphony; /**< Specifies the maximum simultaneous polyphonic voices. A value of zero indicates that the default polyphony of the device is used */ OMX_BOOL bLoadDefaultSound; /**< Whether to load default sound bank at initialization */ OMX_AUDIO_MIDIFORMATTYPE eMidiFormat; /**< Version of the MIDI file */ } OMX_AUDIO_PARAM_MIDITYPE; /** Type of the MIDI sound bank * @ingroup midi */ typedef enum OMX_AUDIO_MIDISOUNDBANKTYPE { OMX_AUDIO_MIDISoundBankUnused = 0, /**< unused/unknown soundbank type */ OMX_AUDIO_MIDISoundBankDLS1, /**< DLS version 1 */ OMX_AUDIO_MIDISoundBankDLS2, /**< DLS version 2 */ OMX_AUDIO_MIDISoundBankMobileDLSBase, /**< Mobile DLS, using the base functionality */ OMX_AUDIO_MIDISoundBankMobileDLSPlusOptions, /**< Mobile DLS, using the specification-defined optional feature set */ OMX_AUDIO_MIDISoundBankKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_MIDISoundBankVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_MIDISoundBankMax = 0x7FFFFFFF } OMX_AUDIO_MIDISOUNDBANKTYPE; /** Bank Layout describes how bank MSB & LSB are used in the DLS instrument definitions sound bank * @ingroup midi */ typedef enum OMX_AUDIO_MIDISOUNDBANKLAYOUTTYPE { OMX_AUDIO_MIDISoundBankLayoutUnused = 0, /**< unused/unknown soundbank type */ OMX_AUDIO_MIDISoundBankLayoutGM, /**< GS layout (based on bank MSB 0x00) */ OMX_AUDIO_MIDISoundBankLayoutGM2, /**< General MIDI 2 layout (using MSB 0x78/0x79, LSB 0x00) */ OMX_AUDIO_MIDISoundBankLayoutUser, /**< Does not conform to any bank numbering standards */ OMX_AUDIO_MIDISoundBankLayoutKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_MIDISoundBankLayoutVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_MIDISoundBankLayoutMax = 0x7FFFFFFF } OMX_AUDIO_MIDISOUNDBANKLAYOUTTYPE; /** MIDI params to load/unload user soundbank * @ingroup midi */ typedef struct OMX_AUDIO_PARAM_MIDILOADUSERSOUNDTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nDLSIndex; /**< DLS file index to be loaded */ OMX_U32 nDLSSize; /**< Size in bytes */ OMX_PTR pDLSData; /**< Pointer to DLS file data */ OMX_AUDIO_MIDISOUNDBANKTYPE eMidiSoundBank; /**< Midi sound bank type enumeration */ OMX_AUDIO_MIDISOUNDBANKLAYOUTTYPE eMidiSoundBankLayout; /**< Midi sound bank layout enumeration */ } OMX_AUDIO_PARAM_MIDILOADUSERSOUNDTYPE; /** Structure for Live MIDI events and MIP messages. * (MIP = Maximum Instantaneous Polyphony; part of the SP-MIDI standard.) * @ingroup midi */ typedef struct OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port that this structure applies to */ OMX_U32 nMidiEventSize; /**< Size of immediate MIDI events or MIP message in bytes */ OMX_U8 nMidiEvents[1]; /**< MIDI event array to be rendered immediately, or an array for the MIP message buffer, where the size is indicated by nMidiEventSize */ } OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE; /** MIDI sound bank/ program pair in a given channel * @ingroup midi */ typedef struct OMX_AUDIO_CONFIG_MIDISOUNDBANKPROGRAMTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port that this structure applies to */ OMX_U32 nChannel; /**< Valid channel values range from 1 to 16 */ OMX_U16 nIDProgram; /**< Valid program ID range is 1 to 128 */ OMX_U16 nIDSoundBank; /**< Sound bank ID */ OMX_U32 nUserSoundBankIndex;/**< User soundbank index, easier to access soundbanks by index if multiple banks are present */ } OMX_AUDIO_CONFIG_MIDISOUNDBANKPROGRAMTYPE; /** MIDI control * @ingroup midi */ typedef struct OMX_AUDIO_CONFIG_MIDICONTROLTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BS32 sPitchTransposition; /**< Pitch transposition in semitones, stored as Q22.10 format based on JAVA MMAPI (JSR-135) requirement */ OMX_BU32 sPlayBackRate; /**< Relative playback rate, stored as Q14.17 fixed-point number based on JSR-135 requirement */ OMX_BU32 sTempo ; /**< Tempo in beats per minute (BPM), stored as Q22.10 fixed-point number based on JSR-135 requirement */ OMX_U32 nMaxPolyphony; /**< Specifies the maximum simultaneous polyphonic voices. A value of zero indicates that the default polyphony of the device is used */ OMX_U32 nNumRepeat; /**< Number of times to repeat playback */ OMX_U32 nStopTime; /**< Time in milliseconds to indicate when playback will stop automatically. Set to zero if not used */ OMX_U16 nChannelMuteMask; /**< 16 bit mask for channel mute status */ OMX_U16 nChannelSoloMask; /**< 16 bit mask for channel solo status */ OMX_U32 nTrack0031MuteMask; /**< 32 bit mask for track mute status. Note: This is for tracks 0-31 */ OMX_U32 nTrack3263MuteMask; /**< 32 bit mask for track mute status. Note: This is for tracks 32-63 */ OMX_U32 nTrack0031SoloMask; /**< 32 bit mask for track solo status. Note: This is for tracks 0-31 */ OMX_U32 nTrack3263SoloMask; /**< 32 bit mask for track solo status. Note: This is for tracks 32-63 */ } OMX_AUDIO_CONFIG_MIDICONTROLTYPE; /** MIDI Playback States * @ingroup midi */ typedef enum OMX_AUDIO_MIDIPLAYBACKSTATETYPE { OMX_AUDIO_MIDIPlayBackStateUnknown = 0, /**< Unknown state or state does not map to other defined states */ OMX_AUDIO_MIDIPlayBackStateClosedEngaged, /**< No MIDI resource is currently open. The MIDI engine is currently processing MIDI events. */ OMX_AUDIO_MIDIPlayBackStateParsing, /**< A MIDI resource is open and is being primed. The MIDI engine is currently processing MIDI events. */ OMX_AUDIO_MIDIPlayBackStateOpenEngaged, /**< A MIDI resource is open and primed but not playing. The MIDI engine is currently processing MIDI events. The transition to this state is only possible from the OMX_AUDIO_MIDIPlayBackStatePlaying state, when the 'playback head' reaches the end of media data or the playback stops due to stop time set.*/ OMX_AUDIO_MIDIPlayBackStatePlaying, /**< A MIDI resource is open and currently playing. The MIDI engine is currently processing MIDI events.*/ OMX_AUDIO_MIDIPlayBackStatePlayingPartially, /**< Best-effort playback due to SP-MIDI/DLS resource constraints */ OMX_AUDIO_MIDIPlayBackStatePlayingSilently, /**< Due to system resource constraints and SP-MIDI content constraints, there is no audible MIDI content during playback currently. The situation may change if resources are freed later.*/ OMX_AUDIO_MIDIPlayBackStateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_MIDIPlayBackStateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_MIDIPlayBackStateMax = 0x7FFFFFFF } OMX_AUDIO_MIDIPLAYBACKSTATETYPE; /** MIDI status * @ingroup midi */ typedef struct OMX_AUDIO_CONFIG_MIDISTATUSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U16 nNumTracks; /**< Number of MIDI tracks in the file, read only field. NOTE: May not return a meaningful value until the entire file is parsed and buffered. */ OMX_U32 nDuration; /**< The length of the currently open MIDI resource in milliseconds. NOTE: May not return a meaningful value until the entire file is parsed and buffered. */ OMX_U32 nPosition; /**< Current Position of the MIDI resource being played in milliseconds */ OMX_BOOL bVibra; /**< Does Vibra track exist? NOTE: May not return a meaningful value until the entire file is parsed and buffered. */ OMX_U32 nNumMetaEvents; /**< Total number of MIDI Meta Events in the currently open MIDI resource. NOTE: May not return a meaningful value until the entire file is parsed and buffered. */ OMX_U32 nNumActiveVoices; /**< Number of active voices in the currently playing MIDI resource. NOTE: May not return a meaningful value until the entire file is parsed and buffered. */ OMX_AUDIO_MIDIPLAYBACKSTATETYPE eMIDIPlayBackState; /**< MIDI playback state enumeration, read only field */ } OMX_AUDIO_CONFIG_MIDISTATUSTYPE; /** MIDI Meta Event structure one per Meta Event. * MIDI Meta Events are like audio metadata, except that they are interspersed * with the MIDI content throughout the file and are not localized in the header. * As such, it is necessary to retrieve information about these Meta Events from * the engine, as it encounters these Meta Events within the MIDI content. * For example, SMF files can have up to 14 types of MIDI Meta Events (copyright, * author, default tempo, etc.) scattered throughout the file. * @ingroup midi */ typedef struct OMX_AUDIO_CONFIG_MIDIMETAEVENTTYPE{ OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nIndex; /**< Index of Meta Event */ OMX_U8 nMetaEventType; /**< Meta Event Type, 7bits (i.e. 0 - 127) */ OMX_U32 nMetaEventSize; /**< size of the Meta Event in bytes */ OMX_U32 nTrack; /**< track number for the meta event */ OMX_U32 nPosition; /**< Position of the meta-event in milliseconds */ } OMX_AUDIO_CONFIG_MIDIMETAEVENTTYPE; /** MIDI Meta Event Data structure - one per Meta Event. * @ingroup midi */ typedef struct OMX_AUDIO_CONFIG_MIDIMETAEVENTDATATYPE{ OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nIndex; /**< Index of Meta Event */ OMX_U32 nMetaEventSize; /**< size of the Meta Event in bytes */ OMX_U8 nData[1]; /**< array of one or more bytes of meta data as indicated by the nMetaEventSize field */ } OMX_AUDIO_CONFIG__MIDIMETAEVENTDATATYPE; /** Audio Volume adjustment for a port */ typedef struct OMX_AUDIO_CONFIG_VOLUMETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port index indicating which port to set. Select the input port to set just that port's volume. Select the output port to adjust the master volume. */ OMX_BOOL bLinear; /**< Is the volume to be set in linear (0.100) or logarithmic scale (mB) */ OMX_BS32 sVolume; /**< Volume linear setting in the 0..100 range, OR Volume logarithmic setting for this port. The values for volume are in mB (millibels = 1/100 dB) relative to a gain of 1 (e.g. the output is the same as the input level). Values are in mB from nMax (maximum volume) to nMin mB (typically negative). Since the volume is "voltage" and not a "power", it takes a setting of -600 mB to decrease the volume by 1/2. If a component cannot accurately set the volume to the requested value, it must set the volume to the closest value BELOW the requested value. When getting the volume setting, the current actual volume must be returned. */ } OMX_AUDIO_CONFIG_VOLUMETYPE; /** Audio Volume adjustment for a channel */ typedef struct OMX_AUDIO_CONFIG_CHANNELVOLUMETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port index indicating which port to set. Select the input port to set just that port's volume. Select the output port to adjust the master volume. */ OMX_U32 nChannel; /**< channel to select from 0 to N-1, using OMX_ALL to apply volume settings to all channels */ OMX_BOOL bLinear; /**< Is the volume to be set in linear (0.100) or logarithmic scale (mB) */ OMX_BS32 sVolume; /**< Volume linear setting in the 0..100 range, OR Volume logarithmic setting for this port. The values for volume are in mB (millibels = 1/100 dB) relative to a gain of 1 (e.g. the output is the same as the input level). Values are in mB from nMax (maximum volume) to nMin mB (typically negative). Since the volume is "voltage" and not a "power", it takes a setting of -600 mB to decrease the volume by 1/2. If a component cannot accurately set the volume to the requested value, it must set the volume to the closest value BELOW the requested value. When getting the volume setting, the current actual volume must be returned. */ OMX_BOOL bIsMIDI; /**< TRUE if nChannel refers to a MIDI channel, FALSE otherwise */ } OMX_AUDIO_CONFIG_CHANNELVOLUMETYPE; /** Audio balance setting */ typedef struct OMX_AUDIO_CONFIG_BALANCETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port index indicating which port to set. Select the input port to set just that port's balance. Select the output port to adjust the master balance. */ OMX_S32 nBalance; /**< balance setting for this port (-100 to 100, where -100 indicates all left, and no right */ } OMX_AUDIO_CONFIG_BALANCETYPE; /** Audio Port mute */ typedef struct OMX_AUDIO_CONFIG_MUTETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port index indicating which port to set. Select the input port to set just that port's mute. Select the output port to adjust the master mute. */ OMX_BOOL bMute; /**< Mute setting for this port */ } OMX_AUDIO_CONFIG_MUTETYPE; /** Audio Channel mute */ typedef struct OMX_AUDIO_CONFIG_CHANNELMUTETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannel; /**< channel to select from 0 to N-1, using OMX_ALL to apply mute settings to all channels */ OMX_BOOL bMute; /**< Mute setting for this channel */ OMX_BOOL bIsMIDI; /**< TRUE if nChannel refers to a MIDI channel, FALSE otherwise */ } OMX_AUDIO_CONFIG_CHANNELMUTETYPE; /** Enable / Disable for loudness control, which boosts bass and to a * smaller extent high end frequencies to compensate for hearing * ability at the extreme ends of the audio spectrum */ typedef struct OMX_AUDIO_CONFIG_LOUDNESSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bLoudness; /**< Enable/disable for loudness */ } OMX_AUDIO_CONFIG_LOUDNESSTYPE; /** Enable / Disable for bass, which controls low frequencies */ typedef struct OMX_AUDIO_CONFIG_BASSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bEnable; /**< Enable/disable for bass control */ OMX_S32 nBass; /**< bass setting for the port, as a continuous value from -100 to 100 (0 means no change in bass level)*/ } OMX_AUDIO_CONFIG_BASSTYPE; /** Enable / Disable for treble, which controls high frequencies tones */ typedef struct OMX_AUDIO_CONFIG_TREBLETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bEnable; /**< Enable/disable for treble control */ OMX_S32 nTreble; /**< treble setting for the port, as a continuous value from -100 to 100 (0 means no change in treble level) */ } OMX_AUDIO_CONFIG_TREBLETYPE; /** An equalizer is typically used for two reasons: to compensate for an * sub-optimal frequency response of a system to make it sound more natural * or to create intentionally some unnatural coloring to the sound to create * an effect. * @ingroup effects */ typedef struct OMX_AUDIO_CONFIG_EQUALIZERTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bEnable; /**< Enable/disable for equalizer */ OMX_BU32 sBandIndex; /**< Band number to be set. Upper Limit is N-1, where N is the number of bands, lower limit is 0 */ OMX_BU32 sCenterFreq; /**< Center frequecies in Hz. This is a read only element and is used to determine the lower, center and upper frequency of this band. */ OMX_BS32 sBandLevel; /**< band level in millibels */ } OMX_AUDIO_CONFIG_EQUALIZERTYPE; /** Stereo widening mode type * @ingroup effects */ typedef enum OMX_AUDIO_STEREOWIDENINGTYPE { OMX_AUDIO_StereoWideningHeadphones, /**< Stereo widening for loudspeakers */ OMX_AUDIO_StereoWideningLoudspeakers, /**< Stereo widening for closely spaced loudspeakers */ OMX_AUDIO_StereoWideningKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_StereoWideningVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_StereoWideningMax = 0x7FFFFFFF } OMX_AUDIO_STEREOWIDENINGTYPE; /** Control for stereo widening, which is a special 2-channel * case of the audio virtualizer effect. For example, for 5.1-channel * output, it translates to virtual surround sound. * @ingroup effects */ typedef struct OMX_AUDIO_CONFIG_STEREOWIDENINGTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bEnable; /**< Enable/disable for stereo widening control */ OMX_AUDIO_STEREOWIDENINGTYPE eWideningType; /**< Stereo widening algorithm type */ OMX_U32 nStereoWidening; /**< stereo widening setting for the port, as a continuous value from 0 to 100 */ } OMX_AUDIO_CONFIG_STEREOWIDENINGTYPE; /** The chorus effect (or ``choralizer'') is any signal processor which makes * one sound source (such as a voice) sound like many such sources singing * (or playing) in unison. Since performance in unison is never exact, chorus * effects simulate this by making independently modified copies of the input * signal. Modifications may include (1) delay, (2) frequency shift, and * (3) amplitude modulation. * @ingroup effects */ typedef struct OMX_AUDIO_CONFIG_CHORUSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bEnable; /**< Enable/disable for chorus */ OMX_BU32 sDelay; /**< average delay in milliseconds */ OMX_BU32 sModulationRate; /**< rate of modulation in millihertz */ OMX_U32 nModulationDepth; /**< depth of modulation as a percentage of delay (i.e. 0 to 100) */ OMX_BU32 nFeedback; /**< Feedback from chorus output to input in percentage */ } OMX_AUDIO_CONFIG_CHORUSTYPE; /** Reverberation is part of the reflected sound that follows the early * reflections. In a typical room, this consists of a dense succession of * echoes whose energy decays exponentially. The reverberation effect structure * as defined here includes both (early) reflections as well as (late) reverberations. * @ingroup effects */ typedef struct OMX_AUDIO_CONFIG_REVERBERATIONTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bEnable; /**< Enable/disable for reverberation control */ OMX_BS32 sRoomLevel; /**< Intensity level for the whole room effect (i.e. both early reflections and late reverberation) in millibels */ OMX_BS32 sRoomHighFreqLevel; /**< Attenuation at high frequencies relative to the intensity at low frequencies in millibels */ OMX_BS32 sReflectionsLevel; /**< Intensity level of early reflections (relative to room value), in millibels */ OMX_BU32 sReflectionsDelay; /**< Delay time of the first reflection relative to the direct path, in milliseconds */ OMX_BS32 sReverbLevel; /**< Intensity level of late reverberation relative to room level, in millibels */ OMX_BU32 sReverbDelay; /**< Time delay from the first early reflection to the beginning of the late reverberation section, in milliseconds */ OMX_BU32 sDecayTime; /**< Late reverberation decay time at low frequencies, in milliseconds */ OMX_BU32 nDecayHighFreqRatio; /**< Ratio of high frequency decay time relative to low frequency decay time in percent */ OMX_U32 nDensity; /**< Modal density in the late reverberation decay, in percent (i.e. 0 - 100) */ OMX_U32 nDiffusion; /**< Echo density in the late reverberation decay, in percent (i.e. 0 - 100) */ OMX_BU32 sReferenceHighFreq; /**< Reference high frequency in Hertz. This is the frequency used as the reference for all the high-frequency settings above */ } OMX_AUDIO_CONFIG_REVERBERATIONTYPE; /** Possible settings for the Echo Cancelation structure to use * @ingroup effects */ typedef enum OMX_AUDIO_ECHOCANTYPE { OMX_AUDIO_EchoCanOff = 0, /**< Echo Cancellation is disabled */ OMX_AUDIO_EchoCanNormal, /**< Echo Cancellation normal operation - echo from plastics and face */ OMX_AUDIO_EchoCanHFree, /**< Echo Cancellation optimized for Hands Free operation */ OMX_AUDIO_EchoCanCarKit, /**< Echo Cancellation optimized for Car Kit (longer echo) */ OMX_AUDIO_EchoCanKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_AUDIO_EchoCanVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_AUDIO_EchoCanMax = 0x7FFFFFFF } OMX_AUDIO_ECHOCANTYPE; /** Enable / Disable for echo cancelation, which removes undesired echo's * from the audio * @ingroup effects */ typedef struct OMX_AUDIO_CONFIG_ECHOCANCELATIONTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_AUDIO_ECHOCANTYPE eEchoCancelation; /**< Echo cancelation settings */ } OMX_AUDIO_CONFIG_ECHOCANCELATIONTYPE; /** Enable / Disable for noise reduction, which undesired noise from * the audio * @ingroup effects */ typedef struct OMX_AUDIO_CONFIG_NOISEREDUCTIONTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BOOL bNoiseReduction; /**< Enable/disable for noise reduction */ } OMX_AUDIO_CONFIG_NOISEREDUCTIONTYPE; /** @} */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_AudioExt.h0100644 0000000 0000000 00000017655 13756501735 021053 0ustar000000000 0000000 /* * Copyright (c) 2010 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** OMX_AudioExt.h - OpenMax IL version 1.1.2 * The OMX_AudioExt header file contains extensions to the * definitions used by both the application and the component to * access video items. */ #ifndef OMX_AudioExt_h #define OMX_AudioExt_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header shall include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include #define OMX_AUDIO_AACToolAndroidSSBR (OMX_AUDIO_AACToolVendor << 0) /**< SSBR: MPEG-4 Single-rate (downsampled) Spectral Band Replication tool allowed or active */ #define OMX_AUDIO_AACToolAndroidDSBR (OMX_AUDIO_AACToolVendor << 1) /**< DSBR: MPEG-4 Dual-rate Spectral Band Replication tool allowed or active */ typedef enum OMX_AUDIO_CODINGEXTTYPE { OMX_AUDIO_CodingAndroidUnused = OMX_AUDIO_CodingKhronosExtensions + 0x00100000, OMX_AUDIO_CodingAndroidAC3, /**< AC3 encoded data */ OMX_AUDIO_CodingAndroidOPUS, /**< OPUS encoded data */ OMX_AUDIO_CodingAndroidEAC3, /**< EAC3 encoded data */ OMX_AUDIO_CodingAndroidAC4, /**< AC4 encoded data */ } OMX_AUDIO_CODINGEXTTYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_AC3TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ } OMX_AUDIO_PARAM_ANDROID_AC3TYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_EAC3TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ } OMX_AUDIO_PARAM_ANDROID_EAC3TYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_AC4TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ } OMX_AUDIO_PARAM_ANDROID_AC4TYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_OPUSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nChannels; /**< Number of channels */ OMX_U32 nBitRate; /**< Bit rate of the encoded data data. Use 0 for variable rate or unknown bit rates. Encoding is set to the bitrate closest to specified value (in bps) */ OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for variable or unknown sampling rate. */ OMX_U32 nAudioBandWidth; /**< Audio band width (in Hz) to which an encoder should limit the audio signal. Use 0 to let encoder decide */ } OMX_AUDIO_PARAM_ANDROID_OPUSTYPE; /** deprecated. use OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */ typedef struct OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_S32 nMaxOutputChannels; /**< Maximum channel count to be output, -1 if unspecified, 0 if downmixing disabled */ OMX_S32 nDrcCut; /**< The DRC attenuation factor, between 0 and 127, -1 if unspecified */ OMX_S32 nDrcBoost; /**< The DRC amplification factor, between 0 and 127, -1 if unspecified */ OMX_S32 nHeavyCompression; /**< 0 for light compression, 1 for heavy compression, -1 if unspecified */ OMX_S32 nTargetReferenceLevel; /**< Target reference level, between 0 and 127, -1 if unspecified */ OMX_S32 nEncodedTargetLevel; /**< Target reference level assumed at the encoder, between 0 and 127, -1 if unspecified */ OMX_S32 nPCMLimiterEnable; /**< Signal level limiting, 0 for disable, 1 for enable, -1 if unspecified */ } OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_S32 nMaxOutputChannels; /**< Maximum channel count to be output, -1 if unspecified, 0 if downmixing disabled */ OMX_S32 nDrcCut; /**< The DRC attenuation factor, between 0 and 127, -1 if unspecified */ OMX_S32 nDrcBoost; /**< The DRC amplification factor, between 0 and 127, -1 if unspecified */ OMX_S32 nHeavyCompression; /**< 0 for light compression, 1 for heavy compression, -1 if unspecified */ OMX_S32 nTargetReferenceLevel; /**< Target reference level, between 0 and 127, -1 if unspecified */ OMX_S32 nEncodedTargetLevel; /**< Target reference level assumed at the encoder, between 0 and 127, -1 if unspecified */ OMX_S32 nPCMLimiterEnable; /**< Signal level limiting, 0 for disable, 1 for enable, -1 if unspecified */ OMX_S32 nDrcEffectType; /**< MPEG-D DRC effect type, between -1 and 6, -2 if unspecified */ } OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_PROFILETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 eProfile; /**< type is OMX_AUDIO_AACPROFILETYPE or OMX_AUDIO_WMAPROFILETYPE depending on context */ OMX_U32 nProfileIndex; /**< Used to query for individual profile support information */ } OMX_AUDIO_PARAM_ANDROID_PROFILETYPE; typedef struct OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_S32 nPresentationId; /**< presentation id */ OMX_S32 nProgramId; /**< program id */ } OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* OMX_AudioExt_h */ /* File EOF */ headers/media_plugin/media/openmax/OMX_Component.h0100644 0000000 0000000 00000057557 13756501735 021300 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** OMX_Component.h - OpenMax IL version 1.1.2 * The OMX_Component header file contains the definitions used to define * the public interface of a component. This header file is intended to * be used by both the application and the component. */ #ifndef OMX_Component_h #define OMX_Component_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header must include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include #include #include #include /** @ingroup comp */ typedef enum OMX_PORTDOMAINTYPE { OMX_PortDomainAudio, OMX_PortDomainVideo, OMX_PortDomainImage, OMX_PortDomainOther, OMX_PortDomainKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_PortDomainVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_PortDomainMax = 0x7ffffff } OMX_PORTDOMAINTYPE; /** @ingroup comp */ typedef struct OMX_PARAM_PORTDEFINITIONTYPE { OMX_U32 nSize; /**< Size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Port number the structure applies to */ OMX_DIRTYPE eDir; /**< Direction (input or output) of this port */ OMX_U32 nBufferCountActual; /**< The actual number of buffers allocated on this port */ OMX_U32 nBufferCountMin; /**< The minimum number of buffers this port requires */ OMX_U32 nBufferSize; /**< Size, in bytes, for buffers to be used for this channel */ OMX_BOOL bEnabled; /**< Ports default to enabled and are enabled/disabled by OMX_CommandPortEnable/OMX_CommandPortDisable. When disabled a port is unpopulated. A disabled port is not populated with buffers on a transition to IDLE. */ OMX_BOOL bPopulated; /**< Port is populated with all of its buffers as indicated by nBufferCountActual. A disabled port is always unpopulated. An enabled port is populated on a transition to OMX_StateIdle and unpopulated on a transition to loaded. */ OMX_PORTDOMAINTYPE eDomain; /**< Domain of the port. Determines the contents of metadata below. */ union { OMX_AUDIO_PORTDEFINITIONTYPE audio; OMX_VIDEO_PORTDEFINITIONTYPE video; OMX_IMAGE_PORTDEFINITIONTYPE image; OMX_OTHER_PORTDEFINITIONTYPE other; } format; OMX_BOOL bBuffersContiguous; OMX_U32 nBufferAlignment; } OMX_PARAM_PORTDEFINITIONTYPE; /** @ingroup comp */ typedef struct OMX_PARAM_U32TYPE { OMX_U32 nSize; /**< Size of this structure, in Bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_U32 nU32; /**< U32 value */ } OMX_PARAM_U32TYPE; /** @ingroup rpm */ typedef enum OMX_SUSPENSIONPOLICYTYPE { OMX_SuspensionDisabled, /**< No suspension; v1.0 behavior */ OMX_SuspensionEnabled, /**< Suspension allowed */ OMX_SuspensionPolicyKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_SuspensionPolicyStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_SuspensionPolicyMax = 0x7fffffff } OMX_SUSPENSIONPOLICYTYPE; /** @ingroup rpm */ typedef struct OMX_PARAM_SUSPENSIONPOLICYTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_SUSPENSIONPOLICYTYPE ePolicy; } OMX_PARAM_SUSPENSIONPOLICYTYPE; /** @ingroup rpm */ typedef enum OMX_SUSPENSIONTYPE { OMX_NotSuspended, /**< component is not suspended */ OMX_Suspended, /**< component is suspended */ OMX_SuspensionKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_SuspensionVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_SuspendMax = 0x7FFFFFFF } OMX_SUSPENSIONTYPE; /** @ingroup rpm */ typedef struct OMX_PARAM_SUSPENSIONTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_SUSPENSIONTYPE eType; } OMX_PARAM_SUSPENSIONTYPE ; typedef struct OMX_CONFIG_BOOLEANTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_BOOL bEnabled; } OMX_CONFIG_BOOLEANTYPE; /* Parameter specifying the content uri to use. */ /** @ingroup cp */ typedef struct OMX_PARAM_CONTENTURITYPE { OMX_U32 nSize; /**< size of the structure in bytes, including actual URI name */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U8 contentURI[1]; /**< The URI name */ } OMX_PARAM_CONTENTURITYPE; /* Parameter specifying the pipe to use. */ /** @ingroup cp */ typedef struct OMX_PARAM_CONTENTPIPETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_HANDLETYPE hPipe; /**< The pipe handle*/ } OMX_PARAM_CONTENTPIPETYPE; /** @ingroup rpm */ typedef struct OMX_RESOURCECONCEALMENTTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_BOOL bResourceConcealmentForbidden; /**< disallow the use of resource concealment methods (like degrading algorithm quality to lower resource consumption or functional bypass) on a component as a resolution to resource conflicts. */ } OMX_RESOURCECONCEALMENTTYPE; /** @ingroup metadata */ typedef enum OMX_METADATACHARSETTYPE { OMX_MetadataCharsetUnknown = 0, OMX_MetadataCharsetASCII, OMX_MetadataCharsetBinary, OMX_MetadataCharsetCodePage1252, OMX_MetadataCharsetUTF8, OMX_MetadataCharsetJavaConformantUTF8, OMX_MetadataCharsetUTF7, OMX_MetadataCharsetImapUTF7, OMX_MetadataCharsetUTF16LE, OMX_MetadataCharsetUTF16BE, OMX_MetadataCharsetGB12345, OMX_MetadataCharsetHZGB2312, OMX_MetadataCharsetGB2312, OMX_MetadataCharsetGB18030, OMX_MetadataCharsetGBK, OMX_MetadataCharsetBig5, OMX_MetadataCharsetISO88591, OMX_MetadataCharsetISO88592, OMX_MetadataCharsetISO88593, OMX_MetadataCharsetISO88594, OMX_MetadataCharsetISO88595, OMX_MetadataCharsetISO88596, OMX_MetadataCharsetISO88597, OMX_MetadataCharsetISO88598, OMX_MetadataCharsetISO88599, OMX_MetadataCharsetISO885910, OMX_MetadataCharsetISO885913, OMX_MetadataCharsetISO885914, OMX_MetadataCharsetISO885915, OMX_MetadataCharsetShiftJIS, OMX_MetadataCharsetISO2022JP, OMX_MetadataCharsetISO2022JP1, OMX_MetadataCharsetISOEUCJP, OMX_MetadataCharsetSMS7Bit, OMX_MetadataCharsetKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_MetadataCharsetVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_MetadataCharsetTypeMax= 0x7FFFFFFF } OMX_METADATACHARSETTYPE; /** @ingroup metadata */ typedef enum OMX_METADATASCOPETYPE { OMX_MetadataScopeAllLevels, OMX_MetadataScopeTopLevel, OMX_MetadataScopePortLevel, OMX_MetadataScopeNodeLevel, OMX_MetadataScopeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_MetadataScopeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_MetadataScopeTypeMax = 0x7fffffff } OMX_METADATASCOPETYPE; /** @ingroup metadata */ typedef enum OMX_METADATASEARCHMODETYPE { OMX_MetadataSearchValueSizeByIndex, OMX_MetadataSearchItemByIndex, OMX_MetadataSearchNextItemByKey, OMX_MetadataSearchKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_MetadataSearchVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_MetadataSearchTypeMax = 0x7fffffff } OMX_METADATASEARCHMODETYPE; /** @ingroup metadata */ typedef struct OMX_CONFIG_METADATAITEMCOUNTTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_METADATASCOPETYPE eScopeMode; OMX_U32 nScopeSpecifier; OMX_U32 nMetadataItemCount; } OMX_CONFIG_METADATAITEMCOUNTTYPE; /** @ingroup metadata */ typedef struct OMX_CONFIG_METADATAITEMTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_METADATASCOPETYPE eScopeMode; OMX_U32 nScopeSpecifier; OMX_U32 nMetadataItemIndex; OMX_METADATASEARCHMODETYPE eSearchMode; OMX_METADATACHARSETTYPE eKeyCharset; OMX_U8 nKeySizeUsed; OMX_U8 nKey[128]; OMX_METADATACHARSETTYPE eValueCharset; OMX_STRING sLanguageCountry; OMX_U32 nValueMaxSize; OMX_U32 nValueSizeUsed; OMX_U8 nValue[1]; } OMX_CONFIG_METADATAITEMTYPE; /* @ingroup metadata */ typedef struct OMX_CONFIG_CONTAINERNODECOUNTTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_BOOL bAllKeys; OMX_U32 nParentNodeID; OMX_U32 nNumNodes; } OMX_CONFIG_CONTAINERNODECOUNTTYPE; /** @ingroup metadata */ typedef struct OMX_CONFIG_CONTAINERNODEIDTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_BOOL bAllKeys; OMX_U32 nParentNodeID; OMX_U32 nNodeIndex; OMX_U32 nNodeID; OMX_STRING cNodeName; OMX_BOOL bIsLeafType; } OMX_CONFIG_CONTAINERNODEIDTYPE; /** @ingroup metadata */ typedef struct OMX_PARAM_METADATAFILTERTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_BOOL bAllKeys; /* if true then this structure refers to all keys and * the three key fields below are ignored */ OMX_METADATACHARSETTYPE eKeyCharset; OMX_U32 nKeySizeUsed; OMX_U8 nKey [128]; OMX_U32 nLanguageCountrySizeUsed; OMX_U8 nLanguageCountry[128]; OMX_BOOL bEnabled; /* if true then key is part of filter (e.g. * retained for query later). If false then * key is not part of filter */ } OMX_PARAM_METADATAFILTERTYPE; /** The OMX_HANDLETYPE structure defines the component handle. The component * handle is used to access all of the component's public methods and also * contains pointers to the component's private data area. The component * handle is initialized by the OMX core (with help from the component) * during the process of loading the component. After the component is * successfully loaded, the application can safely access any of the * component's public functions (although some may return an error because * the state is inappropriate for the access). * * @ingroup comp */ typedef struct OMX_COMPONENTTYPE { /** The size of this structure, in bytes. It is the responsibility of the allocator of this structure to fill in this value. Since this structure is allocated by the GetHandle function, this function will fill in this value. */ OMX_U32 nSize; /** nVersion is the version of the OMX specification that the structure is built against. It is the responsibility of the creator of this structure to initialize this value and every user of this structure should verify that it knows how to use the exact version of this structure found herein. */ OMX_VERSIONTYPE nVersion; /** pComponentPrivate is a pointer to the component private data area. This member is allocated and initialized by the component when the component is first loaded. The application should not access this data area. */ OMX_PTR pComponentPrivate; /** pApplicationPrivate is a pointer that is a parameter to the OMX_GetHandle method, and contains an application private value provided by the IL client. This application private data is returned to the IL Client by OMX in all callbacks */ OMX_PTR pApplicationPrivate; /** refer to OMX_GetComponentVersion in OMX_core.h or the OMX IL specification for details on the GetComponentVersion method. */ OMX_ERRORTYPE (*GetComponentVersion)( OMX_IN OMX_HANDLETYPE hComponent, OMX_OUT OMX_STRING pComponentName, OMX_OUT OMX_VERSIONTYPE* pComponentVersion, OMX_OUT OMX_VERSIONTYPE* pSpecVersion, OMX_OUT OMX_UUIDTYPE* pComponentUUID); /** refer to OMX_SendCommand in OMX_core.h or the OMX IL specification for details on the SendCommand method. */ OMX_ERRORTYPE (*SendCommand)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_COMMANDTYPE Cmd, OMX_IN OMX_U32 nParam1, OMX_IN OMX_PTR pCmdData); /** refer to OMX_GetParameter in OMX_core.h or the OMX IL specification for details on the GetParameter method. */ OMX_ERRORTYPE (*GetParameter)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_INDEXTYPE nParamIndex, OMX_INOUT OMX_PTR pComponentParameterStructure); /** refer to OMX_SetParameter in OMX_core.h or the OMX IL specification for details on the SetParameter method. */ OMX_ERRORTYPE (*SetParameter)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_INDEXTYPE nIndex, OMX_IN OMX_PTR pComponentParameterStructure); /** refer to OMX_GetConfig in OMX_core.h or the OMX IL specification for details on the GetConfig method. */ OMX_ERRORTYPE (*GetConfig)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_INDEXTYPE nIndex, OMX_INOUT OMX_PTR pComponentConfigStructure); /** refer to OMX_SetConfig in OMX_core.h or the OMX IL specification for details on the SetConfig method. */ OMX_ERRORTYPE (*SetConfig)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_INDEXTYPE nIndex, OMX_IN OMX_PTR pComponentConfigStructure); /** refer to OMX_GetExtensionIndex in OMX_core.h or the OMX IL specification for details on the GetExtensionIndex method. */ OMX_ERRORTYPE (*GetExtensionIndex)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_STRING cParameterName, OMX_OUT OMX_INDEXTYPE* pIndexType); /** refer to OMX_GetState in OMX_core.h or the OMX IL specification for details on the GetState method. */ OMX_ERRORTYPE (*GetState)( OMX_IN OMX_HANDLETYPE hComponent, OMX_OUT OMX_STATETYPE* pState); /** The ComponentTunnelRequest method will interact with another OMX component to determine if tunneling is possible and to setup the tunneling. The return codes for this method can be used to determine if tunneling is not possible, or if tunneling is not supported. Base profile components (i.e. non-interop) do not support this method and should return OMX_ErrorNotImplemented The interop profile component MUST support tunneling to another interop profile component with a compatible port parameters. A component may also support proprietary communication. If proprietary communication is supported the negotiation of proprietary communication is done outside of OMX in a vendor specific way. It is only required that the proper result be returned and the details of how the setup is done is left to the component implementation. When this method is invoked when nPort in an output port, the component will: 1. Populate the pTunnelSetup structure with the output port's requirements and constraints for the tunnel. When this method is invoked when nPort in an input port, the component will: 1. Query the necessary parameters from the output port to determine if the ports are compatible for tunneling 2. If the ports are compatible, the component should store the tunnel step provided by the output port 3. Determine which port (either input or output) is the buffer supplier, and call OMX_SetParameter on the output port to indicate this selection. The component will return from this call within 5 msec. @param [in] hComp Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle method. @param [in] nPort nPort is used to select the port on the component to be used for tunneling. @param [in] hTunneledComp Handle of the component to tunnel with. This is the component handle returned by the call to the OMX_GetHandle method. When this parameter is 0x0 the component should setup the port for communication with the application / IL Client. @param [in] nPortOutput nPortOutput is used indicate the port the component should tunnel with. @param [in] pTunnelSetup Pointer to the tunnel setup structure. When nPort is an output port the component should populate the fields of this structure. When When nPort is an input port the component should review the setup provided by the component with the output port. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup tun */ OMX_ERRORTYPE (*ComponentTunnelRequest)( OMX_IN OMX_HANDLETYPE hComp, OMX_IN OMX_U32 nPort, OMX_IN OMX_HANDLETYPE hTunneledComp, OMX_IN OMX_U32 nTunneledPort, OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup); /** refer to OMX_UseBuffer in OMX_core.h or the OMX IL specification for details on the UseBuffer method. @ingroup buf */ OMX_ERRORTYPE (*UseBuffer)( OMX_IN OMX_HANDLETYPE hComponent, OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_PTR pAppPrivate, OMX_IN OMX_U32 nSizeBytes, OMX_IN OMX_U8* pBuffer); /** refer to OMX_AllocateBuffer in OMX_core.h or the OMX IL specification for details on the AllocateBuffer method. @ingroup buf */ OMX_ERRORTYPE (*AllocateBuffer)( OMX_IN OMX_HANDLETYPE hComponent, OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer, OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_PTR pAppPrivate, OMX_IN OMX_U32 nSizeBytes); /** refer to OMX_FreeBuffer in OMX_core.h or the OMX IL specification for details on the FreeBuffer method. @ingroup buf */ OMX_ERRORTYPE (*FreeBuffer)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); /** refer to OMX_EmptyThisBuffer in OMX_core.h or the OMX IL specification for details on the EmptyThisBuffer method. @ingroup buf */ OMX_ERRORTYPE (*EmptyThisBuffer)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); /** refer to OMX_FillThisBuffer in OMX_core.h or the OMX IL specification for details on the FillThisBuffer method. @ingroup buf */ OMX_ERRORTYPE (*FillThisBuffer)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); /** The SetCallbacks method is used by the core to specify the callback structure from the application to the component. This is a blocking call. The component will return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the GetHandle function. @param [in] pCallbacks pointer to an OMX_CALLBACKTYPE structure used to provide the callback information to the component @param [in] pAppData pointer to an application defined value. It is anticipated that the application will pass a pointer to a data structure or a "this pointer" in this area to allow the callback (in the application) to determine the context of the call @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. */ OMX_ERRORTYPE (*SetCallbacks)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_CALLBACKTYPE* pCallbacks, OMX_IN OMX_PTR pAppData); /** ComponentDeInit method is used to deinitialize the component providing a means to free any resources allocated at component initialization. NOTE: After this call the component handle is not valid for further use. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the GetHandle function. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. */ OMX_ERRORTYPE (*ComponentDeInit)( OMX_IN OMX_HANDLETYPE hComponent); /** @ingroup buf */ OMX_ERRORTYPE (*UseEGLImage)( OMX_IN OMX_HANDLETYPE hComponent, OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_IN OMX_U32 nPortIndex, OMX_IN OMX_PTR pAppPrivate, OMX_IN void* eglImage); OMX_ERRORTYPE (*ComponentRoleEnum)( OMX_IN OMX_HANDLETYPE hComponent, OMX_OUT OMX_U8 *cRole, OMX_IN OMX_U32 nIndex); } OMX_COMPONENTTYPE; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_ContentPipe.h0100644 0000000 0000000 00000023313 13756501735 021545 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** OMX_ContentPipe.h - OpenMax IL version 1.1.2 * The OMX_ContentPipe header file contains the definitions used to define * the public interface for content piples. This header file is intended to * be used by the component. */ #ifndef OMX_CONTENTPIPE_H #define OMX_CONTENTPIPE_H #ifndef KD_EACCES /* OpenKODE error codes. CPResult values may be zero (indicating success or one of the following values) */ #define KD_EACCES (1) #define KD_EADDRINUSE (2) #define KD_EAGAIN (5) #define KD_EBADF (7) #define KD_EBUSY (8) #define KD_ECONNREFUSED (9) #define KD_ECONNRESET (10) #define KD_EDEADLK (11) #define KD_EDESTADDRREQ (12) #define KD_ERANGE (35) #define KD_EEXIST (13) #define KD_EFBIG (14) #define KD_EHOSTUNREACH (15) #define KD_EINVAL (17) #define KD_EIO (18) #define KD_EISCONN (20) #define KD_EISDIR (21) #define KD_EMFILE (22) #define KD_ENAMETOOLONG (23) #define KD_ENOENT (24) #define KD_ENOMEM (25) #define KD_ENOSPC (26) #define KD_ENOSYS (27) #define KD_ENOTCONN (28) #define KD_EPERM (33) #define KD_ETIMEDOUT (36) #define KD_EILSEQ (19) #endif /** Map types from OMX standard types only here so interface is as generic as possible. */ typedef OMX_U32 CPresult; typedef char * CPstring; typedef void * CPhandle; typedef OMX_U32 CPuint; typedef OMX_S32 CPint; typedef char CPbyte; typedef OMX_BOOL CPbool; /** enumeration of origin types used in the CP_PIPETYPE's Seek function * @ingroup cp */ typedef enum CP_ORIGINTYPE { CP_OriginBegin, CP_OriginCur, CP_OriginEnd, CP_OriginKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ CP_OriginVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ CP_OriginMax = 0X7FFFFFFF } CP_ORIGINTYPE; /** enumeration of contact access types used in the CP_PIPETYPE's Open function * @ingroup cp */ typedef enum CP_ACCESSTYPE { CP_AccessRead, CP_AccessWrite, CP_AccessReadWrite, CP_AccessKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ CP_AccessVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ CP_AccessMax = 0X7FFFFFFF } CP_ACCESSTYPE; /** enumeration of results returned by the CP_PIPETYPE's CheckAvailableBytes function * @ingroup cp */ typedef enum CP_CHECKBYTESRESULTTYPE { CP_CheckBytesOk, /**< There are at least the request number of bytes available */ CP_CheckBytesNotReady, /**< The pipe is still retrieving bytes and presently lacks sufficient bytes. Client will be called when they are sufficient bytes are available. */ CP_CheckBytesInsufficientBytes, /**< The pipe has retrieved all bytes but those available are less than those requested */ CP_CheckBytesAtEndOfStream, /**< The pipe has reached the end of stream and no more bytes are available. */ CP_CheckBytesOutOfBuffers, /**< All read/write buffers are currently in use. */ CP_CheckBytesKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ CP_CheckBytesVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ CP_CheckBytesMax = 0X7FFFFFFF } CP_CHECKBYTESRESULTTYPE; /** enumeration of content pipe events sent to the client callback. * @ingroup cp */ typedef enum CP_EVENTTYPE{ CP_BytesAvailable, /** bytes requested in a CheckAvailableBytes call are now available*/ CP_Overflow, /** enumeration of content pipe events sent to the client callback*/ CP_PipeDisconnected, /** enumeration of content pipe events sent to the client callback*/ CP_EventKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ CP_EventVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ CP_EventMax = 0X7FFFFFFF } CP_EVENTTYPE; /** content pipe definition * @ingroup cp */ typedef struct CP_PIPETYPE { /** Open a content stream for reading or writing. */ CPresult (*Open)( CPhandle* hContent, CPstring szURI, CP_ACCESSTYPE eAccess ); /** Close a content stream. */ CPresult (*Close)( CPhandle hContent ); /** Create a content source and open it for writing. */ CPresult (*Create)( CPhandle *hContent, CPstring szURI ); /** Check the that specified number of bytes are available for reading or writing (depending on access type).*/ CPresult (*CheckAvailableBytes)( CPhandle hContent, CPuint nBytesRequested, CP_CHECKBYTESRESULTTYPE *eResult ); /** Seek to certain position in the content relative to the specified origin. */ CPresult (*SetPosition)( CPhandle hContent, CPint nOffset, CP_ORIGINTYPE eOrigin); /** Retrieve the current position relative to the start of the content. */ CPresult (*GetPosition)( CPhandle hContent, CPuint *pPosition); /** Retrieve data of the specified size from the content stream (advance content pointer by size of data). Note: pipe client provides pointer. This function is appropriate for small high frequency reads. */ CPresult (*Read)( CPhandle hContent, CPbyte *pData, CPuint nSize); /** Retrieve a buffer allocated by the pipe that contains the requested number of bytes. Buffer contains the next block of bytes, as specified by nSize, of the content. nSize also returns the size of the block actually read. Content pointer advances the by the returned size. Note: pipe provides pointer. This function is appropriate for large reads. The client must call ReleaseReadBuffer when done with buffer. In some cases the requested block may not reside in contiguous memory within the pipe implementation. For instance if the pipe leverages a circular buffer then the requested block may straddle the boundary of the circular buffer. By default a pipe implementation performs a copy in this case to provide the block to the pipe client in one contiguous buffer. If, however, the client sets bForbidCopy, then the pipe returns only those bytes preceding the memory boundary. Here the client may retrieve the data in segments over successive calls. */ CPresult (*ReadBuffer)( CPhandle hContent, CPbyte **ppBuffer, CPuint *nSize, CPbool bForbidCopy); /** Release a buffer obtained by ReadBuffer back to the pipe. */ CPresult (*ReleaseReadBuffer)(CPhandle hContent, CPbyte *pBuffer); /** Write data of the specified size to the content (advance content pointer by size of data). Note: pipe client provides pointer. This function is appropriate for small high frequency writes. */ CPresult (*Write)( CPhandle hContent, CPbyte *data, CPuint nSize); /** Retrieve a buffer allocated by the pipe used to write data to the content. Client will fill buffer with output data. Note: pipe provides pointer. This function is appropriate for large writes. The client must call WriteBuffer when done it has filled the buffer with data.*/ CPresult (*GetWriteBuffer)( CPhandle hContent, CPbyte **ppBuffer, CPuint nSize); /** Deliver a buffer obtained via GetWriteBuffer to the pipe. Pipe will write the the contents of the buffer to content and advance content pointer by the size of the buffer */ CPresult (*WriteBuffer)( CPhandle hContent, CPbyte *pBuffer, CPuint nFilledSize); /** Register a per-handle client callback with the content pipe. */ CPresult (*RegisterCallback)( CPhandle hContent, CPresult (*ClientCallback)(CP_EVENTTYPE eEvent, CPuint iParam)); } CP_PIPETYPE; #endif headers/media_plugin/media/openmax/OMX_Core.h0100644 0000000 0000000 00000215456 13756501735 020220 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** OMX_Core.h - OpenMax IL version 1.1.2 * The OMX_Core header file contains the definitions used by both the * application and the component to access common items. */ #ifndef OMX_Core_h #define OMX_Core_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header shall include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include /** The OMX_COMMANDTYPE enumeration is used to specify the action in the * OMX_SendCommand macro. * @ingroup core */ typedef enum OMX_COMMANDTYPE { OMX_CommandStateSet, /**< Change the component state */ OMX_CommandFlush, /**< Flush the data queue(s) of a component */ OMX_CommandPortDisable, /**< Disable a port on a component. */ OMX_CommandPortEnable, /**< Enable a port on a component. */ OMX_CommandMarkBuffer, /**< Mark a component/buffer for observation */ OMX_CommandKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_CommandVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_CommandMax = 0X7FFFFFFF } OMX_COMMANDTYPE; /** The OMX_STATETYPE enumeration is used to indicate or change the component * state. This enumeration reflects the current state of the component when * used with the OMX_GetState macro or becomes the parameter in a state change * command when used with the OMX_SendCommand macro. * * The component will be in the Loaded state after the component is initially * loaded into memory. In the Loaded state, the component is not allowed to * allocate or hold resources other than to build it's internal parameter * and configuration tables. The application will send one or more * SetParameters/GetParameters and SetConfig/GetConfig commands to the * component and the component will record each of these parameter and * configuration changes for use later. When the application sends the * Idle command, the component will acquire the resources needed for the * specified configuration and will transition to the idle state if the * allocation is successful. If the component cannot successfully * transition to the idle state for any reason, the state of the component * shall be fully rolled back to the Loaded state (e.g. all allocated * resources shall be released). When the component receives the command * to go to the Executing state, it shall begin processing buffers by * sending all input buffers it holds to the application. While * the component is in the Idle state, the application may also send the * Pause command. If the component receives the pause command while in the * Idle state, the component shall send all input buffers it holds to the * application, but shall not begin processing buffers. This will allow the * application to prefill buffers. * * @ingroup comp */ typedef enum OMX_STATETYPE { OMX_StateInvalid, /**< component has detected that it's internal data structures are corrupted to the point that it cannot determine it's state properly */ OMX_StateLoaded, /**< component has been loaded but has not completed initialization. The OMX_SetParameter macro and the OMX_GetParameter macro are the only valid macros allowed to be sent to the component in this state. */ OMX_StateIdle, /**< component initialization has been completed successfully and the component is ready to to start. */ OMX_StateExecuting, /**< component has accepted the start command and is processing data (if data is available) */ OMX_StatePause, /**< component has received pause command */ OMX_StateWaitForResources, /**< component is waiting for resources, either after preemption or before it gets the resources requested. See specification for complete details. */ OMX_StateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_StateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_StateMax = 0X7FFFFFFF } OMX_STATETYPE; /** The OMX_ERRORTYPE enumeration defines the standard OMX Errors. These * errors should cover most of the common failure cases. However, * vendors are free to add additional error messages of their own as * long as they follow these rules: * 1. Vendor error messages shall be in the range of 0x90000000 to * 0x9000FFFF. * 2. Vendor error messages shall be defined in a header file provided * with the component. No error messages are allowed that are * not defined. */ typedef enum OMX_ERRORTYPE { OMX_ErrorNone = 0, /** There were insufficient resources to perform the requested operation */ OMX_ErrorInsufficientResources = (OMX_S32) 0x80001000, /** There was an error, but the cause of the error could not be determined */ OMX_ErrorUndefined = (OMX_S32) 0x80001001, /** The component name string was not valid */ OMX_ErrorInvalidComponentName = (OMX_S32) 0x80001002, /** No component with the specified name string was found */ OMX_ErrorComponentNotFound = (OMX_S32) 0x80001003, /** The component specified did not have a "OMX_ComponentInit" or "OMX_ComponentDeInit entry point */ OMX_ErrorInvalidComponent = (OMX_S32) 0x80001004, /** One or more parameters were not valid */ OMX_ErrorBadParameter = (OMX_S32) 0x80001005, /** The requested function is not implemented */ OMX_ErrorNotImplemented = (OMX_S32) 0x80001006, /** The buffer was emptied before the next buffer was ready */ OMX_ErrorUnderflow = (OMX_S32) 0x80001007, /** The buffer was not available when it was needed */ OMX_ErrorOverflow = (OMX_S32) 0x80001008, /** The hardware failed to respond as expected */ OMX_ErrorHardware = (OMX_S32) 0x80001009, /** The component is in the state OMX_StateInvalid */ OMX_ErrorInvalidState = (OMX_S32) 0x8000100A, /** Stream is found to be corrupt */ OMX_ErrorStreamCorrupt = (OMX_S32) 0x8000100B, /** Ports being connected are not compatible */ OMX_ErrorPortsNotCompatible = (OMX_S32) 0x8000100C, /** Resources allocated to an idle component have been lost resulting in the component returning to the loaded state */ OMX_ErrorResourcesLost = (OMX_S32) 0x8000100D, /** No more indicies can be enumerated */ OMX_ErrorNoMore = (OMX_S32) 0x8000100E, /** The component detected a version mismatch */ OMX_ErrorVersionMismatch = (OMX_S32) 0x8000100F, /** The component is not ready to return data at this time */ OMX_ErrorNotReady = (OMX_S32) 0x80001010, /** There was a timeout that occurred */ OMX_ErrorTimeout = (OMX_S32) 0x80001011, /** This error occurs when trying to transition into the state you are already in */ OMX_ErrorSameState = (OMX_S32) 0x80001012, /** Resources allocated to an executing or paused component have been preempted, causing the component to return to the idle state */ OMX_ErrorResourcesPreempted = (OMX_S32) 0x80001013, /** A non-supplier port sends this error to the IL client (via the EventHandler callback) during the allocation of buffers (on a transition from the LOADED to the IDLE state or on a port restart) when it deems that it has waited an unusually long time for the supplier to send it an allocated buffer via a UseBuffer call. */ OMX_ErrorPortUnresponsiveDuringAllocation = (OMX_S32) 0x80001014, /** A non-supplier port sends this error to the IL client (via the EventHandler callback) during the deallocation of buffers (on a transition from the IDLE to LOADED state or on a port stop) when it deems that it has waited an unusually long time for the supplier to request the deallocation of a buffer header via a FreeBuffer call. */ OMX_ErrorPortUnresponsiveDuringDeallocation = (OMX_S32) 0x80001015, /** A supplier port sends this error to the IL client (via the EventHandler callback) during the stopping of a port (either on a transition from the IDLE to LOADED state or a port stop) when it deems that it has waited an unusually long time for the non-supplier to return a buffer via an EmptyThisBuffer or FillThisBuffer call. */ OMX_ErrorPortUnresponsiveDuringStop = (OMX_S32) 0x80001016, /** Attempting a state transtion that is not allowed */ OMX_ErrorIncorrectStateTransition = (OMX_S32) 0x80001017, /* Attempting a command that is not allowed during the present state. */ OMX_ErrorIncorrectStateOperation = (OMX_S32) 0x80001018, /** The values encapsulated in the parameter or config structure are not supported. */ OMX_ErrorUnsupportedSetting = (OMX_S32) 0x80001019, /** The parameter or config indicated by the given index is not supported. */ OMX_ErrorUnsupportedIndex = (OMX_S32) 0x8000101A, /** The port index supplied is incorrect. */ OMX_ErrorBadPortIndex = (OMX_S32) 0x8000101B, /** The port has lost one or more of its buffers and it thus unpopulated. */ OMX_ErrorPortUnpopulated = (OMX_S32) 0x8000101C, /** Component suspended due to temporary loss of resources */ OMX_ErrorComponentSuspended = (OMX_S32) 0x8000101D, /** Component suspended due to an inability to acquire dynamic resources */ OMX_ErrorDynamicResourcesUnavailable = (OMX_S32) 0x8000101E, /** When the macroblock error reporting is enabled the component returns new error for every frame that has errors */ OMX_ErrorMbErrorsInFrame = (OMX_S32) 0x8000101F, /** A component reports this error when it cannot parse or determine the format of an input stream. */ OMX_ErrorFormatNotDetected = (OMX_S32) 0x80001020, /** The content open operation failed. */ OMX_ErrorContentPipeOpenFailed = (OMX_S32) 0x80001021, /** The content creation operation failed. */ OMX_ErrorContentPipeCreationFailed = (OMX_S32) 0x80001022, /** Separate table information is being used */ OMX_ErrorSeperateTablesUsed = (OMX_S32) 0x80001023, /** Tunneling is unsupported by the component*/ OMX_ErrorTunnelingUnsupported = (OMX_S32) 0x80001024, OMX_ErrorKhronosExtensions = (OMX_S32)0x8F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_ErrorVendorStartUnused = (OMX_S32)0x90000000, /**< Reserved region for introducing Vendor Extensions */ OMX_ErrorMax = 0x7FFFFFFF } OMX_ERRORTYPE; /** @ingroup core */ typedef OMX_ERRORTYPE (* OMX_COMPONENTINITTYPE)(OMX_IN OMX_HANDLETYPE hComponent); /** @ingroup core */ typedef struct OMX_COMPONENTREGISTERTYPE { const char * pName; /* Component name, 128 byte limit (including '\0') applies */ OMX_COMPONENTINITTYPE pInitialize; /* Component instance initialization function */ } OMX_COMPONENTREGISTERTYPE; /** @ingroup core */ extern OMX_COMPONENTREGISTERTYPE OMX_ComponentRegistered[]; /** @ingroup rpm */ typedef struct OMX_PRIORITYMGMTTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nGroupPriority; /**< Priority of the component group */ OMX_U32 nGroupID; /**< ID of the component group */ } OMX_PRIORITYMGMTTYPE; /* Component name and Role names are limited to 128 characters including the terminating '\0'. */ #define OMX_MAX_STRINGNAME_SIZE 128 /** @ingroup comp */ typedef struct OMX_PARAM_COMPONENTROLETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U8 cRole[OMX_MAX_STRINGNAME_SIZE]; /**< name of standard component which defines component role */ } OMX_PARAM_COMPONENTROLETYPE; /** End of Stream Buffer Flag: * * A component sets EOS when it has no more data to emit on a particular * output port. Thus an output port shall set EOS on the last buffer it * emits. A component's determination of when an output port should * cease sending data is implemenation specific. * @ingroup buf */ #define OMX_BUFFERFLAG_EOS 0x00000001 /** Start Time Buffer Flag: * * The source of a stream (e.g. a demux component) sets the STARTTIME * flag on the buffer that contains the starting timestamp for the * stream. The starting timestamp corresponds to the first data that * should be displayed at startup or after a seek. * The first timestamp of the stream is not necessarily the start time. * For instance, in the case of a seek to a particular video frame, * the target frame may be an interframe. Thus the first buffer of * the stream will be the intra-frame preceding the target frame and * the starttime will occur with the target frame (with any other * required frames required to reconstruct the target intervening). * * The STARTTIME flag is directly associated with the buffer's * timestamp ' thus its association to buffer data and its * propagation is identical to the timestamp's. * * When a Sync Component client receives a buffer with the * STARTTIME flag it shall perform a SetConfig on its sync port * using OMX_ConfigTimeClientStartTime and passing the buffer's * timestamp. * * @ingroup buf */ #define OMX_BUFFERFLAG_STARTTIME 0x00000002 /** Decode Only Buffer Flag: * * The source of a stream (e.g. a demux component) sets the DECODEONLY * flag on any buffer that should shall be decoded but should not be * displayed. This flag is used, for instance, when a source seeks to * a target interframe that requires the decode of frames preceding the * target to facilitate the target's reconstruction. In this case the * source would emit the frames preceding the target downstream * but mark them as decode only. * * The DECODEONLY is associated with buffer data and propagated in a * manner identical to the buffer timestamp. * * A component that renders data should ignore all buffers with * the DECODEONLY flag set. * * @ingroup buf */ #define OMX_BUFFERFLAG_DECODEONLY 0x00000004 /* Data Corrupt Flag: This flag is set when the IL client believes the data in the associated buffer is corrupt * @ingroup buf */ #define OMX_BUFFERFLAG_DATACORRUPT 0x00000008 /* End of Frame: The buffer contains exactly one end of frame and no data * occurs after the end of frame. This flag is an optional hint. The absence * of this flag does not imply the absence of an end of frame within the buffer. * @ingroup buf */ #define OMX_BUFFERFLAG_ENDOFFRAME 0x00000010 /* Sync Frame Flag: This flag is set when the buffer content contains a coded sync frame ' * a frame that has no dependency on any other frame information * @ingroup buf */ #define OMX_BUFFERFLAG_SYNCFRAME 0x00000020 /* Extra data present flag: there is extra data appended to the data stream * residing in the buffer * @ingroup buf */ #define OMX_BUFFERFLAG_EXTRADATA 0x00000040 /** Codec Config Buffer Flag: * OMX_BUFFERFLAG_CODECCONFIG is an optional flag that is set by an * output port when all bytes in the buffer form part or all of a set of * codec specific configuration data. Examples include SPS/PPS nal units * for OMX_VIDEO_CodingAVC or AudioSpecificConfig data for * OMX_AUDIO_CodingAAC. Any component that for a given stream sets * OMX_BUFFERFLAG_CODECCONFIG shall not mix codec configuration bytes * with frame data in the same buffer, and shall send all buffers * containing codec configuration bytes before any buffers containing * frame data that those configurations bytes describe. * If the stream format for a particular codec has a frame specific * header at the start of each frame, for example OMX_AUDIO_CodingMP3 or * OMX_AUDIO_CodingAAC in ADTS mode, then these shall be presented as * normal without setting OMX_BUFFERFLAG_CODECCONFIG. * @ingroup buf */ #define OMX_BUFFERFLAG_CODECCONFIG 0x00000080 /** @ingroup buf */ typedef struct OMX_BUFFERHEADERTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U8* pBuffer; /**< Pointer to actual block of memory that is acting as the buffer */ OMX_U32 nAllocLen; /**< size of the buffer allocated, in bytes */ OMX_U32 nFilledLen; /**< number of bytes currently in the buffer */ OMX_U32 nOffset; /**< start offset of valid data in bytes from the start of the buffer */ OMX_PTR pAppPrivate; /**< pointer to any data the application wants to associate with this buffer */ OMX_PTR pPlatformPrivate; /**< pointer to any data the platform wants to associate with this buffer */ OMX_PTR pInputPortPrivate; /**< pointer to any data the input port wants to associate with this buffer */ OMX_PTR pOutputPortPrivate; /**< pointer to any data the output port wants to associate with this buffer */ OMX_HANDLETYPE hMarkTargetComponent; /**< The component that will generate a mark event upon processing this buffer. */ OMX_PTR pMarkData; /**< Application specific data associated with the mark sent on a mark event to disambiguate this mark from others. */ OMX_U32 nTickCount; /**< Optional entry that the component and application can update with a tick count when they access the component. This value should be in microseconds. Since this is a value relative to an arbitrary starting point, this value cannot be used to determine absolute time. This is an optional entry and not all components will update it.*/ OMX_TICKS nTimeStamp; /**< Timestamp corresponding to the sample starting at the first logical sample boundary in the buffer. Timestamps of successive samples within the buffer may be inferred by adding the duration of the of the preceding buffer to the timestamp of the preceding buffer.*/ OMX_U32 nFlags; /**< buffer specific flags */ OMX_U32 nOutputPortIndex; /**< The index of the output port (if any) using this buffer */ OMX_U32 nInputPortIndex; /**< The index of the input port (if any) using this buffer */ } OMX_BUFFERHEADERTYPE; /** The OMX_EXTRADATATYPE enumeration is used to define the * possible extra data payload types. * NB: this enum is binary backwards compatible with the previous * OMX_EXTRADATA_QUANT define. This should be replaced with * OMX_ExtraDataQuantization. */ typedef enum OMX_EXTRADATATYPE { OMX_ExtraDataNone = 0, /**< Indicates that no more extra data sections follow */ OMX_ExtraDataQuantization, /**< The data payload contains quantization data */ OMX_ExtraDataKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_ExtraDataVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_ExtraDataMax = 0x7FFFFFFF } OMX_EXTRADATATYPE; typedef struct OMX_OTHER_EXTRADATATYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_EXTRADATATYPE eType; /* Extra Data type */ OMX_U32 nDataSize; /* Size of the supporting data to follow */ OMX_U8 data[1]; /* Supporting data hint */ } OMX_OTHER_EXTRADATATYPE; /** @ingroup comp */ typedef struct OMX_PORT_PARAM_TYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPorts; /**< The number of ports for this component */ OMX_U32 nStartPortNumber; /** first port number for this type of port */ } OMX_PORT_PARAM_TYPE; /** @ingroup comp */ typedef enum OMX_EVENTTYPE { OMX_EventCmdComplete, /**< component has sucessfully completed a command */ OMX_EventError, /**< component has detected an error condition */ OMX_EventMark, /**< component has detected a buffer mark */ OMX_EventPortSettingsChanged, /**< component is reported a port settings change */ OMX_EventBufferFlag, /**< component has detected an EOS */ OMX_EventResourcesAcquired, /**< component has been granted resources and is automatically starting the state change from OMX_StateWaitForResources to OMX_StateIdle. */ OMX_EventComponentResumed, /**< Component resumed due to reacquisition of resources */ OMX_EventDynamicResourcesAvailable, /**< Component has acquired previously unavailable dynamic resources */ OMX_EventPortFormatDetected, /**< Component has detected a supported format. */ OMX_EventKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_EventVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ /** Event when tunneled decoder has rendered an output or reached EOS * nData1 must contain the number of timestamps returned * pEventData must point to an array of the OMX_VIDEO_RENDEREVENTTYPE structs containing the * render-timestamps of each frame. Component may batch rendered timestamps using this event, * but must signal the event no more than 40ms after the first frame in the batch. The frames * must be ordered by system timestamp inside and across batches. * * The component shall signal the render-timestamp of the very first frame (as well as the * first frame after each flush) unbatched (with nData1 set to 1) within 5 msec. * * If component is doing frame-rate conversion, it must signal the render time of each * converted frame, and must interpolate media timestamps for in-between frames. * * When the component reached EOS, it must signal an EOS timestamp using the same mechanism. * This is in addition to the timestamp of the last rendered frame, and should follow that * frame. */ OMX_EventOutputRendered = 0x7F000001, /** For framework internal use only: event sent by OMXNodeInstance when it receives a graphic * input buffer with a new dataspace for encoding. |arg1| will contain the dataspace. |arg2| * will contain the ColorAspects requested by the component (or framework defaults) using * the following bitfield layout: * * +----------+-------------+----------------+------------+ * | Range | Primaries | MatrixCoeffs | Transfer | * +----------+-------------+----------------+------------+ * bits: 31....24 23.......16 15...........8 7........0 * * TODO: We would really need to tie this to an output buffer, but OMX does not provide a * fool-proof way to do that for video encoders. */ OMX_EventDataSpaceChanged, /** * Event when a component has an updated configuration on output for the client to retrieve. * |arg1| contains the port index (currently only output port is valid). |arg2| contains the * index of the updated config. * * For config updates that's associated with one frame, the update should be applied to the * next output frame that comes in EmptyBufferDone callback. * * Upon receiving this event, the client must call the corresponding OMX_GetConfig to retrieve * the config update. */ OMX_EventConfigUpdate, OMX_EventMax = 0x7FFFFFFF } OMX_EVENTTYPE; typedef struct OMX_CALLBACKTYPE { /** The EventHandler method is used to notify the application when an event of interest occurs. Events are defined in the OMX_EVENTTYPE enumeration. Please see that enumeration for details of what will be returned for each type of event. Callbacks should not return an error to the component, so if an error occurs, the application shall handle it internally. This is a blocking call. The application should return from this call within 5 msec to avoid blocking the component for an excessively long period of time. @param hComponent handle of the component to access. This is the component handle returned by the call to the GetHandle function. @param pAppData pointer to an application defined value that was provided in the pAppData parameter to the OMX_GetHandle method for the component. This application defined value is provided so that the application can have a component specific context when receiving the callback. @param eEvent Event that the component wants to notify the application about. @param nData1 nData will be the OMX_ERRORTYPE for an error event and will be an OMX_COMMANDTYPE for a command complete event and OMX_INDEXTYPE for a OMX_PortSettingsChanged event. @param nData2 nData2 will hold further information related to the event. Can be OMX_STATETYPE for a OMX_CommandStateSet command or port index for a OMX_PortSettingsChanged event. Default value is 0 if not used. ) @param pEventData Pointer to additional event-specific data (see spec for meaning). */ OMX_ERRORTYPE (*EventHandler)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, OMX_IN OMX_EVENTTYPE eEvent, OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, OMX_IN OMX_PTR pEventData); /** The EmptyBufferDone method is used to return emptied buffers from an input port back to the application for reuse. This is a blocking call so the application should not attempt to refill the buffers during this call, but should queue them and refill them in another thread. There is no error return, so the application shall handle any errors generated internally. The application should return from this call within 5 msec. @param hComponent handle of the component to access. This is the component handle returned by the call to the GetHandle function. @param pAppData pointer to an application defined value that was provided in the pAppData parameter to the OMX_GetHandle method for the component. This application defined value is provided so that the application can have a component specific context when receiving the callback. @param pBuffer pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer or AllocateBuffer indicating the buffer that was emptied. @ingroup buf */ OMX_ERRORTYPE (*EmptyBufferDone)( OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); /** The FillBufferDone method is used to return filled buffers from an output port back to the application for emptying and then reuse. This is a blocking call so the application should not attempt to empty the buffers during this call, but should queue the buffers and empty them in another thread. There is no error return, so the application shall handle any errors generated internally. The application shall also update the buffer header to indicate the number of bytes placed into the buffer. The application should return from this call within 5 msec. @param hComponent handle of the component to access. This is the component handle returned by the call to the GetHandle function. @param pAppData pointer to an application defined value that was provided in the pAppData parameter to the OMX_GetHandle method for the component. This application defined value is provided so that the application can have a component specific context when receiving the callback. @param pBuffer pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer or AllocateBuffer indicating the buffer that was filled. @ingroup buf */ OMX_ERRORTYPE (*FillBufferDone)( OMX_OUT OMX_HANDLETYPE hComponent, OMX_OUT OMX_PTR pAppData, OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); } OMX_CALLBACKTYPE; /** The OMX_BUFFERSUPPLIERTYPE enumeration is used to dictate port supplier preference when tunneling between two ports. @ingroup tun buf */ typedef enum OMX_BUFFERSUPPLIERTYPE { OMX_BufferSupplyUnspecified = 0x0, /**< port supplying the buffers is unspecified, or don't care */ OMX_BufferSupplyInput, /**< input port supplies the buffers */ OMX_BufferSupplyOutput, /**< output port supplies the buffers */ OMX_BufferSupplyKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_BufferSupplyVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_BufferSupplyMax = 0x7FFFFFFF } OMX_BUFFERSUPPLIERTYPE; /** buffer supplier parameter * @ingroup tun */ typedef struct OMX_PARAM_BUFFERSUPPLIERTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_BUFFERSUPPLIERTYPE eBufferSupplier; /**< buffer supplier */ } OMX_PARAM_BUFFERSUPPLIERTYPE; /**< indicates that buffers received by an input port of a tunnel may not modify the data in the buffers @ingroup tun */ #define OMX_PORTTUNNELFLAG_READONLY 0x00000001 /** The OMX_TUNNELSETUPTYPE structure is used to pass data from an output port to an input port as part the two ComponentTunnelRequest calls resulting from a OMX_SetupTunnel call from the IL Client. @ingroup tun */ typedef struct OMX_TUNNELSETUPTYPE { OMX_U32 nTunnelFlags; /**< bit flags for tunneling */ OMX_BUFFERSUPPLIERTYPE eSupplier; /**< supplier preference */ } OMX_TUNNELSETUPTYPE; /* OMX Component headers is included to enable the core to use macros for functions into the component for OMX release 1.0. Developers should not access any structures or data from within the component header directly */ /* TO BE REMOVED - #include */ /** GetComponentVersion will return information about the component. This is a blocking call. This macro will go directly from the application to the component (via a core macro). The component will return from this call within 5 msec. @param [in] hComponent handle of component to execute the command @param [out] pComponentName pointer to an empty string of length 128 bytes. The component will write its name into this string. The name will be terminated by a single zero byte. The name of a component will be 127 bytes or less to leave room for the trailing zero byte. An example of a valid component name is "OMX.ABC.ChannelMixer\0". @param [out] pComponentVersion pointer to an OMX Version structure that the component will fill in. The component will fill in a value that indicates the component version. NOTE: the component version is NOT the same as the OMX Specification version (found in all structures). The component version is defined by the vendor of the component and its value is entirely up to the component vendor. @param [out] pSpecVersion pointer to an OMX Version structure that the component will fill in. The SpecVersion is the version of the specification that the component was built against. Please note that this value may or may not match the structure's version. For example, if the component was built against the 2.0 specification, but the application (which creates the structure is built against the 1.0 specification the versions would be different. @param [out] pComponentUUID pointer to the UUID of the component which will be filled in by the component. The UUID is a unique identifier that is set at RUN time for the component and is unique to each instantion of the component. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_GetComponentVersion( \ hComponent, \ pComponentName, \ pComponentVersion, \ pSpecVersion, \ pComponentUUID) \ ((OMX_COMPONENTTYPE*)(hComponent))->GetComponentVersion(\ hComponent, \ pComponentName, \ pComponentVersion, \ pSpecVersion, \ pComponentUUID) /* Macro End */ /** Send a command to the component. This call is a non-blocking call. The component should check the parameters and then queue the command to the component thread to be executed. The component thread shall send the EventHandler() callback at the conclusion of the command. This macro will go directly from the application to the component (via a core macro). The component will return from this call within 5 msec. When the command is "OMX_CommandStateSet" the component will queue a state transition to the new state idenfied in nParam. The component shall transition from executing to loaded state within 500 msec. When the command is "OMX_CommandFlush", to flush a port's buffer queues, the command will force the component to return all buffers NOT CURRENTLY BEING PROCESSED to the application, in the order in which the buffers were received. The component shall finish flusing each port within 5 msec. When the command is "OMX_CommandPortDisable" or "OMX_CommandPortEnable", the component's port (given by the value of nParam) will be stopped or restarted. The component shall finish disabling/reenabling each port within 5 msec. When the command "OMX_CommandMarkBuffer" is used to mark a buffer, the pCmdData will point to a OMX_MARKTYPE structure containing the component handle of the component to examine the buffer chain for the mark. nParam1 contains the index of the port on which the buffer mark is applied. Specification text for more details. @param [in] hComponent handle of component to execute the command @param [in] Cmd Command for the component to execute @param [in] nParam Parameter for the command to be executed. When Cmd has the value OMX_CommandStateSet, value is a member of OMX_STATETYPE. When Cmd has the value OMX_CommandFlush, value of nParam indicates which port(s) to flush. -1 is used to flush all ports a single port index will only flush that port. When Cmd has the value "OMX_CommandPortDisable" or "OMX_CommandPortEnable", the component's port is given by the value of nParam. When Cmd has the value "OMX_CommandMarkBuffer" the components pot is given by the value of nParam. @param [in] pCmdData Parameter pointing to the OMX_MARKTYPE structure when Cmd has the value "OMX_CommandMarkBuffer". @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_SendCommand( \ hComponent, \ Cmd, \ nParam, \ pCmdData) \ ((OMX_COMPONENTTYPE*)(hComponent))->SendCommand( \ hComponent, \ Cmd, \ nParam, \ pCmdData) /* Macro End */ /** The OMX_GetParameter macro will get one of the current parameter settings from the component. This macro cannot only be invoked when the component is in the OMX_StateInvalid state. The nParamIndex parameter is used to indicate which structure is being requested from the component. The application shall allocate the correct structure and shall fill in the structure size and version information before invoking this macro. When the parameter applies to a port, the caller shall fill in the appropriate nPortIndex value indicating the port on which the parameter applies. If the component has not had any settings changed, then the component should return a set of valid DEFAULT parameters for the component. This is a blocking call. The component should return from this call within 20 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] nParamIndex Index of the structure to be filled. This value is from the OMX_INDEXTYPE enumeration. @param [in,out] pComponentParameterStructure Pointer to application allocated structure to be filled by the component. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_GetParameter( \ hComponent, \ nParamIndex, \ pComponentParameterStructure) \ ((OMX_COMPONENTTYPE*)(hComponent))->GetParameter( \ hComponent, \ nParamIndex, \ pComponentParameterStructure) /* Macro End */ /** The OMX_SetParameter macro will send an initialization parameter structure to a component. Each structure shall be sent one at a time, in a separate invocation of the macro. This macro can only be invoked when the component is in the OMX_StateLoaded state, or the port is disabled (when the parameter applies to a port). The nParamIndex parameter is used to indicate which structure is being passed to the component. The application shall allocate the correct structure and shall fill in the structure size and version information (as well as the actual data) before invoking this macro. The application is free to dispose of this structure after the call as the component is required to copy any data it shall retain. This is a blocking call. The component should return from this call within 20 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] nIndex Index of the structure to be sent. This value is from the OMX_INDEXTYPE enumeration. @param [in] pComponentParameterStructure pointer to application allocated structure to be used for initialization by the component. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_SetParameter( \ hComponent, \ nParamIndex, \ pComponentParameterStructure) \ ((OMX_COMPONENTTYPE*)(hComponent))->SetParameter( \ hComponent, \ nParamIndex, \ pComponentParameterStructure) /* Macro End */ /** The OMX_GetConfig macro will get one of the configuration structures from a component. This macro can be invoked anytime after the component has been loaded. The nParamIndex call parameter is used to indicate which structure is being requested from the component. The application shall allocate the correct structure and shall fill in the structure size and version information before invoking this macro. If the component has not had this configuration parameter sent before, then the component should return a set of valid DEFAULT values for the component. This is a blocking call. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] nIndex Index of the structure to be filled. This value is from the OMX_INDEXTYPE enumeration. @param [in,out] pComponentConfigStructure pointer to application allocated structure to be filled by the component. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_GetConfig( \ hComponent, \ nConfigIndex, \ pComponentConfigStructure) \ ((OMX_COMPONENTTYPE*)(hComponent))->GetConfig( \ hComponent, \ nConfigIndex, \ pComponentConfigStructure) /* Macro End */ /** The OMX_SetConfig macro will send one of the configuration structures to a component. Each structure shall be sent one at a time, each in a separate invocation of the macro. This macro can be invoked anytime after the component has been loaded. The application shall allocate the correct structure and shall fill in the structure size and version information (as well as the actual data) before invoking this macro. The application is free to dispose of this structure after the call as the component is required to copy any data it shall retain. This is a blocking call. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] nConfigIndex Index of the structure to be sent. This value is from the OMX_INDEXTYPE enumeration above. @param [in] pComponentConfigStructure pointer to application allocated structure to be used for initialization by the component. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_SetConfig( \ hComponent, \ nConfigIndex, \ pComponentConfigStructure) \ ((OMX_COMPONENTTYPE*)(hComponent))->SetConfig( \ hComponent, \ nConfigIndex, \ pComponentConfigStructure) /* Macro End */ /** The OMX_GetExtensionIndex macro will invoke a component to translate a vendor specific configuration or parameter string into an OMX structure index. There is no requirement for the vendor to support this command for the indexes already found in the OMX_INDEXTYPE enumeration (this is done to save space in small components). The component shall support all vendor supplied extension indexes not found in the master OMX_INDEXTYPE enumeration. This is a blocking call. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the GetHandle function. @param [in] cParameterName OMX_STRING that shall be less than 128 characters long including the trailing null byte. This is the string that will get translated by the component into a configuration index. @param [out] pIndexType a pointer to a OMX_INDEXTYPE to receive the index value. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_GetExtensionIndex( \ hComponent, \ cParameterName, \ pIndexType) \ ((OMX_COMPONENTTYPE*)(hComponent))->GetExtensionIndex( \ hComponent, \ cParameterName, \ pIndexType) /* Macro End */ /** The OMX_GetState macro will invoke the component to get the current state of the component and place the state value into the location pointed to by pState. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [out] pState pointer to the location to receive the state. The value returned is one of the OMX_STATETYPE members @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp */ #define OMX_GetState( \ hComponent, \ pState) \ ((OMX_COMPONENTTYPE*)(hComponent))->GetState( \ hComponent, \ pState) /* Macro End */ /** The OMX_UseBuffer macro will request that the component use a buffer (and allocate its own buffer header) already allocated by another component, or by the IL Client. This is a blocking call. The component should return from this call within 20 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [out] ppBuffer pointer to an OMX_BUFFERHEADERTYPE structure used to receive the pointer to the buffer header @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp buf */ #define OMX_UseBuffer( \ hComponent, \ ppBufferHdr, \ nPortIndex, \ pAppPrivate, \ nSizeBytes, \ pBuffer) \ ((OMX_COMPONENTTYPE*)(hComponent))->UseBuffer( \ hComponent, \ ppBufferHdr, \ nPortIndex, \ pAppPrivate, \ nSizeBytes, \ pBuffer) /** The OMX_AllocateBuffer macro will request that the component allocate a new buffer and buffer header. The component will allocate the buffer and the buffer header and return a pointer to the buffer header. This is a blocking call. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [out] ppBuffer pointer to an OMX_BUFFERHEADERTYPE structure used to receive the pointer to the buffer header @param [in] nPortIndex nPortIndex is used to select the port on the component the buffer will be used with. The port can be found by using the nPortIndex value as an index into the Port Definition array of the component. @param [in] pAppPrivate pAppPrivate is used to initialize the pAppPrivate member of the buffer header structure. @param [in] nSizeBytes size of the buffer to allocate. Used when bAllocateNew is true. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp buf */ #define OMX_AllocateBuffer( \ hComponent, \ ppBuffer, \ nPortIndex, \ pAppPrivate, \ nSizeBytes) \ ((OMX_COMPONENTTYPE*)(hComponent))->AllocateBuffer( \ hComponent, \ ppBuffer, \ nPortIndex, \ pAppPrivate, \ nSizeBytes) /* Macro End */ /** The OMX_FreeBuffer macro will release a buffer header from the component which was allocated using either OMX_AllocateBuffer or OMX_UseBuffer. If the component allocated the buffer (see the OMX_UseBuffer macro) then the component shall free the buffer and buffer header. This is a blocking call. The component should return from this call within 20 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] nPortIndex nPortIndex is used to select the port on the component the buffer will be used with. @param [in] pBuffer pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer or AllocateBuffer. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp buf */ #define OMX_FreeBuffer( \ hComponent, \ nPortIndex, \ pBuffer) \ ((OMX_COMPONENTTYPE*)(hComponent))->FreeBuffer( \ hComponent, \ nPortIndex, \ pBuffer) /* Macro End */ /** The OMX_EmptyThisBuffer macro will send a buffer full of data to an input port of a component. The buffer will be emptied by the component and returned to the application via the EmptyBufferDone call back. This is a non-blocking call in that the component will record the buffer and return immediately and then empty the buffer, later, at the proper time. As expected, this macro may be invoked only while the component is in the OMX_StateExecuting. If nPortIndex does not specify an input port, the component shall return an error. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] pBuffer pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer or AllocateBuffer. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp buf */ #define OMX_EmptyThisBuffer( \ hComponent, \ pBuffer) \ ((OMX_COMPONENTTYPE*)(hComponent))->EmptyThisBuffer( \ hComponent, \ pBuffer) /* Macro End */ /** The OMX_FillThisBuffer macro will send an empty buffer to an output port of a component. The buffer will be filled by the component and returned to the application via the FillBufferDone call back. This is a non-blocking call in that the component will record the buffer and return immediately and then fill the buffer, later, at the proper time. As expected, this macro may be invoked only while the component is in the OMX_ExecutingState. If nPortIndex does not specify an output port, the component shall return an error. The component should return from this call within 5 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [in] pBuffer pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer or AllocateBuffer. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp buf */ #define OMX_FillThisBuffer( \ hComponent, \ pBuffer) \ ((OMX_COMPONENTTYPE*)(hComponent))->FillThisBuffer( \ hComponent, \ pBuffer) /* Macro End */ /** The OMX_UseEGLImage macro will request that the component use a EGLImage provided by EGL (and allocate its own buffer header) This is a blocking call. The component should return from this call within 20 msec. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the OMX_GetHandle function. @param [out] ppBuffer pointer to an OMX_BUFFERHEADERTYPE structure used to receive the pointer to the buffer header. Note that the memory location used for this buffer is NOT visible to the IL Client. @param [in] nPortIndex nPortIndex is used to select the port on the component the buffer will be used with. The port can be found by using the nPortIndex value as an index into the Port Definition array of the component. @param [in] pAppPrivate pAppPrivate is used to initialize the pAppPrivate member of the buffer header structure. @param [in] eglImage eglImage contains the handle of the EGLImage to use as a buffer on the specified port. The component is expected to validate properties of the EGLImage against the configuration of the port to ensure the component can use the EGLImage as a buffer. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup comp buf */ #define OMX_UseEGLImage( \ hComponent, \ ppBufferHdr, \ nPortIndex, \ pAppPrivate, \ eglImage) \ ((OMX_COMPONENTTYPE*)(hComponent))->UseEGLImage( \ hComponent, \ ppBufferHdr, \ nPortIndex, \ pAppPrivate, \ eglImage) /** The OMX_Init method is used to initialize the OMX core. It shall be the first call made into OMX and it should only be executed one time without an interviening OMX_Deinit call. The core should return from this call within 20 msec. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void); /** The OMX_Deinit method is used to deinitialize the OMX core. It shall be the last call made into OMX. In the event that the core determines that thare are components loaded when this call is made, the core may return with an error rather than try to unload the components. The core should return from this call within 20 msec. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void); /** The OMX_ComponentNameEnum method will enumerate through all the names of recognised valid components in the system. This function is provided as a means to detect all the components in the system run-time. There is no strict ordering to the enumeration order of component names, although each name will only be enumerated once. If the OMX core supports run-time installation of new components, it is only requried to detect newly installed components when the first call to enumerate component names is made (i.e. when nIndex is 0x0). The core should return from this call in 20 msec. @param [out] cComponentName pointer to a null terminated string with the component name. The names of the components are strings less than 127 bytes in length plus the trailing null for a maximum size of 128 bytes. An example of a valid component name is "OMX.TI.AUDIO.DSP.MIXER\0". Names are assigned by the vendor, but shall start with "OMX." and then have the Vendor designation next. @param [in] nNameLength number of characters in the cComponentName string. With all component name strings restricted to less than 128 characters (including the trailing null) it is recomended that the caller provide a input string for the cComponentName of 128 characters. @param [in] nIndex number containing the enumeration index for the component. Multiple calls to OMX_ComponentNameEnum with increasing values of nIndex will enumerate through the component names in the system until OMX_ErrorNoMore is returned. The value of nIndex is 0 to (N-1), where N is the number of valid installed components in the system. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. When the value of nIndex exceeds the number of components in the system minus 1, OMX_ErrorNoMore will be returned. Otherwise the appropriate OMX error will be returned. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( OMX_OUT OMX_STRING cComponentName, OMX_IN OMX_U32 nNameLength, OMX_IN OMX_U32 nIndex); /** The OMX_GetHandle method will locate the component specified by the component name given, load that component into memory and then invoke the component's methods to create an instance of the component. The core should return from this call within 20 msec. @param [out] pHandle pointer to an OMX_HANDLETYPE pointer to be filled in by this method. @param [in] cComponentName pointer to a null terminated string with the component name. The names of the components are strings less than 127 bytes in length plus the trailing null for a maximum size of 128 bytes. An example of a valid component name is "OMX.TI.AUDIO.DSP.MIXER\0". Names are assigned by the vendor, but shall start with "OMX." and then have the Vendor designation next. @param [in] pAppData pointer to an application defined value that will be returned during callbacks so that the application can identify the source of the callback. @param [in] pCallBacks pointer to a OMX_CALLBACKTYPE structure that will be passed to the component to initialize it with. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( OMX_OUT OMX_HANDLETYPE* pHandle, OMX_IN OMX_STRING cComponentName, OMX_IN OMX_PTR pAppData, OMX_IN OMX_CALLBACKTYPE* pCallBacks); /** The OMX_FreeHandle method will free a handle allocated by the OMX_GetHandle method. If the component reference count goes to zero, the component will be unloaded from memory. The core should return from this call within 20 msec when the component is in the OMX_StateLoaded state. @param [in] hComponent Handle of the component to be accessed. This is the component handle returned by the call to the GetHandle function. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( OMX_IN OMX_HANDLETYPE hComponent); /** The OMX_SetupTunnel method will handle the necessary calls to the components to setup the specified tunnel the two components. NOTE: This is an actual method (not a #define macro). This method will make calls into the component ComponentTunnelRequest method to do the actual tunnel connection. The ComponentTunnelRequest method on both components will be called. This method shall not be called unless the component is in the OMX_StateLoaded state except when the ports used for the tunnel are disabled. In this case, the component may be in the OMX_StateExecuting, OMX_StatePause, or OMX_StateIdle states. The core should return from this call within 20 msec. @param [in] hOutput Handle of the component to be accessed. Also this is the handle of the component whose port, specified in the nPortOutput parameter will be used the source for the tunnel. This is the component handle returned by the call to the OMX_GetHandle function. There is a requirement that hOutput be the source for the data when tunelling (i.e. nPortOutput is an output port). If 0x0, the component specified in hInput will have it's port specified in nPortInput setup for communication with the application / IL client. @param [in] nPortOutput nPortOutput is used to select the source port on component to be used in the tunnel. @param [in] hInput This is the component to setup the tunnel with. This is the handle of the component whose port, specified in the nPortInput parameter will be used the destination for the tunnel. This is the component handle returned by the call to the OMX_GetHandle function. There is a requirement that hInput be the destination for the data when tunelling (i.e. nPortInut is an input port). If 0x0, the component specified in hOutput will have it's port specified in nPortPOutput setup for communication with the application / IL client. @param [in] nPortInput nPortInput is used to select the destination port on component to be used in the tunnel. @return OMX_ERRORTYPE If the command successfully executes, the return code will be OMX_ErrorNone. Otherwise the appropriate OMX error will be returned. When OMX_ErrorNotImplemented is returned, one or both components is a non-interop component and does not support tunneling. On failure, the ports of both components are setup for communication with the application / IL Client. @ingroup core tun */ OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( OMX_IN OMX_HANDLETYPE hOutput, OMX_IN OMX_U32 nPortOutput, OMX_IN OMX_HANDLETYPE hInput, OMX_IN OMX_U32 nPortInput); /** @ingroup cp */ OMX_API OMX_ERRORTYPE OMX_GetContentPipe( OMX_OUT OMX_HANDLETYPE *hPipe, OMX_IN OMX_STRING szURI); /** The OMX_GetComponentsOfRole method will return the number of components that support the given role and (if the compNames field is non-NULL) the names of those components. The call will fail if an insufficiently sized array of names is supplied. To ensure the array is sufficiently sized the client should: * first call this function with the compNames field NULL to determine the number of component names * second call this function with the compNames field pointing to an array of names allocated according to the number returned by the first call. The core should return from this call within 5 msec. @param [in] role This is generic standard component name consisting only of component class name and the type within that class (e.g. 'audio_decoder.aac'). @param [inout] pNumComps This is used both as input and output. If compNames is NULL, the input is ignored and the output specifies how many components support the given role. If compNames is not NULL, on input it bounds the size of the input structure and on output, it specifies the number of components string names listed within the compNames parameter. @param [inout] compNames If NULL this field is ignored. If non-NULL this points to an array of 128-byte strings which accepts a list of the names of all physical components that implement the specified standard component name. Each name is NULL terminated. numComps indicates the number of names. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_GetComponentsOfRole ( OMX_IN OMX_STRING role, OMX_INOUT OMX_U32 *pNumComps, OMX_INOUT OMX_U8 **compNames); /** The OMX_GetRolesOfComponent method will return the number of roles supported by the given component and (if the roles field is non-NULL) the names of those roles. The call will fail if an insufficiently sized array of names is supplied. To ensure the array is sufficiently sized the client should: * first call this function with the roles field NULL to determine the number of role names * second call this function with the roles field pointing to an array of names allocated according to the number returned by the first call. The core should return from this call within 5 msec. @param [in] compName This is the name of the component being queried about. @param [inout] pNumRoles This is used both as input and output. If roles is NULL, the input is ignored and the output specifies how many roles the component supports. If compNames is not NULL, on input it bounds the size of the input structure and on output, it specifies the number of roles string names listed within the roles parameter. @param [out] roles If NULL this field is ignored. If non-NULL this points to an array of 128-byte strings which accepts a list of the names of all standard components roles implemented on the specified component name. numComps indicates the number of names. @ingroup core */ OMX_API OMX_ERRORTYPE OMX_GetRolesOfComponent ( OMX_IN OMX_STRING compName, OMX_INOUT OMX_U32 *pNumRoles, OMX_OUT OMX_U8 **roles); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_IVCommon.h0100644 0000000 0000000 00000102217 13756501735 021005 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /** * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** * @file OMX_IVCommon.h - OpenMax IL version 1.1.2 * The structures needed by Video and Image components to exchange * parameters and configuration data with the components. */ #ifndef OMX_IVCommon_h #define OMX_IVCommon_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** * Each OMX header must include all required header files to allow the header * to compile without errors. The includes below are required for this header * file to compile successfully */ #include /** @defgroup iv OpenMAX IL Imaging and Video Domain * Common structures for OpenMAX IL Imaging and Video domains * @{ */ /** * Enumeration defining possible uncompressed image/video formats. * * ENUMS: * Unused : Placeholder value when format is N/A * Monochrome : black and white * 8bitRGB332 : Red 7:5, Green 4:2, Blue 1:0 * 12bitRGB444 : Red 11:8, Green 7:4, Blue 3:0 * 16bitARGB4444 : Alpha 15:12, Red 11:8, Green 7:4, Blue 3:0 * 16bitARGB1555 : Alpha 15, Red 14:10, Green 9:5, Blue 4:0 * 16bitRGB565 : Red 15:11, Green 10:5, Blue 4:0 * 16bitBGR565 : Blue 15:11, Green 10:5, Red 4:0 * 18bitRGB666 : Red 17:12, Green 11:6, Blue 5:0 * 18bitARGB1665 : Alpha 17, Red 16:11, Green 10:5, Blue 4:0 * 19bitARGB1666 : Alpha 18, Red 17:12, Green 11:6, Blue 5:0 * 24bitRGB888 : Red 24:16, Green 15:8, Blue 7:0 * 24bitBGR888 : Blue 24:16, Green 15:8, Red 7:0 * 24bitARGB1887 : Alpha 23, Red 22:15, Green 14:7, Blue 6:0 * 25bitARGB1888 : Alpha 24, Red 23:16, Green 15:8, Blue 7:0 * 32bitBGRA8888 : Blue 31:24, Green 23:16, Red 15:8, Alpha 7:0 * 32bitARGB8888 : Alpha 31:24, Red 23:16, Green 15:8, Blue 7:0 * YUV411Planar : U,Y are subsampled by a factor of 4 horizontally * YUV411PackedPlanar : packed per payload in planar slices * YUV420Planar : Three arrays Y,U,V. * YUV420PackedPlanar : packed per payload in planar slices * YUV420SemiPlanar : Two arrays, one is all Y, the other is U and V * YUV422Planar : Three arrays Y,U,V. * YUV422PackedPlanar : packed per payload in planar slices * YUV422SemiPlanar : Two arrays, one is all Y, the other is U and V * YCbYCr : Organized as 16bit YUYV (i.e. YCbYCr) * YCrYCb : Organized as 16bit YVYU (i.e. YCrYCb) * CbYCrY : Organized as 16bit UYVY (i.e. CbYCrY) * CrYCbY : Organized as 16bit VYUY (i.e. CrYCbY) * YUV444Interleaved : Each pixel contains equal parts YUV * RawBayer8bit : SMIA camera output format * RawBayer10bit : SMIA camera output format * RawBayer8bitcompressed : SMIA camera output format */ typedef enum OMX_COLOR_FORMATTYPE { OMX_COLOR_FormatUnused, OMX_COLOR_FormatMonochrome, OMX_COLOR_Format8bitRGB332, OMX_COLOR_Format12bitRGB444, OMX_COLOR_Format16bitARGB4444, OMX_COLOR_Format16bitARGB1555, OMX_COLOR_Format16bitRGB565, OMX_COLOR_Format16bitBGR565, OMX_COLOR_Format18bitRGB666, OMX_COLOR_Format18bitARGB1665, OMX_COLOR_Format19bitARGB1666, OMX_COLOR_Format24bitRGB888, OMX_COLOR_Format24bitBGR888, OMX_COLOR_Format24bitARGB1887, OMX_COLOR_Format25bitARGB1888, OMX_COLOR_Format32bitBGRA8888, OMX_COLOR_Format32bitARGB8888, OMX_COLOR_FormatYUV411Planar, OMX_COLOR_FormatYUV411PackedPlanar, OMX_COLOR_FormatYUV420Planar, OMX_COLOR_FormatYUV420PackedPlanar, OMX_COLOR_FormatYUV420SemiPlanar, OMX_COLOR_FormatYUV422Planar, OMX_COLOR_FormatYUV422PackedPlanar, OMX_COLOR_FormatYUV422SemiPlanar, OMX_COLOR_FormatYCbYCr, OMX_COLOR_FormatYCrYCb, OMX_COLOR_FormatCbYCrY, OMX_COLOR_FormatCrYCbY, OMX_COLOR_FormatYUV444Interleaved, OMX_COLOR_FormatRawBayer8bit, OMX_COLOR_FormatRawBayer10bit, OMX_COLOR_FormatRawBayer8bitcompressed, OMX_COLOR_FormatL2, OMX_COLOR_FormatL4, OMX_COLOR_FormatL8, OMX_COLOR_FormatL16, OMX_COLOR_FormatL24, OMX_COLOR_FormatL32, OMX_COLOR_FormatYUV420PackedSemiPlanar, OMX_COLOR_FormatYUV422PackedSemiPlanar, OMX_COLOR_Format18BitBGR666, OMX_COLOR_Format24BitARGB6666, OMX_COLOR_Format24BitABGR6666, OMX_COLOR_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_COLOR_FormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ /** /** @defgroup imaging OpenMAX IL Imaging Domain * @ingroup iv * Structures for OpenMAX IL Imaging domain * @{ */ /** * Enumeration used to define the possible image compression coding. */ typedef enum OMX_IMAGE_CODINGTYPE { OMX_IMAGE_CodingUnused, /**< Value when format is N/A */ OMX_IMAGE_CodingAutoDetect, /**< Auto detection of image format */ OMX_IMAGE_CodingJPEG, /**< JPEG/JFIF image format */ OMX_IMAGE_CodingJPEG2K, /**< JPEG 2000 image format */ OMX_IMAGE_CodingEXIF, /**< EXIF image format */ OMX_IMAGE_CodingTIFF, /**< TIFF image format */ OMX_IMAGE_CodingGIF, /**< Graphics image format */ OMX_IMAGE_CodingPNG, /**< PNG image format */ OMX_IMAGE_CodingLZW, /**< LZW image format */ OMX_IMAGE_CodingBMP, /**< Windows Bitmap format */ OMX_IMAGE_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_IMAGE_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_IMAGE_CodingMax = 0x7FFFFFFF } OMX_IMAGE_CODINGTYPE; /** * Data structure used to define an image path. The number of image paths * for input and output will vary by type of the image component. * * Input (aka Source) : Zero Inputs, one Output, * Splitter : One Input, 2 or more Outputs, * Processing Element : One Input, one output, * Mixer : 2 or more inputs, one output, * Output (aka Sink) : One Input, zero outputs. * * The PortDefinition structure is used to define all of the parameters * necessary for the compliant component to setup an input or an output * image path. If additional vendor specific data is required, it should * be transmitted to the component using the CustomCommand function. * Compliant components will prepopulate this structure with optimal * values during the OMX_GetParameter() command. * * STRUCT MEMBERS: * cMIMEType : MIME type of data for the port * pNativeRender : Platform specific reference for a display if a * sync, otherwise this field is 0 * nFrameWidth : Width of frame to be used on port if * uncompressed format is used. Use 0 for * unknown, don't care or variable * nFrameHeight : Height of frame to be used on port if * uncompressed format is used. Use 0 for * unknown, don't care or variable * nStride : Number of bytes per span of an image (i.e. * indicates the number of bytes to get from * span N to span N+1, where negative stride * indicates the image is bottom up * nSliceHeight : Height used when encoding in slices * bFlagErrorConcealment : Turns on error concealment if it is supported by * the OMX component * eCompressionFormat : Compression format used in this instance of * the component. When OMX_IMAGE_CodingUnused is * specified, eColorFormat is valid * eColorFormat : Decompressed format used by this component * pNativeWindow : Platform specific reference for a window object if a * display sink , otherwise this field is 0x0. */ typedef struct OMX_IMAGE_PORTDEFINITIONTYPE { OMX_STRING cMIMEType; OMX_NATIVE_DEVICETYPE pNativeRender; OMX_U32 nFrameWidth; OMX_U32 nFrameHeight; OMX_S32 nStride; OMX_U32 nSliceHeight; OMX_BOOL bFlagErrorConcealment; OMX_IMAGE_CODINGTYPE eCompressionFormat; OMX_COLOR_FORMATTYPE eColorFormat; OMX_NATIVE_WINDOWTYPE pNativeWindow; } OMX_IMAGE_PORTDEFINITIONTYPE; /** * Port format parameter. This structure is used to enumerate the various * data input/output format supported by the port. * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Indicates which port to set * nIndex : Indicates the enumeration index for the format from * 0x0 to N-1 * eCompressionFormat : Compression format used in this instance of the * component. When OMX_IMAGE_CodingUnused is specified, * eColorFormat is valid * eColorFormat : Decompressed format used by this component */ typedef struct OMX_IMAGE_PARAM_PORTFORMATTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nIndex; OMX_IMAGE_CODINGTYPE eCompressionFormat; OMX_COLOR_FORMATTYPE eColorFormat; } OMX_IMAGE_PARAM_PORTFORMATTYPE; /** * Flash control type * * ENUMS * Torch : Flash forced constantly on */ typedef enum OMX_IMAGE_FLASHCONTROLTYPE { OMX_IMAGE_FlashControlOn = 0, OMX_IMAGE_FlashControlOff, OMX_IMAGE_FlashControlAuto, OMX_IMAGE_FlashControlRedEyeReduction, OMX_IMAGE_FlashControlFillin, OMX_IMAGE_FlashControlTorch, OMX_IMAGE_FlashControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_IMAGE_FlashControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_IMAGE_FlashControlMax = 0x7FFFFFFF } OMX_IMAGE_FLASHCONTROLTYPE; /** * Flash control configuration * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eFlashControl : Flash control type */ typedef struct OMX_IMAGE_PARAM_FLASHCONTROLTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_IMAGE_FLASHCONTROLTYPE eFlashControl; } OMX_IMAGE_PARAM_FLASHCONTROLTYPE; /** * Focus control type */ typedef enum OMX_IMAGE_FOCUSCONTROLTYPE { OMX_IMAGE_FocusControlOn = 0, OMX_IMAGE_FocusControlOff, OMX_IMAGE_FocusControlAuto, OMX_IMAGE_FocusControlAutoLock, OMX_IMAGE_FocusControlKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_IMAGE_FocusControlVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_IMAGE_FocusControlMax = 0x7FFFFFFF } OMX_IMAGE_FOCUSCONTROLTYPE; /** * Focus control configuration * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eFocusControl : Focus control * nFocusSteps : Focus can take on values from 0 mm to infinity. * Interest is only in number of steps over this range. * nFocusStepIndex : Current focus step index */ typedef struct OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_IMAGE_FOCUSCONTROLTYPE eFocusControl; OMX_U32 nFocusSteps; OMX_U32 nFocusStepIndex; } OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE; /** * Q Factor for JPEG compression, which controls the tradeoff between image * quality and size. Q Factor provides a more simple means of controlling * JPEG compression quality, without directly programming Quantization * tables for chroma and luma * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nQFactor : JPEG Q factor value in the range of 1-100. A factor of 1 * produces the smallest, worst quality images, and a factor * of 100 produces the largest, best quality images. A * typical default is 75 for small good quality images */ typedef struct OMX_IMAGE_PARAM_QFACTORTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nQFactor; } OMX_IMAGE_PARAM_QFACTORTYPE; /** * Quantization table type */ typedef enum OMX_IMAGE_QUANTIZATIONTABLETYPE { OMX_IMAGE_QuantizationTableLuma = 0, OMX_IMAGE_QuantizationTableChroma, OMX_IMAGE_QuantizationTableChromaCb, OMX_IMAGE_QuantizationTableChromaCr, OMX_IMAGE_QuantizationTableKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_IMAGE_QuantizationTableVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_IMAGE_QuantizationTableMax = 0x7FFFFFFF } OMX_IMAGE_QUANTIZATIONTABLETYPE; /** * JPEG quantization tables are used to determine DCT compression for * YUV data, as an alternative to specifying Q factor, providing exact * control of compression * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eQuantizationTable : Quantization table type * nQuantizationMatrix[64] : JPEG quantization table of coefficients stored * in increasing columns then by rows of data (i.e. * row 1, ... row 8). Quantization values are in * the range 0-255 and stored in linear order * (i.e. the component will zig-zag the * quantization table data if required internally) */ typedef struct OMX_IMAGE_PARAM_QUANTIZATIONTABLETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_IMAGE_QUANTIZATIONTABLETYPE eQuantizationTable; OMX_U8 nQuantizationMatrix[64]; } OMX_IMAGE_PARAM_QUANTIZATIONTABLETYPE; /** * Huffman table type, the same Huffman table is applied for chroma and * luma component */ typedef enum OMX_IMAGE_HUFFMANTABLETYPE { OMX_IMAGE_HuffmanTableAC = 0, OMX_IMAGE_HuffmanTableDC, OMX_IMAGE_HuffmanTableACLuma, OMX_IMAGE_HuffmanTableACChroma, OMX_IMAGE_HuffmanTableDCLuma, OMX_IMAGE_HuffmanTableDCChroma, OMX_IMAGE_HuffmanTableKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_IMAGE_HuffmanTableVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_IMAGE_HuffmanTableMax = 0x7FFFFFFF } OMX_IMAGE_HUFFMANTABLETYPE; /** * JPEG Huffman table * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eHuffmanTable : Huffman table type * nNumberOfHuffmanCodeOfLength[16] : 0-16, number of Huffman codes of each * possible length * nHuffmanTable[256] : 0-255, the size used for AC and DC * HuffmanTable are 16 and 162 */ typedef struct OMX_IMAGE_PARAM_HUFFMANTTABLETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_IMAGE_HUFFMANTABLETYPE eHuffmanTable; OMX_U8 nNumberOfHuffmanCodeOfLength[16]; OMX_U8 nHuffmanTable[256]; }OMX_IMAGE_PARAM_HUFFMANTTABLETYPE; /** @} */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_Index.h0100644 0000000 0000000 00000044636 13756501735 020377 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** @file OMX_Index.h - OpenMax IL version 1.1.2 * The OMX_Index header file contains the definitions for both applications * and components . */ #ifndef OMX_Index_h #define OMX_Index_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header must include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include /** The OMX_INDEXTYPE enumeration is used to select a structure when either * getting or setting parameters and/or configuration data. Each entry in * this enumeration maps to an OMX specified structure. When the * OMX_GetParameter, OMX_SetParameter, OMX_GetConfig or OMX_SetConfig methods * are used, the second parameter will always be an entry from this enumeration * and the third entry will be the structure shown in the comments for the entry. * For example, if the application is initializing a cropping function, the * OMX_SetConfig command would have OMX_IndexConfigCommonInputCrop as the second parameter * and would send a pointer to an initialized OMX_RECTTYPE structure as the * third parameter. * * The enumeration entries named with the OMX_Config prefix are sent using * the OMX_SetConfig command and the enumeration entries named with the * OMX_PARAM_ prefix are sent using the OMX_SetParameter command. */ typedef enum OMX_INDEXTYPE { OMX_IndexComponentStartUnused = 0x01000000, OMX_IndexParamPriorityMgmt, /**< reference: OMX_PRIORITYMGMTTYPE */ OMX_IndexParamAudioInit, /**< reference: OMX_PORT_PARAM_TYPE */ OMX_IndexParamImageInit, /**< reference: OMX_PORT_PARAM_TYPE */ OMX_IndexParamVideoInit, /**< reference: OMX_PORT_PARAM_TYPE */ OMX_IndexParamOtherInit, /**< reference: OMX_PORT_PARAM_TYPE */ OMX_IndexParamNumAvailableStreams, /**< reference: OMX_PARAM_U32TYPE */ OMX_IndexParamActiveStream, /**< reference: OMX_PARAM_U32TYPE */ OMX_IndexParamSuspensionPolicy, /**< reference: OMX_PARAM_SUSPENSIONPOLICYTYPE */ OMX_IndexParamComponentSuspended, /**< reference: OMX_PARAM_SUSPENSIONTYPE */ OMX_IndexConfigCapturing, /**< reference: OMX_CONFIG_BOOLEANTYPE */ OMX_IndexConfigCaptureMode, /**< reference: OMX_CONFIG_CAPTUREMODETYPE */ OMX_IndexAutoPauseAfterCapture, /**< reference: OMX_CONFIG_BOOLEANTYPE */ OMX_IndexParamContentURI, /**< reference: OMX_PARAM_CONTENTURITYPE */ OMX_IndexParamCustomContentPipe, /**< reference: OMX_PARAM_CONTENTPIPETYPE */ OMX_IndexParamDisableResourceConcealment, /**< reference: OMX_RESOURCECONCEALMENTTYPE */ OMX_IndexConfigMetadataItemCount, /**< reference: OMX_CONFIG_METADATAITEMCOUNTTYPE */ OMX_IndexConfigContainerNodeCount, /**< reference: OMX_CONFIG_CONTAINERNODECOUNTTYPE */ OMX_IndexConfigMetadataItem, /**< reference: OMX_CONFIG_METADATAITEMTYPE */ OMX_IndexConfigCounterNodeID, /**< reference: OMX_CONFIG_CONTAINERNODEIDTYPE */ OMX_IndexParamMetadataFilterType, /**< reference: OMX_PARAM_METADATAFILTERTYPE */ OMX_IndexParamMetadataKeyFilter, /**< reference: OMX_PARAM_METADATAFILTERTYPE */ OMX_IndexConfigPriorityMgmt, /**< reference: OMX_PRIORITYMGMTTYPE */ OMX_IndexParamStandardComponentRole, /**< reference: OMX_PARAM_COMPONENTROLETYPE */ OMX_IndexComponentEndUnused, OMX_IndexPortStartUnused = 0x02000000, OMX_IndexParamPortDefinition, /**< reference: OMX_PARAM_PORTDEFINITIONTYPE */ OMX_IndexParamCompBufferSupplier, /**< reference: OMX_PARAM_BUFFERSUPPLIERTYPE */ OMX_IndexPortEndUnused, OMX_IndexReservedStartUnused = 0x03000000, /* Audio parameters and configurations */ OMX_IndexAudioStartUnused = 0x04000000, OMX_IndexParamAudioPortFormat, /**< reference: OMX_AUDIO_PARAM_PORTFORMATTYPE */ OMX_IndexParamAudioPcm, /**< reference: OMX_AUDIO_PARAM_PCMMODETYPE */ OMX_IndexParamAudioAac, /**< reference: OMX_AUDIO_PARAM_AACPROFILETYPE */ OMX_IndexParamAudioRa, /**< reference: OMX_AUDIO_PARAM_RATYPE */ OMX_IndexParamAudioMp3, /**< reference: OMX_AUDIO_PARAM_MP3TYPE */ OMX_IndexParamAudioAdpcm, /**< reference: OMX_AUDIO_PARAM_ADPCMTYPE */ OMX_IndexParamAudioG723, /**< reference: OMX_AUDIO_PARAM_G723TYPE */ OMX_IndexParamAudioG729, /**< reference: OMX_AUDIO_PARAM_G729TYPE */ OMX_IndexParamAudioAmr, /**< reference: OMX_AUDIO_PARAM_AMRTYPE */ OMX_IndexParamAudioWma, /**< reference: OMX_AUDIO_PARAM_WMATYPE */ OMX_IndexParamAudioSbc, /**< reference: OMX_AUDIO_PARAM_SBCTYPE */ OMX_IndexParamAudioMidi, /**< reference: OMX_AUDIO_PARAM_MIDITYPE */ OMX_IndexParamAudioGsm_FR, /**< reference: OMX_AUDIO_PARAM_GSMFRTYPE */ OMX_IndexParamAudioMidiLoadUserSound, /**< reference: OMX_AUDIO_PARAM_MIDILOADUSERSOUNDTYPE */ OMX_IndexParamAudioG726, /**< reference: OMX_AUDIO_PARAM_G726TYPE */ OMX_IndexParamAudioGsm_EFR, /**< reference: OMX_AUDIO_PARAM_GSMEFRTYPE */ OMX_IndexParamAudioGsm_HR, /**< reference: OMX_AUDIO_PARAM_GSMHRTYPE */ OMX_IndexParamAudioPdc_FR, /**< reference: OMX_AUDIO_PARAM_PDCFRTYPE */ OMX_IndexParamAudioPdc_EFR, /**< reference: OMX_AUDIO_PARAM_PDCEFRTYPE */ OMX_IndexParamAudioPdc_HR, /**< reference: OMX_AUDIO_PARAM_PDCHRTYPE */ OMX_IndexParamAudioTdma_FR, /**< reference: OMX_AUDIO_PARAM_TDMAFRTYPE */ OMX_IndexParamAudioTdma_EFR, /**< reference: OMX_AUDIO_PARAM_TDMAEFRTYPE */ OMX_IndexParamAudioQcelp8, /**< reference: OMX_AUDIO_PARAM_QCELP8TYPE */ OMX_IndexParamAudioQcelp13, /**< reference: OMX_AUDIO_PARAM_QCELP13TYPE */ OMX_IndexParamAudioEvrc, /**< reference: OMX_AUDIO_PARAM_EVRCTYPE */ OMX_IndexParamAudioSmv, /**< reference: OMX_AUDIO_PARAM_SMVTYPE */ OMX_IndexParamAudioVorbis, /**< reference: OMX_AUDIO_PARAM_VORBISTYPE */ OMX_IndexParamAudioFlac, /**< reference: OMX_AUDIO_PARAM_FLACTYPE */ OMX_IndexAudioEndUnused, OMX_IndexConfigAudioMidiImmediateEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIIMMEDIATEEVENTTYPE */ OMX_IndexConfigAudioMidiControl, /**< reference: OMX_AUDIO_CONFIG_MIDICONTROLTYPE */ OMX_IndexConfigAudioMidiSoundBankProgram, /**< reference: OMX_AUDIO_CONFIG_MIDISOUNDBANKPROGRAMTYPE */ OMX_IndexConfigAudioMidiStatus, /**< reference: OMX_AUDIO_CONFIG_MIDISTATUSTYPE */ OMX_IndexConfigAudioMidiMetaEvent, /**< reference: OMX_AUDIO_CONFIG_MIDIMETAEVENTTYPE */ OMX_IndexConfigAudioMidiMetaEventData, /**< reference: OMX_AUDIO_CONFIG_MIDIMETAEVENTDATATYPE */ OMX_IndexConfigAudioVolume, /**< reference: OMX_AUDIO_CONFIG_VOLUMETYPE */ OMX_IndexConfigAudioBalance, /**< reference: OMX_AUDIO_CONFIG_BALANCETYPE */ OMX_IndexConfigAudioChannelMute, /**< reference: OMX_AUDIO_CONFIG_CHANNELMUTETYPE */ OMX_IndexConfigAudioMute, /**< reference: OMX_AUDIO_CONFIG_MUTETYPE */ OMX_IndexConfigAudioLoudness, /**< reference: OMX_AUDIO_CONFIG_LOUDNESSTYPE */ OMX_IndexConfigAudioEchoCancelation, /**< reference: OMX_AUDIO_CONFIG_ECHOCANCELATIONTYPE */ OMX_IndexConfigAudioNoiseReduction, /**< reference: OMX_AUDIO_CONFIG_NOISEREDUCTIONTYPE */ OMX_IndexConfigAudioBass, /**< reference: OMX_AUDIO_CONFIG_BASSTYPE */ OMX_IndexConfigAudioTreble, /**< reference: OMX_AUDIO_CONFIG_TREBLETYPE */ OMX_IndexConfigAudioStereoWidening, /**< reference: OMX_AUDIO_CONFIG_STEREOWIDENINGTYPE */ OMX_IndexConfigAudioChorus, /**< reference: OMX_AUDIO_CONFIG_CHORUSTYPE */ OMX_IndexConfigAudioEqualizer, /**< reference: OMX_AUDIO_CONFIG_EQUALIZERTYPE */ OMX_IndexConfigAudioReverberation, /**< reference: OMX_AUDIO_CONFIG_REVERBERATIONTYPE */ OMX_IndexConfigAudioChannelVolume, /**< reference: OMX_AUDIO_CONFIG_CHANNELVOLUMETYPE */ /* Image specific parameters and configurations */ OMX_IndexImageStartUnused = 0x05000000, OMX_IndexParamImagePortFormat, /**< reference: OMX_IMAGE_PARAM_PORTFORMATTYPE */ OMX_IndexParamFlashControl, /**< reference: OMX_IMAGE_PARAM_FLASHCONTROLTYPE */ OMX_IndexConfigFocusControl, /**< reference: OMX_IMAGE_CONFIG_FOCUSCONTROLTYPE */ OMX_IndexParamQFactor, /**< reference: OMX_IMAGE_PARAM_QFACTORTYPE */ OMX_IndexParamQuantizationTable, /**< reference: OMX_IMAGE_PARAM_QUANTIZATIONTABLETYPE */ OMX_IndexParamHuffmanTable, /**< reference: OMX_IMAGE_PARAM_HUFFMANTTABLETYPE */ OMX_IndexConfigFlashControl, /**< reference: OMX_IMAGE_PARAM_FLASHCONTROLTYPE */ /* Video specific parameters and configurations */ OMX_IndexVideoStartUnused = 0x06000000, OMX_IndexParamVideoPortFormat, /**< reference: OMX_VIDEO_PARAM_PORTFORMATTYPE */ OMX_IndexParamVideoQuantization, /**< reference: OMX_VIDEO_PARAM_QUANTIZATIONTYPE */ OMX_IndexParamVideoFastUpdate, /**< reference: OMX_VIDEO_PARAM_VIDEOFASTUPDATETYPE */ OMX_IndexParamVideoBitrate, /**< reference: OMX_VIDEO_PARAM_BITRATETYPE */ OMX_IndexParamVideoMotionVector, /**< reference: OMX_VIDEO_PARAM_MOTIONVECTORTYPE */ OMX_IndexParamVideoIntraRefresh, /**< reference: OMX_VIDEO_PARAM_INTRAREFRESHTYPE */ OMX_IndexParamVideoErrorCorrection, /**< reference: OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE */ OMX_IndexParamVideoVBSMC, /**< reference: OMX_VIDEO_PARAM_VBSMCTYPE */ OMX_IndexParamVideoMpeg2, /**< reference: OMX_VIDEO_PARAM_MPEG2TYPE */ OMX_IndexParamVideoMpeg4, /**< reference: OMX_VIDEO_PARAM_MPEG4TYPE */ OMX_IndexParamVideoWmv, /**< reference: OMX_VIDEO_PARAM_WMVTYPE */ OMX_IndexParamVideoRv, /**< reference: OMX_VIDEO_PARAM_RVTYPE */ OMX_IndexParamVideoAvc, /**< reference: OMX_VIDEO_PARAM_AVCTYPE */ OMX_IndexParamVideoH263, /**< reference: OMX_VIDEO_PARAM_H263TYPE */ OMX_IndexParamVideoProfileLevelQuerySupported, /**< reference: OMX_VIDEO_PARAM_PROFILELEVELTYPE */ OMX_IndexParamVideoProfileLevelCurrent, /**< reference: OMX_VIDEO_PARAM_PROFILELEVELTYPE */ OMX_IndexConfigVideoBitrate, /**< reference: OMX_VIDEO_CONFIG_BITRATETYPE */ OMX_IndexConfigVideoFramerate, /**< reference: OMX_CONFIG_FRAMERATETYPE */ OMX_IndexConfigVideoIntraVOPRefresh, /**< reference: OMX_CONFIG_INTRAREFRESHVOPTYPE */ OMX_IndexConfigVideoIntraMBRefresh, /**< reference: OMX_CONFIG_MACROBLOCKERRORMAPTYPE */ OMX_IndexConfigVideoMBErrorReporting, /**< reference: OMX_CONFIG_MBERRORREPORTINGTYPE */ OMX_IndexParamVideoMacroblocksPerFrame, /**< reference: OMX_PARAM_MACROBLOCKSTYPE */ OMX_IndexConfigVideoMacroBlockErrorMap, /**< reference: OMX_CONFIG_MACROBLOCKERRORMAPTYPE */ OMX_IndexParamVideoSliceFMO, /**< reference: OMX_VIDEO_PARAM_AVCSLICEFMO */ OMX_IndexConfigVideoAVCIntraPeriod, /**< reference: OMX_VIDEO_CONFIG_AVCINTRAPERIOD */ OMX_IndexConfigVideoNalSize, /**< reference: OMX_VIDEO_CONFIG_NALSIZE */ OMX_IndexVideoEndUnused, /* Image & Video common Configurations */ OMX_IndexCommonStartUnused = 0x07000000, OMX_IndexParamCommonDeblocking, /**< reference: OMX_PARAM_DEBLOCKINGTYPE */ OMX_IndexParamCommonSensorMode, /**< reference: OMX_PARAM_SENSORMODETYPE */ OMX_IndexParamCommonInterleave, /**< reference: OMX_PARAM_INTERLEAVETYPE */ OMX_IndexConfigCommonColorFormatConversion, /**< reference: OMX_CONFIG_COLORCONVERSIONTYPE */ OMX_IndexConfigCommonScale, /**< reference: OMX_CONFIG_SCALEFACTORTYPE */ OMX_IndexConfigCommonImageFilter, /**< reference: OMX_CONFIG_IMAGEFILTERTYPE */ OMX_IndexConfigCommonColorEnhancement, /**< reference: OMX_CONFIG_COLORENHANCEMENTTYPE */ OMX_IndexConfigCommonColorKey, /**< reference: OMX_CONFIG_COLORKEYTYPE */ OMX_IndexConfigCommonColorBlend, /**< reference: OMX_CONFIG_COLORBLENDTYPE */ OMX_IndexConfigCommonFrameStabilisation,/**< reference: OMX_CONFIG_FRAMESTABTYPE */ OMX_IndexConfigCommonRotate, /**< reference: OMX_CONFIG_ROTATIONTYPE */ OMX_IndexConfigCommonMirror, /**< reference: OMX_CONFIG_MIRRORTYPE */ OMX_IndexConfigCommonOutputPosition, /**< reference: OMX_CONFIG_POINTTYPE */ OMX_IndexConfigCommonInputCrop, /**< reference: OMX_CONFIG_RECTTYPE */ OMX_IndexConfigCommonOutputCrop, /**< reference: OMX_CONFIG_RECTTYPE */ OMX_IndexConfigCommonDigitalZoom, /**< reference: OMX_CONFIG_SCALEFACTORTYPE */ OMX_IndexConfigCommonOpticalZoom, /**< reference: OMX_CONFIG_SCALEFACTORTYPE*/ OMX_IndexConfigCommonWhiteBalance, /**< reference: OMX_CONFIG_WHITEBALCONTROLTYPE */ OMX_IndexConfigCommonExposure, /**< reference: OMX_CONFIG_EXPOSURECONTROLTYPE */ OMX_IndexConfigCommonContrast, /**< reference: OMX_CONFIG_CONTRASTTYPE */ OMX_IndexConfigCommonBrightness, /**< reference: OMX_CONFIG_BRIGHTNESSTYPE */ OMX_IndexConfigCommonBacklight, /**< reference: OMX_CONFIG_BACKLIGHTTYPE */ OMX_IndexConfigCommonGamma, /**< reference: OMX_CONFIG_GAMMATYPE */ OMX_IndexConfigCommonSaturation, /**< reference: OMX_CONFIG_SATURATIONTYPE */ OMX_IndexConfigCommonLightness, /**< reference: OMX_CONFIG_LIGHTNESSTYPE */ OMX_IndexConfigCommonExclusionRect, /**< reference: OMX_CONFIG_RECTTYPE */ OMX_IndexConfigCommonDithering, /**< reference: OMX_CONFIG_DITHERTYPE */ OMX_IndexConfigCommonPlaneBlend, /**< reference: OMX_CONFIG_PLANEBLENDTYPE */ OMX_IndexConfigCommonExposureValue, /**< reference: OMX_CONFIG_EXPOSUREVALUETYPE */ OMX_IndexConfigCommonOutputSize, /**< reference: OMX_FRAMESIZETYPE */ OMX_IndexParamCommonExtraQuantData, /**< reference: OMX_OTHER_EXTRADATATYPE */ OMX_IndexConfigCommonFocusRegion, /**< reference: OMX_CONFIG_FOCUSREGIONTYPE */ OMX_IndexConfigCommonFocusStatus, /**< reference: OMX_PARAM_FOCUSSTATUSTYPE */ OMX_IndexConfigCommonTransitionEffect, /**< reference: OMX_CONFIG_TRANSITIONEFFECTTYPE */ OMX_IndexCommonEndUnused, /* Reserved Configuration range */ OMX_IndexOtherStartUnused = 0x08000000, OMX_IndexParamOtherPortFormat, /**< reference: OMX_OTHER_PARAM_PORTFORMATTYPE */ OMX_IndexConfigOtherPower, /**< reference: OMX_OTHER_CONFIG_POWERTYPE */ OMX_IndexConfigOtherStats, /**< reference: OMX_OTHER_CONFIG_STATSTYPE */ /* Reserved Time range */ OMX_IndexTimeStartUnused = 0x09000000, OMX_IndexConfigTimeScale, /**< reference: OMX_TIME_CONFIG_SCALETYPE */ OMX_IndexConfigTimeClockState, /**< reference: OMX_TIME_CONFIG_CLOCKSTATETYPE */ OMX_IndexConfigTimeActiveRefClock, /**< reference: OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE */ OMX_IndexConfigTimeCurrentMediaTime, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (read only) */ OMX_IndexConfigTimeCurrentWallTime, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (read only) */ OMX_IndexConfigTimeCurrentAudioReference, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (write only) */ OMX_IndexConfigTimeCurrentVideoReference, /**< reference: OMX_TIME_CONFIG_TIMESTAMPTYPE (write only) */ OMX_IndexConfigTimeMediaTimeRequest, /**< reference: OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE (write only) */ OMX_IndexConfigTimeClientStartTime, /** /** Khronos standard extension indices. This enum lists the current Khronos extension indices to OpenMAX IL. */ typedef enum OMX_INDEXEXTTYPE { /* Component parameters and configurations */ OMX_IndexExtComponentStartUnused = OMX_IndexKhronosExtensions + 0x00100000, OMX_IndexConfigCallbackRequest, /**< reference: OMX_CONFIG_CALLBACKREQUESTTYPE */ OMX_IndexConfigCommitMode, /**< reference: OMX_CONFIG_COMMITMODETYPE */ OMX_IndexConfigCommit, /**< reference: OMX_CONFIG_COMMITTYPE */ OMX_IndexConfigAndroidVendorExtension, /**< reference: OMX_CONFIG_VENDOR_EXTENSIONTYPE */ /* Port parameters and configurations */ OMX_IndexExtPortStartUnused = OMX_IndexKhronosExtensions + 0x00200000, /* Audio parameters and configurations */ OMX_IndexExtAudioStartUnused = OMX_IndexKhronosExtensions + 0x00400000, OMX_IndexParamAudioAndroidAc3, /**< reference: OMX_AUDIO_PARAM_ANDROID_AC3TYPE */ OMX_IndexParamAudioAndroidOpus, /**< reference: OMX_AUDIO_PARAM_ANDROID_OPUSTYPE */ OMX_IndexParamAudioAndroidAacPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */ OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */ OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */ OMX_IndexParamAudioAndroidAacDrcPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */ OMX_IndexParamAudioAndroidAc4, /**< reference: OMX_AUDIO_PARAM_ANDROID_AC4TYPE */ OMX_IndexConfigAudioPresentation, /**< reference: OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION */ OMX_IndexExtAudioEndUnused, /* Image parameters and configurations */ OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000, /* Video parameters and configurations */ OMX_IndexExtVideoStartUnused = OMX_IndexKhronosExtensions + 0x00600000, OMX_IndexParamNalStreamFormatSupported, /**< reference: OMX_NALSTREAMFORMATTYPE */ OMX_IndexParamNalStreamFormat, /**< reference: OMX_NALSTREAMFORMATTYPE */ OMX_IndexParamNalStreamFormatSelect, /**< reference: OMX_NALSTREAMFORMATTYPE */ OMX_IndexParamVideoVp8, /**< reference: OMX_VIDEO_PARAM_VP8TYPE */ OMX_IndexConfigVideoVp8ReferenceFrame, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMETYPE */ OMX_IndexConfigVideoVp8ReferenceFrameType, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE */ OMX_IndexParamVideoAndroidVp8Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE */ OMX_IndexParamVideoHevc, /**< reference: OMX_VIDEO_PARAM_HEVCTYPE */ OMX_IndexParamSliceSegments, /**< reference: OMX_VIDEO_SLICESEGMENTSTYPE */ OMX_IndexConfigAndroidIntraRefresh, /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */ OMX_IndexParamAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */ OMX_IndexConfigAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */ OMX_IndexParamMaxFrameDurationForBitrateControl,/**< reference: OMX_PARAM_U32TYPE */ OMX_IndexParamVideoVp9, /**< reference: OMX_VIDEO_PARAM_VP9TYPE */ OMX_IndexParamVideoAndroidVp9Encoder, /**< reference: OMX_VIDEO_PARAM_ANDROID_VP9ENCODERTYPE */ OMX_IndexParamVideoAndroidImageGrid, /**< reference: OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE */ OMX_IndexExtVideoEndUnused, /* Image & Video common configurations */ OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000, /* Other configurations */ OMX_IndexExtOtherStartUnused = OMX_IndexKhronosExtensions + 0x00800000, OMX_IndexConfigAutoFramerateConversion, /**< reference: OMX_CONFIG_BOOLEANTYPE */ OMX_IndexConfigPriority, /**< reference: OMX_PARAM_U32TYPE */ OMX_IndexConfigOperatingRate, /**< reference: OMX_PARAM_U32TYPE in Q16 format for video and in Hz for audio */ OMX_IndexParamConsumerUsageBits, /**< reference: OMX_PARAM_U32TYPE */ OMX_IndexConfigLatency, /**< reference: OMX_PARAM_U32TYPE */ OMX_IndexExtOtherEndUnused, /* Time configurations */ OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000, OMX_IndexExtMax = 0x7FFFFFFF } OMX_INDEXEXTTYPE; #define OMX_MAX_STRINGVALUE_SIZE OMX_MAX_STRINGNAME_SIZE #define OMX_MAX_ANDROID_VENDOR_PARAMCOUNT 32 typedef enum OMX_ANDROID_VENDOR_VALUETYPE { OMX_AndroidVendorValueInt32 = 0, /*<< int32_t value */ OMX_AndroidVendorValueInt64, /*<< int64_t value */ OMX_AndroidVendorValueString, /*<< string value */ OMX_AndroidVendorValueEndUnused, } OMX_ANDROID_VENDOR_VALUETYPE; /** * Structure describing a single value of an Android vendor extension. * * STRUCTURE MEMBERS: * cKey : parameter value name. * eValueType : parameter value type * bSet : if false, the parameter is not set (for OMX_GetConfig) or is unset (OMX_SetConfig) * if true, the parameter is set to the corresponding value below * nInt64 : int64 value * cString : string value */ typedef struct OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE { OMX_U8 cKey[OMX_MAX_STRINGNAME_SIZE]; OMX_ANDROID_VENDOR_VALUETYPE eValueType; OMX_BOOL bSet; union { OMX_S32 nInt32; OMX_S64 nInt64; OMX_U8 cString[OMX_MAX_STRINGVALUE_SIZE]; }; } OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE; /** * OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE is the structure for an Android vendor extension * supported by the component. This structure enumerates the various extension parameters and their * values. * * Android vendor extensions have a name and one or more parameter values - each with a string key - * that are set together. The values are exposed to Android applications via a string key that is * the concatenation of 'vendor', the extension name and the parameter key, each separated by dot * (.), with any trailing '.value' suffix(es) removed (though optionally allowed). * * Extension names and parameter keys are subject to the following rules: * - Each SHALL contain a set of lowercase alphanumeric (underscore allowed) tags separated by * dot (.) or dash (-). * - The first character of the first tag, and any tag following a dot SHALL not start with a * digit. * - Tags 'value', 'vendor', 'omx' and 'android' (even if trailed and/or followed by any number * of underscores) are prohibited in the extension name. * - Tags 'vendor', 'omx' and 'android' (even if trailed and/or followed by any number * of underscores) are prohibited in parameter keys. * - The tag 'value' (even if trailed and/or followed by any number * of underscores) is prohibited in parameter keys with the following exception: * the parameter key may be exactly 'value' * - The parameter key for extensions with a single parameter value SHALL be 'value' * - No two extensions SHALL have the same name * - No extension's name SHALL start with another extension's NAME followed by a dot (.) * - No two parameters of an extension SHALL have the same key * * This config can be used with both OMX_GetConfig and OMX_SetConfig. In the OMX_GetConfig * case, the caller specifies nIndex and nParamSizeUsed. The component fills in cName, * eDir and nParamCount. Additionally, if nParamSizeUsed is not less than nParamCount, the * component fills out the parameter values (nParam) with the current values for each parameter * of the vendor extension. * * The value of nIndex goes from 0 to N-1, where N is the number of Android vendor extensions * supported by the component. The component does not need to report N as the caller can determine * N by enumerating all extensions supported by the component. The component may not support any * extensions. If there are no more extensions, OMX_GetParameter returns OMX_ErrorNoMore. The * component supplies extensions in the order it wants clients to set them. * * The component SHALL return OMX_ErrorNone for all cases where nIndex is less than N (specifically * even in the case of where nParamCount is greater than nParamSizeUsed). * * In the OMX_SetConfig case the field nIndex is ignored. If the component supports an Android * vendor extension with the name in cName, it SHALL configure the parameter values for that * extension according to the parameters in nParam. nParamCount is the number of valid parameters * in the nParam array, and nParamSizeUsed is the size of the nParam array. (nParamSizeUsed * SHALL be at least nParamCount) Parameters that are part of a vendor extension but are not * in the nParam array are assumed to be unset (this is different from not changed). * All parameter values SHALL have distinct keys in nParam (the component can assume that this * is the case. Otherwise, the actual value for the parameters that are multiply defined can * be any of the set values.) * * Return values in case of OMX_SetConfig: * OMX_ErrorUnsupportedIndex: the component does not support the extension specified by cName * OMX_ErrorUnsupportedSetting: the component does not support some or any of the parameters * (names) specified in nParam * OMX_ErrorBadParameter: the parameter is invalid (e.g. nParamCount is greater than * nParamSizeUsed, or some parameter value has invalid type) * * STRUCTURE MEMBERS: * nSize : size of the structure in bytes * nVersion : OMX specification version information * cName : name of vendor extension * nParamCount : the number of parameter values that are part of this vendor extension * nParamSizeUsed : the size of nParam * (must be at least 1 and at most OMX_MAX_ANDROID_VENDOR_PARAMCOUNT) * param : the parameter values */ typedef struct OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nIndex; OMX_U8 cName[OMX_MAX_STRINGNAME_SIZE]; OMX_DIRTYPE eDir; OMX_U32 nParamCount; OMX_U32 nParamSizeUsed; OMX_CONFIG_ANDROID_VENDOR_PARAMTYPE param[1]; } OMX_CONFIG_ANDROID_VENDOR_EXTENSIONTYPE; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* OMX_IndexExt_h */ /* File EOF */ headers/media_plugin/media/openmax/OMX_Other.h0100644 0000000 0000000 00000044204 13756501735 020400 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** @file OMX_Other.h - OpenMax IL version 1.1.2 * The structures needed by Other components to exchange * parameters and configuration data with the components. */ #ifndef OMX_Other_h #define OMX_Other_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header must include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include /** * Enumeration of possible data types which match to multiple domains or no * domain at all. For types which are vendor specific, a value above * OMX_OTHER_VENDORTSTART should be used. */ typedef enum OMX_OTHER_FORMATTYPE { OMX_OTHER_FormatTime = 0, /**< Transmission of various timestamps, elapsed time, time deltas, etc */ OMX_OTHER_FormatPower, /**< Perhaps used for enabling/disabling power management, setting clocks? */ OMX_OTHER_FormatStats, /**< Could be things such as frame rate, frames dropped, etc */ OMX_OTHER_FormatBinary, /**< Arbitrary binary data */ OMX_OTHER_FormatVendorReserved = 1000, /**< Starting value for vendor specific formats */ OMX_OTHER_FormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_OTHER_FormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_OTHER_FormatMax = 0x7FFFFFFF } OMX_OTHER_FORMATTYPE; /** * Enumeration of seek modes. */ typedef enum OMX_TIME_SEEKMODETYPE { OMX_TIME_SeekModeFast = 0, /**< Prefer seeking to an approximation * of the requested seek position over * the actual seek position if it * results in a faster seek. */ OMX_TIME_SeekModeAccurate, /**< Prefer seeking to the actual seek * position over an approximation * of the requested seek position even * if it results in a slower seek. */ OMX_TIME_SeekModeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_TIME_SeekModeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_TIME_SeekModeMax = 0x7FFFFFFF } OMX_TIME_SEEKMODETYPE; /* Structure representing the seekmode of the component */ typedef struct OMX_TIME_CONFIG_SEEKMODETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_TIME_SEEKMODETYPE eType; /**< The seek mode */ } OMX_TIME_CONFIG_SEEKMODETYPE; /** Structure representing a time stamp used with the following configs * on the Clock Component (CC): * * OMX_IndexConfigTimeCurrentWallTime: query of the CC's current wall * time * OMX_IndexConfigTimeCurrentMediaTime: query of the CC's current media * time * OMX_IndexConfigTimeCurrentAudioReference and * OMX_IndexConfigTimeCurrentVideoReference: audio/video reference * clock sending SC its reference time * OMX_IndexConfigTimeClientStartTime: a Clock Component client sends * this structure to the Clock Component via a SetConfig on its * client port when it receives a buffer with * OMX_BUFFERFLAG_STARTTIME set. It must use the timestamp * specified by that buffer for nStartTimestamp. * * It's also used with the following config on components in general: * * OMX_IndexConfigTimePosition: IL client querying component position * (GetConfig) or commanding a component to seek to the given location * (SetConfig) */ typedef struct OMX_TIME_CONFIG_TIMESTAMPTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version * information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_TICKS nTimestamp; /**< timestamp .*/ } OMX_TIME_CONFIG_TIMESTAMPTYPE; /** Enumeration of possible reference clocks to the media time. */ typedef enum OMX_TIME_UPDATETYPE { OMX_TIME_UpdateRequestFulfillment, /**< Update is the fulfillment of a media time request. */ OMX_TIME_UpdateScaleChanged, /**< Update was generated because the scale chagned. */ OMX_TIME_UpdateClockStateChanged, /**< Update was generated because the clock state changed. */ OMX_TIME_UpdateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_TIME_UpdateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_TIME_UpdateMax = 0x7FFFFFFF } OMX_TIME_UPDATETYPE; /** Enumeration of possible reference clocks to the media time. */ typedef enum OMX_TIME_REFCLOCKTYPE { OMX_TIME_RefClockNone, /**< Use no references. */ OMX_TIME_RefClockAudio, /**< Use references sent through OMX_IndexConfigTimeCurrentAudioReference */ OMX_TIME_RefClockVideo, /**< Use references sent through OMX_IndexConfigTimeCurrentVideoReference */ OMX_TIME_RefClockKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_TIME_RefClockVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_TIME_RefClockMax = 0x7FFFFFFF } OMX_TIME_REFCLOCKTYPE; /** Enumeration of clock states. */ typedef enum OMX_TIME_CLOCKSTATE { OMX_TIME_ClockStateRunning, /**< Clock running. */ OMX_TIME_ClockStateWaitingForStartTime, /**< Clock waiting until the * prescribed clients emit their * start time. */ OMX_TIME_ClockStateStopped, /**< Clock stopped. */ OMX_TIME_ClockStateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_TIME_ClockStateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_TIME_ClockStateMax = 0x7FFFFFFF } OMX_TIME_CLOCKSTATE; /** Structure representing a media time request to the clock component. * * A client component sends this structure to the Clock Component via a SetConfig * on its client port to specify a media timestamp the Clock Component * should emit. The Clock Component should fulfill the request by sending a * OMX_TIME_MEDIATIMETYPE when its media clock matches the requested * timestamp. * * The client may require a media time request be fulfilled slightly * earlier than the media time specified. In this case the client specifies * an offset which is equal to the difference between wall time corresponding * to the requested media time and the wall time when it will be * fulfilled. * * A client component may uses these requests and the OMX_TIME_MEDIATIMETYPE to * time events according to timestamps. If a client must perform an operation O at * a time T (e.g. deliver a video frame at its corresponding timestamp), it makes a * media time request at T (perhaps specifying an offset to ensure the request fulfillment * is a little early). When the clock component passes the resulting OMX_TIME_MEDIATIMETYPE * structure back to the client component, the client may perform operation O (perhaps having * to wait a slight amount more time itself as specified by the return values). */ typedef struct OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< port that this structure applies to */ OMX_PTR pClientPrivate; /**< Client private data to disabiguate this media time * from others (e.g. the number of the frame to deliver). * Duplicated in the media time structure that fulfills * this request. A value of zero is reserved for time scale * updates. */ OMX_TICKS nMediaTimestamp; /**< Media timestamp requested.*/ OMX_TICKS nOffset; /**< Amount of wall clock time by which this * request should be fulfilled early */ } OMX_TIME_CONFIG_MEDIATIMEREQUESTTYPE; /**< Structure sent from the clock component client either when fulfilling * a media time request or when the time scale has changed. * * In the former case the Clock Component fills this structure and times its emission * to a client component (via the client port) according to the corresponding media * time request sent by the client. The Clock Component should time the emission to occur * when the requested timestamp matches the Clock Component's media time but also the * prescribed offset early. * * Upon scale changes the clock component clears the nClientPrivate data, sends the current * media time and sets the nScale to the new scale via the client port. It emits a * OMX_TIME_MEDIATIMETYPE to all clients independent of any requests. This allows clients to * alter processing to accomodate scaling. For instance a video component might skip inter-frames * in the case of extreme fastforward. Likewise an audio component might add or remove samples * from an audio frame to scale audio data. * * It is expected that some clock components may not be able to fulfill requests * at exactly the prescribed time. This is acceptable so long as the request is * fulfilled at least as early as described and not later. This structure provides * fields the client may use to wait for the remaining time. * * The client may use either the nOffset or nWallTimeAtMedia fields to determine the * wall time until the nMediaTimestamp actually occurs. In the latter case the * client can get a more accurate value for offset by getting the current wall * from the cloc component and subtracting it from nWallTimeAtMedia. */ typedef struct OMX_TIME_MEDIATIMETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nClientPrivate; /**< Client private data to disabiguate this media time * from others. Copied from the media time request. * A value of zero is reserved for time scale updates. */ OMX_TIME_UPDATETYPE eUpdateType; /**< Reason for the update */ OMX_TICKS nMediaTimestamp; /**< Media time requested. If no media time was * requested then this is the current media time. */ OMX_TICKS nOffset; /**< Amount of wall clock time by which this * request was actually fulfilled early */ OMX_TICKS nWallTimeAtMediaTime; /**< Wall time corresponding to nMediaTimeStamp. * A client may compare this value to current * media time obtained from the Clock Component to determine * the wall time until the media timestamp is really * current. */ OMX_S32 xScale; /**< Current media time scale in Q16 format. */ OMX_TIME_CLOCKSTATE eState; /* Seeking Change. Added 7/12.*/ /**< State of the media time. */ } OMX_TIME_MEDIATIMETYPE; /** Structure representing the current media time scale factor. Applicable only to clock * component, other components see scale changes via OMX_TIME_MEDIATIMETYPE buffers sent via * the clock component client ports. Upon recieving this config the clock component changes * the rate by which the media time increases or decreases effectively implementing trick modes. */ typedef struct OMX_TIME_CONFIG_SCALETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_S32 xScale; /**< This is a value in Q16 format which is used for * scaling the media time */ } OMX_TIME_CONFIG_SCALETYPE; /** Bits used to identify a clock port. Used in OMX_TIME_CONFIG_CLOCKSTATETYPE's nWaitMask field */ #define OMX_CLOCKPORT0 0x00000001 #define OMX_CLOCKPORT1 0x00000002 #define OMX_CLOCKPORT2 0x00000004 #define OMX_CLOCKPORT3 0x00000008 #define OMX_CLOCKPORT4 0x00000010 #define OMX_CLOCKPORT5 0x00000020 #define OMX_CLOCKPORT6 0x00000040 #define OMX_CLOCKPORT7 0x00000080 /** Structure representing the current mode of the media clock. * IL Client uses this config to change or query the mode of the * media clock of the clock component. Applicable only to clock * component. * * On a SetConfig if eState is OMX_TIME_ClockStateRunning media time * starts immediately at the prescribed start time. If * OMX_TIME_ClockStateWaitingForStartTime the Clock Component ignores * the given nStartTime and waits for all clients specified in the * nWaitMask to send starttimes (via * OMX_IndexConfigTimeClientStartTime). The Clock Component then starts * the media clock using the earliest start time supplied. */ typedef struct OMX_TIME_CONFIG_CLOCKSTATETYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version * information */ OMX_TIME_CLOCKSTATE eState; /**< State of the media time. */ OMX_TICKS nStartTime; /**< Start time of the media time. */ OMX_TICKS nOffset; /**< Time to offset the media time by * (e.g. preroll). Media time will be * reported to be nOffset ticks earlier. */ OMX_U32 nWaitMask; /**< Mask of OMX_CLOCKPORT values. */ } OMX_TIME_CONFIG_CLOCKSTATETYPE; /** Structure representing the reference clock currently being used to * compute media time. IL client uses this config to change or query the * clock component's active reference clock */ typedef struct OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_TIME_REFCLOCKTYPE eClock; /**< Reference clock used to compute media time */ } OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE; /** Descriptor for setting specifics of power type. * Note: this structure is listed for backwards compatibility. */ typedef struct OMX_OTHER_CONFIG_POWERTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_BOOL bEnablePM; /**< Flag to enable Power Management */ } OMX_OTHER_CONFIG_POWERTYPE; /** Descriptor for setting specifics of stats type. * Note: this structure is listed for backwards compatibility. */ typedef struct OMX_OTHER_CONFIG_STATSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ /* what goes here */ } OMX_OTHER_CONFIG_STATSTYPE; /** * The PortDefinition structure is used to define all of the parameters * necessary for the compliant component to setup an input or an output other * path. */ typedef struct OMX_OTHER_PORTDEFINITIONTYPE { OMX_OTHER_FORMATTYPE eFormat; /**< Type of data expected for this channel */ } OMX_OTHER_PORTDEFINITIONTYPE; /** Port format parameter. This structure is used to enumerate * the various data input/output format supported by the port. */ typedef struct OMX_OTHER_PARAM_PORTFORMATTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ OMX_U32 nPortIndex; /**< Indicates which port to set */ OMX_U32 nIndex; /**< Indicates the enumeration index for the format from 0x0 to N-1 */ OMX_OTHER_FORMATTYPE eFormat; /**< Type of data expected for this channel */ } OMX_OTHER_PARAM_PORTFORMATTYPE; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_Types.h0100644 0000000 0000000 00000033376 13756501735 020433 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /* * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** OMX_Types.h - OpenMax IL version 1.1.2 * The OMX_Types header file contains the primitive type definitions used by * the core, the application and the component. This file may need to be * modified to be used on systems that do not have "char" set to 8 bits, * "short" set to 16 bits and "long" set to 32 bits. */ #ifndef OMX_Types_h #define OMX_Types_h #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** The OMX_API and OMX_APIENTRY are platform specific definitions used * to declare OMX function prototypes. They are modified to meet the * requirements for a particular platform */ #ifdef __SYMBIAN32__ # ifdef __OMX_EXPORTS # define OMX_API __declspec(dllexport) # else # ifdef _WIN32 # define OMX_API __declspec(dllexport) # else # define OMX_API __declspec(dllimport) # endif # endif #else # ifdef _WIN32 # ifdef __OMX_EXPORTS # define OMX_API __declspec(dllexport) # else //# define OMX_API __declspec(dllimport) #define OMX_API # endif # else # ifdef __OMX_EXPORTS # define OMX_API # else # define OMX_API extern # endif # endif #endif #ifndef OMX_APIENTRY #define OMX_APIENTRY #endif /** OMX_IN is used to identify inputs to an OMX function. This designation will also be used in the case of a pointer that points to a parameter that is used as an output. */ #ifndef OMX_IN #define OMX_IN #endif /** OMX_OUT is used to identify outputs from an OMX function. This designation will also be used in the case of a pointer that points to a parameter that is used as an input. */ #ifndef OMX_OUT #define OMX_OUT #endif /** OMX_INOUT is used to identify parameters that may be either inputs or outputs from an OMX function at the same time. This designation will also be used in the case of a pointer that points to a parameter that is used both as an input and an output. */ #ifndef OMX_INOUT #define OMX_INOUT #endif /** OMX_ALL is used to as a wildcard to select all entities of the same type * when specifying the index, or referring to a object by an index. (i.e. * use OMX_ALL to indicate all N channels). When used as a port index * for a config or parameter this OMX_ALL denotes that the config or * parameter applies to the entire component not just one port. */ #define OMX_ALL 0xFFFFFFFF /** In the following we define groups that help building doxygen documentation */ /** @defgroup core OpenMAX IL core * Functions and structure related to the OMX IL core */ /** @defgroup comp OpenMAX IL component * Functions and structure related to the OMX IL component */ /** @defgroup rpm Resource and Policy Management * Structures for resource and policy management of components */ /** @defgroup buf Buffer Management * Buffer handling functions and structures */ /** @defgroup tun Tunneling * @ingroup core comp * Structures and functions to manage tunnels among component ports */ /** @defgroup cp Content Pipes * @ingroup core */ /** @defgroup metadata Metadata handling * */ /** OMX_U8 is an 8 bit unsigned quantity that is byte aligned */ typedef unsigned char OMX_U8; /** OMX_S8 is an 8 bit signed quantity that is byte aligned */ typedef signed char OMX_S8; /** OMX_U16 is a 16 bit unsigned quantity that is 16 bit word aligned */ typedef unsigned short OMX_U16; /** OMX_S16 is a 16 bit signed quantity that is 16 bit word aligned */ typedef signed short OMX_S16; /** OMX_U32 is a 32 bit unsigned quantity that is 32 bit word aligned */ typedef uint32_t OMX_U32; /** OMX_S32 is a 32 bit signed quantity that is 32 bit word aligned */ typedef int32_t OMX_S32; /* Users with compilers that cannot accept the "long long" designation should define the OMX_SKIP64BIT macro. It should be noted that this may cause some components to fail to compile if the component was written to require 64 bit integral types. However, these components would NOT compile anyway since the compiler does not support the way the component was written. */ #ifndef OMX_SKIP64BIT #ifdef __SYMBIAN32__ /** OMX_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ typedef unsigned long long OMX_U64; /** OMX_S64 is a 64 bit signed quantity that is 64 bit word aligned */ typedef signed long long OMX_S64; #elif defined(WIN32) /** OMX_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ typedef unsigned __int64 OMX_U64; /** OMX_S64 is a 64 bit signed quantity that is 64 bit word aligned */ typedef signed __int64 OMX_S64; #else /* WIN32 */ /** OMX_U64 is a 64 bit unsigned quantity that is 64 bit word aligned */ typedef unsigned long long OMX_U64; /** OMX_S64 is a 64 bit signed quantity that is 64 bit word aligned */ typedef signed long long OMX_S64; #endif /* WIN32 */ #endif /** The OMX_BOOL type is intended to be used to represent a true or a false value when passing parameters to and from the OMX core and components. The OMX_BOOL is a 32 bit quantity and is aligned on a 32 bit word boundary. */ typedef enum OMX_BOOL { OMX_FALSE = 0, OMX_TRUE = !OMX_FALSE, OMX_BOOL_MAX = 0x7FFFFFFF } OMX_BOOL; /* * Temporary Android 64 bit modification * * #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS * overrides all OMX pointer types to be uint32_t. * * After this change, OMX codecs will work in 32 bit only, so 64 bit processes * must communicate to a remote 32 bit process for OMX to work. */ #ifdef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS typedef uint32_t OMX_PTR; typedef OMX_PTR OMX_STRING; typedef OMX_PTR OMX_BYTE; #else /* OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS */ /** The OMX_PTR type is intended to be used to pass pointers between the OMX applications and the OMX Core and components. This is a 32 bit pointer and is aligned on a 32 bit boundary. */ typedef void* OMX_PTR; /** The OMX_STRING type is intended to be used to pass "C" type strings between the application and the core and component. The OMX_STRING type is a 32 bit pointer to a zero terminated string. The pointer is word aligned and the string is byte aligned. */ typedef char* OMX_STRING; /** The OMX_BYTE type is intended to be used to pass arrays of bytes such as buffers between the application and the component and core. The OMX_BYTE type is a 32 bit pointer to a zero terminated string. The pointer is word aligned and the string is byte aligned. */ typedef unsigned char* OMX_BYTE; #endif /* OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS */ /** OMX_UUIDTYPE is a very long unique identifier to uniquely identify at runtime. This identifier should be generated by a component in a way that guarantees that every instance of the identifier running on the system is unique. */ typedef unsigned char OMX_UUIDTYPE[128]; /** The OMX_DIRTYPE enumeration is used to indicate if a port is an input or an output port. This enumeration is common across all component types. */ typedef enum OMX_DIRTYPE { OMX_DirInput, /**< Port is an input port */ OMX_DirOutput, /**< Port is an output port */ OMX_DirMax = 0x7FFFFFFF } OMX_DIRTYPE; /** The OMX_ENDIANTYPE enumeration is used to indicate the bit ordering for numerical data (i.e. big endian, or little endian). */ typedef enum OMX_ENDIANTYPE { OMX_EndianBig, /**< big endian */ OMX_EndianLittle, /**< little endian */ OMX_EndianMax = 0x7FFFFFFF } OMX_ENDIANTYPE; /** The OMX_NUMERICALDATATYPE enumeration is used to indicate if data is signed, unsigned or floating point (Android extension). Android floating point support policy: If component does not support floating point raw audio, it can reset configuration to signed 16-bit integer (support for which is required.) nBitsPerSample will be set to 32 for float data. */ typedef enum OMX_NUMERICALDATATYPE { OMX_NumericalDataSigned, /**< signed data */ OMX_NumericalDataUnsigned, /**< unsigned data */ OMX_NumericalDataFloat = 0x7F000001, /**< floating point data */ OMX_NumercialDataMax = 0x7FFFFFFF } OMX_NUMERICALDATATYPE; /** Unsigned bounded value type */ typedef struct OMX_BU32 { OMX_U32 nValue; /**< actual value */ OMX_U32 nMin; /**< minimum for value (i.e. nValue >= nMin) */ OMX_U32 nMax; /**< maximum for value (i.e. nValue <= nMax) */ } OMX_BU32; /** Signed bounded value type */ typedef struct OMX_BS32 { OMX_S32 nValue; /**< actual value */ OMX_S32 nMin; /**< minimum for value (i.e. nValue >= nMin) */ OMX_S32 nMax; /**< maximum for value (i.e. nValue <= nMax) */ } OMX_BS32; /** Structure representing some time or duration in microseconds. This structure * must be interpreted as a signed 64 bit value. The quantity is signed to accommodate * negative deltas and preroll scenarios. The quantity is represented in microseconds * to accomodate high resolution timestamps (e.g. DVD presentation timestamps based * on a 90kHz clock) and to allow more accurate and synchronized delivery (e.g. * individual audio samples delivered at 192 kHz). The quantity is 64 bit to * accommodate a large dynamic range (signed 32 bit values would allow only for plus * or minus 35 minutes). * * Implementations with limited precision may convert the signed 64 bit value to * a signed 32 bit value internally but risk loss of precision. */ #ifndef OMX_SKIP64BIT typedef OMX_S64 OMX_TICKS; #else typedef struct OMX_TICKS { OMX_U32 nLowPart; /** low bits of the signed 64 bit tick value */ OMX_U32 nHighPart; /** high bits of the signed 64 bit tick value */ } OMX_TICKS; #endif #define OMX_TICKS_PER_SECOND 1000000 /** Define the public interface for the OMX Handle. The core will not use this value internally, but the application should only use this value. */ typedef OMX_PTR OMX_HANDLETYPE; typedef struct OMX_MARKTYPE { OMX_HANDLETYPE hMarkTargetComponent; /**< The component that will generate a mark event upon processing the mark. */ OMX_PTR pMarkData; /**< Application specific data associated with the mark sent on a mark event to disambiguate this mark from others. */ } OMX_MARKTYPE; /** OMX_NATIVE_DEVICETYPE is used to map a OMX video port to the * platform & operating specific object used to reference the display * or can be used by a audio port for native audio rendering */ typedef OMX_PTR OMX_NATIVE_DEVICETYPE; /** OMX_NATIVE_WINDOWTYPE is used to map a OMX video port to the * platform & operating specific object used to reference the window */ typedef OMX_PTR OMX_NATIVE_WINDOWTYPE; /** The OMX_VERSIONTYPE union is used to specify the version for a structure or component. For a component, the version is entirely specified by the component vendor. Components doing the same function from different vendors may or may not have the same version. For structures, the version shall be set by the entity that allocates the structure. For structures specified in the OMX 1.1 specification, the value of the version shall be set to 1.1.0.0 in all cases. Access to the OMX_VERSIONTYPE can be by a single 32 bit access (e.g. by nVersion) or by accessing one of the structure elements to, for example, check only the Major revision. */ typedef union OMX_VERSIONTYPE { struct { OMX_U8 nVersionMajor; /**< Major version accessor element */ OMX_U8 nVersionMinor; /**< Minor version accessor element */ OMX_U8 nRevision; /**< Revision version accessor element */ OMX_U8 nStep; /**< Step version accessor element */ } s; OMX_U32 nVersion; /**< 32 bit value to make accessing the version easily done in a single word size copy/compare operation */ } OMX_VERSIONTYPE; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_Video.h0100644 0000000 0000000 00000130002 13756501735 020355 0ustar000000000 0000000 /* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * 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. * ------------------------------------------------------------------- */ /** * Copyright (c) 2008 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** * @file OMX_Video.h - OpenMax IL version 1.1.2 * The structures is needed by Video components to exchange parameters * and configuration data with OMX components. */ #ifndef OMX_Video_h #define OMX_Video_h /** @defgroup video OpenMAX IL Video Domain * @ingroup iv * Structures for OpenMAX IL Video domain * @{ */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** * Each OMX header must include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include /** * Enumeration used to define the possible video compression codings. * NOTE: This essentially refers to file extensions. If the coding is * being used to specify the ENCODE type, then additional work * must be done to configure the exact flavor of the compression * to be used. For decode cases where the user application can * not differentiate between MPEG-4 and H.264 bit streams, it is * up to the codec to handle this. */ typedef enum OMX_VIDEO_CODINGTYPE { OMX_VIDEO_CodingUnused, /**< Value when coding is N/A */ OMX_VIDEO_CodingAutoDetect, /**< Autodetection of coding type */ OMX_VIDEO_CodingMPEG2, /**< AKA: H.262 */ OMX_VIDEO_CodingH263, /**< H.263 */ OMX_VIDEO_CodingMPEG4, /**< MPEG-4 */ OMX_VIDEO_CodingWMV, /**< all versions of Windows Media Video */ OMX_VIDEO_CodingRV, /**< all versions of Real Video */ OMX_VIDEO_CodingAVC, /**< H.264/AVC */ OMX_VIDEO_CodingMJPEG, /**< Motion JPEG */ OMX_VIDEO_CodingVP8, /**< Google VP8, formerly known as On2 VP8 */ OMX_VIDEO_CodingVP9, /**< Google VP9 */ OMX_VIDEO_CodingHEVC, /**< ITU H.265/HEVC */ OMX_VIDEO_CodingDolbyVision,/**< Dolby Vision */ OMX_VIDEO_CodingImageHEIC, /**< HEIF image encoded with HEVC */ OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_CodingMax = 0x7FFFFFFF } OMX_VIDEO_CODINGTYPE; /** * Data structure used to define a video path. The number of Video paths for * input and output will vary by type of the Video component. * * Input (aka Source) : zero Inputs, one Output, * Splitter : one Input, 2 or more Outputs, * Processing Element : one Input, one output, * Mixer : 2 or more inputs, one output, * Output (aka Sink) : one Input, zero outputs. * * The PortDefinition structure is used to define all of the parameters * necessary for the compliant component to setup an input or an output video * path. If additional vendor specific data is required, it should be * transmitted to the component using the CustomCommand function. Compliant * components will prepopulate this structure with optimal values during the * GetDefaultInitParams command. * * STRUCT MEMBERS: * cMIMEType : MIME type of data for the port * pNativeRender : Platform specific reference for a display if a * sync, otherwise this field is 0 * nFrameWidth : Width of frame to be used on channel if * uncompressed format is used. Use 0 for unknown, * don't care or variable * nFrameHeight : Height of frame to be used on channel if * uncompressed format is used. Use 0 for unknown, * don't care or variable * nStride : Number of bytes per span of an image * (i.e. indicates the number of bytes to get * from span N to span N+1, where negative stride * indicates the image is bottom up * nSliceHeight : Height used when encoding in slices * nBitrate : Bit rate of frame to be used on channel if * compressed format is used. Use 0 for unknown, * don't care or variable * xFramerate : Frame rate to be used on channel if uncompressed * format is used. Use 0 for unknown, don't care or * variable. Units are Q16 frames per second. * bFlagErrorConcealment : Turns on error concealment if it is supported by * the OMX component * eCompressionFormat : Compression format used in this instance of the * component. When OMX_VIDEO_CodingUnused is * specified, eColorFormat is used * eColorFormat : Decompressed format used by this component * pNativeWindow : Platform specific reference for a window object if a * display sink , otherwise this field is 0x0. */ typedef struct OMX_VIDEO_PORTDEFINITIONTYPE { OMX_STRING cMIMEType; OMX_NATIVE_DEVICETYPE pNativeRender; OMX_U32 nFrameWidth; OMX_U32 nFrameHeight; OMX_S32 nStride; OMX_U32 nSliceHeight; OMX_U32 nBitrate; OMX_U32 xFramerate; OMX_BOOL bFlagErrorConcealment; OMX_VIDEO_CODINGTYPE eCompressionFormat; OMX_COLOR_FORMATTYPE eColorFormat; OMX_NATIVE_WINDOWTYPE pNativeWindow; } OMX_VIDEO_PORTDEFINITIONTYPE; /** * Port format parameter. This structure is used to enumerate the various * data input/output format supported by the port. * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Indicates which port to set * nIndex : Indicates the enumeration index for the format from * 0x0 to N-1 * eCompressionFormat : Compression format used in this instance of the * component. When OMX_VIDEO_CodingUnused is specified, * eColorFormat is used * eColorFormat : Decompressed format used by this component * xFrameRate : Indicates the video frame rate in Q16 format */ typedef struct OMX_VIDEO_PARAM_PORTFORMATTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nIndex; OMX_VIDEO_CODINGTYPE eCompressionFormat; OMX_COLOR_FORMATTYPE eColorFormat; OMX_U32 xFramerate; } OMX_VIDEO_PARAM_PORTFORMATTYPE; /** * This is a structure for configuring video compression quantization * parameter values. Codecs may support different QP values for different * frame types. * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version info * nPortIndex : Port that this structure applies to * nQpI : QP value to use for index frames * nQpP : QP value to use for P frames * nQpB : QP values to use for bidirectional frames */ typedef struct OMX_VIDEO_PARAM_QUANTIZATIONTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nQpI; OMX_U32 nQpP; OMX_U32 nQpB; } OMX_VIDEO_PARAM_QUANTIZATIONTYPE; /** * Structure for configuration of video fast update parameters. * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version info * nPortIndex : Port that this structure applies to * bEnableVFU : Enable/Disable video fast update * nFirstGOB : Specifies the number of the first macroblock row * nFirstMB : specifies the first MB relative to the specified first GOB * nNumMBs : Specifies the number of MBs to be refreshed from nFirstGOB * and nFirstMB */ typedef struct OMX_VIDEO_PARAM_VIDEOFASTUPDATETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bEnableVFU; OMX_U32 nFirstGOB; OMX_U32 nFirstMB; OMX_U32 nNumMBs; } OMX_VIDEO_PARAM_VIDEOFASTUPDATETYPE; /** * Enumeration of possible bitrate control types */ typedef enum OMX_VIDEO_CONTROLRATETYPE { OMX_Video_ControlRateDisable, OMX_Video_ControlRateVariable, OMX_Video_ControlRateConstant, OMX_Video_ControlRateVariableSkipFrames, OMX_Video_ControlRateConstantSkipFrames, OMX_Video_ControlRateConstantQuality, OMX_Video_ControlRateKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_Video_ControlRateVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_Video_ControlRateMax = 0x7FFFFFFF } OMX_VIDEO_CONTROLRATETYPE; /** * Structure for configuring bitrate mode of a codec. * * STRUCT MEMBERS: * nSize : Size of the struct in bytes * nVersion : OMX spec version info * nPortIndex : Port that this struct applies to * eControlRate : Control rate type enum * nTargetBitrate : Target bitrate to encode with (used when eControlRate is * not OMX_Video_ControlRateConstantQuality) * nQualityFactor : Quality to encode with (used when eControlRate is * OMX_Video_ControlRateConstantQuality only) */ typedef struct OMX_VIDEO_PARAM_BITRATETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_CONTROLRATETYPE eControlRate; union { OMX_U32 nTargetBitrate; OMX_U32 nQualityFactor; }; } OMX_VIDEO_PARAM_BITRATETYPE; /** * Enumeration of possible motion vector (MV) types */ typedef enum OMX_VIDEO_MOTIONVECTORTYPE { OMX_Video_MotionVectorPixel, OMX_Video_MotionVectorHalfPel, OMX_Video_MotionVectorQuarterPel, OMX_Video_MotionVectorEighthPel, OMX_Video_MotionVectorKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_Video_MotionVectorVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_Video_MotionVectorMax = 0x7FFFFFFF } OMX_VIDEO_MOTIONVECTORTYPE; /** * Structure for configuring the number of motion vectors used as well * as their accuracy. * * STRUCT MEMBERS: * nSize : Size of the struct in bytes * nVersion : OMX spec version info * nPortIndex : port that this structure applies to * eAccuracy : Enumerated MV accuracy * bUnrestrictedMVs : Allow unrestricted MVs * bFourMV : Allow use of 4 MVs * sXSearchRange : Search range in horizontal direction for MVs * sYSearchRange : Search range in vertical direction for MVs */ typedef struct OMX_VIDEO_PARAM_MOTIONVECTORTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_MOTIONVECTORTYPE eAccuracy; OMX_BOOL bUnrestrictedMVs; OMX_BOOL bFourMV; OMX_S32 sXSearchRange; OMX_S32 sYSearchRange; } OMX_VIDEO_PARAM_MOTIONVECTORTYPE; /** * Enumeration of possible methods to use for Intra Refresh */ typedef enum OMX_VIDEO_INTRAREFRESHTYPE { OMX_VIDEO_IntraRefreshCyclic, OMX_VIDEO_IntraRefreshAdaptive, OMX_VIDEO_IntraRefreshBoth, OMX_VIDEO_IntraRefreshKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_IntraRefreshVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_IntraRefreshMax = 0x7FFFFFFF } OMX_VIDEO_INTRAREFRESHTYPE; /** * Structure for configuring intra refresh mode * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eRefreshMode : Cyclic, Adaptive, or Both * nAirMBs : Number of intra macroblocks to refresh in a frame when * AIR is enabled * nAirRef : Number of times a motion marked macroblock has to be * intra coded * nCirMBs : Number of consecutive macroblocks to be coded as "intra" * when CIR is enabled */ typedef struct OMX_VIDEO_PARAM_INTRAREFRESHTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_INTRAREFRESHTYPE eRefreshMode; OMX_U32 nAirMBs; OMX_U32 nAirRef; OMX_U32 nCirMBs; } OMX_VIDEO_PARAM_INTRAREFRESHTYPE; /** * Structure for enabling various error correction methods for video * compression. * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * bEnableHEC : Enable/disable header extension codes (HEC) * bEnableResync : Enable/disable resynchronization markers * nResynchMarkerSpacing : Resynch markers interval (in bits) to be * applied in the stream * bEnableDataPartitioning : Enable/disable data partitioning * bEnableRVLC : Enable/disable reversible variable length * coding */ typedef struct OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bEnableHEC; OMX_BOOL bEnableResync; OMX_U32 nResynchMarkerSpacing; OMX_BOOL bEnableDataPartitioning; OMX_BOOL bEnableRVLC; } OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE; /** * Configuration of variable block-size motion compensation (VBSMC) * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * b16x16 : Enable inter block search 16x16 * b16x8 : Enable inter block search 16x8 * b8x16 : Enable inter block search 8x16 * b8x8 : Enable inter block search 8x8 * b8x4 : Enable inter block search 8x4 * b4x8 : Enable inter block search 4x8 * b4x4 : Enable inter block search 4x4 */ typedef struct OMX_VIDEO_PARAM_VBSMCTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL b16x16; OMX_BOOL b16x8; OMX_BOOL b8x16; OMX_BOOL b8x8; OMX_BOOL b8x4; OMX_BOOL b4x8; OMX_BOOL b4x4; } OMX_VIDEO_PARAM_VBSMCTYPE; /** * H.263 profile types, each profile indicates support for various * performance bounds and different annexes. * * ENUMS: * Baseline : Baseline Profile: H.263 (V1), no optional modes * H320 Coding : H.320 Coding Efficiency Backward Compatibility * Profile: H.263+ (V2), includes annexes I, J, L.4 * and T * BackwardCompatible : Backward Compatibility Profile: H.263 (V1), * includes annex F * ISWV2 : Interactive Streaming Wireless Profile: H.263+ * (V2), includes annexes I, J, K and T * ISWV3 : Interactive Streaming Wireless Profile: H.263++ * (V3), includes profile 3 and annexes V and W.6.3.8 * HighCompression : Conversational High Compression Profile: H.263++ * (V3), includes profiles 1 & 2 and annexes D and U * Internet : Conversational Internet Profile: H.263++ (V3), * includes profile 5 and annex K * Interlace : Conversational Interlace Profile: H.263++ (V3), * includes profile 5 and annex W.6.3.11 * HighLatency : High Latency Profile: H.263++ (V3), includes * profile 6 and annexes O.1 and P.5 */ typedef enum OMX_VIDEO_H263PROFILETYPE { OMX_VIDEO_H263ProfileBaseline = 0x01, OMX_VIDEO_H263ProfileH320Coding = 0x02, OMX_VIDEO_H263ProfileBackwardCompatible = 0x04, OMX_VIDEO_H263ProfileISWV2 = 0x08, OMX_VIDEO_H263ProfileISWV3 = 0x10, OMX_VIDEO_H263ProfileHighCompression = 0x20, OMX_VIDEO_H263ProfileInternet = 0x40, OMX_VIDEO_H263ProfileInterlace = 0x80, OMX_VIDEO_H263ProfileHighLatency = 0x100, OMX_VIDEO_H263ProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_H263ProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_H263ProfileMax = 0x7FFFFFFF } OMX_VIDEO_H263PROFILETYPE; /** * H.263 level types, each level indicates support for various frame sizes, * bit rates, decoder frame rates. */ typedef enum OMX_VIDEO_H263LEVELTYPE { OMX_VIDEO_H263Level10 = 0x01, OMX_VIDEO_H263Level20 = 0x02, OMX_VIDEO_H263Level30 = 0x04, OMX_VIDEO_H263Level40 = 0x08, OMX_VIDEO_H263Level45 = 0x10, OMX_VIDEO_H263Level50 = 0x20, OMX_VIDEO_H263Level60 = 0x40, OMX_VIDEO_H263Level70 = 0x80, OMX_VIDEO_H263LevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_H263LevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_H263LevelMax = 0x7FFFFFFF } OMX_VIDEO_H263LEVELTYPE; /** * Specifies the picture type. These values should be OR'd to signal all * pictures types which are allowed. * * ENUMS: * Generic Picture Types: I, P and B * H.263 Specific Picture Types: SI and SP * H.264 Specific Picture Types: EI and EP * MPEG-4 Specific Picture Types: S */ typedef enum OMX_VIDEO_PICTURETYPE { OMX_VIDEO_PictureTypeI = 0x01, OMX_VIDEO_PictureTypeP = 0x02, OMX_VIDEO_PictureTypeB = 0x04, OMX_VIDEO_PictureTypeSI = 0x08, OMX_VIDEO_PictureTypeSP = 0x10, OMX_VIDEO_PictureTypeEI = 0x11, OMX_VIDEO_PictureTypeEP = 0x12, OMX_VIDEO_PictureTypeS = 0x14, OMX_VIDEO_PictureTypeKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_PictureTypeVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_PictureTypeMax = 0x7FFFFFFF } OMX_VIDEO_PICTURETYPE; /** * H.263 Params * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nPFrames : Number of P frames between each I frame * nBFrames : Number of B frames between each I frame * eProfile : H.263 profile(s) to use * eLevel : H.263 level(s) to use * bPLUSPTYPEAllowed : Indicating that it is allowed to use PLUSPTYPE * (specified in the 1998 version of H.263) to * indicate custom picture sizes or clock * frequencies * nAllowedPictureTypes : Specifies the picture types allowed in the * bitstream * bForceRoundingTypeToZero : value of the RTYPE bit (bit 6 of MPPTYPE) is * not constrained. It is recommended to change * the value of the RTYPE bit for each reference * picture in error-free communication * nPictureHeaderRepetition : Specifies the frequency of picture header * repetition * nGOBHeaderInterval : Specifies the interval of non-empty GOB * headers in units of GOBs */ typedef struct OMX_VIDEO_PARAM_H263TYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nPFrames; OMX_U32 nBFrames; OMX_VIDEO_H263PROFILETYPE eProfile; OMX_VIDEO_H263LEVELTYPE eLevel; OMX_BOOL bPLUSPTYPEAllowed; OMX_U32 nAllowedPictureTypes; OMX_BOOL bForceRoundingTypeToZero; OMX_U32 nPictureHeaderRepetition; OMX_U32 nGOBHeaderInterval; } OMX_VIDEO_PARAM_H263TYPE; /** * MPEG-2 profile types, each profile indicates support for various * performance bounds and different annexes. */ typedef enum OMX_VIDEO_MPEG2PROFILETYPE { OMX_VIDEO_MPEG2ProfileSimple = 0, /**< Simple Profile */ OMX_VIDEO_MPEG2ProfileMain, /**< Main Profile */ OMX_VIDEO_MPEG2Profile422, /**< 4:2:2 Profile */ OMX_VIDEO_MPEG2ProfileSNR, /**< SNR Profile */ OMX_VIDEO_MPEG2ProfileSpatial, /**< Spatial Profile */ OMX_VIDEO_MPEG2ProfileHigh, /**< High Profile */ OMX_VIDEO_MPEG2ProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_MPEG2ProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_MPEG2ProfileMax = 0x7FFFFFFF } OMX_VIDEO_MPEG2PROFILETYPE; /** * MPEG-2 level types, each level indicates support for various frame * sizes, bit rates, decoder frame rates. No need */ typedef enum OMX_VIDEO_MPEG2LEVELTYPE { OMX_VIDEO_MPEG2LevelLL = 0, /**< Low Level */ OMX_VIDEO_MPEG2LevelML, /**< Main Level */ OMX_VIDEO_MPEG2LevelH14, /**< High 1440 */ OMX_VIDEO_MPEG2LevelHL, /**< High Level */ OMX_VIDEO_MPEG2LevelHP, /**< HighP Level */ OMX_VIDEO_MPEG2LevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_MPEG2LevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_MPEG2LevelMax = 0x7FFFFFFF } OMX_VIDEO_MPEG2LEVELTYPE; /** * MPEG-2 params * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nPFrames : Number of P frames between each I frame * nBFrames : Number of B frames between each I frame * eProfile : MPEG-2 profile(s) to use * eLevel : MPEG-2 levels(s) to use */ typedef struct OMX_VIDEO_PARAM_MPEG2TYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nPFrames; OMX_U32 nBFrames; OMX_VIDEO_MPEG2PROFILETYPE eProfile; OMX_VIDEO_MPEG2LEVELTYPE eLevel; } OMX_VIDEO_PARAM_MPEG2TYPE; /** * MPEG-4 profile types, each profile indicates support for various * performance bounds and different annexes. * * ENUMS: * - Simple Profile, Levels 1-3 * - Simple Scalable Profile, Levels 1-2 * - Core Profile, Levels 1-2 * - Main Profile, Levels 2-4 * - N-bit Profile, Level 2 * - Scalable Texture Profile, Level 1 * - Simple Face Animation Profile, Levels 1-2 * - Simple Face and Body Animation (FBA) Profile, Levels 1-2 * - Basic Animated Texture Profile, Levels 1-2 * - Hybrid Profile, Levels 1-2 * - Advanced Real Time Simple Profiles, Levels 1-4 * - Core Scalable Profile, Levels 1-3 * - Advanced Coding Efficiency Profile, Levels 1-4 * - Advanced Core Profile, Levels 1-2 * - Advanced Scalable Texture, Levels 2-3 */ typedef enum OMX_VIDEO_MPEG4PROFILETYPE { OMX_VIDEO_MPEG4ProfileSimple = 0x01, OMX_VIDEO_MPEG4ProfileSimpleScalable = 0x02, OMX_VIDEO_MPEG4ProfileCore = 0x04, OMX_VIDEO_MPEG4ProfileMain = 0x08, OMX_VIDEO_MPEG4ProfileNbit = 0x10, OMX_VIDEO_MPEG4ProfileScalableTexture = 0x20, OMX_VIDEO_MPEG4ProfileSimpleFace = 0x40, OMX_VIDEO_MPEG4ProfileSimpleFBA = 0x80, OMX_VIDEO_MPEG4ProfileBasicAnimated = 0x100, OMX_VIDEO_MPEG4ProfileHybrid = 0x200, OMX_VIDEO_MPEG4ProfileAdvancedRealTime = 0x400, OMX_VIDEO_MPEG4ProfileCoreScalable = 0x800, OMX_VIDEO_MPEG4ProfileAdvancedCoding = 0x1000, OMX_VIDEO_MPEG4ProfileAdvancedCore = 0x2000, OMX_VIDEO_MPEG4ProfileAdvancedScalable = 0x4000, OMX_VIDEO_MPEG4ProfileAdvancedSimple = 0x8000, OMX_VIDEO_MPEG4ProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_MPEG4ProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_MPEG4ProfileMax = 0x7FFFFFFF } OMX_VIDEO_MPEG4PROFILETYPE; /** * MPEG-4 level types, each level indicates support for various frame * sizes, bit rates, decoder frame rates. No need */ typedef enum OMX_VIDEO_MPEG4LEVELTYPE { OMX_VIDEO_MPEG4Level0 = 0x01, /**< Level 0 */ OMX_VIDEO_MPEG4Level0b = 0x02, /**< Level 0b */ OMX_VIDEO_MPEG4Level1 = 0x04, /**< Level 1 */ OMX_VIDEO_MPEG4Level2 = 0x08, /**< Level 2 */ OMX_VIDEO_MPEG4Level3 = 0x10, /**< Level 3 */ /* normally levels are powers of 2s, but 3b was missed and levels must be properly ordered */ OMX_VIDEO_MPEG4Level3b = 0x18, /**< Level 3a */ OMX_VIDEO_MPEG4Level4 = 0x20, /**< Level 4 */ OMX_VIDEO_MPEG4Level4a = 0x40, /**< Level 4a */ OMX_VIDEO_MPEG4Level5 = 0x80, /**< Level 5 */ OMX_VIDEO_MPEG4Level6 = 0x100, /**< Level 6 */ OMX_VIDEO_MPEG4LevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_MPEG4LevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_MPEG4LevelMax = 0x7FFFFFFF } OMX_VIDEO_MPEG4LEVELTYPE; /** * MPEG-4 configuration. This structure handles configuration options * which are specific to MPEG4 algorithms * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nSliceHeaderSpacing : Number of macroblocks between slice header (H263+ * Annex K). Put zero if not used * bSVH : Enable Short Video Header mode * bGov : Flag to enable GOV * nPFrames : Number of P frames between each I frame (also called * GOV period) * nBFrames : Number of B frames between each I frame * nIDCVLCThreshold : Value of intra DC VLC threshold * bACPred : Flag to use ac prediction * nMaxPacketSize : Maximum size of packet in bytes. * nTimeIncRes : Used to pass VOP time increment resolution for MPEG4. * Interpreted as described in MPEG4 standard. * eProfile : MPEG-4 profile(s) to use. * eLevel : MPEG-4 level(s) to use. * nAllowedPictureTypes : Specifies the picture types allowed in the bitstream * nHeaderExtension : Specifies the number of consecutive video packet * headers within a VOP * bReversibleVLC : Specifies whether reversible variable length coding * is in use */ typedef struct OMX_VIDEO_PARAM_MPEG4TYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nSliceHeaderSpacing; OMX_BOOL bSVH; OMX_BOOL bGov; OMX_U32 nPFrames; OMX_U32 nBFrames; OMX_U32 nIDCVLCThreshold; OMX_BOOL bACPred; OMX_U32 nMaxPacketSize; OMX_U32 nTimeIncRes; OMX_VIDEO_MPEG4PROFILETYPE eProfile; OMX_VIDEO_MPEG4LEVELTYPE eLevel; OMX_U32 nAllowedPictureTypes; OMX_U32 nHeaderExtension; OMX_BOOL bReversibleVLC; } OMX_VIDEO_PARAM_MPEG4TYPE; /** * WMV Versions */ typedef enum OMX_VIDEO_WMVFORMATTYPE { OMX_VIDEO_WMVFormatUnused = 0x01, /**< Format unused or unknown */ OMX_VIDEO_WMVFormat7 = 0x02, /**< Windows Media Video format 7 */ OMX_VIDEO_WMVFormat8 = 0x04, /**< Windows Media Video format 8 */ OMX_VIDEO_WMVFormat9 = 0x08, /**< Windows Media Video format 9 */ OMX_VIDEO_WMFFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_WMFFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_WMVFormatMax = 0x7FFFFFFF } OMX_VIDEO_WMVFORMATTYPE; /** * WMV Params * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eFormat : Version of WMV stream / data */ typedef struct OMX_VIDEO_PARAM_WMVTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_WMVFORMATTYPE eFormat; } OMX_VIDEO_PARAM_WMVTYPE; /** * Real Video Version */ typedef enum OMX_VIDEO_RVFORMATTYPE { OMX_VIDEO_RVFormatUnused = 0, /**< Format unused or unknown */ OMX_VIDEO_RVFormat8, /**< Real Video format 8 */ OMX_VIDEO_RVFormat9, /**< Real Video format 9 */ OMX_VIDEO_RVFormatG2, /**< Real Video Format G2 */ OMX_VIDEO_RVFormatKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_RVFormatVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_RVFormatMax = 0x7FFFFFFF } OMX_VIDEO_RVFORMATTYPE; /** * Real Video Params * * STUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * eFormat : Version of RV stream / data * nBitsPerPixel : Bits per pixel coded in the frame * nPaddedWidth : Padded width in pixel of a video frame * nPaddedHeight : Padded Height in pixels of a video frame * nFrameRate : Rate of video in frames per second * nBitstreamFlags : Flags which internal information about the bitstream * nBitstreamVersion : Bitstream version * nMaxEncodeFrameSize: Max encoded frame size * bEnablePostFilter : Turn on/off post filter * bEnableTemporalInterpolation : Turn on/off temporal interpolation * bEnableLatencyMode : When enabled, the decoder does not display a decoded * frame until it has detected that no enhancement layer * frames or dependent B frames will be coming. This * detection usually occurs when a subsequent non-B * frame is encountered */ typedef struct OMX_VIDEO_PARAM_RVTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_RVFORMATTYPE eFormat; OMX_U16 nBitsPerPixel; OMX_U16 nPaddedWidth; OMX_U16 nPaddedHeight; OMX_U32 nFrameRate; OMX_U32 nBitstreamFlags; OMX_U32 nBitstreamVersion; OMX_U32 nMaxEncodeFrameSize; OMX_BOOL bEnablePostFilter; OMX_BOOL bEnableTemporalInterpolation; OMX_BOOL bEnableLatencyMode; } OMX_VIDEO_PARAM_RVTYPE; /** * AVC profile types, each profile indicates support for various * performance bounds and different annexes. */ typedef enum OMX_VIDEO_AVCPROFILETYPE { OMX_VIDEO_AVCProfileBaseline = 0x01, /**< Baseline profile */ OMX_VIDEO_AVCProfileMain = 0x02, /**< Main profile */ OMX_VIDEO_AVCProfileExtended = 0x04, /**< Extended profile */ OMX_VIDEO_AVCProfileHigh = 0x08, /**< High profile */ OMX_VIDEO_AVCProfileHigh10 = 0x10, /**< High 10 profile */ OMX_VIDEO_AVCProfileHigh422 = 0x20, /**< High 4:2:2 profile */ OMX_VIDEO_AVCProfileHigh444 = 0x40, /**< High 4:4:4 profile */ OMX_VIDEO_AVCProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_AVCProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_AVCProfileMax = 0x7FFFFFFF } OMX_VIDEO_AVCPROFILETYPE; /** * AVC level types, each level indicates support for various frame sizes, * bit rates, decoder frame rates. No need */ typedef enum OMX_VIDEO_AVCLEVELTYPE { OMX_VIDEO_AVCLevel1 = 0x01, /**< Level 1 */ OMX_VIDEO_AVCLevel1b = 0x02, /**< Level 1b */ OMX_VIDEO_AVCLevel11 = 0x04, /**< Level 1.1 */ OMX_VIDEO_AVCLevel12 = 0x08, /**< Level 1.2 */ OMX_VIDEO_AVCLevel13 = 0x10, /**< Level 1.3 */ OMX_VIDEO_AVCLevel2 = 0x20, /**< Level 2 */ OMX_VIDEO_AVCLevel21 = 0x40, /**< Level 2.1 */ OMX_VIDEO_AVCLevel22 = 0x80, /**< Level 2.2 */ OMX_VIDEO_AVCLevel3 = 0x100, /**< Level 3 */ OMX_VIDEO_AVCLevel31 = 0x200, /**< Level 3.1 */ OMX_VIDEO_AVCLevel32 = 0x400, /**< Level 3.2 */ OMX_VIDEO_AVCLevel4 = 0x800, /**< Level 4 */ OMX_VIDEO_AVCLevel41 = 0x1000, /**< Level 4.1 */ OMX_VIDEO_AVCLevel42 = 0x2000, /**< Level 4.2 */ OMX_VIDEO_AVCLevel5 = 0x4000, /**< Level 5 */ OMX_VIDEO_AVCLevel51 = 0x8000, /**< Level 5.1 */ OMX_VIDEO_AVCLevel52 = 0x10000, /**< Level 5.2 */ OMX_VIDEO_AVCLevel6 = 0x20000, /**< Level 6 */ OMX_VIDEO_AVCLevel61 = 0x40000, /**< Level 6.1 */ OMX_VIDEO_AVCLevel62 = 0x80000, /**< Level 6.2 */ OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF } OMX_VIDEO_AVCLEVELTYPE; /** * AVC loop filter modes * * OMX_VIDEO_AVCLoopFilterEnable : Enable * OMX_VIDEO_AVCLoopFilterDisable : Disable * OMX_VIDEO_AVCLoopFilterDisableSliceBoundary : Disabled on slice boundaries */ typedef enum OMX_VIDEO_AVCLOOPFILTERTYPE { OMX_VIDEO_AVCLoopFilterEnable = 0, OMX_VIDEO_AVCLoopFilterDisable, OMX_VIDEO_AVCLoopFilterDisableSliceBoundary, OMX_VIDEO_AVCLoopFilterKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_AVCLoopFilterVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_AVCLoopFilterMax = 0x7FFFFFFF } OMX_VIDEO_AVCLOOPFILTERTYPE; /** * AVC params * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nSliceHeaderSpacing : Number of macroblocks between slice header, put * zero if not used * nPFrames : Number of P frames between each I frame * nBFrames : Number of B frames between each I frame * bUseHadamard : Enable/disable Hadamard transform * nRefFrames : Max number of reference frames to use for inter * motion search (1-16) * nRefIdxTrailing : Pic param set ref frame index (index into ref * frame buffer of trailing frames list), B frame * support * nRefIdxForward : Pic param set ref frame index (index into ref * frame buffer of forward frames list), B frame * support * bEnableUEP : Enable/disable unequal error protection. This * is only valid of data partitioning is enabled. * bEnableFMO : Enable/disable flexible macroblock ordering * bEnableASO : Enable/disable arbitrary slice ordering * bEnableRS : Enable/disable sending of redundant slices * eProfile : AVC profile(s) to use * eLevel : AVC level(s) to use * nAllowedPictureTypes : Specifies the picture types allowed in the * bitstream * bFrameMBsOnly : specifies that every coded picture of the * coded video sequence is a coded frame * containing only frame macroblocks * bMBAFF : Enable/disable switching between frame and * field macroblocks within a picture * bEntropyCodingCABAC : Entropy decoding method to be applied for the * syntax elements for which two descriptors appear * in the syntax tables * bWeightedPPrediction : Enable/disable weighted prediction shall not * be applied to P and SP slices * nWeightedBipredicitonMode : Default weighted prediction is applied to B * slices * bconstIpred : Enable/disable intra prediction * bDirect8x8Inference : Specifies the method used in the derivation * process for luma motion vectors for B_Skip, * B_Direct_16x16 and B_Direct_8x8 as specified * in subclause 8.4.1.2 of the AVC spec * bDirectSpatialTemporal : Flag indicating spatial or temporal direct * mode used in B slice coding (related to * bDirect8x8Inference) . Spatial direct mode is * more common and should be the default. * nCabacInitIdx : Index used to init CABAC contexts * eLoopFilterMode : Enable/disable loop filter */ typedef struct OMX_VIDEO_PARAM_AVCTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nSliceHeaderSpacing; OMX_U32 nPFrames; OMX_U32 nBFrames; OMX_BOOL bUseHadamard; OMX_U32 nRefFrames; OMX_U32 nRefIdx10ActiveMinus1; OMX_U32 nRefIdx11ActiveMinus1; OMX_BOOL bEnableUEP; OMX_BOOL bEnableFMO; OMX_BOOL bEnableASO; OMX_BOOL bEnableRS; OMX_VIDEO_AVCPROFILETYPE eProfile; OMX_VIDEO_AVCLEVELTYPE eLevel; OMX_U32 nAllowedPictureTypes; OMX_BOOL bFrameMBsOnly; OMX_BOOL bMBAFF; OMX_BOOL bEntropyCodingCABAC; OMX_BOOL bWeightedPPrediction; OMX_U32 nWeightedBipredicitonMode; OMX_BOOL bconstIpred ; OMX_BOOL bDirect8x8Inference; OMX_BOOL bDirectSpatialTemporal; OMX_U32 nCabacInitIdc; OMX_VIDEO_AVCLOOPFILTERTYPE eLoopFilterMode; } OMX_VIDEO_PARAM_AVCTYPE; typedef struct OMX_VIDEO_PARAM_PROFILELEVELTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 eProfile; /**< type is OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE, or OMX_VIDEO_MPEG4PROFILETYPE depending on context */ OMX_U32 eLevel; /**< type is OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE, or OMX_VIDEO_MPEG4PROFILETYPE depending on context */ OMX_U32 nProfileIndex; /**< Used to query for individual profile support information, This parameter is valid only for OMX_IndexParamVideoProfileLevelQuerySupported index, For all other indices this parameter is to be ignored. */ } OMX_VIDEO_PARAM_PROFILELEVELTYPE; /** * Structure for dynamically configuring bitrate mode of a codec. * * STRUCT MEMBERS: * nSize : Size of the struct in bytes * nVersion : OMX spec version info * nPortIndex : Port that this struct applies to * nEncodeBitrate : Target average bitrate to be generated in bps */ typedef struct OMX_VIDEO_CONFIG_BITRATETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nEncodeBitrate; } OMX_VIDEO_CONFIG_BITRATETYPE; /** * Defines Encoder Frame Rate setting * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * xEncodeFramerate : Encoding framerate represented in Q16 format */ typedef struct OMX_CONFIG_FRAMERATETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 xEncodeFramerate; /* Q16 format */ } OMX_CONFIG_FRAMERATETYPE; typedef struct OMX_CONFIG_INTRAREFRESHVOPTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL IntraRefreshVOP; } OMX_CONFIG_INTRAREFRESHVOPTYPE; typedef struct OMX_CONFIG_MACROBLOCKERRORMAPTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nErrMapSize; /* Size of the Error Map in bytes */ OMX_U8 ErrMap[1]; /* Error map hint */ } OMX_CONFIG_MACROBLOCKERRORMAPTYPE; typedef struct OMX_CONFIG_MBERRORREPORTINGTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bEnabled; } OMX_CONFIG_MBERRORREPORTINGTYPE; typedef struct OMX_PARAM_MACROBLOCKSTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nMacroblocks; } OMX_PARAM_MACROBLOCKSTYPE; /** * AVC Slice Mode modes * * OMX_VIDEO_SLICEMODE_AVCDefault : Normal frame encoding, one slice per frame * OMX_VIDEO_SLICEMODE_AVCMBSlice : NAL mode, number of MBs per frame * OMX_VIDEO_SLICEMODE_AVCByteSlice : NAL mode, number of bytes per frame */ typedef enum OMX_VIDEO_AVCSLICEMODETYPE { OMX_VIDEO_SLICEMODE_AVCDefault = 0, OMX_VIDEO_SLICEMODE_AVCMBSlice, OMX_VIDEO_SLICEMODE_AVCByteSlice, OMX_VIDEO_SLICEMODE_AVCKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_SLICEMODE_AVCVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_SLICEMODE_AVCLevelMax = 0x7FFFFFFF } OMX_VIDEO_AVCSLICEMODETYPE; /** * AVC FMO Slice Mode Params * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nNumSliceGroups : Specifies the number of slice groups * nSliceGroupMapType : Specifies the type of slice groups * eSliceMode : Specifies the type of slice */ typedef struct OMX_VIDEO_PARAM_AVCSLICEFMO { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U8 nNumSliceGroups; OMX_U8 nSliceGroupMapType; OMX_VIDEO_AVCSLICEMODETYPE eSliceMode; } OMX_VIDEO_PARAM_AVCSLICEFMO; /** * AVC IDR Period Configs * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nIDRPeriod : Specifies periodicity of IDR frames * nPFrames : Specifies internal of coding Intra frames */ typedef struct OMX_VIDEO_CONFIG_AVCINTRAPERIOD { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nIDRPeriod; OMX_U32 nPFrames; } OMX_VIDEO_CONFIG_AVCINTRAPERIOD; /** * AVC NAL Size Configs * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nNaluBytes : Specifies the NAL unit size */ typedef struct OMX_VIDEO_CONFIG_NALSIZE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nNaluBytes; } OMX_VIDEO_CONFIG_NALSIZE; /** @} */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* File EOF */ headers/media_plugin/media/openmax/OMX_VideoExt.h0100644 0000000 0000000 00000047706 13756501735 021060 0ustar000000000 0000000 /* * Copyright (c) 2010 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject * to the following conditions: * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** OMX_VideoExt.h - OpenMax IL version 1.1.2 * The OMX_VideoExt header file contains extensions to the * definitions used by both the application and the component to * access video items. */ #ifndef OMX_VideoExt_h #define OMX_VideoExt_h #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Each OMX header shall include all required header files to allow the * header to compile without errors. The includes below are required * for this header file to compile successfully */ #include /** NALU Formats */ typedef enum OMX_NALUFORMATSTYPE { OMX_NaluFormatStartCodes = 1, OMX_NaluFormatOneNaluPerBuffer = 2, OMX_NaluFormatOneByteInterleaveLength = 4, OMX_NaluFormatTwoByteInterleaveLength = 8, OMX_NaluFormatFourByteInterleaveLength = 16, OMX_NaluFormatCodingMax = 0x7FFFFFFF } OMX_NALUFORMATSTYPE; /** NAL Stream Format */ typedef struct OMX_NALSTREAMFORMATTYPE{ OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_NALUFORMATSTYPE eNaluFormat; } OMX_NALSTREAMFORMATTYPE; /** AVC additional profiles */ typedef enum OMX_VIDEO_AVCPROFILEEXTTYPE { OMX_VIDEO_AVCProfileConstrainedBaseline = 0x10000, /**< Constrained baseline profile */ OMX_VIDEO_AVCProfileConstrainedHigh = 0x80000, /**< Constrained high profile */ } OMX_VIDEO_AVCPROFILEEXTTYPE; /** VP8 profiles */ typedef enum OMX_VIDEO_VP8PROFILETYPE { OMX_VIDEO_VP8ProfileMain = 0x01, OMX_VIDEO_VP8ProfileUnknown = 0x6EFFFFFF, OMX_VIDEO_VP8ProfileMax = 0x7FFFFFFF } OMX_VIDEO_VP8PROFILETYPE; /** VP8 levels */ typedef enum OMX_VIDEO_VP8LEVELTYPE { OMX_VIDEO_VP8Level_Version0 = 0x01, OMX_VIDEO_VP8Level_Version1 = 0x02, OMX_VIDEO_VP8Level_Version2 = 0x04, OMX_VIDEO_VP8Level_Version3 = 0x08, OMX_VIDEO_VP8LevelUnknown = 0x6EFFFFFF, OMX_VIDEO_VP8LevelMax = 0x7FFFFFFF } OMX_VIDEO_VP8LEVELTYPE; /** VP8 Param */ typedef struct OMX_VIDEO_PARAM_VP8TYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_VP8PROFILETYPE eProfile; OMX_VIDEO_VP8LEVELTYPE eLevel; OMX_U32 nDCTPartitions; OMX_BOOL bErrorResilientMode; } OMX_VIDEO_PARAM_VP8TYPE; /** Structure for configuring VP8 reference frames */ typedef struct OMX_VIDEO_VP8REFERENCEFRAMETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bPreviousFrameRefresh; OMX_BOOL bGoldenFrameRefresh; OMX_BOOL bAlternateFrameRefresh; OMX_BOOL bUsePreviousFrame; OMX_BOOL bUseGoldenFrame; OMX_BOOL bUseAlternateFrame; } OMX_VIDEO_VP8REFERENCEFRAMETYPE; /** Structure for querying VP8 reference frame type */ typedef struct OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bIsIntraFrame; OMX_BOOL bIsGoldenOrAlternateFrame; } OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE; /** Maximum number of VP8 temporal layers */ #define OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS 3 /** VP8 temporal layer patterns */ typedef enum OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE { OMX_VIDEO_VPXTemporalLayerPatternNone = 0, OMX_VIDEO_VPXTemporalLayerPatternWebRTC = 1, OMX_VIDEO_VPXTemporalLayerPatternMax = 0x7FFFFFFF } OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE; /** * Android specific VP8/VP9 encoder params * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nKeyFrameInterval : Key frame interval in frames * eTemporalPattern : Type of temporal layer pattern * nTemporalLayerCount : Number of temporal coding layers * nTemporalLayerBitrateRatio : Bitrate ratio allocation between temporal * streams in percentage * nMinQuantizer : Minimum (best quality) quantizer * nMaxQuantizer : Maximum (worst quality) quantizer */ typedef struct OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nKeyFrameInterval; // distance between consecutive key_frames (including one // of the key_frames). 0 means interval is unspecified and // can be freely chosen by the codec. 1 means a stream of // only key_frames. OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE eTemporalPattern; OMX_U32 nTemporalLayerCount; OMX_U32 nTemporalLayerBitrateRatio[OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS]; OMX_U32 nMinQuantizer; OMX_U32 nMaxQuantizer; } OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE; /** VP9 profiles */ typedef enum OMX_VIDEO_VP9PROFILETYPE { OMX_VIDEO_VP9Profile0 = 0x1, OMX_VIDEO_VP9Profile1 = 0x2, OMX_VIDEO_VP9Profile2 = 0x4, OMX_VIDEO_VP9Profile3 = 0x8, // HDR profiles also support passing HDR metadata OMX_VIDEO_VP9Profile2HDR = 0x1000, OMX_VIDEO_VP9Profile3HDR = 0x2000, OMX_VIDEO_VP9Profile2HDR10Plus = 0x4000, OMX_VIDEO_VP9Profile3HDR10Plus = 0x8000, OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF, OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF } OMX_VIDEO_VP9PROFILETYPE; /** VP9 levels */ typedef enum OMX_VIDEO_VP9LEVELTYPE { OMX_VIDEO_VP9Level1 = 0x1, OMX_VIDEO_VP9Level11 = 0x2, OMX_VIDEO_VP9Level2 = 0x4, OMX_VIDEO_VP9Level21 = 0x8, OMX_VIDEO_VP9Level3 = 0x10, OMX_VIDEO_VP9Level31 = 0x20, OMX_VIDEO_VP9Level4 = 0x40, OMX_VIDEO_VP9Level41 = 0x80, OMX_VIDEO_VP9Level5 = 0x100, OMX_VIDEO_VP9Level51 = 0x200, OMX_VIDEO_VP9Level52 = 0x400, OMX_VIDEO_VP9Level6 = 0x800, OMX_VIDEO_VP9Level61 = 0x1000, OMX_VIDEO_VP9Level62 = 0x2000, OMX_VIDEO_VP9LevelUnknown = 0x6EFFFFFF, OMX_VIDEO_VP9LevelMax = 0x7FFFFFFF } OMX_VIDEO_VP9LEVELTYPE; /** * VP9 Parameters. * Encoder specific parameters (decoders should ignore these fields): * - bErrorResilientMode * - nTileRows * - nTileColumns * - bEnableFrameParallelDecoding */ typedef struct OMX_VIDEO_PARAM_VP9TYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_VP9PROFILETYPE eProfile; OMX_VIDEO_VP9LEVELTYPE eLevel; OMX_BOOL bErrorResilientMode; OMX_U32 nTileRows; OMX_U32 nTileColumns; OMX_BOOL bEnableFrameParallelDecoding; } OMX_VIDEO_PARAM_VP9TYPE; /** HEVC Profile enum type */ typedef enum OMX_VIDEO_HEVCPROFILETYPE { OMX_VIDEO_HEVCProfileUnknown = 0x0, OMX_VIDEO_HEVCProfileMain = 0x1, OMX_VIDEO_HEVCProfileMain10 = 0x2, OMX_VIDEO_HEVCProfileMainStill = 0x4, // Main10 profile with HDR SEI support. OMX_VIDEO_HEVCProfileMain10HDR10 = 0x1000, OMX_VIDEO_HEVCProfileMain10HDR10Plus = 0x2000, OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF } OMX_VIDEO_HEVCPROFILETYPE; /** HEVC Level enum type */ typedef enum OMX_VIDEO_HEVCLEVELTYPE { OMX_VIDEO_HEVCLevelUnknown = 0x0, OMX_VIDEO_HEVCMainTierLevel1 = 0x1, OMX_VIDEO_HEVCHighTierLevel1 = 0x2, OMX_VIDEO_HEVCMainTierLevel2 = 0x4, OMX_VIDEO_HEVCHighTierLevel2 = 0x8, OMX_VIDEO_HEVCMainTierLevel21 = 0x10, OMX_VIDEO_HEVCHighTierLevel21 = 0x20, OMX_VIDEO_HEVCMainTierLevel3 = 0x40, OMX_VIDEO_HEVCHighTierLevel3 = 0x80, OMX_VIDEO_HEVCMainTierLevel31 = 0x100, OMX_VIDEO_HEVCHighTierLevel31 = 0x200, OMX_VIDEO_HEVCMainTierLevel4 = 0x400, OMX_VIDEO_HEVCHighTierLevel4 = 0x800, OMX_VIDEO_HEVCMainTierLevel41 = 0x1000, OMX_VIDEO_HEVCHighTierLevel41 = 0x2000, OMX_VIDEO_HEVCMainTierLevel5 = 0x4000, OMX_VIDEO_HEVCHighTierLevel5 = 0x8000, OMX_VIDEO_HEVCMainTierLevel51 = 0x10000, OMX_VIDEO_HEVCHighTierLevel51 = 0x20000, OMX_VIDEO_HEVCMainTierLevel52 = 0x40000, OMX_VIDEO_HEVCHighTierLevel52 = 0x80000, OMX_VIDEO_HEVCMainTierLevel6 = 0x100000, OMX_VIDEO_HEVCHighTierLevel6 = 0x200000, OMX_VIDEO_HEVCMainTierLevel61 = 0x400000, OMX_VIDEO_HEVCHighTierLevel61 = 0x800000, OMX_VIDEO_HEVCMainTierLevel62 = 0x1000000, OMX_VIDEO_HEVCHighTierLevel62 = 0x2000000, OMX_VIDEO_HEVCHighTiermax = 0x7FFFFFFF } OMX_VIDEO_HEVCLEVELTYPE; /** Structure for controlling HEVC video encoding */ typedef struct OMX_VIDEO_PARAM_HEVCTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_HEVCPROFILETYPE eProfile; OMX_VIDEO_HEVCLEVELTYPE eLevel; OMX_U32 nKeyFrameInterval; // distance between consecutive I-frames (including one // of the I frames). 0 means interval is unspecified and // can be freely chosen by the codec. 1 means a stream of // only I frames. } OMX_VIDEO_PARAM_HEVCTYPE; /** Structure to define if dependent slice segments should be used */ typedef struct OMX_VIDEO_SLICESEGMENTSTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bDepedentSegments; OMX_BOOL bEnableLoopFilterAcrossSlices; } OMX_VIDEO_SLICESEGMENTSTYPE; /** Structure to return timestamps of rendered output frames as well as EOS * for tunneled components. */ typedef struct OMX_VIDEO_RENDEREVENTTYPE { OMX_S64 nMediaTimeUs; // timestamp of rendered video frame OMX_S64 nSystemTimeNs; // system monotonic time at the time frame was rendered // Use INT64_MAX for nMediaTimeUs to signal that the EOS // has been reached. In this case, nSystemTimeNs MUST be // the system time when the last frame was rendered. // This MUST be done in addition to returning (and // following) the render information for the last frame. } OMX_VIDEO_RENDEREVENTTYPE; /** Dolby Vision Profile enum type */ typedef enum OMX_VIDEO_DOLBYVISIONPROFILETYPE { OMX_VIDEO_DolbyVisionProfileUnknown = 0x0, OMX_VIDEO_DolbyVisionProfileDvavPer = 0x1, OMX_VIDEO_DolbyVisionProfileDvavPen = 0x2, OMX_VIDEO_DolbyVisionProfileDvheDer = 0x4, OMX_VIDEO_DolbyVisionProfileDvheDen = 0x8, OMX_VIDEO_DolbyVisionProfileDvheDtr = 0x10, OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20, OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40, OMX_VIDEO_DolbyVisionProfileDvheDtb = 0x80, OMX_VIDEO_DolbyVisionProfileDvheSt = 0x100, OMX_VIDEO_DolbyVisionProfileDvavSe = 0x200, OMX_VIDEO_DolbyVisionProfileMax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONPROFILETYPE; /** Dolby Vision Level enum type */ typedef enum OMX_VIDEO_DOLBYVISIONLEVELTYPE { OMX_VIDEO_DolbyVisionLevelUnknown = 0x0, OMX_VIDEO_DolbyVisionLevelHd24 = 0x1, OMX_VIDEO_DolbyVisionLevelHd30 = 0x2, OMX_VIDEO_DolbyVisionLevelFhd24 = 0x4, OMX_VIDEO_DolbyVisionLevelFhd30 = 0x8, OMX_VIDEO_DolbyVisionLevelFhd60 = 0x10, OMX_VIDEO_DolbyVisionLevelUhd24 = 0x20, OMX_VIDEO_DolbyVisionLevelUhd30 = 0x40, OMX_VIDEO_DolbyVisionLevelUhd48 = 0x80, OMX_VIDEO_DolbyVisionLevelUhd60 = 0x100, OMX_VIDEO_DolbyVisionLevelmax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONLEVELTYPE; /** * Structure for configuring video compression intra refresh period * * STRUCT MEMBERS: * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nRefreshPeriod : Intra refreh period in frames. Value 0 means disable intra refresh */ typedef struct OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_U32 nRefreshPeriod; } OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE; /** Maximum number of temporal layers supported by AVC/HEVC */ #define OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS 8 /** temporal layer patterns */ typedef enum OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE { OMX_VIDEO_AndroidTemporalLayeringPatternNone = 0, // pattern as defined by WebRTC OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC = 1 << 0, // pattern where frames in any layer other than the base layer only depend on at most the very // last frame from each preceding layer (other than the base layer.) OMX_VIDEO_AndroidTemporalLayeringPatternAndroid = 1 << 1, } OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE; /** * Android specific param for configuration of temporal layering. * Android only supports temporal layering where successive layers each double the * previous layer's framerate. * NOTE: Reading this parameter at run-time SHALL return actual run-time values. * * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to (output port for encoders) * eSupportedPatterns : A bitmask of supported layering patterns * nLayerCountMax : Max number of temporal coding layers supported * by the encoder (must be at least 1, 1 meaning temporal layering * is NOT supported) * nBLayerCountMax : Max number of layers that can contain B frames * (0) to (nLayerCountMax - 1) * ePattern : Layering pattern. * nPLayerCountActual : Number of temporal layers to be coded with non-B frames, * starting from and including the base-layer. * (1 to nLayerCountMax - nBLayerCountActual) * If nPLayerCountActual is 1 and nBLayerCountActual is 0, temporal * layering is disabled. Otherwise, it is enabled. * nBLayerCountActual : Number of temporal layers to be coded with B frames, * starting after non-B layers. * (0 to nBLayerCountMax) * bBitrateRatiosSpecified : Flag to indicate if layer-wise bitrate * distribution is specified. * nBitrateRatios : Bitrate ratio (100 based) per layer (index 0 is base layer). * Honored if bBitrateRatiosSpecified is set. * i.e for 4 layers with desired distribution (25% 25% 25% 25%), * nBitrateRatio = {25, 50, 75, 100, ... } * Values in indices not less than 'the actual number of layers * minus 1' MAY be ignored and assumed to be 100. */ typedef struct OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE eSupportedPatterns; OMX_U32 nLayerCountMax; OMX_U32 nBLayerCountMax; OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern; OMX_U32 nPLayerCountActual; OMX_U32 nBLayerCountActual; OMX_BOOL bBitrateRatiosSpecified; OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS]; } OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE; /** * Android specific config for changing the temporal-layer count or * bitrate-distribution at run-time. * * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to (output port for encoders) * ePattern : Layering pattern. * nPLayerCountActual : Number of temporal layers to be coded with non-B frames. * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) * nBLayerCountActual : Number of temporal layers to be coded with B frames. * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) * bBitrateRatiosSpecified : Flag to indicate if layer-wise bitrate * distribution is specified. * nBitrateRatios : Bitrate ratio (100 based, Q16 values) per layer (0 is base layer). * Honored if bBitrateRatiosSpecified is set. * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) */ typedef struct OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern; OMX_U32 nPLayerCountActual; OMX_U32 nBLayerCountActual; OMX_BOOL bBitrateRatiosSpecified; OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS]; } OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE; /** * Android specific param for specifying image grid layout information for image encoding * use cases, corresponding to index OMX_IndexParamVideoAndroidImageGrid. * * OMX_VIDEO_CodingImageHEIC encoders must handle this param type. When this param is set * on the component with bEnabled set to true, nTileWidth, nTileHeight, nGridRows, * nGridCols indicates the desired grid config by the client. The component can use this * as a heuristic, and is free to choose any suitable grid configs. The client shall * always get the actual from the component after the param is set. Encoder will receive * each input image in full, and shall encode it into tiles in row-major, top-row first, * left-to-right order, and send each encoded tile in a separate output buffer. All output * buffers for the same input buffer shall carry the same timestamp as the input buffer. * If the input buffer is marked EOS, the EOS should only appear on the last output buffer * for that input buffer. * * OMX_VIDEO_CodingHEVC encoders might also receive this param when it's used for image * encoding, although in this case the param only serves as a hint. The encoder will * receive the input image tiles in row-major, top-row first, left-to-right order. * The grid config can be used for quality control, or optimizations. * * If this param is not set, the component shall assume that grid option is disabled. * * nSize : Size of the structure in bytes * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to (output port for encoders) * bEnabled : Whether grid is enabled. If true, the other parameters * specifies the grid config; otherwise they shall be ignored. * nTileWidth : Width of each tile. * nTileHeight : Height of each tile. * nGridRows : Number of rows in the grid. * nGridCols : Number of cols in the grid. */ typedef struct OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_BOOL bEnabled; OMX_U32 nTileWidth; OMX_U32 nTileHeight; OMX_U32 nGridRows; OMX_U32 nGridCols; } OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* OMX_VideoExt_h */ /* File EOF */ include/0040755 0000000 0000000 00000000000 13756501735 011247 5ustar000000000 0000000 include/OWNERS0100644 0000000 0000000 00000000427 13756501735 012207 0ustar000000000 0000000 alexeykuzmin@google.com dangittik@google.com jreck@google.com lajos@google.com mathias@google.com michaelwr@google.com nona@google.com racarr@google.com romainguy@android.com santoscordon@google.com stoza@google.com svv@google.com # For multinetwork.h only. lorenzo@google.com include/android/0040755 0000000 0000000 00000000000 13756501735 012667 5ustar000000000 0000000 include/android/asset_manager.h0100644 0000000 0000000 00000015030 13756501735 015645 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Asset * @{ */ /** * @file asset_manager.h */ #ifndef ANDROID_ASSET_MANAGER_H #define ANDROID_ASSET_MANAGER_H #include #include #ifdef __cplusplus extern "C" { #endif #if !defined(__ANDROID__) && !defined(__RENAME_IF_FILE_OFFSET64) #define __RENAME_IF_FILE_OFFSET64(x) #endif struct AAssetManager; /** * {@link AAssetManager} provides access to an application's raw assets by * creating {@link AAsset} objects. * * AAssetManager is a wrapper to the low-level native implementation * of the java {@link AAssetManager}, a pointer can be obtained using * AAssetManager_fromJava(). * * The asset hierarchy may be examined like a filesystem, using * {@link AAssetDir} objects to peruse a single directory. * * A native {@link AAssetManager} pointer may be shared across multiple threads. */ typedef struct AAssetManager AAssetManager; struct AAssetDir; /** * {@link AAssetDir} provides access to a chunk of the asset hierarchy as if * it were a single directory. The contents are populated by the * {@link AAssetManager}. * * The list of files will be sorted in ascending order by ASCII value. */ typedef struct AAssetDir AAssetDir; struct AAsset; /** * {@link AAsset} provides access to a read-only asset. * * {@link AAsset} objects are NOT thread-safe, and should not be shared across * threads. */ typedef struct AAsset AAsset; /** Available access modes for opening assets with {@link AAssetManager_open} */ enum { /** No specific information about how data will be accessed. **/ AASSET_MODE_UNKNOWN = 0, /** Read chunks, and seek forward and backward. */ AASSET_MODE_RANDOM = 1, /** Read sequentially, with an occasional forward seek. */ AASSET_MODE_STREAMING = 2, /** Caller plans to ask for a read-only buffer with all data. */ AASSET_MODE_BUFFER = 3 }; /** * Open the named directory within the asset hierarchy. The directory can then * be inspected with the AAssetDir functions. To open the top-level directory, * pass in "" as the dirName. * * The object returned here should be freed by calling AAssetDir_close(). */ AAssetDir* AAssetManager_openDir(AAssetManager* mgr, const char* dirName); /** * Open an asset. * * The object returned here should be freed by calling AAsset_close(). */ AAsset* AAssetManager_open(AAssetManager* mgr, const char* filename, int mode); /** * Iterate over the files in an asset directory. A NULL string is returned * when all the file names have been returned. * * The returned file name is suitable for passing to AAssetManager_open(). * * The string returned here is owned by the AssetDir implementation and is not * guaranteed to remain valid if any other calls are made on this AAssetDir * instance. */ const char* AAssetDir_getNextFileName(AAssetDir* assetDir); /** * Reset the iteration state of AAssetDir_getNextFileName() to the beginning. */ void AAssetDir_rewind(AAssetDir* assetDir); /** * Close an opened AAssetDir, freeing any related resources. */ void AAssetDir_close(AAssetDir* assetDir); /** * Attempt to read 'count' bytes of data from the current offset. * * Returns the number of bytes read, zero on EOF, or < 0 on error. */ int AAsset_read(AAsset* asset, void* buf, size_t count); /** * Seek to the specified offset within the asset data. 'whence' uses the * same constants as lseek()/fseek(). * * Returns the new position on success, or (off_t) -1 on error. */ off_t AAsset_seek(AAsset* asset, off_t offset, int whence) __RENAME_IF_FILE_OFFSET64(AAsset_seek64); /** * Seek to the specified offset within the asset data. 'whence' uses the * same constants as lseek()/fseek(). * * Uses 64-bit data type for large files as opposed to the 32-bit type used * by AAsset_seek. * * Returns the new position on success, or (off64_t) -1 on error. */ off64_t AAsset_seek64(AAsset* asset, off64_t offset, int whence); /** * Close the asset, freeing all associated resources. */ void AAsset_close(AAsset* asset); /** * Get a pointer to a buffer holding the entire contents of the assset. * * Returns NULL on failure. */ const void* AAsset_getBuffer(AAsset* asset); /** * Report the total size of the asset data. */ off_t AAsset_getLength(AAsset* asset) __RENAME_IF_FILE_OFFSET64(AAsset_getLength64); /** * Report the total size of the asset data. Reports the size using a 64-bit * number insted of 32-bit as AAsset_getLength. */ off64_t AAsset_getLength64(AAsset* asset); /** * Report the total amount of asset data that can be read from the current position. */ off_t AAsset_getRemainingLength(AAsset* asset) __RENAME_IF_FILE_OFFSET64(AAsset_getRemainingLength64); /** * Report the total amount of asset data that can be read from the current position. * * Uses a 64-bit number instead of a 32-bit number as AAsset_getRemainingLength does. */ off64_t AAsset_getRemainingLength64(AAsset* asset); /** * Open a new file descriptor that can be used to read the asset data. If the * start or length cannot be represented by a 32-bit number, it will be * truncated. If the file is large, use AAsset_openFileDescriptor64 instead. * * Returns < 0 if direct fd access is not possible (for example, if the asset is * compressed). */ int AAsset_openFileDescriptor(AAsset* asset, off_t* outStart, off_t* outLength) __RENAME_IF_FILE_OFFSET64(AAsset_openFileDescriptor64); /** * Open a new file descriptor that can be used to read the asset data. * * Uses a 64-bit number for the offset and length instead of 32-bit instead of * as AAsset_openFileDescriptor does. * * Returns < 0 if direct fd access is not possible (for example, if the asset is * compressed). */ int AAsset_openFileDescriptor64(AAsset* asset, off64_t* outStart, off64_t* outLength); /** * Returns whether this asset's internal buffer is allocated in ordinary RAM (i.e. not * mmapped). */ int AAsset_isAllocated(AAsset* asset); #ifdef __cplusplus }; #endif #endif // ANDROID_ASSET_MANAGER_H /** @} */ include/android/asset_manager_jni.h0100644 0000000 0000000 00000002421 13756501735 016505 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Asset * @{ */ /** * @file asset_manager_jni.h */ #ifndef ANDROID_ASSET_MANAGER_JNI_H #define ANDROID_ASSET_MANAGER_JNI_H #include #include #ifdef __cplusplus extern "C" { #endif /** * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager * object. Note that the caller is responsible for obtaining and holding a VM reference * to the jobject to prevent its being garbage collected while the native object is * in use. */ AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager); #ifdef __cplusplus }; #endif #endif // ANDROID_ASSET_MANAGER_JNI_H /** @} */ include/android/bitmap.h0100644 0000000 0000000 00000006472 13756501735 014322 0ustar000000000 0000000 /* * Copyright (C) 2009 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. */ /** * @addtogroup Bitmap * @{ */ /** * @file bitmap.h */ #ifndef ANDROID_BITMAP_H #define ANDROID_BITMAP_H #include #include #ifdef __cplusplus extern "C" { #endif /** AndroidBitmap functions result code. */ enum { /** Operation was successful. */ ANDROID_BITMAP_RESULT_SUCCESS = 0, /** Bad parameter. */ ANDROID_BITMAP_RESULT_BAD_PARAMETER = -1, /** JNI exception occured. */ ANDROID_BITMAP_RESULT_JNI_EXCEPTION = -2, /** Allocation failed. */ ANDROID_BITMAP_RESULT_ALLOCATION_FAILED = -3, }; /** Backward compatibility: this macro used to be misspelled. */ #define ANDROID_BITMAP_RESUT_SUCCESS ANDROID_BITMAP_RESULT_SUCCESS /** Bitmap pixel format. */ enum AndroidBitmapFormat { /** No format. */ ANDROID_BITMAP_FORMAT_NONE = 0, /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/ ANDROID_BITMAP_FORMAT_RGBA_8888 = 1, /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/ ANDROID_BITMAP_FORMAT_RGB_565 = 4, /** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/ ANDROID_BITMAP_FORMAT_RGBA_4444 = 7, /** Alpha: 8 bits. */ ANDROID_BITMAP_FORMAT_A_8 = 8, }; /** Bitmap info, see AndroidBitmap_getInfo(). */ typedef struct { /** The bitmap width in pixels. */ uint32_t width; /** The bitmap height in pixels. */ uint32_t height; /** The number of byte per row. */ uint32_t stride; /** The bitmap pixel format. See {@link AndroidBitmapFormat} */ int32_t format; /** Unused. */ uint32_t flags; // 0 for now } AndroidBitmapInfo; /** * Given a java bitmap object, fill out the AndroidBitmapInfo struct for it. * If the call fails, the info parameter will be ignored. */ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info); /** * Given a java bitmap object, attempt to lock the pixel address. * Locking will ensure that the memory for the pixels will not move * until the unlockPixels call, and ensure that, if the pixels had been * previously purged, they will have been restored. * * If this call succeeds, it must be balanced by a call to * AndroidBitmap_unlockPixels, after which time the address of the pixels should * no longer be used. * * If this succeeds, *addrPtr will be set to the pixel address. If the call * fails, addrPtr will be ignored. */ int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr); /** * Call this to balance a successful call to AndroidBitmap_lockPixels. */ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap); #ifdef __cplusplus } #endif #endif /** @} */ include/android/choreographer.h0100644 0000000 0000000 00000006726 13756501735 015700 0ustar000000000 0000000 /* * 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. */ /** * @addtogroup Choreographer * @{ */ /** * @file choreographer.h */ #ifndef ANDROID_CHOREOGRAPHER_H #define ANDROID_CHOREOGRAPHER_H #include #include __BEGIN_DECLS struct AChoreographer; typedef struct AChoreographer AChoreographer; /** * Prototype of the function that is called when a new frame is being rendered. * It's passed the time that the frame is being rendered as nanoseconds in the * CLOCK_MONOTONIC time base, as well as the data pointer provided by the * application that registered a callback. All callbacks that run as part of * rendering a frame will observe the same frame time, so it should be used * whenever events need to be synchronized (e.g. animations). */ typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); /** * Prototype of the function that is called when a new frame is being rendered. * It's passed the time that the frame is being rendered as nanoseconds in the * CLOCK_MONOTONIC time base, as well as the data pointer provided by the * application that registered a callback. All callbacks that run as part of * rendering a frame will observe the same frame time, so it should be used * whenever events need to be synchronized (e.g. animations). */ typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); #if __ANDROID_API__ >= 24 /** * Get the AChoreographer instance for the current thread. This must be called * on an ALooper thread. */ AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); /** * Deprecated: Use AChoreographer_postFrameCallback64 instead. */ void AChoreographer_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29); /** * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. */ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29); #endif /* __ANDROID_API__ >= 24 */ #if __ANDROID_API__ >= 29 /** * Power a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. */ void AChoreographer_postFrameCallback64(AChoreographer* chroreographer, AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); /** * Post a callback to be run on the frame following the specified delay. The * data pointer provided will be passed to the callback function when it's * called. */ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); #endif /* __ANDROID_API__ >= 29 */ __END_DECLS #endif // ANDROID_CHOREOGRAPHER_H /** @} */ include/android/configuration.h0100644 0000000 0000000 00000063277 13756501735 015723 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Configuration * @{ */ /** * @file configuration.h */ #ifndef ANDROID_CONFIGURATION_H #define ANDROID_CONFIGURATION_H #include #include #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ #endif #ifdef __cplusplus extern "C" { #endif struct AConfiguration; /** * {@link AConfiguration} is an opaque type used to get and set * various subsystem configurations. * * A {@link AConfiguration} pointer can be obtained using: * - AConfiguration_new() * - AConfiguration_fromAssetManager() */ typedef struct AConfiguration AConfiguration; /** * Define flags and constants for various subsystem configurations. */ enum { /** Orientation: not specified. */ ACONFIGURATION_ORIENTATION_ANY = 0x0000, /** * Orientation: value corresponding to the * port * resource qualifier. */ ACONFIGURATION_ORIENTATION_PORT = 0x0001, /** * Orientation: value corresponding to the * land * resource qualifier. */ ACONFIGURATION_ORIENTATION_LAND = 0x0002, /** @deprecated Not currently supported or used. */ ACONFIGURATION_ORIENTATION_SQUARE = 0x0003, /** Touchscreen: not specified. */ ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000, /** * Touchscreen: value corresponding to the * notouch * resource qualifier. */ ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001, /** @deprecated Not currently supported or used. */ ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002, /** * Touchscreen: value corresponding to the * finger * resource qualifier. */ ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003, /** Density: default density. */ ACONFIGURATION_DENSITY_DEFAULT = 0, /** * Density: value corresponding to the * ldpi * resource qualifier. */ ACONFIGURATION_DENSITY_LOW = 120, /** * Density: value corresponding to the * mdpi * resource qualifier. */ ACONFIGURATION_DENSITY_MEDIUM = 160, /** * Density: value corresponding to the * tvdpi * resource qualifier. */ ACONFIGURATION_DENSITY_TV = 213, /** * Density: value corresponding to the * hdpi * resource qualifier. */ ACONFIGURATION_DENSITY_HIGH = 240, /** * Density: value corresponding to the * xhdpi * resource qualifier. */ ACONFIGURATION_DENSITY_XHIGH = 320, /** * Density: value corresponding to the * xxhdpi * resource qualifier. */ ACONFIGURATION_DENSITY_XXHIGH = 480, /** * Density: value corresponding to the * xxxhdpi * resource qualifier. */ ACONFIGURATION_DENSITY_XXXHIGH = 640, /** Density: any density. */ ACONFIGURATION_DENSITY_ANY = 0xfffe, /** Density: no density specified. */ ACONFIGURATION_DENSITY_NONE = 0xffff, /** Keyboard: not specified. */ ACONFIGURATION_KEYBOARD_ANY = 0x0000, /** * Keyboard: value corresponding to the * nokeys * resource qualifier. */ ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001, /** * Keyboard: value corresponding to the * qwerty * resource qualifier. */ ACONFIGURATION_KEYBOARD_QWERTY = 0x0002, /** * Keyboard: value corresponding to the * 12key * resource qualifier. */ ACONFIGURATION_KEYBOARD_12KEY = 0x0003, /** Navigation: not specified. */ ACONFIGURATION_NAVIGATION_ANY = 0x0000, /** * Navigation: value corresponding to the * nonav * resource qualifier. */ ACONFIGURATION_NAVIGATION_NONAV = 0x0001, /** * Navigation: value corresponding to the * dpad * resource qualifier. */ ACONFIGURATION_NAVIGATION_DPAD = 0x0002, /** * Navigation: value corresponding to the * trackball * resource qualifier. */ ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003, /** * Navigation: value corresponding to the * wheel * resource qualifier. */ ACONFIGURATION_NAVIGATION_WHEEL = 0x0004, /** Keyboard availability: not specified. */ ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000, /** * Keyboard availability: value corresponding to the * keysexposed * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_NO = 0x0001, /** * Keyboard availability: value corresponding to the * keyshidden * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_YES = 0x0002, /** * Keyboard availability: value corresponding to the * keyssoft * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003, /** Navigation availability: not specified. */ ACONFIGURATION_NAVHIDDEN_ANY = 0x0000, /** * Navigation availability: value corresponding to the * navexposed * resource qualifier. */ ACONFIGURATION_NAVHIDDEN_NO = 0x0001, /** * Navigation availability: value corresponding to the * navhidden * resource qualifier. */ ACONFIGURATION_NAVHIDDEN_YES = 0x0002, /** Screen size: not specified. */ ACONFIGURATION_SCREENSIZE_ANY = 0x00, /** * Screen size: value indicating the screen is at least * approximately 320x426 dp units, corresponding to the * small * resource qualifier. */ ACONFIGURATION_SCREENSIZE_SMALL = 0x01, /** * Screen size: value indicating the screen is at least * approximately 320x470 dp units, corresponding to the * normal * resource qualifier. */ ACONFIGURATION_SCREENSIZE_NORMAL = 0x02, /** * Screen size: value indicating the screen is at least * approximately 480x640 dp units, corresponding to the * large * resource qualifier. */ ACONFIGURATION_SCREENSIZE_LARGE = 0x03, /** * Screen size: value indicating the screen is at least * approximately 720x960 dp units, corresponding to the * xlarge * resource qualifier. */ ACONFIGURATION_SCREENSIZE_XLARGE = 0x04, /** Screen layout: not specified. */ ACONFIGURATION_SCREENLONG_ANY = 0x00, /** * Screen layout: value that corresponds to the * notlong * resource qualifier. */ ACONFIGURATION_SCREENLONG_NO = 0x1, /** * Screen layout: value that corresponds to the * long * resource qualifier. */ ACONFIGURATION_SCREENLONG_YES = 0x2, ACONFIGURATION_SCREENROUND_ANY = 0x00, ACONFIGURATION_SCREENROUND_NO = 0x1, ACONFIGURATION_SCREENROUND_YES = 0x2, /** Wide color gamut: not specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00, /** * Wide color gamut: value that corresponds to * no * nowidecg resource qualifier specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1, /** * Wide color gamut: value that corresponds to * * widecg resource qualifier specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2, /** HDR: not specified. */ ACONFIGURATION_HDR_ANY = 0x00, /** * HDR: value that corresponds to * * lowdr resource qualifier specified. */ ACONFIGURATION_HDR_NO = 0x1, /** * HDR: value that corresponds to * * highdr resource qualifier specified. */ ACONFIGURATION_HDR_YES = 0x2, /** UI mode: not specified. */ ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00, /** * UI mode: value that corresponds to * no * UI mode type resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01, /** * UI mode: value that corresponds to * desk resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02, /** * UI mode: value that corresponds to * car resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03, /** * UI mode: value that corresponds to * television resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04, /** * UI mode: value that corresponds to * appliance resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05, /** * UI mode: value that corresponds to * watch resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06, /** * UI mode: value that corresponds to * vr resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07, /** UI night mode: not specified.*/ ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00, /** * UI night mode: value that corresponds to * notnight resource qualifier specified. */ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1, /** * UI night mode: value that corresponds to * night resource qualifier specified. */ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2, /** Screen width DPI: not specified. */ ACONFIGURATION_SCREEN_WIDTH_DP_ANY = 0x0000, /** Screen height DPI: not specified. */ ACONFIGURATION_SCREEN_HEIGHT_DP_ANY = 0x0000, /** Smallest screen width DPI: not specified.*/ ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY = 0x0000, /** Layout direction: not specified. */ ACONFIGURATION_LAYOUTDIR_ANY = 0x00, /** * Layout direction: value that corresponds to * ldltr resource qualifier specified. */ ACONFIGURATION_LAYOUTDIR_LTR = 0x01, /** * Layout direction: value that corresponds to * ldrtl resource qualifier specified. */ ACONFIGURATION_LAYOUTDIR_RTL = 0x02, /** * Bit mask for * mcc * configuration. */ ACONFIGURATION_MCC = 0x0001, /** * Bit mask for * mnc * configuration. */ ACONFIGURATION_MNC = 0x0002, /** * Bit mask for * locale * configuration. */ ACONFIGURATION_LOCALE = 0x0004, /** * Bit mask for * touchscreen * configuration. */ ACONFIGURATION_TOUCHSCREEN = 0x0008, /** * Bit mask for * keyboard * configuration. */ ACONFIGURATION_KEYBOARD = 0x0010, /** * Bit mask for * keyboardHidden * configuration. */ ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020, /** * Bit mask for * navigation * configuration. */ ACONFIGURATION_NAVIGATION = 0x0040, /** * Bit mask for * orientation * configuration. */ ACONFIGURATION_ORIENTATION = 0x0080, /** * Bit mask for * density * configuration. */ ACONFIGURATION_DENSITY = 0x0100, /** * Bit mask for * screen size * configuration. */ ACONFIGURATION_SCREEN_SIZE = 0x0200, /** * Bit mask for * platform version * configuration. */ ACONFIGURATION_VERSION = 0x0400, /** * Bit mask for screen layout configuration. */ ACONFIGURATION_SCREEN_LAYOUT = 0x0800, /** * Bit mask for * ui mode * configuration. */ ACONFIGURATION_UI_MODE = 0x1000, /** * Bit mask for * smallest screen width * configuration. */ ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000, /** * Bit mask for * layout direction * configuration. */ ACONFIGURATION_LAYOUTDIR = 0x4000, ACONFIGURATION_SCREEN_ROUND = 0x8000, /** * Bit mask for * wide color gamut * and HDR configurations. */ ACONFIGURATION_COLOR_MODE = 0x10000, /** * Constant used to to represent MNC (Mobile Network Code) zero. * 0 cannot be used, since it is used to represent an undefined MNC. */ ACONFIGURATION_MNC_ZERO = 0xffff, }; /** * Create a new AConfiguration, initialized with no values set. */ AConfiguration* AConfiguration_new(); /** * Free an AConfiguration that was previously created with * AConfiguration_new(). */ void AConfiguration_delete(AConfiguration* config); /** * Create and return a new AConfiguration based on the current configuration in * use in the given {@link AAssetManager}. */ void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am); /** * Copy the contents of 'src' to 'dest'. */ void AConfiguration_copy(AConfiguration* dest, AConfiguration* src); /** * Return the current MCC set in the configuration. 0 if not set. */ int32_t AConfiguration_getMcc(AConfiguration* config); /** * Set the current MCC in the configuration. 0 to clear. */ void AConfiguration_setMcc(AConfiguration* config, int32_t mcc); /** * Return the current MNC set in the configuration. 0 if not set. */ int32_t AConfiguration_getMnc(AConfiguration* config); /** * Set the current MNC in the configuration. 0 to clear. */ void AConfiguration_setMnc(AConfiguration* config, int32_t mnc); /** * Return the current language code set in the configuration. The output will * be filled with an array of two characters. They are not 0-terminated. If * a language is not set, they will be 0. */ void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage); /** * Set the current language code in the configuration, from the first two * characters in the string. */ void AConfiguration_setLanguage(AConfiguration* config, const char* language); /** * Return the current country code set in the configuration. The output will * be filled with an array of two characters. They are not 0-terminated. If * a country is not set, they will be 0. */ void AConfiguration_getCountry(AConfiguration* config, char* outCountry); /** * Set the current country code in the configuration, from the first two * characters in the string. */ void AConfiguration_setCountry(AConfiguration* config, const char* country); /** * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration. */ int32_t AConfiguration_getOrientation(AConfiguration* config); /** * Set the current orientation in the configuration. */ void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation); /** * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration. */ int32_t AConfiguration_getTouchscreen(AConfiguration* config); /** * Set the current touchscreen in the configuration. */ void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen); /** * Return the current ACONFIGURATION_DENSITY_* set in the configuration. */ int32_t AConfiguration_getDensity(AConfiguration* config); /** * Set the current density in the configuration. */ void AConfiguration_setDensity(AConfiguration* config, int32_t density); /** * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration. */ int32_t AConfiguration_getKeyboard(AConfiguration* config); /** * Set the current keyboard in the configuration. */ void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard); /** * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration. */ int32_t AConfiguration_getNavigation(AConfiguration* config); /** * Set the current navigation in the configuration. */ void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation); /** * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration. */ int32_t AConfiguration_getKeysHidden(AConfiguration* config); /** * Set the current keys hidden in the configuration. */ void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden); /** * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration. */ int32_t AConfiguration_getNavHidden(AConfiguration* config); /** * Set the current nav hidden in the configuration. */ void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden); /** * Return the current SDK (API) version set in the configuration. */ int32_t AConfiguration_getSdkVersion(AConfiguration* config); /** * Set the current SDK version in the configuration. */ void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion); /** * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration. */ int32_t AConfiguration_getScreenSize(AConfiguration* config); /** * Set the current screen size in the configuration. */ void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize); /** * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration. */ int32_t AConfiguration_getScreenLong(AConfiguration* config); /** * Set the current screen long in the configuration. */ void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong); /** * Return the current ACONFIGURATION_SCREENROUND_* set in the configuration. */ int32_t AConfiguration_getScreenRound(AConfiguration* config); /** * Set the current screen round in the configuration. */ void AConfiguration_setScreenRound(AConfiguration* config, int32_t screenRound); /** * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration. */ int32_t AConfiguration_getUiModeType(AConfiguration* config); /** * Set the current UI mode type in the configuration. */ void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType); /** * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration. */ int32_t AConfiguration_getUiModeNight(AConfiguration* config); /** * Set the current UI mode night in the configuration. */ void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight); #if __ANDROID_API__ >= 13 /** * Return the current configuration screen width in dp units, or * ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set. */ int32_t AConfiguration_getScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); /** * Set the configuration's current screen width in dp units. */ void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); /** * Return the current configuration screen height in dp units, or * ACONFIGURATION_SCREEN_HEIGHT_DP_ANY if not set. */ int32_t AConfiguration_getScreenHeightDp(AConfiguration* config) __INTRODUCED_IN(13); /** * Set the configuration's current screen width in dp units. */ void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); /** * Return the configuration's smallest screen width in dp units, or * ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY if not set. */ int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); /** * Set the configuration's smallest screen width in dp units. */ void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); #endif /* __ANDROID_API__ >= 13 */ #if __ANDROID_API__ >= 17 /** * Return the configuration's layout direction, or * ACONFIGURATION_LAYOUTDIR_ANY if not set. */ int32_t AConfiguration_getLayoutDirection(AConfiguration* config) __INTRODUCED_IN(17); /** * Set the configuration's layout direction. */ void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) __INTRODUCED_IN(17); #endif /* __ANDROID_API__ >= 17 */ /** * Perform a diff between two configurations. Returns a bit mask of * ACONFIGURATION_* constants, each bit set meaning that configuration element * is different between them. */ int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2); /** * Determine whether 'base' is a valid configuration for use within the * environment 'requested'. Returns 0 if there are any values in 'base' * that conflict with 'requested'. Returns 1 if it does not conflict. */ int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested); /** * Determine whether the configuration in 'test' is better than the existing * configuration in 'base'. If 'requested' is non-NULL, this decision is based * on the overall configuration given there. If it is NULL, this decision is * simply based on which configuration is more specific. Returns non-0 if * 'test' is better than 'base'. * * This assumes you have already filtered the configurations with * AConfiguration_match(). */ int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test, AConfiguration* requested); #ifdef __cplusplus }; #endif #endif // ANDROID_CONFIGURATION_H /** @} */ include/android/font.h0100644 0000000 0000000 00000022153 13756501735 014006 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ /** * @addtogroup Font * { */ /** * @file font.h * @brief Provides some constants used in system_fonts.h or fonts_matcher.h * * Available since API level 29. */ #ifndef ANDROID_FONT_H #define ANDROID_FONT_H #include #include #include /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ __BEGIN_DECLS #if __ANDROID_API__ >= 29 enum { /** The minimum value fot the font weight value. */ AFONT_WEIGHT_MIN = 0, /** A font weight value for the thin weight. */ AFONT_WEIGHT_THIN = 100, /** A font weight value for the extra-light weight. */ AFONT_WEIGHT_EXTRA_LIGHT = 200, /** A font weight value for the light weight. */ AFONT_WEIGHT_LIGHT = 300, /** A font weight value for the normal weight. */ AFONT_WEIGHT_NORMAL = 400, /** A font weight value for the medium weight. */ AFONT_WEIGHT_MEDIUM = 500, /** A font weight value for the semi-bold weight. */ AFONT_WEIGHT_SEMI_BOLD = 600, /** A font weight value for the bold weight. */ AFONT_WEIGHT_BOLD = 700, /** A font weight value for the extra-bold weight. */ AFONT_WEIGHT_EXTRA_BOLD = 800, /** A font weight value for the black weight. */ AFONT_WEIGHT_BLACK = 900, /** The maximum value for the font weight value. */ AFONT_WEIGHT_MAX = 1000 }; /** * AFont provides information of the single font configuration. */ struct AFont; /** * Close an AFont. * * \param font a font returned by ASystemFontIterator_next or AFontMatchert_match. * Do nothing if NULL is passed. */ void AFont_close(AFont* _Nullable font) __INTRODUCED_IN(29); /** * Return an absolute path to the current font file. * * Here is a list of font formats returned by this method: *
    *
  • OpenType
  • *
  • OpenType Font Collection
  • *
  • TrueType
  • *
  • TrueType Collection
  • *
* The file extension could be one of *.otf, *.ttf, *.otc or *.ttc. * * The font file returned is guaranteed to be opend with O_RDONLY. * Note that the returned pointer is valid until AFont_close() is called for the given font. * * \param font a font object. Passing NULL is not allowed. * \return a string of the font file path. */ const char* _Nonnull AFont_getFontFilePath(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return a weight value associated with the current font. * * The weight values are positive and less than or equal to 1000. * Here are pairs of the common names and their values. *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ValueNameNDK Definition
100Thin{@link AFONT_WEIGHT_THIN}
200Extra Light (Ultra Light){@link AFONT_WEIGHT_EXTRA_LIGHT}
300Light{@link AFONT_WEIGHT_LIGHT}
400Normal (Regular){@link AFONT_WEIGHT_NORMAL}
500Medium{@link AFONT_WEIGHT_MEDIUM}
600Semi Bold (Demi Bold){@link AFONT_WEIGHT_SEMI_BOLD}
700Bold{@link AFONT_WEIGHT_BOLD}
800Extra Bold (Ultra Bold){@link AFONT_WEIGHT_EXTRA_BOLD}
900Black (Heavy){@link AFONT_WEIGHT_BLACK}
*

* Note that the weight value may fall in between above values, e.g. 250 weight. * * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass) * * \param font a font object. Passing NULL is not allowed. * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned. */ uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return true if the current font is italic, otherwise returns false. * * \param font a font object. Passing NULL is not allowed. * \return true if italic, otherwise false. */ bool AFont_isItalic(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return a IETF BCP47 compliant language tag associated with the current font. * * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)") * * Note that the returned pointer is valid until AFont_close() is called. * * \param font a font object. Passing NULL is not allowed. * \return a IETF BCP47 compliant language tag or nullptr if not available. */ const char* _Nullable AFont_getLocale(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return a font collection index value associated with the current font. * * In case the target font file is a font collection (e.g. .ttc or .otc), this * returns a non-negative value as an font offset in the collection. This * always returns 0 if the target font file is a regular font. * * \param font a font object. Passing NULL is not allowed. * \return a font collection index. */ size_t AFont_getCollectionIndex(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return a count of font variation settings associated with the current font * * The font variation settings are provided as multiple tag-values pairs. * * For example, bold italic font may have following font variation settings: * 'wght' 700, 'slnt' -12 * In this case, AFont_getAxisCount returns 2 and AFont_getAxisTag * and AFont_getAxisValue will return following values. * \code{.cpp} * AFont* font = AFontIterator_next(ite); * * // Returns the number of axes * AFont_getAxisCount(font); // Returns 2 * * // Returns the tag-value pair for the first axis. * AFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874) * AFont_getAxisValue(font, 0); // Returns 700.0 * * // Returns the tag-value pair for the second axis. * AFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74) * AFont_getAxisValue(font, 1); // Returns -12.0 * \endcode * * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) * * \param font a font object. Passing NULL is not allowed. * \return a number of font variation settings. */ size_t AFont_getAxisCount(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return an OpenType axis tag associated with the current font. * * See AFont_getAxisCount for more details. * * \param font a font object. Passing NULL is not allowed. * \param axisIndex an index to the font variation settings. Passing value larger than or * equal to {@link AFont_getAxisCount} is not allowed. * \return an OpenType axis tag value for the given font variation setting. */ uint32_t AFont_getAxisTag(const AFont* _Nonnull font, uint32_t axisIndex) __INTRODUCED_IN(29); /** * Return an OpenType axis value associated with the current font. * * See AFont_getAxisCount for more details. * * \param font a font object. Passing NULL is not allowed. * \param axisIndex an index to the font variation settings. Passing value larger than or * equal to {@link ASYstemFont_getAxisCount} is not allwed. * \return a float value for the given font variation setting. */ float AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex) __INTRODUCED_IN(29); #endif // __ANDROID_API__ >= 29 __END_DECLS #endif // ANDROID_FONT_H /** @} */ include/android/font_matcher.h0100644 0000000 0000000 00000016541 13756501735 015515 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ /** * @addtogroup Font * { */ /** * @file font_matcher.h * @brief Provides the font matching logic with various inputs. * * You can use this class for deciding what font is to be used for drawing text. * * A matcher is created from text style, locales and UI compatibility. The match function for * matcher object can be called multiple times until close function is called. * * Even if no font can render the given text, the match function will return a non-null result for * drawing Tofu character. * * Examples: * \code{.cpp} * // Simple font query for the ASCII character. * std::vector text = { 'A' }; * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); * // runLength will be 1 and the font will points a valid font file. * AFontMatcher_destroy(matcher); * * // Querying font for CJK characters * std::vector text = { 0x9AA8 }; * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); * AFontMatcher_setLocales(matcher, "zh-CN,ja-JP"); * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); * // runLength will be 1 and the font will points a Simplified Chinese font. * AFontMatcher_setLocales(matcher, "ja-JP,zh-CN"); * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); * // runLength will be 1 and the font will points a Japanese font. * AFontMatcher_destroy(matcher); * * // Querying font for text/color emoji * std::vector text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 }; * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); * // runLength will be 8 and the font will points a color emoji font. * AFontMatcher_destroy(matcher); * * // Mixture of multiple script of characters. * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character. * std::vector text = { 0x05D0, 0x0E01 }; * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); * // runLength will be 1 and the font will points a Hebrew font. * AFontMatcher_destroy(matcher); * \endcode * * Available since API level 29. */ #ifndef ANDROID_FONT_MATCHER_H #define ANDROID_FONT_MATCHER_H #include #include #include #include /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ __BEGIN_DECLS #if __ANDROID_API__ >= 29 enum { /** A family variant value for the system default variant. */ AFAMILY_VARIANT_DEFAULT = 0, /** * A family variant value for the compact font family variant. * * The compact font family has Latin-based vertical metrics. */ AFAMILY_VARIANT_COMPACT = 1, /** * A family variant value for the elegant font family variant. * * The elegant font family may have larger vertical metrics than Latin font. */ AFAMILY_VARIANT_ELEGANT = 2, }; /** * AFontMatcher performs match operation on given parameters and available font files. * This matcher is not a thread-safe object. Do not pass this matcher to other threads. */ struct AFontMatcher; /** * Select the best font from given parameters. * */ /** * Creates a new AFontMatcher object */ AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29); /** * Destroy the matcher object. * * \param matcher a matcher object. Passing NULL is not allowed. */ void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29); /** * Set font style to matcher. * * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL} * with non-italic style. * * \param matcher a matcher object. Passing NULL is not allowed. * \param weight a font weight value. Only from 0 to 1000 value is valid * \param italic true if italic, otherwise false. */ void AFontMatcher_setStyle( AFontMatcher* _Nonnull matcher, uint16_t weight, bool italic) __INTRODUCED_IN(29); /** * Set font locales to matcher. * * If this function is not called, the matcher performs with empty locale list. * * \param matcher a matcher object. Passing NULL is not allowed. * \param languageTags a null character terminated comma separated IETF BCP47 compliant language * tags. */ void AFontMatcher_setLocales( AFontMatcher* _Nonnull matcher, const char* _Nonnull languageTags) __INTRODUCED_IN(29); /** * Set family variant to matcher. * * If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}. * * \param matcher a matcher object. Passing NULL is not allowed. * \param familyVariant Must be one of {@link AFAMILY_VARIANT_DEFAULT}, * {@link AFAMILY_VARIANT_COMPACT} or {@link AFAMILY_VARIANT_ELEGANT} is valid. */ void AFontMatcher_setFamilyVariant( AFontMatcher* _Nonnull matcher, uint32_t familyVariant) __INTRODUCED_IN(29); /** * Performs the matching from the generic font family for the text and select one font. * * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families) * * Even if no font can render the given text, this function will return a non-null result for * drawing Tofu character. * * \param matcher a matcher object. Passing NULL is not allowed. * \param familyName a null character terminated font family name * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string. * \param textLength a length of the given text buffer. This must not be zero. * \param runLengthOut if not null, the font run length will be filled. * \return a font to be used for given text and params. You need to release the returned font by * ASystemFont_close when it is no longer needed. */ AFont* _Nonnull AFontMatcher_match( const AFontMatcher* _Nonnull matcher, const char* _Nonnull familyName, const uint16_t* _Nonnull text, const uint32_t textLength, uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29); #endif // __ANDROID_API__ >= 29 __END_DECLS #endif // ANDROID_FONT_MATCHER_H /** @} */ include/android/hardware_buffer_jni.h0100644 0000000 0000000 00000003240 13756501735 017022 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ /** * @addtogroup AHardwareBuffer * @{ */ /** * @file hardware_buffer_jni.h * @brief JNI glue for native hardware buffers. */ #ifndef ANDROID_HARDWARE_BUFFER_JNI_H #define ANDROID_HARDWARE_BUFFER_JNI_H #include #include #include __BEGIN_DECLS /** * Return the AHardwareBuffer wrapped by a Java HardwareBuffer object. * * This method does not acquire any additional reference to the AHardwareBuffer * that is returned. To keep the AHardwareBuffer live after the Java * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire() * to acquire an additional reference. */ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, jobject hardwareBufferObj) __INTRODUCED_IN(26); /** * Return a new Java HardwareBuffer object that wraps the passed native * AHardwareBuffer object. */ jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env, AHardwareBuffer* hardwareBuffer) __INTRODUCED_IN(26); __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_JNI_H /** @} */ include/android/input.h0100644 0000000 0000000 00000144537 13756501735 014212 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Input * @{ */ /** * @file input.h */ #ifndef _ANDROID_INPUT_H #define _ANDROID_INPUT_H #include /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ /* * Structures and functions to receive and process input events in * native code. * * NOTE: These functions MUST be implemented by /system/lib/libui.so */ #include #include #include #include #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ #endif #ifdef __cplusplus extern "C" { #endif /** * Key states (may be returned by queries about the current state of a * particular key code, scan code or switch). */ enum { /** The key state is unknown or the requested key itself is not supported. */ AKEY_STATE_UNKNOWN = -1, /** The key is up. */ AKEY_STATE_UP = 0, /** The key is down. */ AKEY_STATE_DOWN = 1, /** The key is down but is a virtual key press that is being emulated by the system. */ AKEY_STATE_VIRTUAL = 2 }; /** * Meta key / modifier state. */ enum { /** No meta keys are pressed. */ AMETA_NONE = 0, /** This mask is used to check whether one of the ALT meta keys is pressed. */ AMETA_ALT_ON = 0x02, /** This mask is used to check whether the left ALT meta key is pressed. */ AMETA_ALT_LEFT_ON = 0x10, /** This mask is used to check whether the right ALT meta key is pressed. */ AMETA_ALT_RIGHT_ON = 0x20, /** This mask is used to check whether one of the SHIFT meta keys is pressed. */ AMETA_SHIFT_ON = 0x01, /** This mask is used to check whether the left SHIFT meta key is pressed. */ AMETA_SHIFT_LEFT_ON = 0x40, /** This mask is used to check whether the right SHIFT meta key is pressed. */ AMETA_SHIFT_RIGHT_ON = 0x80, /** This mask is used to check whether the SYM meta key is pressed. */ AMETA_SYM_ON = 0x04, /** This mask is used to check whether the FUNCTION meta key is pressed. */ AMETA_FUNCTION_ON = 0x08, /** This mask is used to check whether one of the CTRL meta keys is pressed. */ AMETA_CTRL_ON = 0x1000, /** This mask is used to check whether the left CTRL meta key is pressed. */ AMETA_CTRL_LEFT_ON = 0x2000, /** This mask is used to check whether the right CTRL meta key is pressed. */ AMETA_CTRL_RIGHT_ON = 0x4000, /** This mask is used to check whether one of the META meta keys is pressed. */ AMETA_META_ON = 0x10000, /** This mask is used to check whether the left META meta key is pressed. */ AMETA_META_LEFT_ON = 0x20000, /** This mask is used to check whether the right META meta key is pressed. */ AMETA_META_RIGHT_ON = 0x40000, /** This mask is used to check whether the CAPS LOCK meta key is on. */ AMETA_CAPS_LOCK_ON = 0x100000, /** This mask is used to check whether the NUM LOCK meta key is on. */ AMETA_NUM_LOCK_ON = 0x200000, /** This mask is used to check whether the SCROLL LOCK meta key is on. */ AMETA_SCROLL_LOCK_ON = 0x400000, }; struct AInputEvent; /** * Input events. * * Input events are opaque structures. Use the provided accessors functions to * read their properties. */ typedef struct AInputEvent AInputEvent; /** * Input event types. */ enum { /** Indicates that the input event is a key event. */ AINPUT_EVENT_TYPE_KEY = 1, /** Indicates that the input event is a motion event. */ AINPUT_EVENT_TYPE_MOTION = 2 }; /** * Key event actions. */ enum { /** The key has been pressed down. */ AKEY_EVENT_ACTION_DOWN = 0, /** The key has been released. */ AKEY_EVENT_ACTION_UP = 1, /** * Multiple duplicate key events have occurred in a row, or a * complex string is being delivered. The repeat_count property * of the key event contains the number of times the given key * code should be executed. */ AKEY_EVENT_ACTION_MULTIPLE = 2 }; /** * Key event flags. */ enum { /** This mask is set if the device woke because of this key event. */ AKEY_EVENT_FLAG_WOKE_HERE = 0x1, /** This mask is set if the key event was generated by a software keyboard. */ AKEY_EVENT_FLAG_SOFT_KEYBOARD = 0x2, /** This mask is set if we don't want the key event to cause us to leave touch mode. */ AKEY_EVENT_FLAG_KEEP_TOUCH_MODE = 0x4, /** * This mask is set if an event was known to come from a trusted * part of the system. That is, the event is known to come from * the user, and could not have been spoofed by a third party * component. */ AKEY_EVENT_FLAG_FROM_SYSTEM = 0x8, /** * This mask is used for compatibility, to identify enter keys that are * coming from an IME whose enter key has been auto-labelled "next" or * "done". This allows TextView to dispatch these as normal enter keys * for old applications, but still do the appropriate action when * receiving them. */ AKEY_EVENT_FLAG_EDITOR_ACTION = 0x10, /** * When associated with up key events, this indicates that the key press * has been canceled. Typically this is used with virtual touch screen * keys, where the user can slide from the virtual key area on to the * display: in that case, the application will receive a canceled up * event and should not perform the action normally associated with the * key. Note that for this to work, the application can not perform an * action for a key until it receives an up or the long press timeout has * expired. */ AKEY_EVENT_FLAG_CANCELED = 0x20, /** * This key event was generated by a virtual (on-screen) hard key area. * Typically this is an area of the touchscreen, outside of the regular * display, dedicated to "hardware" buttons. */ AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY = 0x40, /** * This flag is set for the first key repeat that occurs after the * long press timeout. */ AKEY_EVENT_FLAG_LONG_PRESS = 0x80, /** * Set when a key event has AKEY_EVENT_FLAG_CANCELED set because a long * press action was executed while it was down. */ AKEY_EVENT_FLAG_CANCELED_LONG_PRESS = 0x100, /** * Set for AKEY_EVENT_ACTION_UP when this event's key code is still being * tracked from its initial down. That is, somebody requested that tracking * started on the key down and a long press has not caused * the tracking to be canceled. */ AKEY_EVENT_FLAG_TRACKING = 0x200, /** * Set when a key event has been synthesized to implement default behavior * for an event that the application did not handle. * Fallback key events are generated by unhandled trackball motions * (to emulate a directional keypad) and by certain unhandled key presses * that are declared in the key map (such as special function numeric keypad * keys when numlock is off). */ AKEY_EVENT_FLAG_FALLBACK = 0x400, }; /** * Bit shift for the action bits holding the pointer index as * defined by AMOTION_EVENT_ACTION_POINTER_INDEX_MASK. */ #define AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 8 /** Motion event actions */ enum { /** Bit mask of the parts of the action code that are the action itself. */ AMOTION_EVENT_ACTION_MASK = 0xff, /** * Bits in the action code that represent a pointer index, used with * AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP. Shifting * down by AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer * index where the data for the pointer going up or down can be found. */ AMOTION_EVENT_ACTION_POINTER_INDEX_MASK = 0xff00, /** A pressed gesture has started, the motion contains the initial starting location. */ AMOTION_EVENT_ACTION_DOWN = 0, /** * A pressed gesture has finished, the motion contains the final release location * as well as any intermediate points since the last down or move event. */ AMOTION_EVENT_ACTION_UP = 1, /** * A change has happened during a press gesture (between AMOTION_EVENT_ACTION_DOWN and * AMOTION_EVENT_ACTION_UP). The motion contains the most recent point, as well as * any intermediate points since the last down or move event. */ AMOTION_EVENT_ACTION_MOVE = 2, /** * The current gesture has been aborted. * You will not receive any more points in it. You should treat this as * an up event, but not perform any action that you normally would. */ AMOTION_EVENT_ACTION_CANCEL = 3, /** * A movement has happened outside of the normal bounds of the UI element. * This does not provide a full gesture, but only the initial location of the movement/touch. */ AMOTION_EVENT_ACTION_OUTSIDE = 4, /** * A non-primary pointer has gone down. * The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed. */ AMOTION_EVENT_ACTION_POINTER_DOWN = 5, /** * A non-primary pointer has gone up. * The bits in AMOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed. */ AMOTION_EVENT_ACTION_POINTER_UP = 6, /** * A change happened but the pointer is not down (unlike AMOTION_EVENT_ACTION_MOVE). * The motion contains the most recent point, as well as any intermediate points since * the last hover move event. */ AMOTION_EVENT_ACTION_HOVER_MOVE = 7, /** * The motion event contains relative vertical and/or horizontal scroll offsets. * Use getAxisValue to retrieve the information from AMOTION_EVENT_AXIS_VSCROLL * and AMOTION_EVENT_AXIS_HSCROLL. * The pointer may or may not be down when this event is dispatched. * This action is always delivered to the winder under the pointer, which * may not be the window currently touched. */ AMOTION_EVENT_ACTION_SCROLL = 8, /** The pointer is not down but has entered the boundaries of a window or view. */ AMOTION_EVENT_ACTION_HOVER_ENTER = 9, /** The pointer is not down but has exited the boundaries of a window or view. */ AMOTION_EVENT_ACTION_HOVER_EXIT = 10, /* One or more buttons have been pressed. */ AMOTION_EVENT_ACTION_BUTTON_PRESS = 11, /* One or more buttons have been released. */ AMOTION_EVENT_ACTION_BUTTON_RELEASE = 12, }; /** * Motion event flags. */ enum { /** * This flag indicates that the window that received this motion event is partly * or wholly obscured by another visible window above it. This flag is set to true * even if the event did not directly pass through the obscured area. * A security sensitive application can check this flag to identify situations in which * a malicious application may have covered up part of its content for the purpose * of misleading the user or hijacking touches. An appropriate response might be * to drop the suspect touches or to take additional precautions to confirm the user's * actual intent. */ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1, }; /** * Motion event edge touch flags. */ enum { /** No edges intersected. */ AMOTION_EVENT_EDGE_FLAG_NONE = 0, /** Flag indicating the motion event intersected the top edge of the screen. */ AMOTION_EVENT_EDGE_FLAG_TOP = 0x01, /** Flag indicating the motion event intersected the bottom edge of the screen. */ AMOTION_EVENT_EDGE_FLAG_BOTTOM = 0x02, /** Flag indicating the motion event intersected the left edge of the screen. */ AMOTION_EVENT_EDGE_FLAG_LEFT = 0x04, /** Flag indicating the motion event intersected the right edge of the screen. */ AMOTION_EVENT_EDGE_FLAG_RIGHT = 0x08 }; /** * Constants that identify each individual axis of a motion event. * @anchor AMOTION_EVENT_AXIS */ enum { /** * Axis constant: X axis of a motion event. * * - For a touch screen, reports the absolute X screen position of the center of * the touch contact area. The units are display pixels. * - For a touch pad, reports the absolute X surface position of the center of the touch * contact area. The units are device-dependent. * - For a mouse, reports the absolute X screen position of the mouse pointer. * The units are display pixels. * - For a trackball, reports the relative horizontal displacement of the trackball. * The value is normalized to a range from -1.0 (left) to 1.0 (right). * - For a joystick, reports the absolute X position of the joystick. * The value is normalized to a range from -1.0 (left) to 1.0 (right). */ AMOTION_EVENT_AXIS_X = 0, /** * Axis constant: Y axis of a motion event. * * - For a touch screen, reports the absolute Y screen position of the center of * the touch contact area. The units are display pixels. * - For a touch pad, reports the absolute Y surface position of the center of the touch * contact area. The units are device-dependent. * - For a mouse, reports the absolute Y screen position of the mouse pointer. * The units are display pixels. * - For a trackball, reports the relative vertical displacement of the trackball. * The value is normalized to a range from -1.0 (up) to 1.0 (down). * - For a joystick, reports the absolute Y position of the joystick. * The value is normalized to a range from -1.0 (up or far) to 1.0 (down or near). */ AMOTION_EVENT_AXIS_Y = 1, /** * Axis constant: Pressure axis of a motion event. * * - For a touch screen or touch pad, reports the approximate pressure applied to the surface * by a finger or other tool. The value is normalized to a range from * 0 (no pressure at all) to 1 (normal pressure), although values higher than 1 * may be generated depending on the calibration of the input device. * - For a trackball, the value is set to 1 if the trackball button is pressed * or 0 otherwise. * - For a mouse, the value is set to 1 if the primary mouse button is pressed * or 0 otherwise. */ AMOTION_EVENT_AXIS_PRESSURE = 2, /** * Axis constant: Size axis of a motion event. * * - For a touch screen or touch pad, reports the approximate size of the contact area in * relation to the maximum detectable size for the device. The value is normalized * to a range from 0 (smallest detectable size) to 1 (largest detectable size), * although it is not a linear scale. This value is of limited use. * To obtain calibrated size information, see * {@link AMOTION_EVENT_AXIS_TOUCH_MAJOR} or {@link AMOTION_EVENT_AXIS_TOOL_MAJOR}. */ AMOTION_EVENT_AXIS_SIZE = 3, /** * Axis constant: TouchMajor axis of a motion event. * * - For a touch screen, reports the length of the major axis of an ellipse that * represents the touch area at the point of contact. * The units are display pixels. * - For a touch pad, reports the length of the major axis of an ellipse that * represents the touch area at the point of contact. * The units are device-dependent. */ AMOTION_EVENT_AXIS_TOUCH_MAJOR = 4, /** * Axis constant: TouchMinor axis of a motion event. * * - For a touch screen, reports the length of the minor axis of an ellipse that * represents the touch area at the point of contact. * The units are display pixels. * - For a touch pad, reports the length of the minor axis of an ellipse that * represents the touch area at the point of contact. * The units are device-dependent. * * When the touch is circular, the major and minor axis lengths will be equal to one another. */ AMOTION_EVENT_AXIS_TOUCH_MINOR = 5, /** * Axis constant: ToolMajor axis of a motion event. * * - For a touch screen, reports the length of the major axis of an ellipse that * represents the size of the approaching finger or tool used to make contact. * - For a touch pad, reports the length of the major axis of an ellipse that * represents the size of the approaching finger or tool used to make contact. * The units are device-dependent. * * When the touch is circular, the major and minor axis lengths will be equal to one another. * * The tool size may be larger than the touch size since the tool may not be fully * in contact with the touch sensor. */ AMOTION_EVENT_AXIS_TOOL_MAJOR = 6, /** * Axis constant: ToolMinor axis of a motion event. * * - For a touch screen, reports the length of the minor axis of an ellipse that * represents the size of the approaching finger or tool used to make contact. * - For a touch pad, reports the length of the minor axis of an ellipse that * represents the size of the approaching finger or tool used to make contact. * The units are device-dependent. * * When the touch is circular, the major and minor axis lengths will be equal to one another. * * The tool size may be larger than the touch size since the tool may not be fully * in contact with the touch sensor. */ AMOTION_EVENT_AXIS_TOOL_MINOR = 7, /** * Axis constant: Orientation axis of a motion event. * * - For a touch screen or touch pad, reports the orientation of the finger * or tool in radians relative to the vertical plane of the device. * An angle of 0 radians indicates that the major axis of contact is oriented * upwards, is perfectly circular or is of unknown orientation. A positive angle * indicates that the major axis of contact is oriented to the right. A negative angle * indicates that the major axis of contact is oriented to the left. * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians * (finger pointing fully right). * - For a stylus, the orientation indicates the direction in which the stylus * is pointing in relation to the vertical axis of the current orientation of the screen. * The range is from -PI radians to PI radians, where 0 is pointing up, * -PI/2 radians is pointing left, -PI or PI radians is pointing down, and PI/2 radians * is pointing right. See also {@link AMOTION_EVENT_AXIS_TILT}. */ AMOTION_EVENT_AXIS_ORIENTATION = 8, /** * Axis constant: Vertical Scroll axis of a motion event. * * - For a mouse, reports the relative movement of the vertical scroll wheel. * The value is normalized to a range from -1.0 (down) to 1.0 (up). * * This axis should be used to scroll views vertically. */ AMOTION_EVENT_AXIS_VSCROLL = 9, /** * Axis constant: Horizontal Scroll axis of a motion event. * * - For a mouse, reports the relative movement of the horizontal scroll wheel. * The value is normalized to a range from -1.0 (left) to 1.0 (right). * * This axis should be used to scroll views horizontally. */ AMOTION_EVENT_AXIS_HSCROLL = 10, /** * Axis constant: Z axis of a motion event. * * - For a joystick, reports the absolute Z position of the joystick. * The value is normalized to a range from -1.0 (high) to 1.0 (low). * On game pads with two analog joysticks, this axis is often reinterpreted * to report the absolute X position of the second joystick instead. */ AMOTION_EVENT_AXIS_Z = 11, /** * Axis constant: X Rotation axis of a motion event. * * - For a joystick, reports the absolute rotation angle about the X axis. * The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise). */ AMOTION_EVENT_AXIS_RX = 12, /** * Axis constant: Y Rotation axis of a motion event. * * - For a joystick, reports the absolute rotation angle about the Y axis. * The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise). */ AMOTION_EVENT_AXIS_RY = 13, /** * Axis constant: Z Rotation axis of a motion event. * * - For a joystick, reports the absolute rotation angle about the Z axis. * The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise). * On game pads with two analog joysticks, this axis is often reinterpreted * to report the absolute Y position of the second joystick instead. */ AMOTION_EVENT_AXIS_RZ = 14, /** * Axis constant: Hat X axis of a motion event. * * - For a joystick, reports the absolute X position of the directional hat control. * The value is normalized to a range from -1.0 (left) to 1.0 (right). */ AMOTION_EVENT_AXIS_HAT_X = 15, /** * Axis constant: Hat Y axis of a motion event. * * - For a joystick, reports the absolute Y position of the directional hat control. * The value is normalized to a range from -1.0 (up) to 1.0 (down). */ AMOTION_EVENT_AXIS_HAT_Y = 16, /** * Axis constant: Left Trigger axis of a motion event. * * - For a joystick, reports the absolute position of the left trigger control. * The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed). */ AMOTION_EVENT_AXIS_LTRIGGER = 17, /** * Axis constant: Right Trigger axis of a motion event. * * - For a joystick, reports the absolute position of the right trigger control. * The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed). */ AMOTION_EVENT_AXIS_RTRIGGER = 18, /** * Axis constant: Throttle axis of a motion event. * * - For a joystick, reports the absolute position of the throttle control. * The value is normalized to a range from 0.0 (fully open) to 1.0 (fully closed). */ AMOTION_EVENT_AXIS_THROTTLE = 19, /** * Axis constant: Rudder axis of a motion event. * * - For a joystick, reports the absolute position of the rudder control. * The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right). */ AMOTION_EVENT_AXIS_RUDDER = 20, /** * Axis constant: Wheel axis of a motion event. * * - For a joystick, reports the absolute position of the steering wheel control. * The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right). */ AMOTION_EVENT_AXIS_WHEEL = 21, /** * Axis constant: Gas axis of a motion event. * * - For a joystick, reports the absolute position of the gas (accelerator) control. * The value is normalized to a range from 0.0 (no acceleration) * to 1.0 (maximum acceleration). */ AMOTION_EVENT_AXIS_GAS = 22, /** * Axis constant: Brake axis of a motion event. * * - For a joystick, reports the absolute position of the brake control. * The value is normalized to a range from 0.0 (no braking) to 1.0 (maximum braking). */ AMOTION_EVENT_AXIS_BRAKE = 23, /** * Axis constant: Distance axis of a motion event. * * - For a stylus, reports the distance of the stylus from the screen. * A value of 0.0 indicates direct contact and larger values indicate increasing * distance from the surface. */ AMOTION_EVENT_AXIS_DISTANCE = 24, /** * Axis constant: Tilt axis of a motion event. * * - For a stylus, reports the tilt angle of the stylus in radians where * 0 radians indicates that the stylus is being held perpendicular to the * surface, and PI/2 radians indicates that the stylus is being held flat * against the surface. */ AMOTION_EVENT_AXIS_TILT = 25, /** * Axis constant: Generic scroll axis of a motion event. * * - This is used for scroll axis motion events that can't be classified as strictly * vertical or horizontal. The movement of a rotating scroller is an example of this. */ AMOTION_EVENT_AXIS_SCROLL = 26, /** * Axis constant: The movement of x position of a motion event. * * - For a mouse, reports a difference of x position between the previous position. * This is useful when pointer is captured, in that case the mouse pointer doesn't * change the location but this axis reports the difference which allows the app * to see how the mouse is moved. */ AMOTION_EVENT_AXIS_RELATIVE_X = 27, /** * Axis constant: The movement of y position of a motion event. * * Same as {@link RELATIVE_X}, but for y position. */ AMOTION_EVENT_AXIS_RELATIVE_Y = 28, /** * Axis constant: Generic 1 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_1 = 32, /** * Axis constant: Generic 2 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_2 = 33, /** * Axis constant: Generic 3 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_3 = 34, /** * Axis constant: Generic 4 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_4 = 35, /** * Axis constant: Generic 5 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_5 = 36, /** * Axis constant: Generic 6 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_6 = 37, /** * Axis constant: Generic 7 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_7 = 38, /** * Axis constant: Generic 8 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_8 = 39, /** * Axis constant: Generic 9 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_9 = 40, /** * Axis constant: Generic 10 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_10 = 41, /** * Axis constant: Generic 11 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_11 = 42, /** * Axis constant: Generic 12 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_12 = 43, /** * Axis constant: Generic 13 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_13 = 44, /** * Axis constant: Generic 14 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_14 = 45, /** * Axis constant: Generic 15 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_15 = 46, /** * Axis constant: Generic 16 axis of a motion event. * The interpretation of a generic axis is device-specific. */ AMOTION_EVENT_AXIS_GENERIC_16 = 47, // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. }; /** * Constants that identify buttons that are associated with motion events. * Refer to the documentation on the MotionEvent class for descriptions of each button. */ enum { /** primary */ AMOTION_EVENT_BUTTON_PRIMARY = 1 << 0, /** secondary */ AMOTION_EVENT_BUTTON_SECONDARY = 1 << 1, /** tertiary */ AMOTION_EVENT_BUTTON_TERTIARY = 1 << 2, /** back */ AMOTION_EVENT_BUTTON_BACK = 1 << 3, /** forward */ AMOTION_EVENT_BUTTON_FORWARD = 1 << 4, AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5, AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6, }; /** * Constants that identify tool types. * Refer to the documentation on the MotionEvent class for descriptions of each tool type. */ enum { /** unknown */ AMOTION_EVENT_TOOL_TYPE_UNKNOWN = 0, /** finger */ AMOTION_EVENT_TOOL_TYPE_FINGER = 1, /** stylus */ AMOTION_EVENT_TOOL_TYPE_STYLUS = 2, /** mouse */ AMOTION_EVENT_TOOL_TYPE_MOUSE = 3, /** eraser */ AMOTION_EVENT_TOOL_TYPE_ERASER = 4, }; /** * Input source masks. * * Refer to the documentation on android.view.InputDevice for more details about input sources * and their correct interpretation. */ enum { /** mask */ AINPUT_SOURCE_CLASS_MASK = 0x000000ff, /** none */ AINPUT_SOURCE_CLASS_NONE = 0x00000000, /** button */ AINPUT_SOURCE_CLASS_BUTTON = 0x00000001, /** pointer */ AINPUT_SOURCE_CLASS_POINTER = 0x00000002, /** navigation */ AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004, /** position */ AINPUT_SOURCE_CLASS_POSITION = 0x00000008, /** joystick */ AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010, }; /** * Input sources. */ enum { /** unknown */ AINPUT_SOURCE_UNKNOWN = 0x00000000, /** keyboard */ AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON, /** dpad */ AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON, /** gamepad */ AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON, /** touchscreen */ AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER, /** mouse */ AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER, /** stylus */ AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER, /** bluetooth stylus */ AINPUT_SOURCE_BLUETOOTH_STYLUS = 0x00008000 | AINPUT_SOURCE_STYLUS, /** trackball */ AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION, /** mouse relative */ AINPUT_SOURCE_MOUSE_RELATIVE = 0x00020000 | AINPUT_SOURCE_CLASS_NAVIGATION, /** touchpad */ AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION, /** navigation */ AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE, /** joystick */ AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, /** rotary encoder */ AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, /** any */ AINPUT_SOURCE_ANY = 0xffffff00, }; /** * Keyboard types. * * Refer to the documentation on android.view.InputDevice for more details. */ enum { /** none */ AINPUT_KEYBOARD_TYPE_NONE = 0, /** non alphabetic */ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC = 1, /** alphabetic */ AINPUT_KEYBOARD_TYPE_ALPHABETIC = 2, }; /** * Constants used to retrieve information about the range of motion for a particular * coordinate of a motion event. * * Refer to the documentation on android.view.InputDevice for more details about input sources * and their correct interpretation. * * @deprecated These constants are deprecated. Use {@link AMOTION_EVENT_AXIS AMOTION_EVENT_AXIS_*} constants instead. */ enum { /** x */ AINPUT_MOTION_RANGE_X = AMOTION_EVENT_AXIS_X, /** y */ AINPUT_MOTION_RANGE_Y = AMOTION_EVENT_AXIS_Y, /** pressure */ AINPUT_MOTION_RANGE_PRESSURE = AMOTION_EVENT_AXIS_PRESSURE, /** size */ AINPUT_MOTION_RANGE_SIZE = AMOTION_EVENT_AXIS_SIZE, /** touch major */ AINPUT_MOTION_RANGE_TOUCH_MAJOR = AMOTION_EVENT_AXIS_TOUCH_MAJOR, /** touch minor */ AINPUT_MOTION_RANGE_TOUCH_MINOR = AMOTION_EVENT_AXIS_TOUCH_MINOR, /** tool major */ AINPUT_MOTION_RANGE_TOOL_MAJOR = AMOTION_EVENT_AXIS_TOOL_MAJOR, /** tool minor */ AINPUT_MOTION_RANGE_TOOL_MINOR = AMOTION_EVENT_AXIS_TOOL_MINOR, /** orientation */ AINPUT_MOTION_RANGE_ORIENTATION = AMOTION_EVENT_AXIS_ORIENTATION, }; /** * Input event accessors. * * Note that most functions can only be used on input events that are of a given type. * Calling these functions on input events of other types will yield undefined behavior. */ /*** Accessors for all input events. ***/ /** Get the input event type. */ int32_t AInputEvent_getType(const AInputEvent* event); /** Get the id for the device that an input event came from. * * Input events can be generated by multiple different input devices. * Use the input device id to obtain information about the input * device that was responsible for generating a particular event. * * An input device id of 0 indicates that the event didn't come from a physical device; * other numbers are arbitrary and you shouldn't depend on the values. * Use the provided input device query API to obtain information about input devices. */ int32_t AInputEvent_getDeviceId(const AInputEvent* event); /** Get the input event source. */ int32_t AInputEvent_getSource(const AInputEvent* event); /*** Accessors for key events only. ***/ /** Get the key event action. */ int32_t AKeyEvent_getAction(const AInputEvent* key_event); /** Get the key event flags. */ int32_t AKeyEvent_getFlags(const AInputEvent* key_event); /** * Get the key code of the key event. * This is the physical key that was pressed, not the Unicode character. */ int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event); /** * Get the hardware key id of this key event. * These values are not reliable and vary from device to device. */ int32_t AKeyEvent_getScanCode(const AInputEvent* key_event); /** Get the meta key state. */ int32_t AKeyEvent_getMetaState(const AInputEvent* key_event); /** * Get the repeat count of the event. * For both key up an key down events, this is the number of times the key has * repeated with the first down starting at 0 and counting up from there. For * multiple key events, this is the number of down/up pairs that have occurred. */ int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event); /** * Get the time of the most recent key down event, in the * java.lang.System.nanoTime() time base. If this is a down event, * this will be the same as eventTime. * Note that when chording keys, this value is the down time of the most recently * pressed key, which may not be the same physical key of this event. */ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event); /** * Get the time this event occurred, in the * java.lang.System.nanoTime() time base. */ int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); /*** Accessors for motion events only. ***/ /** Get the combined motion event action code and pointer index. */ int32_t AMotionEvent_getAction(const AInputEvent* motion_event); /** Get the motion event flags. */ int32_t AMotionEvent_getFlags(const AInputEvent* motion_event); /** * Get the state of any meta / modifier keys that were in effect when the * event was generated. */ int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event); #if __ANDROID_API__ >= 14 /** Get the button state of all buttons that are pressed. */ int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event) __INTRODUCED_IN(14); #endif /** * Get a bitfield indicating which edges, if any, were touched by this motion event. * For touch events, clients can use this to determine if the user's finger was * touching the edge of the display. */ int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event); /** * Get the time when the user originally pressed down to start a stream of * position events, in the java.lang.System.nanoTime() time base. */ int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event); /** * Get the time when this specific event was generated, * in the java.lang.System.nanoTime() time base. */ int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event); /** * Get the X coordinate offset. * For touch events on the screen, this is the delta that was added to the raw * screen coordinates to adjust for the absolute position of the containing windows * and views. */ float AMotionEvent_getXOffset(const AInputEvent* motion_event); /** * Get the Y coordinate offset. * For touch events on the screen, this is the delta that was added to the raw * screen coordinates to adjust for the absolute position of the containing windows * and views. */ float AMotionEvent_getYOffset(const AInputEvent* motion_event); /** * Get the precision of the X coordinates being reported. * You can multiply this number with an X coordinate sample to find the * actual hardware value of the X coordinate. */ float AMotionEvent_getXPrecision(const AInputEvent* motion_event); /** * Get the precision of the Y coordinates being reported. * You can multiply this number with a Y coordinate sample to find the * actual hardware value of the Y coordinate. */ float AMotionEvent_getYPrecision(const AInputEvent* motion_event); /** * Get the number of pointers of data contained in this event. * Always >= 1. */ size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event); /** * Get the pointer identifier associated with a particular pointer * data index in this event. The identifier tells you the actual pointer * number associated with the data, accounting for individual pointers * going up and down since the start of the current gesture. */ int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index); #if __ANDROID_API__ >= 14 /** * Get the tool type of a pointer for the given pointer index. * The tool type indicates the type of tool used to make contact such as a * finger or stylus, if known. */ int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index) __INTRODUCED_IN(14); #endif /** * Get the original raw X coordinate of this event. * For touch events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. */ float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index); /** * Get the original raw X coordinate of this event. * For touch events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. */ float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current X coordinate of this event for the given pointer index. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current Y coordinate of this event for the given pointer index. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current pressure of this event for the given pointer index. * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), * although values higher than 1 may be generated depending on the calibration of * the input device. */ float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current scaled value of the approximate size for the given pointer index. * This represents some approximation of the area of the screen being * pressed; the actual value in pixels corresponding to the * touch is normalized with the device specific range of values * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. */ float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current length of the major axis of an ellipse that describes the touch area * at the point of contact for the given pointer index. */ float AMotionEvent_getTouchMajor(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current length of the minor axis of an ellipse that describes the touch area * at the point of contact for the given pointer index. */ float AMotionEvent_getTouchMinor(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current length of the major axis of an ellipse that describes the size * of the approaching tool for the given pointer index. * The tool area represents the estimated size of the finger or pen that is * touching the device independent of its actual touch area at the point of contact. */ float AMotionEvent_getToolMajor(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current length of the minor axis of an ellipse that describes the size * of the approaching tool for the given pointer index. * The tool area represents the estimated size of the finger or pen that is * touching the device independent of its actual touch area at the point of contact. */ float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_index); /** * Get the current orientation of the touch area and tool area in radians clockwise from * vertical for the given pointer index. * An angle of 0 degrees indicates that the major axis of contact is oriented * upwards, is perfectly circular or is of unknown orientation. A positive angle * indicates that the major axis of contact is oriented to the right. A negative angle * indicates that the major axis of contact is oriented to the left. * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians * (finger pointing fully right). */ float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index); #if __ANDROID_API__ >= 13 /** Get the value of the request axis for the given pointer index. */ float AMotionEvent_getAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index) __INTRODUCED_IN(13); #endif /** * Get the number of historical points in this event. These are movements that * have occurred between this event and the previous event. This only applies * to AMOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0. * Historical samples are indexed from oldest to newest. */ size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event); /** * Get the time that a historical movement occurred between this event and * the previous event, in the java.lang.System.nanoTime() time base. */ int64_t AMotionEvent_getHistoricalEventTime(const AInputEvent* motion_event, size_t history_index); /** * Get the historical raw X coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. * For touch events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ float AMotionEvent_getHistoricalRawX(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical raw Y coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. * For touch events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ float AMotionEvent_getHistoricalRawY(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical X coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ float AMotionEvent_getHistoricalX(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical Y coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ float AMotionEvent_getHistoricalY(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical pressure of this event for the given pointer index that * occurred between this event and the previous motion event. * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), * although values higher than 1 may be generated depending on the calibration of * the input device. */ float AMotionEvent_getHistoricalPressure(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the current scaled value of the approximate size for the given pointer index that * occurred between this event and the previous motion event. * This represents some approximation of the area of the screen being * pressed; the actual value in pixels corresponding to the * touch is normalized with the device specific range of values * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. */ float AMotionEvent_getHistoricalSize(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical length of the major axis of an ellipse that describes the touch area * at the point of contact for the given pointer index that * occurred between this event and the previous motion event. */ float AMotionEvent_getHistoricalTouchMajor(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical length of the minor axis of an ellipse that describes the touch area * at the point of contact for the given pointer index that * occurred between this event and the previous motion event. */ float AMotionEvent_getHistoricalTouchMinor(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical length of the major axis of an ellipse that describes the size * of the approaching tool for the given pointer index that * occurred between this event and the previous motion event. * The tool area represents the estimated size of the finger or pen that is * touching the device independent of its actual touch area at the point of contact. */ float AMotionEvent_getHistoricalToolMajor(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical length of the minor axis of an ellipse that describes the size * of the approaching tool for the given pointer index that * occurred between this event and the previous motion event. * The tool area represents the estimated size of the finger or pen that is * touching the device independent of its actual touch area at the point of contact. */ float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); /** * Get the historical orientation of the touch area and tool area in radians clockwise from * vertical for the given pointer index that * occurred between this event and the previous motion event. * An angle of 0 degrees indicates that the major axis of contact is oriented * upwards, is perfectly circular or is of unknown orientation. A positive angle * indicates that the major axis of contact is oriented to the right. A negative angle * indicates that the major axis of contact is oriented to the left. * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians * (finger pointing fully right). */ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); #if __ANDROID_API__ >= 13 /** * Get the historical value of the request axis for the given pointer index * that occurred between this event and the previous motion event. */ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index, size_t history_index) __INTRODUCED_IN(13); #endif struct AInputQueue; /** * Input queue * * An input queue is the facility through which you retrieve input * events. */ typedef struct AInputQueue AInputQueue; /** * Add this input queue to a looper for processing. See * ALooper_addFd() for information on the ident, callback, and data params. */ void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); /** * Remove the input queue from the looper it is currently attached to. */ void AInputQueue_detachLooper(AInputQueue* queue); /** * Returns true if there are one or more events available in the * input queue. Returns 1 if the queue has events; 0 if * it does not have events; and a negative value if there is an error. */ int32_t AInputQueue_hasEvents(AInputQueue* queue); /** * Returns the next available event from the queue. Returns a negative * value if no events are available or an error has occurred. */ int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent); /** * Sends the key for standard pre-dispatching -- that is, possibly deliver * it to the current IME to be consumed before the app. Returns 0 if it * was not pre-dispatched, meaning you can process it right now. If non-zero * is returned, you must abandon the current event processing and allow the * event to appear again in the event queue (if it does not get consumed during * pre-dispatching). */ int32_t AInputQueue_preDispatchEvent(AInputQueue* queue, AInputEvent* event); /** * Report that dispatching has finished with the given event. * This must be called after receiving an event with AInputQueue_get_event(). */ void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); #ifdef __cplusplus } #endif #endif // _ANDROID_INPUT_H /** @} */ include/android/keycodes.h0100644 0000000 0000000 00000071503 13756501735 014651 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Input * @{ */ /** * @file keycodes.h */ #ifndef _ANDROID_KEYCODES_H #define _ANDROID_KEYCODES_H /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ #include #ifdef __cplusplus extern "C" { #endif /** * Key codes. */ enum { /** Unknown key code. */ AKEYCODE_UNKNOWN = 0, /** Soft Left key. * Usually situated below the display on phones and used as a multi-function * feature key for selecting a software defined function shown on the bottom left * of the display. */ AKEYCODE_SOFT_LEFT = 1, /** Soft Right key. * Usually situated below the display on phones and used as a multi-function * feature key for selecting a software defined function shown on the bottom right * of the display. */ AKEYCODE_SOFT_RIGHT = 2, /** Home key. * This key is handled by the framework and is never delivered to applications. */ AKEYCODE_HOME = 3, /** Back key. */ AKEYCODE_BACK = 4, /** Call key. */ AKEYCODE_CALL = 5, /** End Call key. */ AKEYCODE_ENDCALL = 6, /** '0' key. */ AKEYCODE_0 = 7, /** '1' key. */ AKEYCODE_1 = 8, /** '2' key. */ AKEYCODE_2 = 9, /** '3' key. */ AKEYCODE_3 = 10, /** '4' key. */ AKEYCODE_4 = 11, /** '5' key. */ AKEYCODE_5 = 12, /** '6' key. */ AKEYCODE_6 = 13, /** '7' key. */ AKEYCODE_7 = 14, /** '8' key. */ AKEYCODE_8 = 15, /** '9' key. */ AKEYCODE_9 = 16, /** '*' key. */ AKEYCODE_STAR = 17, /** '#' key. */ AKEYCODE_POUND = 18, /** Directional Pad Up key. * May also be synthesized from trackball motions. */ AKEYCODE_DPAD_UP = 19, /** Directional Pad Down key. * May also be synthesized from trackball motions. */ AKEYCODE_DPAD_DOWN = 20, /** Directional Pad Left key. * May also be synthesized from trackball motions. */ AKEYCODE_DPAD_LEFT = 21, /** Directional Pad Right key. * May also be synthesized from trackball motions. */ AKEYCODE_DPAD_RIGHT = 22, /** Directional Pad Center key. * May also be synthesized from trackball motions. */ AKEYCODE_DPAD_CENTER = 23, /** Volume Up key. * Adjusts the speaker volume up. */ AKEYCODE_VOLUME_UP = 24, /** Volume Down key. * Adjusts the speaker volume down. */ AKEYCODE_VOLUME_DOWN = 25, /** Power key. */ AKEYCODE_POWER = 26, /** Camera key. * Used to launch a camera application or take pictures. */ AKEYCODE_CAMERA = 27, /** Clear key. */ AKEYCODE_CLEAR = 28, /** 'A' key. */ AKEYCODE_A = 29, /** 'B' key. */ AKEYCODE_B = 30, /** 'C' key. */ AKEYCODE_C = 31, /** 'D' key. */ AKEYCODE_D = 32, /** 'E' key. */ AKEYCODE_E = 33, /** 'F' key. */ AKEYCODE_F = 34, /** 'G' key. */ AKEYCODE_G = 35, /** 'H' key. */ AKEYCODE_H = 36, /** 'I' key. */ AKEYCODE_I = 37, /** 'J' key. */ AKEYCODE_J = 38, /** 'K' key. */ AKEYCODE_K = 39, /** 'L' key. */ AKEYCODE_L = 40, /** 'M' key. */ AKEYCODE_M = 41, /** 'N' key. */ AKEYCODE_N = 42, /** 'O' key. */ AKEYCODE_O = 43, /** 'P' key. */ AKEYCODE_P = 44, /** 'Q' key. */ AKEYCODE_Q = 45, /** 'R' key. */ AKEYCODE_R = 46, /** 'S' key. */ AKEYCODE_S = 47, /** 'T' key. */ AKEYCODE_T = 48, /** 'U' key. */ AKEYCODE_U = 49, /** 'V' key. */ AKEYCODE_V = 50, /** 'W' key. */ AKEYCODE_W = 51, /** 'X' key. */ AKEYCODE_X = 52, /** 'Y' key. */ AKEYCODE_Y = 53, /** 'Z' key. */ AKEYCODE_Z = 54, /** ',' key. */ AKEYCODE_COMMA = 55, /** '.' key. */ AKEYCODE_PERIOD = 56, /** Left Alt modifier key. */ AKEYCODE_ALT_LEFT = 57, /** Right Alt modifier key. */ AKEYCODE_ALT_RIGHT = 58, /** Left Shift modifier key. */ AKEYCODE_SHIFT_LEFT = 59, /** Right Shift modifier key. */ AKEYCODE_SHIFT_RIGHT = 60, /** Tab key. */ AKEYCODE_TAB = 61, /** Space key. */ AKEYCODE_SPACE = 62, /** Symbol modifier key. * Used to enter alternate symbols. */ AKEYCODE_SYM = 63, /** Explorer special function key. * Used to launch a browser application. */ AKEYCODE_EXPLORER = 64, /** Envelope special function key. * Used to launch a mail application. */ AKEYCODE_ENVELOPE = 65, /** Enter key. */ AKEYCODE_ENTER = 66, /** Backspace key. * Deletes characters before the insertion point, unlike {@link AKEYCODE_FORWARD_DEL}. */ AKEYCODE_DEL = 67, /** '`' (backtick) key. */ AKEYCODE_GRAVE = 68, /** '-'. */ AKEYCODE_MINUS = 69, /** '=' key. */ AKEYCODE_EQUALS = 70, /** '[' key. */ AKEYCODE_LEFT_BRACKET = 71, /** ']' key. */ AKEYCODE_RIGHT_BRACKET = 72, /** '\' key. */ AKEYCODE_BACKSLASH = 73, /** ';' key. */ AKEYCODE_SEMICOLON = 74, /** ''' (apostrophe) key. */ AKEYCODE_APOSTROPHE = 75, /** '/' key. */ AKEYCODE_SLASH = 76, /** '@' key. */ AKEYCODE_AT = 77, /** Number modifier key. * Used to enter numeric symbols. * This key is not {@link AKEYCODE_NUM_LOCK}; it is more like {@link AKEYCODE_ALT_LEFT}. */ AKEYCODE_NUM = 78, /** Headset Hook key. * Used to hang up calls and stop media. */ AKEYCODE_HEADSETHOOK = 79, /** Camera Focus key. * Used to focus the camera. */ AKEYCODE_FOCUS = 80, /** '+' key. */ AKEYCODE_PLUS = 81, /** Menu key. */ AKEYCODE_MENU = 82, /** Notification key. */ AKEYCODE_NOTIFICATION = 83, /** Search key. */ AKEYCODE_SEARCH = 84, /** Play/Pause media key. */ AKEYCODE_MEDIA_PLAY_PAUSE= 85, /** Stop media key. */ AKEYCODE_MEDIA_STOP = 86, /** Play Next media key. */ AKEYCODE_MEDIA_NEXT = 87, /** Play Previous media key. */ AKEYCODE_MEDIA_PREVIOUS = 88, /** Rewind media key. */ AKEYCODE_MEDIA_REWIND = 89, /** Fast Forward media key. */ AKEYCODE_MEDIA_FAST_FORWARD = 90, /** Mute key. * Mutes the microphone, unlike {@link AKEYCODE_VOLUME_MUTE}. */ AKEYCODE_MUTE = 91, /** Page Up key. */ AKEYCODE_PAGE_UP = 92, /** Page Down key. */ AKEYCODE_PAGE_DOWN = 93, /** Picture Symbols modifier key. * Used to switch symbol sets (Emoji, Kao-moji). */ AKEYCODE_PICTSYMBOLS = 94, /** Switch Charset modifier key. * Used to switch character sets (Kanji, Katakana). */ AKEYCODE_SWITCH_CHARSET = 95, /** A Button key. * On a game controller, the A button should be either the button labeled A * or the first button on the bottom row of controller buttons. */ AKEYCODE_BUTTON_A = 96, /** B Button key. * On a game controller, the B button should be either the button labeled B * or the second button on the bottom row of controller buttons. */ AKEYCODE_BUTTON_B = 97, /** C Button key. * On a game controller, the C button should be either the button labeled C * or the third button on the bottom row of controller buttons. */ AKEYCODE_BUTTON_C = 98, /** X Button key. * On a game controller, the X button should be either the button labeled X * or the first button on the upper row of controller buttons. */ AKEYCODE_BUTTON_X = 99, /** Y Button key. * On a game controller, the Y button should be either the button labeled Y * or the second button on the upper row of controller buttons. */ AKEYCODE_BUTTON_Y = 100, /** Z Button key. * On a game controller, the Z button should be either the button labeled Z * or the third button on the upper row of controller buttons. */ AKEYCODE_BUTTON_Z = 101, /** L1 Button key. * On a game controller, the L1 button should be either the button labeled L1 (or L) * or the top left trigger button. */ AKEYCODE_BUTTON_L1 = 102, /** R1 Button key. * On a game controller, the R1 button should be either the button labeled R1 (or R) * or the top right trigger button. */ AKEYCODE_BUTTON_R1 = 103, /** L2 Button key. * On a game controller, the L2 button should be either the button labeled L2 * or the bottom left trigger button. */ AKEYCODE_BUTTON_L2 = 104, /** R2 Button key. * On a game controller, the R2 button should be either the button labeled R2 * or the bottom right trigger button. */ AKEYCODE_BUTTON_R2 = 105, /** Left Thumb Button key. * On a game controller, the left thumb button indicates that the left (or only) * joystick is pressed. */ AKEYCODE_BUTTON_THUMBL = 106, /** Right Thumb Button key. * On a game controller, the right thumb button indicates that the right * joystick is pressed. */ AKEYCODE_BUTTON_THUMBR = 107, /** Start Button key. * On a game controller, the button labeled Start. */ AKEYCODE_BUTTON_START = 108, /** Select Button key. * On a game controller, the button labeled Select. */ AKEYCODE_BUTTON_SELECT = 109, /** Mode Button key. * On a game controller, the button labeled Mode. */ AKEYCODE_BUTTON_MODE = 110, /** Escape key. */ AKEYCODE_ESCAPE = 111, /** Forward Delete key. * Deletes characters ahead of the insertion point, unlike {@link AKEYCODE_DEL}. */ AKEYCODE_FORWARD_DEL = 112, /** Left Control modifier key. */ AKEYCODE_CTRL_LEFT = 113, /** Right Control modifier key. */ AKEYCODE_CTRL_RIGHT = 114, /** Caps Lock key. */ AKEYCODE_CAPS_LOCK = 115, /** Scroll Lock key. */ AKEYCODE_SCROLL_LOCK = 116, /** Left Meta modifier key. */ AKEYCODE_META_LEFT = 117, /** Right Meta modifier key. */ AKEYCODE_META_RIGHT = 118, /** Function modifier key. */ AKEYCODE_FUNCTION = 119, /** System Request / Print Screen key. */ AKEYCODE_SYSRQ = 120, /** Break / Pause key. */ AKEYCODE_BREAK = 121, /** Home Movement key. * Used for scrolling or moving the cursor around to the start of a line * or to the top of a list. */ AKEYCODE_MOVE_HOME = 122, /** End Movement key. * Used for scrolling or moving the cursor around to the end of a line * or to the bottom of a list. */ AKEYCODE_MOVE_END = 123, /** Insert key. * Toggles insert / overwrite edit mode. */ AKEYCODE_INSERT = 124, /** Forward key. * Navigates forward in the history stack. Complement of {@link AKEYCODE_BACK}. */ AKEYCODE_FORWARD = 125, /** Play media key. */ AKEYCODE_MEDIA_PLAY = 126, /** Pause media key. */ AKEYCODE_MEDIA_PAUSE = 127, /** Close media key. * May be used to close a CD tray, for example. */ AKEYCODE_MEDIA_CLOSE = 128, /** Eject media key. * May be used to eject a CD tray, for example. */ AKEYCODE_MEDIA_EJECT = 129, /** Record media key. */ AKEYCODE_MEDIA_RECORD = 130, /** F1 key. */ AKEYCODE_F1 = 131, /** F2 key. */ AKEYCODE_F2 = 132, /** F3 key. */ AKEYCODE_F3 = 133, /** F4 key. */ AKEYCODE_F4 = 134, /** F5 key. */ AKEYCODE_F5 = 135, /** F6 key. */ AKEYCODE_F6 = 136, /** F7 key. */ AKEYCODE_F7 = 137, /** F8 key. */ AKEYCODE_F8 = 138, /** F9 key. */ AKEYCODE_F9 = 139, /** F10 key. */ AKEYCODE_F10 = 140, /** F11 key. */ AKEYCODE_F11 = 141, /** F12 key. */ AKEYCODE_F12 = 142, /** Num Lock key. * This is the Num Lock key; it is different from {@link AKEYCODE_NUM}. * This key alters the behavior of other keys on the numeric keypad. */ AKEYCODE_NUM_LOCK = 143, /** Numeric keypad '0' key. */ AKEYCODE_NUMPAD_0 = 144, /** Numeric keypad '1' key. */ AKEYCODE_NUMPAD_1 = 145, /** Numeric keypad '2' key. */ AKEYCODE_NUMPAD_2 = 146, /** Numeric keypad '3' key. */ AKEYCODE_NUMPAD_3 = 147, /** Numeric keypad '4' key. */ AKEYCODE_NUMPAD_4 = 148, /** Numeric keypad '5' key. */ AKEYCODE_NUMPAD_5 = 149, /** Numeric keypad '6' key. */ AKEYCODE_NUMPAD_6 = 150, /** Numeric keypad '7' key. */ AKEYCODE_NUMPAD_7 = 151, /** Numeric keypad '8' key. */ AKEYCODE_NUMPAD_8 = 152, /** Numeric keypad '9' key. */ AKEYCODE_NUMPAD_9 = 153, /** Numeric keypad '/' key (for division). */ AKEYCODE_NUMPAD_DIVIDE = 154, /** Numeric keypad '*' key (for multiplication). */ AKEYCODE_NUMPAD_MULTIPLY = 155, /** Numeric keypad '-' key (for subtraction). */ AKEYCODE_NUMPAD_SUBTRACT = 156, /** Numeric keypad '+' key (for addition). */ AKEYCODE_NUMPAD_ADD = 157, /** Numeric keypad '.' key (for decimals or digit grouping). */ AKEYCODE_NUMPAD_DOT = 158, /** Numeric keypad ',' key (for decimals or digit grouping). */ AKEYCODE_NUMPAD_COMMA = 159, /** Numeric keypad Enter key. */ AKEYCODE_NUMPAD_ENTER = 160, /** Numeric keypad '=' key. */ AKEYCODE_NUMPAD_EQUALS = 161, /** Numeric keypad '(' key. */ AKEYCODE_NUMPAD_LEFT_PAREN = 162, /** Numeric keypad ')' key. */ AKEYCODE_NUMPAD_RIGHT_PAREN = 163, /** Volume Mute key. * Mutes the speaker, unlike {@link AKEYCODE_MUTE}. * This key should normally be implemented as a toggle such that the first press * mutes the speaker and the second press restores the original volume. */ AKEYCODE_VOLUME_MUTE = 164, /** Info key. * Common on TV remotes to show additional information related to what is * currently being viewed. */ AKEYCODE_INFO = 165, /** Channel up key. * On TV remotes, increments the television channel. */ AKEYCODE_CHANNEL_UP = 166, /** Channel down key. * On TV remotes, decrements the television channel. */ AKEYCODE_CHANNEL_DOWN = 167, /** Zoom in key. */ AKEYCODE_ZOOM_IN = 168, /** Zoom out key. */ AKEYCODE_ZOOM_OUT = 169, /** TV key. * On TV remotes, switches to viewing live TV. */ AKEYCODE_TV = 170, /** Window key. * On TV remotes, toggles picture-in-picture mode or other windowing functions. */ AKEYCODE_WINDOW = 171, /** Guide key. * On TV remotes, shows a programming guide. */ AKEYCODE_GUIDE = 172, /** DVR key. * On some TV remotes, switches to a DVR mode for recorded shows. */ AKEYCODE_DVR = 173, /** Bookmark key. * On some TV remotes, bookmarks content or web pages. */ AKEYCODE_BOOKMARK = 174, /** Toggle captions key. * Switches the mode for closed-captioning text, for example during television shows. */ AKEYCODE_CAPTIONS = 175, /** Settings key. * Starts the system settings activity. */ AKEYCODE_SETTINGS = 176, /** TV power key. * On TV remotes, toggles the power on a television screen. */ AKEYCODE_TV_POWER = 177, /** TV input key. * On TV remotes, switches the input on a television screen. */ AKEYCODE_TV_INPUT = 178, /** Set-top-box power key. * On TV remotes, toggles the power on an external Set-top-box. */ AKEYCODE_STB_POWER = 179, /** Set-top-box input key. * On TV remotes, switches the input mode on an external Set-top-box. */ AKEYCODE_STB_INPUT = 180, /** A/V Receiver power key. * On TV remotes, toggles the power on an external A/V Receiver. */ AKEYCODE_AVR_POWER = 181, /** A/V Receiver input key. * On TV remotes, switches the input mode on an external A/V Receiver. */ AKEYCODE_AVR_INPUT = 182, /** Red "programmable" key. * On TV remotes, acts as a contextual/programmable key. */ AKEYCODE_PROG_RED = 183, /** Green "programmable" key. * On TV remotes, actsas a contextual/programmable key. */ AKEYCODE_PROG_GREEN = 184, /** Yellow "programmable" key. * On TV remotes, acts as a contextual/programmable key. */ AKEYCODE_PROG_YELLOW = 185, /** Blue "programmable" key. * On TV remotes, acts as a contextual/programmable key. */ AKEYCODE_PROG_BLUE = 186, /** App switch key. * Should bring up the application switcher dialog. */ AKEYCODE_APP_SWITCH = 187, /** Generic Game Pad Button #1.*/ AKEYCODE_BUTTON_1 = 188, /** Generic Game Pad Button #2.*/ AKEYCODE_BUTTON_2 = 189, /** Generic Game Pad Button #3.*/ AKEYCODE_BUTTON_3 = 190, /** Generic Game Pad Button #4.*/ AKEYCODE_BUTTON_4 = 191, /** Generic Game Pad Button #5.*/ AKEYCODE_BUTTON_5 = 192, /** Generic Game Pad Button #6.*/ AKEYCODE_BUTTON_6 = 193, /** Generic Game Pad Button #7.*/ AKEYCODE_BUTTON_7 = 194, /** Generic Game Pad Button #8.*/ AKEYCODE_BUTTON_8 = 195, /** Generic Game Pad Button #9.*/ AKEYCODE_BUTTON_9 = 196, /** Generic Game Pad Button #10.*/ AKEYCODE_BUTTON_10 = 197, /** Generic Game Pad Button #11.*/ AKEYCODE_BUTTON_11 = 198, /** Generic Game Pad Button #12.*/ AKEYCODE_BUTTON_12 = 199, /** Generic Game Pad Button #13.*/ AKEYCODE_BUTTON_13 = 200, /** Generic Game Pad Button #14.*/ AKEYCODE_BUTTON_14 = 201, /** Generic Game Pad Button #15.*/ AKEYCODE_BUTTON_15 = 202, /** Generic Game Pad Button #16.*/ AKEYCODE_BUTTON_16 = 203, /** Language Switch key. * Toggles the current input language such as switching between English and Japanese on * a QWERTY keyboard. On some devices, the same function may be performed by * pressing Shift+Spacebar. */ AKEYCODE_LANGUAGE_SWITCH = 204, /** Manner Mode key. * Toggles silent or vibrate mode on and off to make the device behave more politely * in certain settings such as on a crowded train. On some devices, the key may only * operate when long-pressed. */ AKEYCODE_MANNER_MODE = 205, /** 3D Mode key. * Toggles the display between 2D and 3D mode. */ AKEYCODE_3D_MODE = 206, /** Contacts special function key. * Used to launch an address book application. */ AKEYCODE_CONTACTS = 207, /** Calendar special function key. * Used to launch a calendar application. */ AKEYCODE_CALENDAR = 208, /** Music special function key. * Used to launch a music player application. */ AKEYCODE_MUSIC = 209, /** Calculator special function key. * Used to launch a calculator application. */ AKEYCODE_CALCULATOR = 210, /** Japanese full-width / half-width key. */ AKEYCODE_ZENKAKU_HANKAKU = 211, /** Japanese alphanumeric key. */ AKEYCODE_EISU = 212, /** Japanese non-conversion key. */ AKEYCODE_MUHENKAN = 213, /** Japanese conversion key. */ AKEYCODE_HENKAN = 214, /** Japanese katakana / hiragana key. */ AKEYCODE_KATAKANA_HIRAGANA = 215, /** Japanese Yen key. */ AKEYCODE_YEN = 216, /** Japanese Ro key. */ AKEYCODE_RO = 217, /** Japanese kana key. */ AKEYCODE_KANA = 218, /** Assist key. * Launches the global assist activity. Not delivered to applications. */ AKEYCODE_ASSIST = 219, /** Brightness Down key. * Adjusts the screen brightness down. */ AKEYCODE_BRIGHTNESS_DOWN = 220, /** Brightness Up key. * Adjusts the screen brightness up. */ AKEYCODE_BRIGHTNESS_UP = 221, /** Audio Track key. * Switches the audio tracks. */ AKEYCODE_MEDIA_AUDIO_TRACK = 222, /** Sleep key. * Puts the device to sleep. Behaves somewhat like {@link AKEYCODE_POWER} but it * has no effect if the device is already asleep. */ AKEYCODE_SLEEP = 223, /** Wakeup key. * Wakes up the device. Behaves somewhat like {@link AKEYCODE_POWER} but it * has no effect if the device is already awake. */ AKEYCODE_WAKEUP = 224, /** Pairing key. * Initiates peripheral pairing mode. Useful for pairing remote control * devices or game controllers, especially if no other input mode is * available. */ AKEYCODE_PAIRING = 225, /** Media Top Menu key. * Goes to the top of media menu. */ AKEYCODE_MEDIA_TOP_MENU = 226, /** '11' key. */ AKEYCODE_11 = 227, /** '12' key. */ AKEYCODE_12 = 228, /** Last Channel key. * Goes to the last viewed channel. */ AKEYCODE_LAST_CHANNEL = 229, /** TV data service key. * Displays data services like weather, sports. */ AKEYCODE_TV_DATA_SERVICE = 230, /** Voice Assist key. * Launches the global voice assist activity. Not delivered to applications. */ AKEYCODE_VOICE_ASSIST = 231, /** Radio key. * Toggles TV service / Radio service. */ AKEYCODE_TV_RADIO_SERVICE = 232, /** Teletext key. * Displays Teletext service. */ AKEYCODE_TV_TELETEXT = 233, /** Number entry key. * Initiates to enter multi-digit channel nubmber when each digit key is assigned * for selecting separate channel. Corresponds to Number Entry Mode (0x1D) of CEC * User Control Code. */ AKEYCODE_TV_NUMBER_ENTRY = 234, /** Analog Terrestrial key. * Switches to analog terrestrial broadcast service. */ AKEYCODE_TV_TERRESTRIAL_ANALOG = 235, /** Digital Terrestrial key. * Switches to digital terrestrial broadcast service. */ AKEYCODE_TV_TERRESTRIAL_DIGITAL = 236, /** Satellite key. * Switches to digital satellite broadcast service. */ AKEYCODE_TV_SATELLITE = 237, /** BS key. * Switches to BS digital satellite broadcasting service available in Japan. */ AKEYCODE_TV_SATELLITE_BS = 238, /** CS key. * Switches to CS digital satellite broadcasting service available in Japan. */ AKEYCODE_TV_SATELLITE_CS = 239, /** BS/CS key. * Toggles between BS and CS digital satellite services. */ AKEYCODE_TV_SATELLITE_SERVICE = 240, /** Toggle Network key. * Toggles selecting broacast services. */ AKEYCODE_TV_NETWORK = 241, /** Antenna/Cable key. * Toggles broadcast input source between antenna and cable. */ AKEYCODE_TV_ANTENNA_CABLE = 242, /** HDMI #1 key. * Switches to HDMI input #1. */ AKEYCODE_TV_INPUT_HDMI_1 = 243, /** HDMI #2 key. * Switches to HDMI input #2. */ AKEYCODE_TV_INPUT_HDMI_2 = 244, /** HDMI #3 key. * Switches to HDMI input #3. */ AKEYCODE_TV_INPUT_HDMI_3 = 245, /** HDMI #4 key. * Switches to HDMI input #4. */ AKEYCODE_TV_INPUT_HDMI_4 = 246, /** Composite #1 key. * Switches to composite video input #1. */ AKEYCODE_TV_INPUT_COMPOSITE_1 = 247, /** Composite #2 key. * Switches to composite video input #2. */ AKEYCODE_TV_INPUT_COMPOSITE_2 = 248, /** Component #1 key. * Switches to component video input #1. */ AKEYCODE_TV_INPUT_COMPONENT_1 = 249, /** Component #2 key. * Switches to component video input #2. */ AKEYCODE_TV_INPUT_COMPONENT_2 = 250, /** VGA #1 key. * Switches to VGA (analog RGB) input #1. */ AKEYCODE_TV_INPUT_VGA_1 = 251, /** Audio description key. * Toggles audio description off / on. */ AKEYCODE_TV_AUDIO_DESCRIPTION = 252, /** Audio description mixing volume up key. * Louden audio description volume as compared with normal audio volume. */ AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253, /** Audio description mixing volume down key. * Lessen audio description volume as compared with normal audio volume. */ AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254, /** Zoom mode key. * Changes Zoom mode (Normal, Full, Zoom, Wide-zoom, etc.) */ AKEYCODE_TV_ZOOM_MODE = 255, /** Contents menu key. * Goes to the title list. Corresponds to Contents Menu (0x0B) of CEC User Control * Code */ AKEYCODE_TV_CONTENTS_MENU = 256, /** Media context menu key. * Goes to the context menu of media contents. Corresponds to Media Context-sensitive * Menu (0x11) of CEC User Control Code. */ AKEYCODE_TV_MEDIA_CONTEXT_MENU = 257, /** Timer programming key. * Goes to the timer recording menu. Corresponds to Timer Programming (0x54) of * CEC User Control Code. */ AKEYCODE_TV_TIMER_PROGRAMMING = 258, /** Help key. */ AKEYCODE_HELP = 259, AKEYCODE_NAVIGATE_PREVIOUS = 260, AKEYCODE_NAVIGATE_NEXT = 261, AKEYCODE_NAVIGATE_IN = 262, AKEYCODE_NAVIGATE_OUT = 263, /** Primary stem key for Wear * Main power/reset button on watch. */ AKEYCODE_STEM_PRIMARY = 264, /** Generic stem key 1 for Wear */ AKEYCODE_STEM_1 = 265, /** Generic stem key 2 for Wear */ AKEYCODE_STEM_2 = 266, /** Generic stem key 3 for Wear */ AKEYCODE_STEM_3 = 267, /** Directional Pad Up-Left */ AKEYCODE_DPAD_UP_LEFT = 268, /** Directional Pad Down-Left */ AKEYCODE_DPAD_DOWN_LEFT = 269, /** Directional Pad Up-Right */ AKEYCODE_DPAD_UP_RIGHT = 270, /** Directional Pad Down-Right */ AKEYCODE_DPAD_DOWN_RIGHT = 271, /** Skip forward media key */ AKEYCODE_MEDIA_SKIP_FORWARD = 272, /** Skip backward media key */ AKEYCODE_MEDIA_SKIP_BACKWARD = 273, /** Step forward media key. * Steps media forward one from at a time. */ AKEYCODE_MEDIA_STEP_FORWARD = 274, /** Step backward media key. * Steps media backward one from at a time. */ AKEYCODE_MEDIA_STEP_BACKWARD = 275, /** Put device to sleep unless a wakelock is held. */ AKEYCODE_SOFT_SLEEP = 276, /** Cut key. */ AKEYCODE_CUT = 277, /** Copy key. */ AKEYCODE_COPY = 278, /** Paste key. */ AKEYCODE_PASTE = 279, /** fingerprint navigation key, up. */ AKEYCODE_SYSTEM_NAVIGATION_UP = 280, /** fingerprint navigation key, down. */ AKEYCODE_SYSTEM_NAVIGATION_DOWN = 281, /** fingerprint navigation key, left. */ AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282, /** fingerprint navigation key, right. */ AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283, /** all apps */ AKEYCODE_ALL_APPS = 284, /** refresh key */ AKEYCODE_REFRESH = 285, /** Thumbs up key. Apps can use this to let user upvote content. */ AKEYCODE_THUMBS_UP = 286, /** Thumbs down key. Apps can use this to let user downvote content. */ AKEYCODE_THUMBS_DOWN = 287, /** Used to switch current account that is consuming content. * May be consumed by system to switch current viewer profile. */ AKEYCODE_PROFILE_SWITCH = 288 // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. }; #ifdef __cplusplus } #endif #endif // _ANDROID_KEYCODES_H /** @} */ include/android/looper.h0100644 0000000 0000000 00000022222 13756501735 014335 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Looper * @{ */ /** * @file looper.h */ #ifndef ANDROID_LOOPER_H #define ANDROID_LOOPER_H #ifdef __cplusplus extern "C" { #endif struct ALooper; /** * ALooper * * A looper is the state tracking an event loop for a thread. * Loopers do not define event structures or other such things; rather * they are a lower-level facility to attach one or more discrete objects * listening for an event. An "event" here is simply data available on * a file descriptor: each attached object has an associated file descriptor, * and waiting for "events" means (internally) polling on all of these file * descriptors until one or more of them have data available. * * A thread can have only one ALooper associated with it. */ typedef struct ALooper ALooper; /** * Returns the looper associated with the calling thread, or NULL if * there is not one. */ ALooper* ALooper_forThread(); /** Option for for ALooper_prepare(). */ enum { /** * This looper will accept calls to ALooper_addFd() that do not * have a callback (that is provide NULL for the callback). In * this case the caller of ALooper_pollOnce() or ALooper_pollAll() * MUST check the return from these functions to discover when * data is available on such fds and process it. */ ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1<<0 }; /** * Prepares a looper associated with the calling thread, and returns it. * If the thread already has a looper, it is returned. Otherwise, a new * one is created, associated with the thread, and returned. * * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. */ ALooper* ALooper_prepare(int opts); /** Result from ALooper_pollOnce() and ALooper_pollAll(). */ enum { /** * The poll was awoken using wake() before the timeout expired * and no callbacks were executed and no other file descriptors were ready. */ ALOOPER_POLL_WAKE = -1, /** * Result from ALooper_pollOnce() and ALooper_pollAll(): * One or more callbacks were executed. */ ALOOPER_POLL_CALLBACK = -2, /** * Result from ALooper_pollOnce() and ALooper_pollAll(): * The timeout expired. */ ALOOPER_POLL_TIMEOUT = -3, /** * Result from ALooper_pollOnce() and ALooper_pollAll(): * An error occurred. */ ALOOPER_POLL_ERROR = -4, }; /** * Acquire a reference on the given ALooper object. This prevents the object * from being deleted until the reference is removed. This is only needed * to safely hand an ALooper from one thread to another. */ void ALooper_acquire(ALooper* looper); /** * Remove a reference that was previously acquired with ALooper_acquire(). */ void ALooper_release(ALooper* looper); /** * Flags for file descriptor events that a looper can monitor. * * These flag bits can be combined to monitor multiple events at once. */ enum { /** * The file descriptor is available for read operations. */ ALOOPER_EVENT_INPUT = 1 << 0, /** * The file descriptor is available for write operations. */ ALOOPER_EVENT_OUTPUT = 1 << 1, /** * The file descriptor has encountered an error condition. * * The looper always sends notifications about errors; it is not necessary * to specify this event flag in the requested event set. */ ALOOPER_EVENT_ERROR = 1 << 2, /** * The file descriptor was hung up. * For example, indicates that the remote end of a pipe or socket was closed. * * The looper always sends notifications about hangups; it is not necessary * to specify this event flag in the requested event set. */ ALOOPER_EVENT_HANGUP = 1 << 3, /** * The file descriptor is invalid. * For example, the file descriptor was closed prematurely. * * The looper always sends notifications about invalid file descriptors; it is not necessary * to specify this event flag in the requested event set. */ ALOOPER_EVENT_INVALID = 1 << 4, }; /** * For callback-based event loops, this is the prototype of the function * that is called when a file descriptor event occurs. * It is given the file descriptor it is associated with, * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), * and the data pointer that was originally supplied. * * Implementations should return 1 to continue receiving callbacks, or 0 * to have this file descriptor and callback unregistered from the looper. */ typedef int (*ALooper_callbackFunc)(int fd, int events, void* data); /** * Waits for events to be available, with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. * * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before * the timeout expired and no callbacks were invoked and no other file * descriptors were ready. * * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. * * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given * timeout expired. * * Returns ALOOPER_POLL_ERROR if an error occurred. * * Returns a value >= 0 containing an identifier (the same identifier * `ident` passed to ALooper_addFd()) if its file descriptor has data * and it has no callback function (requiring the caller here to * handle it). In this (and only this) case outFd, outEvents and * outData will contain the poll events and data associated with the * fd, otherwise they will be set to NULL. * * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); /** * Like ALooper_pollOnce(), but performs all pending callbacks until all * data has been consumed or a file descriptor is available with no callback. * This function will never return ALOOPER_POLL_CALLBACK. */ int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); /** * Wakes the poll asynchronously. * * This method can be called on any thread. * This method returns immediately. */ void ALooper_wake(ALooper* looper); /** * Adds a new file descriptor to be polled by the looper. * If the same file descriptor was previously added, it is replaced. * * "fd" is the file descriptor to be added. * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. * "callback" is the function to call when there is an event on the file descriptor. * "data" is a private data pointer to supply to the callback. * * There are two main uses of this function: * * (1) If "callback" is non-NULL, then this function will be called when there is * data on the file descriptor. It should execute any events it has pending, * appropriately reading from the file descriptor. The 'ident' is ignored in this case. * * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce * when its file descriptor has data available, requiring the caller to take * care of processing it. * * Returns 1 if the file descriptor was added or -1 if an error occurred. * * This method can be called on any thread. * This method may block briefly if it needs to wake the poll. */ int ALooper_addFd(ALooper* looper, int fd, int ident, int events, ALooper_callbackFunc callback, void* data); /** * Removes a previously added file descriptor from the looper. * * When this method returns, it is safe to close the file descriptor since the looper * will no longer have a reference to it. However, it is possible for the callback to * already be running or for it to run one last time if the file descriptor was already * signalled. Calling code is responsible for ensuring that this case is safely handled. * For example, if the callback takes care of removing itself during its own execution either * by returning 0 or by calling this method, then it can be guaranteed to not be invoked * again at any later time unless registered anew. * * Returns 1 if the file descriptor was removed, 0 if none was previously registered * or -1 if an error occurred. * * This method can be called on any thread. * This method may block briefly if it needs to wake the poll. */ int ALooper_removeFd(ALooper* looper, int fd); #ifdef __cplusplus }; #endif #endif // ANDROID_LOOPER_H /** @} */ include/android/multinetwork.h0100644 0000000 0000000 00000014270 13756501735 015605 0ustar000000000 0000000 /* * 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. */ /** * @addtogroup Networking * @{ */ /** * @file multinetwork.h */ #ifndef ANDROID_MULTINETWORK_H #define ANDROID_MULTINETWORK_H #include #include #include __BEGIN_DECLS /** * The corresponding C type for android.net.Network#getNetworkHandle() return * values. The Java signed long value can be safely cast to a net_handle_t: * * [C] ((net_handle_t) java_long_network_handle) * [C++] static_cast(java_long_network_handle) * * as appropriate. */ typedef uint64_t net_handle_t; /** * The value NETWORK_UNSPECIFIED indicates no specific network. * * For some functions (documented below), a previous binding may be cleared * by an invocation with NETWORK_UNSPECIFIED. * * Depending on the context it may indicate an error. It is expressly * not used to indicate some notion of the "current default network". */ #define NETWORK_UNSPECIFIED ((net_handle_t)0) /** * All functions below that return an int return 0 on success or -1 * on failure with an appropriate errno value set. */ #if __ANDROID_API__ >= 23 /** * Set the network to be used by the given socket file descriptor. * * To clear a previous socket binding, invoke with NETWORK_UNSPECIFIED. * * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)) * */ int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23); /** * Binds the current process to |network|. All sockets created in the future * (and not explicitly bound via android_setsocknetwork()) will be bound to * |network|. All host name resolutions will be limited to |network| as well. * Note that if the network identified by |network| ever disconnects, all * sockets created in this way will cease to work and all host name * resolutions will fail. This is by design so an application doesn't * accidentally use sockets it thinks are still bound to a particular network. * * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED. * * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)) * */ int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); /** * Perform hostname resolution via the DNS servers associated with |network|. * * All arguments (apart from |network|) are used identically as those passed * to getaddrinfo(3). Return and error values are identical to those of * getaddrinfo(3), and in particular gai_strerror(3) can be used as expected. * Similar to getaddrinfo(3): * - |hints| may be NULL (in which case man page documented defaults apply) * - either |node| or |service| may be NULL, but not both * - |res| must not be NULL * * This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String)) * */ int android_getaddrinfofornetwork(net_handle_t network, const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) __INTRODUCED_IN(23); #endif /* __ANDROID_API__ >= 23 */ #if __ANDROID_API__ >= 29 /** * Possible values of the flags argument to android_res_nsend and android_res_nquery. * Values are ORed together. */ enum ResNsendFlags : uint32_t { /** * Send a single request to a single resolver and fail on timeout or network errors */ ANDROID_RESOLV_NO_RETRY = 1 << 0, /** * Do not cache the result of the lookup. The lookup may return a result that is already * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified. */ ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1, /** * Don't lookup the request in cache. */ ANDROID_RESOLV_NO_CACHE_LOOKUP = 1 << 2, }; /** * Look up the {|ns_class|, |ns_type|} Resource Record (RR) associated * with Domain Name |dname| on the given |network|. * The typical value for |ns_class| is ns_c_in, while |type| can be any * record type (for instance, ns_t_aaaa or ns_t_txt). * |flags| is a additional config to control actual querying behavior, see * ResNsendFlags for detail. * * Returns a file descriptor to watch for read events, or a negative * POSIX error code (see errno.h) if an immediate error occurs. */ int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type, uint32_t flags) __INTRODUCED_IN(29); /** * Issue the query |msg| on the given |network|. * |flags| is a additional config to control actual querying behavior, see * ResNsendFlags for detail. * * Returns a file descriptor to watch for read events, or a negative * POSIX error code (see errno.h) if an immediate error occurs. */ int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen, uint32_t flags) __INTRODUCED_IN(29); /** * Read a result for the query associated with the |fd| descriptor. * Closes |fd| before returning. * * Returns: * < 0: negative POSIX error code (see errno.h for possible values). |rcode| is not set. * >= 0: length of |answer|. |rcode| is the resolver return code (e.g., ns_r_nxdomain) */ int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) __INTRODUCED_IN(29); /** * Attempts to cancel the in-progress query associated with the |nsend_fd| * descriptor. */ void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29); #endif /* __ANDROID_API__ >= 29 */ __END_DECLS #endif // ANDROID_MULTINETWORK_H /** @} */ include/android/native_activity.h0100644 0000000 0000000 00000027004 13756501735 016242 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup NativeActivity Native Activity * @{ */ /** * @file native_activity.h */ #ifndef ANDROID_NATIVE_ACTIVITY_H #define ANDROID_NATIVE_ACTIVITY_H #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** * {@link ANativeActivityCallbacks} */ struct ANativeActivityCallbacks; /** * This structure defines the native side of an android.app.NativeActivity. * It is created by the framework, and handed to the application's native * code as it is being launched. */ typedef struct ANativeActivity { /** * Pointer to the callback function table of the native application. * You can set the functions here to your own callbacks. The callbacks * pointer itself here should not be changed; it is allocated and managed * for you by the framework. */ struct ANativeActivityCallbacks* callbacks; /** * The global handle on the process's Java VM. */ JavaVM* vm; /** * JNI context for the main thread of the app. Note that this field * can ONLY be used from the main thread of the process; that is, the * thread that calls into the ANativeActivityCallbacks. */ JNIEnv* env; /** * The NativeActivity object handle. * * IMPORTANT NOTE: This member is mis-named. It should really be named * 'activity' instead of 'clazz', since it's a reference to the * NativeActivity instance created by the system for you. * * We unfortunately cannot change this without breaking NDK * source-compatibility. */ jobject clazz; /** * Path to this application's internal data directory. */ const char* internalDataPath; /** * Path to this application's external (removable/mountable) data directory. */ const char* externalDataPath; /** * The platform's SDK version code. */ int32_t sdkVersion; /** * This is the native instance of the application. It is not used by * the framework, but can be set by the application to its own instance * state. */ void* instance; /** * Pointer to the Asset Manager instance for the application. The application * uses this to access binary assets bundled inside its own .apk file. */ AAssetManager* assetManager; /** * Available starting with Honeycomb: path to the directory containing * the application's OBB files (if any). If the app doesn't have any * OBB files, this directory may not exist. */ const char* obbPath; } ANativeActivity; /** * These are the callbacks the framework makes into a native application. * All of these callbacks happen on the main thread of the application. * By default, all callbacks are NULL; set to a pointer to your own function * to have it called. */ typedef struct ANativeActivityCallbacks { /** * NativeActivity has started. See Java documentation for Activity.onStart() * for more information. */ void (*onStart)(ANativeActivity* activity); /** * NativeActivity has resumed. See Java documentation for Activity.onResume() * for more information. */ void (*onResume)(ANativeActivity* activity); /** * Framework is asking NativeActivity to save its current instance state. * See Java documentation for Activity.onSaveInstanceState() for more * information. The returned pointer needs to be created with malloc(); * the framework will call free() on it for you. You also must fill in * outSize with the number of bytes in the allocation. Note that the * saved state will be persisted, so it can not contain any active * entities (pointers to memory, file descriptors, etc). */ void* (*onSaveInstanceState)(ANativeActivity* activity, size_t* outSize); /** * NativeActivity has paused. See Java documentation for Activity.onPause() * for more information. */ void (*onPause)(ANativeActivity* activity); /** * NativeActivity has stopped. See Java documentation for Activity.onStop() * for more information. */ void (*onStop)(ANativeActivity* activity); /** * NativeActivity is being destroyed. See Java documentation for Activity.onDestroy() * for more information. */ void (*onDestroy)(ANativeActivity* activity); /** * Focus has changed in this NativeActivity's window. This is often used, * for example, to pause a game when it loses input focus. */ void (*onWindowFocusChanged)(ANativeActivity* activity, int hasFocus); /** * The drawing window for this native activity has been created. You * can use the given native window object to start drawing. */ void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window); /** * The drawing window for this native activity has been resized. You should * retrieve the new size from the window and ensure that your rendering in * it now matches. */ void (*onNativeWindowResized)(ANativeActivity* activity, ANativeWindow* window); /** * The drawing window for this native activity needs to be redrawn. To avoid * transient artifacts during screen changes (such resizing after rotation), * applications should not return from this function until they have finished * drawing their window in its current state. */ void (*onNativeWindowRedrawNeeded)(ANativeActivity* activity, ANativeWindow* window); /** * The drawing window for this native activity is going to be destroyed. * You MUST ensure that you do not touch the window object after returning * from this function: in the common case of drawing to the window from * another thread, that means the implementation of this callback must * properly synchronize with the other thread to stop its drawing before * returning from here. */ void (*onNativeWindowDestroyed)(ANativeActivity* activity, ANativeWindow* window); /** * The input queue for this native activity's window has been created. * You can use the given input queue to start retrieving input events. */ void (*onInputQueueCreated)(ANativeActivity* activity, AInputQueue* queue); /** * The input queue for this native activity's window is being destroyed. * You should no longer try to reference this object upon returning from this * function. */ void (*onInputQueueDestroyed)(ANativeActivity* activity, AInputQueue* queue); /** * The rectangle in the window in which content should be placed has changed. */ void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect); /** * The current device AConfiguration has changed. The new configuration can * be retrieved from assetManager. */ void (*onConfigurationChanged)(ANativeActivity* activity); /** * The system is running low on memory. Use this callback to release * resources you do not need, to help the system avoid killing more * important processes. */ void (*onLowMemory)(ANativeActivity* activity); } ANativeActivityCallbacks; /** * This is the function that must be in the native code to instantiate the * application's native activity. It is called with the activity instance (see * above); if the code is being instantiated from a previously saved instance, * the savedState will be non-NULL and point to the saved data. You must make * any copy of this data you need -- it will be released after you return from * this function. */ typedef void ANativeActivity_createFunc(ANativeActivity* activity, void* savedState, size_t savedStateSize); /** * The name of the function that NativeInstance looks for when launching its * native code. This is the default function that is used, you can specify * "android.app.func_name" string meta-data in your manifest to use a different * function. */ extern ANativeActivity_createFunc ANativeActivity_onCreate; /** * Finish the given activity. Its finish() method will be called, causing it * to be stopped and destroyed. Note that this method can be called from * *any* thread; it will send a message to the main thread of the process * where the Java finish call will take place. */ void ANativeActivity_finish(ANativeActivity* activity); /** * Change the window format of the given activity. Calls getWindow().setFormat() * of the given activity. Note that this method can be called from * *any* thread; it will send a message to the main thread of the process * where the Java finish call will take place. */ void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format); /** * Change the window flags of the given activity. Calls getWindow().setFlags() * of the given activity. Note that this method can be called from * *any* thread; it will send a message to the main thread of the process * where the Java finish call will take place. See window.h for flag constants. */ void ANativeActivity_setWindowFlags(ANativeActivity* activity, uint32_t addFlags, uint32_t removeFlags); /** * Flags for ANativeActivity_showSoftInput; see the Java InputMethodManager * API for documentation. */ enum { /** * Implicit request to show the input window, not as the result * of a direct request by the user. */ ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = 0x0001, /** * The user has forced the input method open (such as by * long-pressing menu) so it should not be closed until they * explicitly do so. */ ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED = 0x0002, }; /** * Show the IME while in the given activity. Calls InputMethodManager.showSoftInput() * for the given activity. Note that this method can be called from * *any* thread; it will send a message to the main thread of the process * where the Java finish call will take place. */ void ANativeActivity_showSoftInput(ANativeActivity* activity, uint32_t flags); /** * Flags for ANativeActivity_hideSoftInput; see the Java InputMethodManager * API for documentation. */ enum { /** * The soft input window should only be hidden if it was not * explicitly shown by the user. */ ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = 0x0001, /** * The soft input window should normally be hidden, unless it was * originally shown with {@link ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED}. */ ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = 0x0002, }; /** * Hide the IME while in the given activity. Calls InputMethodManager.hideSoftInput() * for the given activity. Note that this method can be called from * *any* thread; it will send a message to the main thread of the process * where the Java finish call will take place. */ void ANativeActivity_hideSoftInput(ANativeActivity* activity, uint32_t flags); #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_ACTIVITY_H /** @} */ include/android/native_window_jni.h0100644 0000000 0000000 00000003407 13756501735 016556 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup NativeActivity Native Activity * @{ */ /** * @file native_window_jni.h */ #ifndef ANDROID_NATIVE_WINDOW_JNI_H #define ANDROID_NATIVE_WINDOW_JNI_H #include #include #include #ifdef __cplusplus extern "C" { #endif /** * Return the ANativeWindow associated with a Java Surface object, * for interacting with it through native code. This acquires a reference * on the ANativeWindow that is returned; be sure to use ANativeWindow_release() * when done with it so that it doesn't leak. */ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); #if __ANDROID_API__ >= 26 /** * Return a Java Surface object derived from the ANativeWindow, for interacting * with it through Java code. The returned Java object acquires a reference on * the ANativeWindow; maintains it through general Java object's life cycle; * and will automatically release the reference when the Java object gets garbage * collected. */ jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) __INTRODUCED_IN(26); #endif #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_WINDOW_H /** @} */ include/android/obb.h0100644 0000000 0000000 00000003155 13756501735 013603 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Storage * @{ */ /** * @file obb.h */ #ifndef ANDROID_OBB_H #define ANDROID_OBB_H #include #ifdef __cplusplus extern "C" { #endif struct AObbInfo; /** {@link AObbInfo} is an opaque type representing information for obb storage. */ typedef struct AObbInfo AObbInfo; /** Flag for an obb file, returned by AObbInfo_getFlags(). */ enum { /** overlay */ AOBBINFO_OVERLAY = 0x0001, }; /** * Scan an OBB and get information about it. */ AObbInfo* AObbScanner_getObbInfo(const char* filename); /** * Destroy the AObbInfo object. You must call this when finished with the object. */ void AObbInfo_delete(AObbInfo* obbInfo); /** * Get the package name for the OBB. */ const char* AObbInfo_getPackageName(AObbInfo* obbInfo); /** * Get the version of an OBB file. */ int32_t AObbInfo_getVersion(AObbInfo* obbInfo); /** * Get the flags of an OBB file. */ int32_t AObbInfo_getFlags(AObbInfo* obbInfo); #ifdef __cplusplus }; #endif #endif // ANDROID_OBB_H /** @} */ include/android/sensor.h0100644 0000000 0000000 00000073333 13756501735 014357 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Sensor * @{ */ /** * @file sensor.h */ #ifndef ANDROID_SENSOR_H #define ANDROID_SENSOR_H /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ /** * Structures and functions to receive and process sensor events in * native code. * */ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct AHardwareBuffer AHardwareBuffer; #define ASENSOR_RESOLUTION_INVALID (nanf("")) #define ASENSOR_FIFO_COUNT_INVALID (-1) #define ASENSOR_DELAY_INVALID INT32_MIN #define ASENSOR_INVALID (-1) /* (Keep in sync with hardware/sensors-base.h and Sensor.java.) */ /** * Sensor types. * * See * [android.hardware.SensorEvent#values](https://developer.android.com/reference/android/hardware/SensorEvent.html#values) * for detailed explanations of the data returned for each of these types. */ enum { /** * Invalid sensor type. Returned by {@link ASensor_getType} as error value. */ ASENSOR_TYPE_INVALID = -1, /** * {@link ASENSOR_TYPE_ACCELEROMETER} * reporting-mode: continuous * * All values are in SI units (m/s^2) and measure the acceleration of the * device minus the force of gravity. */ ASENSOR_TYPE_ACCELEROMETER = 1, /** * {@link ASENSOR_TYPE_MAGNETIC_FIELD} * reporting-mode: continuous * * All values are in micro-Tesla (uT) and measure the geomagnetic * field in the X, Y and Z axis. */ ASENSOR_TYPE_MAGNETIC_FIELD = 2, /** * {@link ASENSOR_TYPE_GYROSCOPE} * reporting-mode: continuous * * All values are in radians/second and measure the rate of rotation * around the X, Y and Z axis. */ ASENSOR_TYPE_GYROSCOPE = 4, /** * {@link ASENSOR_TYPE_LIGHT} * reporting-mode: on-change * * The light sensor value is returned in SI lux units. */ ASENSOR_TYPE_LIGHT = 5, /** * {@link ASENSOR_TYPE_PRESSURE} * * The pressure sensor value is returned in hPa (millibar). */ ASENSOR_TYPE_PRESSURE = 6, /** * {@link ASENSOR_TYPE_PROXIMITY} * reporting-mode: on-change * * The proximity sensor which turns the screen off and back on during calls is the * wake-up proximity sensor. Implement wake-up proximity sensor before implementing * a non wake-up proximity sensor. For the wake-up proximity sensor set the flag * SENSOR_FLAG_WAKE_UP. * The value corresponds to the distance to the nearest object in centimeters. */ ASENSOR_TYPE_PROXIMITY = 8, /** * {@link ASENSOR_TYPE_GRAVITY} * * All values are in SI units (m/s^2) and measure the direction and * magnitude of gravity. When the device is at rest, the output of * the gravity sensor should be identical to that of the accelerometer. */ ASENSOR_TYPE_GRAVITY = 9, /** * {@link ASENSOR_TYPE_LINEAR_ACCELERATION} * reporting-mode: continuous * * All values are in SI units (m/s^2) and measure the acceleration of the * device not including the force of gravity. */ ASENSOR_TYPE_LINEAR_ACCELERATION = 10, /** * {@link ASENSOR_TYPE_ROTATION_VECTOR} */ ASENSOR_TYPE_ROTATION_VECTOR = 11, /** * {@link ASENSOR_TYPE_RELATIVE_HUMIDITY} * * The relative humidity sensor value is returned in percent. */ ASENSOR_TYPE_RELATIVE_HUMIDITY = 12, /** * {@link ASENSOR_TYPE_AMBIENT_TEMPERATURE} * * The ambient temperature sensor value is returned in Celcius. */ ASENSOR_TYPE_AMBIENT_TEMPERATURE = 13, /** * {@link ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED} */ ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14, /** * {@link ASENSOR_TYPE_GAME_ROTATION_VECTOR} */ ASENSOR_TYPE_GAME_ROTATION_VECTOR = 15, /** * {@link ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED} */ ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED = 16, /** * {@link ASENSOR_TYPE_SIGNIFICANT_MOTION} */ ASENSOR_TYPE_SIGNIFICANT_MOTION = 17, /** * {@link ASENSOR_TYPE_STEP_DETECTOR} */ ASENSOR_TYPE_STEP_DETECTOR = 18, /** * {@link ASENSOR_TYPE_STEP_COUNTER} */ ASENSOR_TYPE_STEP_COUNTER = 19, /** * {@link ASENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR} */ ASENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20, /** * {@link ASENSOR_TYPE_HEART_RATE} */ ASENSOR_TYPE_HEART_RATE = 21, /** * {@link ASENSOR_TYPE_POSE_6DOF} */ ASENSOR_TYPE_POSE_6DOF = 28, /** * {@link ASENSOR_TYPE_STATIONARY_DETECT} */ ASENSOR_TYPE_STATIONARY_DETECT = 29, /** * {@link ASENSOR_TYPE_MOTION_DETECT} */ ASENSOR_TYPE_MOTION_DETECT = 30, /** * {@link ASENSOR_TYPE_HEART_BEAT} */ ASENSOR_TYPE_HEART_BEAT = 31, /** * This sensor type is for delivering additional sensor information aside * from sensor event data. * * Additional information may include: * - {@link ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE} * - {@link ASENSOR_ADDITIONAL_INFO_SAMPLING} * - {@link ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT} * - {@link ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY} * - {@link ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION} * * This type will never bind to a sensor. In other words, no sensor in the * sensor list can have the type {@link ASENSOR_TYPE_ADDITIONAL_INFO}. * * If a device supports the sensor additional information feature, it will * report additional information events via {@link ASensorEvent} and will * have {@link ASensorEvent#type} set to * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set * to the handle of the reporting sensor. * * Additional information reports consist of multiple frames ordered by * {@link ASensorEvent#timestamp}. The first frame in the report will have * a {@link AAdditionalInfoEvent#type} of * {@link ASENSOR_ADDITIONAL_INFO_BEGIN}, and the last frame in the report * will have a {@link AAdditionalInfoEvent#type} of * {@link ASENSOR_ADDITIONAL_INFO_END}. * */ ASENSOR_TYPE_ADDITIONAL_INFO = 33, /** * {@link ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT} */ ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT = 34, /** * {@link ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED} */ ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED = 35, }; /** * Sensor accuracy measure. */ enum { /** no contact */ ASENSOR_STATUS_NO_CONTACT = -1, /** unreliable */ ASENSOR_STATUS_UNRELIABLE = 0, /** low accuracy */ ASENSOR_STATUS_ACCURACY_LOW = 1, /** medium accuracy */ ASENSOR_STATUS_ACCURACY_MEDIUM = 2, /** high accuracy */ ASENSOR_STATUS_ACCURACY_HIGH = 3 }; /** * Sensor Reporting Modes. */ enum { /** invalid reporting mode */ AREPORTING_MODE_INVALID = -1, /** continuous reporting */ AREPORTING_MODE_CONTINUOUS = 0, /** reporting on change */ AREPORTING_MODE_ON_CHANGE = 1, /** on shot reporting */ AREPORTING_MODE_ONE_SHOT = 2, /** special trigger reporting */ AREPORTING_MODE_SPECIAL_TRIGGER = 3 }; /** * Sensor Direct Report Rates. */ enum { /** stopped */ ASENSOR_DIRECT_RATE_STOP = 0, /** nominal 50Hz */ ASENSOR_DIRECT_RATE_NORMAL = 1, /** nominal 200Hz */ ASENSOR_DIRECT_RATE_FAST = 2, /** nominal 800Hz */ ASENSOR_DIRECT_RATE_VERY_FAST = 3 }; /** * Sensor Direct Channel Type. */ enum { /** shared memory created by ASharedMemory_create */ ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY = 1, /** AHardwareBuffer */ ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2 }; /** * Sensor Additional Info Types. * * Used to populate {@link AAdditionalInfoEvent#type}. */ enum { /** Marks the beginning of additional information frames */ ASENSOR_ADDITIONAL_INFO_BEGIN = 0, /** Marks the end of additional information frames */ ASENSOR_ADDITIONAL_INFO_END = 1, /** * Estimation of the delay that is not tracked by sensor timestamps. This * includes delay introduced by sensor front-end filtering, data transport, * etc. * float[2]: delay in seconds, standard deviation of estimated value */ ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY = 0x10000, /** float: Celsius temperature */ ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE, /** * First three rows of a homogeneous matrix, which represents calibration to * a three-element vector raw sensor reading. * float[12]: 3x4 matrix in row major order */ ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION, /** * Location and orientation of sensor element in the device frame: origin is * the geometric center of the mobile device screen surface; the axis * definition corresponds to Android sensor definitions. * float[12]: 3x4 matrix in row major order */ ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT, /** * float[2]: raw sample period in seconds, * standard deviation of sampling period */ ASENSOR_ADDITIONAL_INFO_SAMPLING, }; /* * A few useful constants */ /** Earth's gravity in m/s^2 */ #define ASENSOR_STANDARD_GRAVITY (9.80665f) /** Maximum magnetic field on Earth's surface in uT */ #define ASENSOR_MAGNETIC_FIELD_EARTH_MAX (60.0f) /** Minimum magnetic field on Earth's surface in uT*/ #define ASENSOR_MAGNETIC_FIELD_EARTH_MIN (30.0f) /** * A sensor event. */ /* NOTE: changes to these structs have to be backward compatible */ typedef struct ASensorVector { union { float v[3]; struct { float x; float y; float z; }; struct { float azimuth; float pitch; float roll; }; }; int8_t status; uint8_t reserved[3]; } ASensorVector; typedef struct AMetaDataEvent { int32_t what; int32_t sensor; } AMetaDataEvent; typedef struct AUncalibratedEvent { union { float uncalib[3]; struct { float x_uncalib; float y_uncalib; float z_uncalib; }; }; union { float bias[3]; struct { float x_bias; float y_bias; float z_bias; }; }; } AUncalibratedEvent; typedef struct AHeartRateEvent { float bpm; int8_t status; } AHeartRateEvent; typedef struct ADynamicSensorEvent { int32_t connected; int32_t handle; } ADynamicSensorEvent; typedef struct AAdditionalInfoEvent { int32_t type; int32_t serial; union { int32_t data_int32[14]; float data_float[14]; }; } AAdditionalInfoEvent; /* NOTE: changes to this struct has to be backward compatible */ typedef struct ASensorEvent { int32_t version; /* sizeof(struct ASensorEvent) */ int32_t sensor; int32_t type; int32_t reserved0; int64_t timestamp; union { union { float data[16]; ASensorVector vector; ASensorVector acceleration; ASensorVector magnetic; float temperature; float distance; float light; float pressure; float relative_humidity; AUncalibratedEvent uncalibrated_gyro; AUncalibratedEvent uncalibrated_magnetic; AMetaDataEvent meta_data; AHeartRateEvent heart_rate; ADynamicSensorEvent dynamic_sensor_meta; AAdditionalInfoEvent additional_info; }; union { uint64_t data[8]; uint64_t step_counter; } u64; }; uint32_t flags; int32_t reserved1[3]; } ASensorEvent; struct ASensorManager; /** * {@link ASensorManager} is an opaque type to manage sensors and * events queues. * * {@link ASensorManager} is a singleton that can be obtained using * ASensorManager_getInstance(). * * This file provides a set of functions that uses {@link * ASensorManager} to access and list hardware sensors, and * create and destroy event queues: * - ASensorManager_getSensorList() * - ASensorManager_getDefaultSensor() * - ASensorManager_getDefaultSensorEx() * - ASensorManager_createEventQueue() * - ASensorManager_destroyEventQueue() */ typedef struct ASensorManager ASensorManager; struct ASensorEventQueue; /** * {@link ASensorEventQueue} is an opaque type that provides access to * {@link ASensorEvent} from hardware sensors. * * A new {@link ASensorEventQueue} can be obtained using ASensorManager_createEventQueue(). * * This file provides a set of functions to enable and disable * sensors, check and get events, and set event rates on a {@link * ASensorEventQueue}. * - ASensorEventQueue_enableSensor() * - ASensorEventQueue_disableSensor() * - ASensorEventQueue_hasEvents() * - ASensorEventQueue_getEvents() * - ASensorEventQueue_setEventRate() * - ASensorEventQueue_requestAdditionalInfoEvents() */ typedef struct ASensorEventQueue ASensorEventQueue; struct ASensor; /** * {@link ASensor} is an opaque type that provides information about * an hardware sensors. * * A {@link ASensor} pointer can be obtained using * ASensorManager_getDefaultSensor(), * ASensorManager_getDefaultSensorEx() or from a {@link ASensorList}. * * This file provides a set of functions to access properties of a * {@link ASensor}: * - ASensor_getName() * - ASensor_getVendor() * - ASensor_getType() * - ASensor_getResolution() * - ASensor_getMinDelay() * - ASensor_getFifoMaxEventCount() * - ASensor_getFifoReservedEventCount() * - ASensor_getStringType() * - ASensor_getReportingMode() * - ASensor_isWakeUpSensor() * - ASensor_getHandle() */ typedef struct ASensor ASensor; /** * {@link ASensorRef} is a type for constant pointers to {@link ASensor}. * * This is used to define entry in {@link ASensorList} arrays. */ typedef ASensor const* ASensorRef; /** * {@link ASensorList} is an array of reference to {@link ASensor}. * * A {@link ASensorList} can be initialized using ASensorManager_getSensorList(). */ typedef ASensorRef const* ASensorList; /*****************************************************************************/ /** * Get a reference to the sensor manager. ASensorManager is a singleton * per package as different packages may have access to different sensors. * * Deprecated: Use ASensorManager_getInstanceForPackage(const char*) instead. * * Example: * * ASensorManager* sensorManager = ASensorManager_getInstance(); * */ #if __ANDROID_API__ >= 26 __attribute__ ((deprecated)) ASensorManager* ASensorManager_getInstance(); #else ASensorManager* ASensorManager_getInstance(); #endif #if __ANDROID_API__ >= 26 /** * Get a reference to the sensor manager. ASensorManager is a singleton * per package as different packages may have access to different sensors. * * Example: * * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz"); * */ ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName) __INTRODUCED_IN(26); #endif /** * Returns the list of available sensors. */ int ASensorManager_getSensorList(ASensorManager* manager, ASensorList* list); /** * Returns the default sensor for the given type, or NULL if no sensor * of that type exists. */ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type); #if __ANDROID_API__ >= 21 /** * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor * of this type and wakeUp properties exists. */ ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp) __INTRODUCED_IN(21); #endif /** * Creates a new sensor event queue and associate it with a looper. * * "ident" is a identifier for the events that will be returned when * calling ALooper_pollOnce(). The identifier must be >= 0, or * ALOOPER_POLL_CALLBACK if providing a non-NULL callback. */ ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager, ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); /** * Destroys the event queue and free all resources associated to it. */ int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue); #if __ANDROID_API__ >= 26 /** * Create direct channel based on shared memory * * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used * for configuring sensor direct report. * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param fd file descriptor representing a shared memory created by * {@link ASharedMemory_create} * \param size size to be used, must be less or equal to size of shared memory. * * \return a positive integer as a channel id to be used in * {@link ASensorManager_destroyDirectChannel} and * {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures. */ int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int fd, size_t size) __INTRODUCED_IN(26); /** * Create direct channel based on AHardwareBuffer * * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used * for configuring sensor direct report. * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param buffer {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}. * \param size the intended size to be used, must be less or equal to size of buffer. * * \return a positive integer as a channel id to be used in * {@link ASensorManager_destroyDirectChannel} and * {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures. */ int ASensorManager_createHardwareBufferDirectChannel( ASensorManager* manager, AHardwareBuffer const * buffer, size_t size) __INTRODUCED_IN(26); /** * Destroy a direct channel * * Destroy a direct channel previously created using {@link ASensorManager_createDirectChannel}. * The buffer used for creating direct channel does not get destroyed with * {@link ASensorManager_destroy} and has to be close or released separately. * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param channelId channel id (a positive integer) returned from * {@link ASensorManager_createSharedMemoryDirectChannel} or * {@link ASensorManager_createHardwareBufferDirectChannel}. */ void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId) __INTRODUCED_IN(26); /** * Configure direct report on channel * * Configure sensor direct report on a direct channel: set rate to value other than * {@link ASENSOR_DIRECT_RATE_STOP} so that sensor event can be directly * written into the shared memory region used for creating the buffer. It returns a positive token * which can be used for identify sensor events from different sensors on success. Calling with rate * {@link ASENSOR_DIRECT_RATE_STOP} will stop direct report of the sensor specified in the channel. * * To stop all active sensor direct report configured to a channel, set sensor to NULL and rate to * {@link ASENSOR_DIRECT_RATE_STOP}. * * In order to successfully configure a direct report, the sensor has to support the specified rate * and the channel type, which can be checked by {@link ASensor_getHighestDirectReportRateLevel} and * {@link ASensor_isDirectChannelTypeSupported}, respectively. * * Example: * * ASensorManager *manager = ...; * ASensor *sensor = ...; * int channelId = ...; * * ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST); * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param sensor a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate * is {@link ASENSOR_DIRECT_RATE_STOP}, denoting stopping of all active sensor * direct report. * \param channelId channel id (a positive integer) returned from * {@link ASensorManager_createSharedMemoryDirectChannel} or * {@link ASensorManager_createHardwareBufferDirectChannel}. * * \return positive token for success or negative error code. */ int ASensorManager_configureDirectReport(ASensorManager* manager, ASensor const* sensor, int channelId, int rate) __INTRODUCED_IN(26); #endif /* __ANDROID_API__ >= 26 */ /*****************************************************************************/ /** * Enable the selected sensor with sampling and report parameters * * Enable the selected sensor at a specified sampling period and max batch report latency. * To disable sensor, use {@link ASensorEventQueue_disableSensor}. * * \param queue {@link ASensorEventQueue} for sensor event to be report to. * \param sensor {@link ASensor} to be enabled. * \param samplingPeriodUs sampling period of sensor in microseconds. * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are * delievered in microseconds. For sensor streaming, set to 0. * \return 0 on success or a negative error code on failure. */ int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs); /** * Enable the selected sensor at default sampling rate. * * Start event reports of a sensor to specified sensor event queue at a default rate. * * \param queue {@link ASensorEventQueue} for sensor event to be report to. * \param sensor {@link ASensor} to be enabled. * * \return 0 on success or a negative error code on failure. */ int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor); /** * Disable the selected sensor. * * Stop event reports from the sensor to specified sensor event queue. * * \param queue {@link ASensorEventQueue} to be changed * \param sensor {@link ASensor} to be disabled * \return 0 on success or a negative error code on failure. */ int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor); /** * Sets the delivery rate of events in microseconds for the given sensor. * * This function has to be called after {@link ASensorEventQueue_enableSensor}. * Note that this is a hint only, generally event will arrive at a higher * rate. It is an error to set a rate inferior to the value returned by * ASensor_getMinDelay(). * * \param queue {@link ASensorEventQueue} to which sensor event is delivered. * \param sensor {@link ASensor} of which sampling rate to be updated. * \param usec sensor sampling period (1/sampling rate) in microseconds * \return 0 on sucess or a negative error code on failure. */ int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec); /** * Determine if a sensor event queue has pending event to be processed. * * \param queue {@link ASensorEventQueue} to be queried * \return 1 if the queue has events; 0 if it does not have events; * or a negative value if there is an error. */ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue); /** * Retrieve pending events in sensor event queue * * Retrieve next available events from the queue to a specified event array. * * \param queue {@link ASensorEventQueue} to get events from * \param events pointer to an array of {@link ASensorEvents}. * \param count max number of event that can be filled into array event. * \return number of events returned on success; negative error code when * no events are pending or an error has occurred. * * Examples: * * ASensorEvent event; * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1); * * ASensorEvent eventBuffer[8]; * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8); * */ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count); #if __ANDROID_API__ >= __ANDROID_API_Q__ /** * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on * the given {@link ASensorEventQueue}. * * Sensor data events are always delivered to the {@ASensorEventQueue}. * * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through * {@link ASensorEventQueue_getEvents}. The client is responsible for checking * {@link ASensorEvent#type} to determine the event type prior to handling of * the event. * * The client must be tolerant of any value for * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future * and may delivered to the client. * * \param queue {@link ASensorEventQueue} to configure * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events, * false to stop receiving events * \return 0 on success or a negative error code on failure */ int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable); #endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */ /*****************************************************************************/ /** * Returns this sensor's name (non localized) */ const char* ASensor_getName(ASensor const* sensor); /** * Returns this sensor's vendor's name (non localized) */ const char* ASensor_getVendor(ASensor const* sensor); /** * Return this sensor's type */ int ASensor_getType(ASensor const* sensor); /** * Returns this sensors's resolution */ float ASensor_getResolution(ASensor const* sensor); /** * Returns the minimum delay allowed between events in microseconds. * A value of zero means that this sensor doesn't report events at a * constant rate, but rather only when a new data is available. */ int ASensor_getMinDelay(ASensor const* sensor); #if __ANDROID_API__ >= 21 /** * Returns the maximum size of batches for this sensor. Batches will often be * smaller, as the hardware fifo might be used for other sensors. */ int ASensor_getFifoMaxEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the hardware batch fifo size reserved to this sensor. */ int ASensor_getFifoReservedEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns this sensor's string type. */ const char* ASensor_getStringType(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the reporting mode for this sensor. One of AREPORTING_MODE_* constants. */ int ASensor_getReportingMode(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns true if this is a wake up sensor, false otherwise. */ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); #endif /* __ANDROID_API__ >= 21 */ #if __ANDROID_API__ >= 26 /** * Test if sensor supports a certain type of direct channel. * * \param sensor a {@link ASensor} to denote the sensor to be checked. * \param channelType Channel type constant, either * {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} * or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}. * \returns true if sensor supports the specified direct channel type. */ bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType) __INTRODUCED_IN(26); /** * Get the highest direct rate level that a sensor support. * * \param sensor a {@link ASensor} to denote the sensor to be checked. * * \return a ASENSOR_DIRECT_RATE_... enum denoting the highest rate level supported by the sensor. * If return value is {@link ASENSOR_DIRECT_RATE_STOP}, it means the sensor * does not support direct report. */ int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26); #endif /* __ANDROID_API__ >= 26 */ #if __ANDROID_API__ >= __ANDROID_API_Q__ /** * Returns the sensor's handle. * * The handle identifies the sensor within the system and is included in the * {@link ASensorEvent#sensor} field of sensor events, including those sent with type * {@link ASENSOR_TYPE_ADDITIONAL_INFO}. * * A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the * sensor that generated the event. * * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists * between the values. */ int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__); #endif /* __ANDROID_API__ >= ANDROID_API_Q__ */ #ifdef __cplusplus }; #endif #endif // ANDROID_SENSOR_H /** @} */ include/android/sharedmem.h0100644 0000000 0000000 00000007715 13756501735 015014 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ /** * @addtogroup Memory * @{ */ /** * @file sharedmem.h * @brief Shared memory buffers that can be shared across process. */ #ifndef ANDROID_SHARED_MEMORY_H #define ANDROID_SHARED_MEMORY_H #include #include /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ #ifdef __cplusplus extern "C" { #endif #if __ANDROID_API__ >= 26 /** * Create a shared memory region. * * Create shared memory region and returns an file descriptor. The resulting file descriptor can be * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory * region can be restricted with {@link ASharedMemory_setProt}. * * Use close() to release the shared memory region. * * Available since API level 26. * * \param name an optional name. * \param size size of the shared memory region * \return file descriptor that denotes the shared memory; error code on failure. */ int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26); /** * Get the size of the shared memory region. * * Available since API level 26. * * \param fd file descriptor of the shared memory region * \return size in bytes; 0 if fd is not a valid shared memory file descriptor. */ size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26); /** * Restrict access of shared memory region. * * This function restricts access of a shared memory region. Access can only be removed. The effect * applies globally to all file descriptors in all processes across the system that refer to this * shared memory region. Existing memory mapped regions are not affected. * * It is a common use case to create a shared memory region, map it read/write locally to intialize * content, and then send the shared memory to another process with read only access. Code example * as below (error handling omited). * * * int fd = ASharedMemory_create("memory", 128); * * // By default it has PROT_READ | PROT_WRITE | PROT_EXEC. * size_t memSize = ASharedMemory_getSize(fd); * char *buffer = (char *) mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); * * strcpy(buffer, "This is an example."); // trivially initialize content * * // limit access to read only * ASharedMemory_setProt(fd, PROT_READ); * * // share fd with another process here and the other process can only map with PROT_READ. * * Available since API level 26. * * \param fd file descriptor of the shared memory region. * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting * updated access. Note access can only be removed, but not added back. * \return 0 for success, error code on failure. */ int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26); #endif // __ANDROID_API__ >= 26 #ifdef __cplusplus }; #endif #endif // ANDROID_SHARED_MEMORY_H /** @} */ include/android/sharedmem_jni.h0100644 0000000 0000000 00000004672 13756501735 015653 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ /** * @addtogroup Memory * @{ */ /** * @file sharedmem_jni.h * @brief Shared memory buffers that can be shared across process. */ #ifndef ANDROID_SHARED_MEMORY_JNI_H #define ANDROID_SHARED_MEMORY_JNI_H #include #include #include #include /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ #ifdef __cplusplus extern "C" { #endif #if __ANDROID_API__ >= 27 /** * Returns a dup'd FD from the given Java android.os.SharedMemory object. The returned file * descriptor has all the same properties & capabilities as the FD returned from * ASharedMemory_create(), however the protection flags will be the same as those of the * android.os.SharedMemory object. * * Use close() to release the shared memory region. * * Available since API level 27. * * \param env The JNIEnv* pointer * \param sharedMemory The Java android.os.SharedMemory object * \return file descriptor that denotes the shared memory; -1 if the shared memory object is * already closed, if the JNIEnv or jobject is NULL, or if there are too many open file * descriptors (errno=EMFILE) */ int ASharedMemory_dupFromJava(JNIEnv* env, jobject sharedMemory) __INTRODUCED_IN(27); #endif // __ANDROID_API__ >= 27 #ifdef __cplusplus }; #endif #endif // ANDROID_SHARED_MEMORY_JNI_H /** @} */ include/android/storage_manager.h0100644 0000000 0000000 00000010576 13756501735 016204 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup Storage * @{ */ /** * @file storage_manager.h */ #ifndef ANDROID_STORAGE_MANAGER_H #define ANDROID_STORAGE_MANAGER_H #include #ifdef __cplusplus extern "C" { #endif struct AStorageManager; /** * {@link AStorageManager} manages application OBB storage, a pointer * can be obtained with AStorageManager_new(). */ typedef struct AStorageManager AStorageManager; /** * The different states of a OBB storage passed to AStorageManager_obbCallbackFunc(). */ enum { /** * The OBB container is now mounted and ready for use. Can be returned * as the status for callbacks made during asynchronous OBB actions. */ AOBB_STATE_MOUNTED = 1, /** * The OBB container is now unmounted and not usable. Can be returned * as the status for callbacks made during asynchronous OBB actions. */ AOBB_STATE_UNMOUNTED = 2, /** * There was an internal system error encountered while trying to * mount the OBB. Can be returned as the status for callbacks made * during asynchronous OBB actions. */ AOBB_STATE_ERROR_INTERNAL = 20, /** * The OBB could not be mounted by the system. Can be returned as the * status for callbacks made during asynchronous OBB actions. */ AOBB_STATE_ERROR_COULD_NOT_MOUNT = 21, /** * The OBB could not be unmounted. This most likely indicates that a * file is in use on the OBB. Can be returned as the status for * callbacks made during asynchronous OBB actions. */ AOBB_STATE_ERROR_COULD_NOT_UNMOUNT = 22, /** * A call was made to unmount the OBB when it was not mounted. Can be * returned as the status for callbacks made during asynchronous OBB * actions. */ AOBB_STATE_ERROR_NOT_MOUNTED = 23, /** * The OBB has already been mounted. Can be returned as the status for * callbacks made during asynchronous OBB actions. */ AOBB_STATE_ERROR_ALREADY_MOUNTED = 24, /** * The current application does not have permission to use this OBB. * This could be because the OBB indicates it's owned by a different * package. Can be returned as the status for callbacks made during * asynchronous OBB actions. */ AOBB_STATE_ERROR_PERMISSION_DENIED = 25, }; /** * Obtains a new instance of AStorageManager. */ AStorageManager* AStorageManager_new(); /** * Release AStorageManager instance. */ void AStorageManager_delete(AStorageManager* mgr); /** * Callback function for asynchronous calls made on OBB files. * * "state" is one of the following constants: * - {@link AOBB_STATE_MOUNTED} * - {@link AOBB_STATE_UNMOUNTED} * - {@link AOBB_STATE_ERROR_INTERNAL} * - {@link AOBB_STATE_ERROR_COULD_NOT_MOUNT} * - {@link AOBB_STATE_ERROR_COULD_NOT_UNMOUNT} * - {@link AOBB_STATE_ERROR_NOT_MOUNTED} * - {@link AOBB_STATE_ERROR_ALREADY_MOUNTED} * - {@link AOBB_STATE_ERROR_PERMISSION_DENIED} */ typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int32_t state, void* data); /** * Attempts to mount an OBB file. This is an asynchronous operation. */ void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key, AStorageManager_obbCallbackFunc cb, void* data); /** * Attempts to unmount an OBB file. This is an asynchronous operation. */ void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force, AStorageManager_obbCallbackFunc cb, void* data); /** * Check whether an OBB is mounted. */ int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename); /** * Get the mounted path for an OBB. */ const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char* filename); #ifdef __cplusplus }; #endif #endif // ANDROID_STORAGE_MANAGER_H /** @} */ include/android/surface_control.h0100644 0000000 0000000 00000037171 13756501735 016236 0ustar000000000 0000000 /* * Copyright 2018 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. */ /** * @addtogroup NativeActivity Native Activity * @{ */ /** * @file surface_control.h */ #ifndef ANDROID_SURFACE_CONTROL_H #define ANDROID_SURFACE_CONTROL_H #include #include #include #include #include __BEGIN_DECLS #if __ANDROID_API__ >= 29 struct ASurfaceControl; /** * The SurfaceControl API can be used to provide a hierarchy of surfaces for * composition to the system compositor. ASurfaceControl represents a content node in * this hierarchy. */ typedef struct ASurfaceControl ASurfaceControl; /* * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent. * |debug_name| is a debug name associated with this surface. It can be used to * identify this surface in the SurfaceFlinger's layer tree. It must not be * null. * * The caller takes ownership of the ASurfaceControl returned and must release it * using ASurfaceControl_release below. */ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name) __INTRODUCED_IN(29); ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) __INTRODUCED_IN(29); /** * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long * as their parent remains on display. */ void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29); struct ASurfaceTransaction; /** * ASurfaceTransaction is a collection of updates to the surface tree that must * be applied atomically. */ typedef struct ASurfaceTransaction ASurfaceTransaction; /** * The caller takes ownership of the transaction and must release it using * ASurfaceControl_delete below. */ ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29); /** * Destroys the |transaction| object. */ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); /** * Applies the updates accumulated in |transaction|. * * Note that the transaction is guaranteed to be applied atomically. The * transactions which are applied on the same thread are also guaranteed to be * applied in order. */ void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); /** * An opaque handle returned during a callback that can be used to query general stats and stats for * surfaces which were either removed or for which buffers were updated after this transaction was * applied. */ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; /** * Since the transactions are applied asynchronously, the * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame * including the updates in a transaction was presented. * * |context| is the optional context provided by the client that is passed into * the callback. * * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query * information about the transaction. The handle is only valid during the callback. * * THREADING * The transaction completed callback can be invoked on any thread. */ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats) __INTRODUCED_IN(29); /** * Returns the timestamp of when the frame was latched by the framework. Once a frame is * latched by the framework, it is presented at the following hardware vsync. */ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); /** * Returns a sync fence that signals when the transaction has been presented. * The recipient of the callback takes ownership of the fence and is responsible for closing * it. */ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); /** * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions. * When the client is done using the array, it must release it by calling * ASurfaceTransactionStats_releaseASurfaceControls. * * |outASurfaceControlsSize| returns the size of the ASurfaceControls array. */ void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats, ASurfaceControl*** outASurfaceControls, size_t* outASurfaceControlsSize) __INTRODUCED_IN(29); /** * Releases the array of ASurfaceControls that were returned by * ASurfaceTransactionStats_getASurfaceControls. */ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls) __INTRODUCED_IN(29); /** * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1. */ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, ASurfaceControl* surface_control) __INTRODUCED_IN(29); /** * The returns the fence used to signal the release of the PREVIOUS buffer set on * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS * buffer is already released. The recipient of the callback takes ownership of the * previousReleaseFenceFd and is responsible for closing it. * * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a * transaction which is applied, the framework takes a ref on this buffer. The framework treats the * addition of a buffer to a particular surface as a unique ref. When a transaction updates or * removes a buffer from a surface, or removes the surface itself from the tree, this ref is * guaranteed to be released in the OnComplete callback for this transaction. The * ASurfaceControlStats provided in the callback for this surface may contain an optional fence * which must be signaled before the ref is assumed to be released. * * The client must ensure that all pending refs on a buffer are released before attempting to reuse * this buffer, otherwise synchronization errors may occur. */ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( ASurfaceTransactionStats* surface_transaction_stats, ASurfaceControl* surface_control) __INTRODUCED_IN(29); /** * Sets the callback that will be invoked when the updates from this transaction * are presented. For details on the callback semantics and data, see the * comments on the ASurfaceTransaction_OnComplete declaration above. */ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context, ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29); /** * Reparents the |surface_control| from its old parent to the |new_parent| surface control. * Any children of the* reparented |surface_control| will remain children of the |surface_control|. * * The |new_parent| can be null. Surface controls with a null parent do not appear on the display. */ void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, ASurfaceControl* new_parent) __INTRODUCED_IN(29); /* Parameter for ASurfaceTransaction_setVisibility */ enum { ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0, ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1, }; /** * Updates the visibility of |surface_control|. If show is set to * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will * be hidden. */ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t visibility) __INTRODUCED_IN(29); /** * Updates the z order index for |surface_control|. Note that the z order for a surface * is relative to other surfaces which are siblings of this surface. The behavior of sibilings with * the same z order is undefined. * * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0. */ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int32_t z_order) __INTRODUCED_IN(29); /** * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the * acquire_fence_fd should be a file descriptor that is signaled when all pending work * for the buffer is complete and the buffer can be safely read. * * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible * for closing it. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, AHardwareBuffer* buffer, int acquire_fence_fd = -1) __INTRODUCED_IN(29); /** * Updates the color for |surface_control|. This will make the background color for the * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|, * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha| * will be the dataspace and alpha set for the background color layer. */ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float r, float g, float b, float alpha, ADataSpace dataspace) __INTRODUCED_IN(29); /** * |source| the sub-rect within the buffer's content to be rendered inside the surface's area * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width * and height must be > 0. * * |destination| specifies the rect in the parent's space where this surface will be drawn. The post * source rect bounds are scaled to fit the destination rect. The surface's destination rect is * clipped by the bounds of its parent. The destination rect's width and height must be > 0. * * |transform| the transform applied after the source rect is applied to the buffer. This parameter * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_* * enum. */ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect& source, const ARect& destination, int32_t transform) __INTRODUCED_IN(29); /* Parameter for ASurfaceTransaction_setBufferTransparency */ enum { ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0, ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1, ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2, }; /** * Updates whether the content for the buffer associated with this surface is * completely opaque. If true, every pixel of content inside the buffer must be * opaque or visual errors can occur. */ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t transparency) __INTRODUCED_IN(29); /** * Updates the region for the content on this surface updated in this * transaction. If unspecified, the complete surface is assumed to be damaged. */ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect rects[], uint32_t count) __INTRODUCED_IN(29); /** * Specifies a desiredPresentTime for the transaction. The framework will try to present * the transaction at or after the time specified. * * Transactions will not be presented until all of their acquire fences have signaled even if the * app requests an earlier present time. * * If an earlier transaction has a desired present time of x, and a later transaction has a desired * present time that is before x, the later transaction will not preempt the earlier transaction. */ void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, int64_t desiredPresentTime) __INTRODUCED_IN(29); /** * Sets the alpha for the buffer. It uses a premultiplied blending. * * The |alpha| must be between 0.0 and 1.0. */ void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float alpha) __INTRODUCED_IN(29); /** * Sets the data space of the surface_control's buffers. * * If no data space is set, the surface control defaults to ADATASPACE_SRGB. */ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, ADataSpace data_space) __INTRODUCED_IN(29); /* * SMPTE ST 2086 "Mastering Display Color Volume" static metadata * * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering * the surface's buffer. */ void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, struct AHdrMetadata_smpte2086* metadata) __INTRODUCED_IN(29); /* * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface. * * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering * the surface's buffer. */ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, struct AHdrMetadata_cta861_3* metadata) __INTRODUCED_IN(29); #endif // __ANDROID_API__ >= 29 __END_DECLS #endif // ANDROID_SURFACE_CONTROL_H include/android/surface_texture.h0100644 0000000 0000000 00000016176 13756501735 016260 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup SurfaceTexture * @{ */ /** * @file surface_texture.h */ #ifndef ANDROID_NATIVE_SURFACE_TEXTURE_H #define ANDROID_NATIVE_SURFACE_TEXTURE_H /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ #include #include #include __BEGIN_DECLS struct ASurfaceTexture; /** * {@link ASurfaceTexture} is an opaque type to manage SurfaceTexture from native code * * {@link ASurfaceTexture} can be obtained from an android.graphics.SurfaceTexture object using * ASurfaceTexture_fromSurfaceTexture(). */ typedef struct ASurfaceTexture ASurfaceTexture; #if __ANDROID_API__ >= 28 /** * Release the reference to the native ASurfaceTexture acquired with * ASurfaceTexture_fromSurfaceTexture(). * Failing to do so will result in leaked memory and graphic resources. * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Returns a reference to an ANativeWindow (i.e. the Producer) for this SurfaceTexture. * This is equivalent to Java's: Surface sur = new Surface(surfaceTexture); * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * @return A reference to an ANativeWindow. This reference MUST BE released when no longer needed * using ANativeWindow_release(). Failing to do so will result in leaked resources. nullptr is * returned if \p st is null or if it's not an instance of android.graphics.SurfaceTexture */ ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread. A * new OpenGL ES texture object is created and populated with the SurfaceTexture image frame * that was current at the time of the last call to {@link #detachFromGLContext}. This new * texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target. * * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES * contexts. Note, however, that the image contents are only accessible from one OpenGL ES * context at a time. * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \param texName The name of the OpenGL ES texture that will be created. This texture name * must be unusued in the OpenGL ES context that is current on the calling thread. * \return 0 on success, negative posix error code otherwise (see ) */ int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t texName) __INTRODUCED_IN(28); /** * Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object. * This call must be made with the OpenGL ES context current on the calling thread. The OpenGL * ES texture object will be deleted as a result of this call. After calling this method all * calls to {@link #updateTexImage} will fail until a successful call to {@link #attachToGLContext} * is made. * * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES * contexts. Note, however, that the image contents are only accessible from one OpenGL ES * context at a time. * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see ) */ int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Update the texture image to the most recent frame from the image stream. This may only be * called while the OpenGL ES context that owns the texture is current on the calling thread. * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target. * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see ) */ int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set by * the most recent call to updateTexImage. * * This transform matrix maps 2D homogeneous texture coordinates of the form (s, t, 0, 1) with s * and t in the inclusive range [0, 1] to the texture coordinate that should be used to sample * that location from the texture. Sampling the texture outside of the range of this transform * is undefined. * * The matrix is stored in column-major order so that it may be passed directly to OpenGL ES via * the glLoadMatrixf or glUniformMatrix4fv functions. * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \param mtx the array into which the 4x4 matrix will be stored. The array must have exactly * 16 elements. */ void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) __INTRODUCED_IN(28); /** * Retrieve the timestamp associated with the texture image set by the most recent call to * updateTexImage. * * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp * should be unaffected by time-of-day adjustments, and for a camera should be strictly * monotonic but for a MediaPlayer may be reset when the position is set. The * specific meaning and zero point of the timestamp depends on the source providing images to * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot * generally be compared across SurfaceTexture instances, or across multiple program * invocations. It is mostly useful for determining time offsets between subsequent frames. * * For EGL/Vulkan producers, this timestamp is the desired present time set with the * EGL_ANDROID_presentation_time or VK_GOOGLE_display_timing extensions * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) __INTRODUCED_IN(28); #endif /* __ANDROID_API__ >= 28 */ __END_DECLS #endif /* ANDROID_NATIVE_SURFACE_TEXTURE_H */ include/android/surface_texture_jni.h0100644 0000000 0000000 00000003306 13756501735 017107 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup SurfaceTexture * @{ */ /** * @file surface_texture_jni.h */ #ifndef ANDROID_NATIVE_SURFACE_TEXTURE_JNI_H #define ANDROID_NATIVE_SURFACE_TEXTURE_JNI_H #include #include __BEGIN_DECLS /** * Get a reference to the native ASurfaceTexture from the corresponding java object. * * The caller must keep a reference to the Java SurfaceTexture during the lifetime of the returned * ASurfaceTexture. Failing to do so could result in the ASurfaceTexture to stop functioning * properly once the Java object gets finalized. * However, this will not result in program termination. * * \param env JNI environment * \param surfacetexture Instance of Java SurfaceTexture object * \return native ASurfaceTexture reference or nullptr if the java object is not a SurfaceTexture. * The returned reference MUST BE released when it's no longer needed using * ASurfaceTexture_release(). */ ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture); __END_DECLS #endif /* ANDROID_NATIVE_SURFACE_TEXTURE_JNI_H */ include/android/system_fonts.h0100644 0000000 0000000 00000010026 13756501735 015571 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup Font * { */ /** * @file system_fonts.h * @brief Provides the system font configurations. * * These APIs provides the list of system installed font files with additional metadata about the * font. * * The ASystemFontIterator_open method will give you an iterator which can iterate all system * installed font files as shown in the following example. * * \code{.cpp} * ASystemFontIterator* iterator = ASystemFontIterator_open(); * ASystemFont* font = NULL; * * while ((font = ASystemFontIterator_next(iterator)) != nullptr) { * // Look if the font is your desired one. * if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font) * && ASystemFont_getLocale(font) == NULL) { * break; * } * ASystemFont_close(font); * } * ASystemFontIterator_close(iterator); * * int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY); * int collectionIndex = ASystemFont_getCollectionINdex(font); * std::vector> variationSettings; * for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) { * variationSettings.push_back(std::make_pair( * ASystemFont_getAxisTag(font, i), * ASystemFont_getAxisValue(font, i))); * } * ASystemFont_close(font); * * // Use this font for your text rendering engine. * * \endcode * * Available since API level 29. */ #ifndef ANDROID_SYSTEM_FONTS_H #define ANDROID_SYSTEM_FONTS_H #include #include #include #include /****************************************************************** * * IMPORTANT NOTICE: * * This file is part of Android's set of stable system headers * exposed by the Android NDK (Native Development Kit). * * Third-party source AND binary code relies on the definitions * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. * * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ __BEGIN_DECLS #if __ANDROID_API__ >= 29 /** * ASystemFontIterator provides access to the system font configuration. * * ASystemFontIterator is an iterator for all available system font settings. * This iterator is not a thread-safe object. Do not pass this iterator to other threads. */ struct ASystemFontIterator; /** * Create a system font iterator. * * Use ASystemFont_close() to close the iterator. * * \return a pointer for a newly allocated iterator, nullptr on failure. */ ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); /** * Close an opened system font iterator, freeing any related resources. * * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed. */ void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29); /** * Move to the next system font. * * \param iterator an iterator for the system fonts. Passing NULL is not allowed. * \return a font. If no more font is available, returns nullptr. You need to release the returned * font by ASystemFont_close when it is no longer needed. */ AFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29); #endif // __ANDROID_API__ >= 29 __END_DECLS #endif // ANDROID_SYSTEM_FONTS_H /** @} */ include/android/trace.h0100644 0000000 0000000 00000007622 13756501735 014142 0ustar000000000 0000000 /* * 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. */ /** * @addtogroup Tracing * @{ */ /** * @file trace.h * @brief Writes trace events to the system trace buffer. * * These trace events can be collected and visualized using the Systrace tool. * For information about using the Systrace tool, read Analyzing UI Performance with Systrace. * * Available since API level 23. */ #ifndef ANDROID_NATIVE_TRACE_H #define ANDROID_NATIVE_TRACE_H #include #include #include #ifdef __cplusplus extern "C" { #endif #if __ANDROID_API__ >= 23 /** * Returns true if tracing is enabled. Use this to avoid expensive computation only necessary * when tracing is enabled. * * Available since API level 23. */ bool ATrace_isEnabled() __INTRODUCED_IN(23); /** * Writes a tracing message to indicate that the given section of code has begun. This call must be * followed by a corresponding call to {@link ATrace_endSection} on the same thread. * * Note: At this time the vertical bar character '|' and newline character '\\n' are used internally * by the tracing mechanism. If \p sectionName contains these characters they will be replaced with a * space character in the trace. * * Available since API level 23. */ void ATrace_beginSection(const char* sectionName) __INTRODUCED_IN(23); /** * Writes a tracing message to indicate that a given section of code has ended. This call must be * preceeded by a corresponding call to {@link ATrace_beginSection} on the same thread. Calling this method * will mark the end of the most recently begun section of code, so care must be taken to ensure * that {@link ATrace_beginSection}/{@link ATrace_endSection} pairs are properly nested and called from the same thread. * * Available since API level 23. */ void ATrace_endSection() __INTRODUCED_IN(23); #endif /* __ANDROID_API__ >= 23 */ #if __ANDROID_API__ >= __ANDROID_API_Q__ /** * Writes a trace message to indicate that a given section of code has * begun. Must be followed by a call to {@link ATrace_endAsyncSection} with the same * methodName and cookie. Unlike {@link ATrace_beginSection} and {@link ATrace_endSection}, * asynchronous events do not need to be nested. The name and cookie used to * begin an event must be used to end it. * * \param sectionName The method name to appear in the trace. * \param cookie Unique identifier for distinguishing simultaneous events */ void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29); /** * Writes a trace message to indicate that the current method has ended. * Must be called exactly once for each call to {@link ATrace_beginAsyncSection} * using the same name and cookie. * * \param methodName The method name to appear in the trace. * \param cookie Unique identifier for distinguishing simultaneous events */ void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29); /** * Writes trace message to indicate the value of a given counter. * * \param counterName The counter name to appear in the trace. * \param counterValue The counter value. */ void ATrace_setCounter(const char* counterName, int64_t counterValue) __INTRODUCED_IN(29); #endif /* __ANDROID_API__ >= 29 */ #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_TRACE_H /** @} */ include/android/window.h0100644 0000000 0000000 00000022526 13756501735 014353 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup NativeActivity Native Activity * @{ */ /** * @file window.h */ #ifndef ANDROID_WINDOW_H #define ANDROID_WINDOW_H #ifdef __cplusplus extern "C" { #endif /** * Window flags, as per the Java API at android.view.WindowManager.LayoutParams. */ enum { /** * As long as this window is visible to the user, allow the lock * screen to activate while the screen is on. This can be used * independently, or in combination with {@link * AWINDOW_FLAG_KEEP_SCREEN_ON} and/or {@link * AWINDOW_FLAG_SHOW_WHEN_LOCKED} */ AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, /** Everything behind this window will be dimmed. */ AWINDOW_FLAG_DIM_BEHIND = 0x00000002, /** * Blur everything behind this window. * @deprecated Blurring is no longer supported. */ AWINDOW_FLAG_BLUR_BEHIND = 0x00000004, /** * This window won't ever get key input focus, so the * user can not send key or other button events to it. Those will * instead go to whatever focusable window is behind it. This flag * will also enable {@link AWINDOW_FLAG_NOT_TOUCH_MODAL} whether or not that * is explicitly set. * * Setting this flag also implies that the window will not need to * interact with * a soft input method, so it will be Z-ordered and positioned * independently of any active input method (typically this means it * gets Z-ordered on top of the input method, so it can use the full * screen for its content and cover the input method if needed. You * can use {@link AWINDOW_FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */ AWINDOW_FLAG_NOT_FOCUSABLE = 0x00000008, /** this window can never receive touch events. */ AWINDOW_FLAG_NOT_TOUCHABLE = 0x00000010, /** * Even when this window is focusable (its * {@link AWINDOW_FLAG_NOT_FOCUSABLE} is not set), allow any pointer events * outside of the window to be sent to the windows behind it. Otherwise * it will consume all pointer events itself, regardless of whether they * are inside of the window. */ AWINDOW_FLAG_NOT_TOUCH_MODAL = 0x00000020, /** * When set, if the device is asleep when the touch * screen is pressed, you will receive this first touch event. Usually * the first touch event is consumed by the system since the user can * not see what they are pressing on. * * @deprecated This flag has no effect. */ AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, /** * As long as this window is visible to the user, keep * the device's screen turned on and bright. */ AWINDOW_FLAG_KEEP_SCREEN_ON = 0x00000080, /** * Place the window within the entire screen, ignoring * decorations around the border (such as the status bar). The * window must correctly position its contents to take the screen * decoration into account. */ AWINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100, /** allow window to extend outside of the screen. */ AWINDOW_FLAG_LAYOUT_NO_LIMITS = 0x00000200, /** * Hide all screen decorations (such as the status * bar) while this window is displayed. This allows the window to * use the entire display space for itself -- the status bar will * be hidden when an app window with this flag set is on the top * layer. A fullscreen window will ignore a value of {@link * AWINDOW_SOFT_INPUT_ADJUST_RESIZE}; the window will stay * fullscreen and will not resize. */ AWINDOW_FLAG_FULLSCREEN = 0x00000400, /** * Override {@link AWINDOW_FLAG_FULLSCREEN} and force the * screen decorations (such as the status bar) to be shown. */ AWINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, /** * Turn on dithering when compositing this window to * the screen. * @deprecated This flag is no longer used. */ AWINDOW_FLAG_DITHER = 0x00001000, /** * Treat the content of the window as secure, preventing * it from appearing in screenshots or from being viewed on non-secure * displays. */ AWINDOW_FLAG_SECURE = 0x00002000, /** * A special mode where the layout parameters are used * to perform scaling of the surface when it is composited to the * screen. */ AWINDOW_FLAG_SCALED = 0x00004000, /** * Intended for windows that will often be used when the user is * holding the screen against their face, it will aggressively * filter the event stream to prevent unintended presses in this * situation that may not be desired for a particular window, when * such an event stream is detected, the application will receive * a {@link AMOTION_EVENT_ACTION_CANCEL} to indicate this so * applications can handle this accordingly by taking no action on * the event until the finger is released. */ AWINDOW_FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, /** * A special option only for use in combination with * {@link AWINDOW_FLAG_LAYOUT_IN_SCREEN}. When requesting layout in the * screen your window may appear on top of or behind screen decorations * such as the status bar. By also including this flag, the window * manager will report the inset rectangle needed to ensure your * content is not covered by screen decorations. */ AWINDOW_FLAG_LAYOUT_INSET_DECOR = 0x00010000, /** * Invert the state of {@link AWINDOW_FLAG_NOT_FOCUSABLE} with * respect to how this window interacts with the current method. * That is, if FLAG_NOT_FOCUSABLE is set and this flag is set, * then the window will behave as if it needs to interact with the * input method and thus be placed behind/away from it; if {@link * AWINDOW_FLAG_NOT_FOCUSABLE} is not set and this flag is set, * then the window will behave as if it doesn't need to interact * with the input method and can be placed to use more space and * cover the input method. */ AWINDOW_FLAG_ALT_FOCUSABLE_IM = 0x00020000, /** * If you have set {@link AWINDOW_FLAG_NOT_TOUCH_MODAL}, you * can set this flag to receive a single special MotionEvent with * the action * {@link AMOTION_EVENT_ACTION_OUTSIDE} for * touches that occur outside of your window. Note that you will not * receive the full down/move/up gesture, only the location of the * first down as an {@link AMOTION_EVENT_ACTION_OUTSIDE}. */ AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, /** * Special flag to let windows be shown when the screen * is locked. This will let application windows take precedence over * key guard or any other lock screens. Can be used with * {@link AWINDOW_FLAG_KEEP_SCREEN_ON} to turn screen on and display windows * directly before showing the key guard window. Can be used with * {@link AWINDOW_FLAG_DISMISS_KEYGUARD} to automatically fully dismisss * non-secure keyguards. This flag only applies to the top-most * full-screen window. */ AWINDOW_FLAG_SHOW_WHEN_LOCKED = 0x00080000, /** * Ask that the system wallpaper be shown behind * your window. The window surface must be translucent to be able * to actually see the wallpaper behind it; this flag just ensures * that the wallpaper surface will be there if this window actually * has translucent regions. */ AWINDOW_FLAG_SHOW_WALLPAPER = 0x00100000, /** * When set as a window is being added or made * visible, once the window has been shown then the system will * poke the power manager's user activity (as if the user had woken * up the device) to turn the screen on. */ AWINDOW_FLAG_TURN_SCREEN_ON = 0x00200000, /** * When set the window will cause the keyguard to * be dismissed, only if it is not a secure lock keyguard. Because such * a keyguard is not needed for security, it will never re-appear if * the user navigates to another window (in contrast to * {@link AWINDOW_FLAG_SHOW_WHEN_LOCKED}, which will only temporarily * hide both secure and non-secure keyguards but ensure they reappear * when the user moves to another UI that doesn't hide them). * If the keyguard is currently active and is secure (requires an * unlock pattern) than the user will still need to confirm it before * seeing this window, unless {@link AWINDOW_FLAG_SHOW_WHEN_LOCKED} has * also been set. */ AWINDOW_FLAG_DISMISS_KEYGUARD = 0x00400000, }; #ifdef __cplusplus }; #endif #endif // ANDROID_WINDOW_H /** @} */ include/audiomanager/0040755 0000000 0000000 00000000000 13756501735 013703 5ustar000000000 0000000 include/audiomanager/AudioManager.h0100644 0000000 0000000 00000003016 13756501735 016405 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_AUDIOMANAGER_H #define ANDROID_AUDIOMANAGER_H namespace android { // must be kept in sync with definitions in AudioPlaybackConfiguration.java #define PLAYER_PIID_INVALID -1 typedef enum { PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11, PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12, PLAYER_TYPE_AAUDIO = 13, PLAYER_TYPE_HW_SOURCE = 14, PLAYER_TYPE_EXTERNAL_PROXY = 15, } player_type_t; typedef enum { PLAYER_STATE_UNKNOWN = -1, PLAYER_STATE_RELEASED = 0, PLAYER_STATE_IDLE = 1, PLAYER_STATE_STARTED = 2, PLAYER_STATE_PAUSED = 3, PLAYER_STATE_STOPPED = 4, } player_state_t; // must be kept in sync with definitions in AudioManager.java #define RECORD_RIID_INVALID -1 typedef enum { RECORDER_STATE_UNKNOWN = -1, RECORDER_STATE_STARTED = 0, RECORDER_STATE_STOPPED = 1, } recorder_state_t; }; // namespace android #endif // ANDROID_AUDIOMANAGER_H include/audiomanager/IAudioManager.h0100644 0000000 0000000 00000005260 13756501735 016521 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_IAUDIOMANAGER_H #define ANDROID_IAUDIOMANAGER_H #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class IAudioManager : public IInterface { public: // These transaction IDs must be kept in sync with the method order from // IAudioService.aidl. enum { TRACK_PLAYER = IBinder::FIRST_CALL_TRANSACTION, PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 1, PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 2, RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 3, TRACK_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 4, RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 5, RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6, }; DECLARE_META_INTERFACE(AudioManager) // The parcels created by these methods must be kept in sync with the // corresponding methods from IAudioService.aidl and objects it imports. virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage, audio_content_type_t content, const sp& player) = 0; /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage, audio_content_type_t content)= 0; /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0; /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0; virtual audio_unique_id_t trackRecorder(const sp& recorder) = 0; /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0; /*oneway*/ virtual status_t releaseRecorder(audio_unique_id_t riid) = 0; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IAUDIOMANAGER_H include/binder0100644 0000000 0000000 00000000000 13756501735 017717 2../libs/binder/include/binder/ustar000000000 0000000 include/diskusage/0040755 0000000 0000000 00000000000 13756501735 013226 5ustar000000000 0000000 include/diskusage/dirsize.h0100644 0000000 0000000 00000001522 13756501735 015045 0ustar000000000 0000000 /* * * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LIBDISKUSAGE_DIRSIZE_H #define __LIBDISKUSAGE_DIRSIZE_H #include __BEGIN_DECLS int64_t stat_size(struct stat *s); int64_t calculate_dir_size(int dfd); __END_DECLS #endif /* __LIBDISKUSAGE_DIRSIZE_H */ include/gfx/0040755 0000000 0000000 00000000000 13756501735 012033 5ustar000000000 0000000 include/gfx/.clang-format0100644 0000000 0000000 00000000440 13756501735 014401 0ustar000000000 0000000 BasedOnStyle: Google AccessModifierOffset: -2 AllowShortFunctionsOnASingleLine: Inline BinPackParameters: false ColumnLimit: 100 CommentPragmas: NOLINT:.* ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 8 IndentWidth: 4 include/gui0100644 0000000 0000000 00000000000 13756501735 016023 2../libs/gui/include/guiustar000000000 0000000 include/input/0040755 0000000 0000000 00000000000 13756501735 012406 5ustar000000000 0000000 include/input/DisplayViewport.h0100644 0000000 0000000 00000011433 13756501735 015723 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H #define _LIBINPUT_DISPLAY_VIEWPORT_H #include #include #include #include #include using android::base::StringPrintf; namespace android { /** * Describes the different type of viewports supported by input flinger. * Keep in sync with values in InputManagerService.java. */ enum class ViewportType : int32_t { VIEWPORT_INTERNAL = 1, VIEWPORT_EXTERNAL = 2, VIEWPORT_VIRTUAL = 3, }; static const char* viewportTypeToString(ViewportType type) { switch(type) { case ViewportType::VIEWPORT_INTERNAL: return "INTERNAL"; case ViewportType::VIEWPORT_EXTERNAL: return "EXTERNAL"; case ViewportType::VIEWPORT_VIRTUAL: return "VIRTUAL"; default: return "UNKNOWN"; } } /* * Describes how coordinates are mapped on a physical display. * See com.android.server.display.DisplayViewport. */ struct DisplayViewport { int32_t displayId; // -1 if invalid int32_t orientation; int32_t logicalLeft; int32_t logicalTop; int32_t logicalRight; int32_t logicalBottom; int32_t physicalLeft; int32_t physicalTop; int32_t physicalRight; int32_t physicalBottom; int32_t deviceWidth; int32_t deviceHeight; std::string uniqueId; // The actual (hardware) port that the associated display is connected to. // Not all viewports will have this specified. std::optional physicalPort; ViewportType type; DisplayViewport() : displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0), logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0), physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0), deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt), type(ViewportType::VIEWPORT_INTERNAL) { } bool operator==(const DisplayViewport& other) const { return displayId == other.displayId && orientation == other.orientation && logicalLeft == other.logicalLeft && logicalTop == other.logicalTop && logicalRight == other.logicalRight && logicalBottom == other.logicalBottom && physicalLeft == other.physicalLeft && physicalTop == other.physicalTop && physicalRight == other.physicalRight && physicalBottom == other.physicalBottom && deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight && uniqueId == other.uniqueId && physicalPort == other.physicalPort && type == other.type; } bool operator!=(const DisplayViewport& other) const { return !(*this == other); } inline bool isValid() const { return displayId >= 0; } void setNonDisplayViewport(int32_t width, int32_t height) { displayId = ADISPLAY_ID_NONE; orientation = DISPLAY_ORIENTATION_0; logicalLeft = 0; logicalTop = 0; logicalRight = width; logicalBottom = height; physicalLeft = 0; physicalTop = 0; physicalRight = width; physicalBottom = height; deviceWidth = width; deviceHeight = height; uniqueId.clear(); physicalPort = std::nullopt; type = ViewportType::VIEWPORT_INTERNAL; } std::string toString() const { return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, " "logicalFrame=[%d, %d, %d, %d], " "physicalFrame=[%d, %d, %d, %d], " "deviceSize=[%d, %d]", viewportTypeToString(type), displayId, uniqueId.c_str(), physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "", orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, deviceHeight); } }; } // namespace android #endif // _LIBINPUT_DISPLAY_VIEWPORT_H include/input/IInputFlinger.h0100644 0000000 0000000 00000003461 13756501735 015277 0ustar000000000 0000000 /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_IINPUT_FLINGER_H #define _LIBINPUT_IINPUT_FLINGER_H #include #include #include #include #include namespace android { /* * This class defines the Binder IPC interface for accessing various * InputFlinger features. */ class IInputFlinger : public IInterface { public: DECLARE_META_INTERFACE(InputFlinger) virtual void setInputWindows(const std::vector& inputHandles, const sp& setInputWindowsListener) = 0; virtual void registerInputChannel(const sp& channel) = 0; virtual void unregisterInputChannel(const sp& channel) = 0; }; /** * Binder implementation. */ class BnInputFlinger : public BnInterface { public: enum { SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_INPUT_CHANNEL_TRANSACTION, UNREGISTER_INPUT_CHANNEL_TRANSACTION }; virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; } // namespace android #endif // _LIBINPUT_IINPUT_FLINGER_H include/input/ISetInputWindowsListener.h0100644 0000000 0000000 00000002315 13756501735 017522 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #pragma once #include #include namespace android { class ISetInputWindowsListener : public IInterface { public: DECLARE_META_INTERFACE(SetInputWindowsListener) virtual void onSetInputWindowsFinished() = 0; }; class BnSetInputWindowsListener: public BnInterface { public: enum SetInputWindowsTag : uint32_t { ON_SET_INPUT_WINDOWS_FINISHED }; virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; }; }; // namespace android include/input/Input.h0100644 0000000 0000000 00000053662 13756501735 013667 0ustar000000000 0000000 /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_INPUT_H #define _LIBINPUT_INPUT_H #pragma GCC system_header /** * Native input event structures. */ #include #include #include #include #include #include #include /* * Additional private constants not defined in ndk/ui/input.h. */ enum { /* Signifies that the key is being predispatched */ AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000, /* Private control to determine when an app is tracking a key sequence. */ AKEY_EVENT_FLAG_START_TRACKING = 0x40000000, /* Key event is inconsistent with previously sent key events. */ AKEY_EVENT_FLAG_TAINTED = 0x80000000, }; enum { /** * This flag indicates that the window that received this motion event is partly * or wholly obscured by another visible window above it. This flag is set to true * even if the event did not directly pass through the obscured area. * A security sensitive application can check this flag to identify situations in which * a malicious application may have covered up part of its content for the purpose * of misleading the user or hijacking touches. An appropriate response might be * to drop the suspect touches or to take additional precautions to confirm the user's * actual intent. */ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2, /** * This flag indicates that the event has been generated by a gesture generator. It * provides a hint to the GestureDetector to not apply any touch slop. */ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8, /* Motion event is inconsistent with previously sent motion events. */ AMOTION_EVENT_FLAG_TAINTED = 0x80000000, }; enum { /* Used when a motion event is not associated with any display. * Typically used for non-pointer events. */ ADISPLAY_ID_NONE = -1, /* The default display id. */ ADISPLAY_ID_DEFAULT = 0, }; enum { /* * Indicates that an input device has switches. * This input source flag is hidden from the API because switches are only used by the system * and applications have no way to interact with them. */ AINPUT_SOURCE_SWITCH = 0x80000000, }; enum { /** * Constants for LEDs. Hidden from the API since we don't actually expose a way to interact * with LEDs to developers * * NOTE: If you add LEDs here, you must also add them to InputEventLabels.h */ ALED_NUM_LOCK = 0x00, ALED_CAPS_LOCK = 0x01, ALED_SCROLL_LOCK = 0x02, ALED_COMPOSE = 0x03, ALED_KANA = 0x04, ALED_SLEEP = 0x05, ALED_SUSPEND = 0x06, ALED_MUTE = 0x07, ALED_MISC = 0x08, ALED_MAIL = 0x09, ALED_CHARGING = 0x0a, ALED_CONTROLLER_1 = 0x10, ALED_CONTROLLER_2 = 0x11, ALED_CONTROLLER_3 = 0x12, ALED_CONTROLLER_4 = 0x13, }; /* Maximum number of controller LEDs we support */ #define MAX_CONTROLLER_LEDS 4 /* * SystemUiVisibility constants from View. */ enum { ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0, ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001, }; /* * Maximum number of pointers supported per motion event. * Smallest number of pointers is 1. * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers * will occasionally emit 11. There is not much harm making this constant bigger.) */ #define MAX_POINTERS 16 /* * Maximum number of samples supported per motion event. */ #define MAX_SAMPLES UINT16_MAX /* * Maximum pointer id value supported in a motion event. * Smallest pointer id is 0. * (This is limited by our use of BitSet32 to track pointer assignments.) */ #define MAX_POINTER_ID 31 /* * Declare a concrete type for the NDK's input event forward declaration. */ struct AInputEvent { virtual ~AInputEvent() { } }; /* * Declare a concrete type for the NDK's input device forward declaration. */ struct AInputDevice { virtual ~AInputDevice() { } }; namespace android { #ifdef __ANDROID__ class Parcel; #endif /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. * * These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java. */ enum { /* These flags originate in RawEvents and are generally set in the key map. * NOTE: If you want a flag to be able to set in a keylayout file, then you must add it to * InputEventLabels.h as well. */ // Indicates that the event should wake the device. POLICY_FLAG_WAKE = 0x00000001, // Indicates that the key is virtual, such as a capacitive button, and should // generate haptic feedback. Virtual keys may be suppressed for some time // after a recent touch to prevent accidental activation of virtual keys adjacent // to the touch screen during an edge swipe. POLICY_FLAG_VIRTUAL = 0x00000002, // Indicates that the key is the special function modifier. POLICY_FLAG_FUNCTION = 0x00000004, // Indicates that the key represents a special gesture that has been detected by // the touch firmware or driver. Causes touch events from the same device to be canceled. POLICY_FLAG_GESTURE = 0x00000008, POLICY_FLAG_RAW_MASK = 0x0000ffff, /* These flags are set by the input dispatcher. */ // Indicates that the input event was injected. POLICY_FLAG_INJECTED = 0x01000000, // Indicates that the input event is from a trusted source such as a directly attached // input device or an application with system-wide event injection permission. POLICY_FLAG_TRUSTED = 0x02000000, // Indicates that the input event has passed through an input filter. POLICY_FLAG_FILTERED = 0x04000000, // Disables automatic key repeating behavior. POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000, /* These flags are set by the input reader policy as it intercepts each event. */ // Indicates that the device was in an interactive state when the // event was intercepted. POLICY_FLAG_INTERACTIVE = 0x20000000, // Indicates that the event should be dispatched to applications. // The input event should still be sent to the InputDispatcher so that it can see all // input events received include those that it will not deliver. POLICY_FLAG_PASS_TO_USER = 0x40000000, }; /** * Classifications of the current gesture, if available. * * The following values must be kept in sync with MotionEvent.java */ enum class MotionClassification : uint8_t { /** * No classification is available. */ NONE = 0, /** * Too early to classify the current gesture. Need more events. Look for changes in the * upcoming motion events. */ AMBIGUOUS_GESTURE = 1, /** * The current gesture likely represents a user intentionally exerting force on the touchscreen. */ DEEP_PRESS = 2, }; /** * String representation of MotionClassification */ const char* motionClassificationToString(MotionClassification classification); /* * Pointer coordinate data. */ struct PointerCoords { enum { MAX_AXES = 30 }; // 30 so that sizeof(PointerCoords) == 128 // Bitfield of axes that are present in this structure. uint64_t bits __attribute__((aligned(8))); // Values of axes that are stored in this structure packed in order by axis id // for each axis that is present in the structure according to 'bits'. float values[MAX_AXES]; inline void clear() { BitSet64::clear(bits); } bool isEmpty() const { return BitSet64::isEmpty(bits); } float getAxisValue(int32_t axis) const; status_t setAxisValue(int32_t axis, float value); void scale(float globalScale); // Scale the pointer coordinates according to a global scale and a // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); void applyOffset(float xOffset, float yOffset); inline float getX() const { return getAxisValue(AMOTION_EVENT_AXIS_X); } inline float getY() const { return getAxisValue(AMOTION_EVENT_AXIS_Y); } #ifdef __ANDROID__ status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; #endif bool operator==(const PointerCoords& other) const; inline bool operator!=(const PointerCoords& other) const { return !(*this == other); } void copyFrom(const PointerCoords& other); private: void tooManyAxes(int axis); }; /* * Pointer property data. */ struct PointerProperties { // The id of the pointer. int32_t id; // The pointer tool type. int32_t toolType; inline void clear() { id = -1; toolType = 0; } bool operator==(const PointerProperties& other) const; inline bool operator!=(const PointerProperties& other) const { return !(*this == other); } void copyFrom(const PointerProperties& other); }; /* * Input events. */ class InputEvent : public AInputEvent { public: virtual ~InputEvent() { } virtual int32_t getType() const = 0; inline int32_t getDeviceId() const { return mDeviceId; } inline int32_t getSource() const { return mSource; } inline void setSource(int32_t source) { mSource = source; } inline int32_t getDisplayId() const { return mDisplayId; } inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; } protected: void initialize(int32_t deviceId, int32_t source, int32_t displayId); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; int32_t mDisplayId; }; /* * Key events. */ class KeyEvent : public InputEvent { public: virtual ~KeyEvent() { } virtual int32_t getType() const { return AINPUT_EVENT_TYPE_KEY; } inline int32_t getAction() const { return mAction; } inline int32_t getFlags() const { return mFlags; } inline void setFlags(int32_t flags) { mFlags = flags; } inline int32_t getKeyCode() const { return mKeyCode; } inline int32_t getScanCode() const { return mScanCode; } inline int32_t getMetaState() const { return mMetaState; } inline int32_t getRepeatCount() const { return mRepeatCount; } inline nsecs_t getDownTime() const { return mDownTime; } inline nsecs_t getEventTime() const { return mEventTime; } static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); void initialize( int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); void initialize(const KeyEvent& from); protected: int32_t mAction; int32_t mFlags; int32_t mKeyCode; int32_t mScanCode; int32_t mMetaState; int32_t mRepeatCount; nsecs_t mDownTime; nsecs_t mEventTime; }; /* * Motion events. */ class MotionEvent : public InputEvent { public: virtual ~MotionEvent() { } virtual int32_t getType() const { return AINPUT_EVENT_TYPE_MOTION; } inline int32_t getAction() const { return mAction; } inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; } inline int32_t getActionIndex() const { return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } inline void setAction(int32_t action) { mAction = action; } inline int32_t getFlags() const { return mFlags; } inline void setFlags(int32_t flags) { mFlags = flags; } inline int32_t getEdgeFlags() const { return mEdgeFlags; } inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; } inline int32_t getMetaState() const { return mMetaState; } inline void setMetaState(int32_t metaState) { mMetaState = metaState; } inline int32_t getButtonState() const { return mButtonState; } inline void setButtonState(int32_t buttonState) { mButtonState = buttonState; } inline MotionClassification getClassification() const { return mClassification; } inline int32_t getActionButton() const { return mActionButton; } inline void setActionButton(int32_t button) { mActionButton = button; } inline float getXOffset() const { return mXOffset; } inline float getYOffset() const { return mYOffset; } inline float getXPrecision() const { return mXPrecision; } inline float getYPrecision() const { return mYPrecision; } inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } inline size_t getPointerCount() const { return mPointerProperties.size(); } inline const PointerProperties* getPointerProperties(size_t pointerIndex) const { return &mPointerProperties[pointerIndex]; } inline int32_t getPointerId(size_t pointerIndex) const { return mPointerProperties[pointerIndex].id; } inline int32_t getToolType(size_t pointerIndex) const { return mPointerProperties[pointerIndex].toolType; } inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } const PointerCoords* getRawPointerCoords(size_t pointerIndex) const; float getRawAxisValue(int32_t axis, size_t pointerIndex) const; inline float getRawX(size_t pointerIndex) const { return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } inline float getRawY(size_t pointerIndex) const { return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } float getAxisValue(int32_t axis, size_t pointerIndex) const; inline float getX(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } inline float getY(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } inline float getPressure(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex); } inline float getSize(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex); } inline float getTouchMajor(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex); } inline float getTouchMinor(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex); } inline float getToolMajor(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex); } inline float getToolMinor(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex); } inline float getOrientation(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex); } inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; } inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const { return mSampleEventTimes[historicalIndex]; } const PointerCoords* getHistoricalRawPointerCoords( size_t pointerIndex, size_t historicalIndex) const; float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const; inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalRawAxisValue( AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); } inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalRawAxisValue( AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); } float getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const; inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); } inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); } inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex); } inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex); } inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex); } inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex); } inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex); } inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex); } inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalAxisValue( AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex); } ssize_t findPointerIndex(int32_t pointerId) const; void initialize( int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); void addSample( nsecs_t eventTime, const PointerCoords* pointerCoords); void offsetLocation(float xOffset, float yOffset); void scale(float globalScaleFactor); // Apply 3x3 perspective matrix transformation. // Matrix is in row-major form and compatible with SkMatrix. void transform(const float matrix[9]); #ifdef __ANDROID__ status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; #endif static bool isTouchEvent(int32_t source, int32_t action); inline bool isTouchEvent() const { return isTouchEvent(mSource, mAction); } // Low-level accessors. inline const PointerProperties* getPointerProperties() const { return mPointerProperties.array(); } inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } inline const PointerCoords* getSamplePointerCoords() const { return mSamplePointerCoords.array(); } static const char* getLabel(int32_t axis); static int32_t getAxisFromLabel(const char* label); protected: int32_t mAction; int32_t mActionButton; int32_t mFlags; int32_t mEdgeFlags; int32_t mMetaState; int32_t mButtonState; MotionClassification mClassification; float mXOffset; float mYOffset; float mXPrecision; float mYPrecision; nsecs_t mDownTime; Vector mPointerProperties; Vector mSampleEventTimes; Vector mSamplePointerCoords; }; /* * Input event factory. */ class InputEventFactoryInterface { protected: virtual ~InputEventFactoryInterface() { } public: InputEventFactoryInterface() { } virtual KeyEvent* createKeyEvent() = 0; virtual MotionEvent* createMotionEvent() = 0; }; /* * A simple input event factory implementation that uses a single preallocated instance * of each type of input event that are reused for each request. */ class PreallocatedInputEventFactory : public InputEventFactoryInterface { public: PreallocatedInputEventFactory() { } virtual ~PreallocatedInputEventFactory() { } virtual KeyEvent* createKeyEvent() { return & mKeyEvent; } virtual MotionEvent* createMotionEvent() { return & mMotionEvent; } private: KeyEvent mKeyEvent; MotionEvent mMotionEvent; }; /* * An input event factory implementation that maintains a pool of input events. */ class PooledInputEventFactory : public InputEventFactoryInterface { public: explicit PooledInputEventFactory(size_t maxPoolSize = 20); virtual ~PooledInputEventFactory(); virtual KeyEvent* createKeyEvent(); virtual MotionEvent* createMotionEvent(); void recycle(InputEvent* event); private: const size_t mMaxPoolSize; Vector mKeyEventPool; Vector mMotionEventPool; }; } // namespace android #endif // _LIBINPUT_INPUT_H include/input/InputApplication.h0100644 0000000 0000000 00000004504 13756501735 016042 0ustar000000000 0000000 /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _UI_INPUT_APPLICATION_H #define _UI_INPUT_APPLICATION_H #include #include #include #include #include #include namespace android { /* * Describes the properties of an application that can receive input. */ struct InputApplicationInfo { sp token; std::string name; nsecs_t dispatchingTimeout; status_t write(Parcel& output) const; static InputApplicationInfo read(const Parcel& from); }; /* * Handle for an application that can receive input. * * Used by the native input dispatcher as a handle for the window manager objects * that describe an application. */ class InputApplicationHandle : public RefBase { public: inline const InputApplicationInfo* getInfo() const { return &mInfo; } inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : ""; } inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } inline sp getApplicationToken() const { return mInfo.token; } /** * Requests that the state of this object be updated to reflect * the most current available information about the application. * * This method should only be called from within the input dispatcher's * critical section. * * Returns true on success, or false if the handle is no longer valid. */ virtual bool updateInfo() = 0; protected: InputApplicationHandle(); virtual ~InputApplicationHandle(); InputApplicationInfo mInfo; }; } // namespace android #endif // _UI_INPUT_APPLICATION_H include/input/InputDevice.h0100644 0000000 0000000 00000015045 13756501735 015000 0ustar000000000 0000000 /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_INPUT_DEVICE_H #define _LIBINPUT_INPUT_DEVICE_H #include #include #include namespace android { /* * Identifies a device. */ struct InputDeviceIdentifier { inline InputDeviceIdentifier() : bus(0), vendor(0), product(0), version(0) { } // Information provided by the kernel. std::string name; std::string location; std::string uniqueId; uint16_t bus; uint16_t vendor; uint16_t product; uint16_t version; // A composite input device descriptor string that uniquely identifies the device // even across reboots or reconnections. The value of this field is used by // upper layers of the input system to associate settings with individual devices. // It is hashed from whatever kernel provided information is available. // Ideally, the way this value is computed should not change between Android releases // because that would invalidate persistent settings that rely on it. std::string descriptor; // A value added to uniquely identify a device in the absence of a unique id. This // is intended to be a minimum way to distinguish from other active devices and may // reuse values that are not associated with an input anymore. uint16_t nonce; /** * Return InputDeviceIdentifier.name that has been adjusted as follows: * - all characters besides alphanumerics, dash, * and underscore have been replaced with underscores. * This helps in situations where a file that matches the device name is needed, * while conforming to the filename limitations. */ std::string getCanonicalName() const; }; /* * Describes the characteristics and capabilities of an input device. */ class InputDeviceInfo { public: InputDeviceInfo(); InputDeviceInfo(const InputDeviceInfo& other); ~InputDeviceInfo(); struct MotionRange { int32_t axis; uint32_t source; float min; float max; float flat; float fuzz; float resolution; }; void initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } inline int32_t getGeneration() const { return mGeneration; } inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; } inline const std::string& getAlias() const { return mAlias; } inline const std::string& getDisplayName() const { return mAlias.empty() ? mIdentifier.name : mAlias; } inline bool isExternal() const { return mIsExternal; } inline bool hasMic() const { return mHasMic; } inline uint32_t getSources() const { return mSources; } const MotionRange* getMotionRange(int32_t axis, uint32_t source) const; void addSource(uint32_t source); void addMotionRange(int32_t axis, uint32_t source, float min, float max, float flat, float fuzz, float resolution); void addMotionRange(const MotionRange& range); inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } inline void setKeyCharacterMap(const sp& value) { mKeyCharacterMap = value; } inline sp getKeyCharacterMap() const { return mKeyCharacterMap; } inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; } inline bool hasVibrator() const { return mHasVibrator; } inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; } inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; } inline const std::vector& getMotionRanges() const { return mMotionRanges; } private: int32_t mId; int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; std::string mAlias; bool mIsExternal; bool mHasMic; uint32_t mSources; int32_t mKeyboardType; sp mKeyCharacterMap; bool mHasVibrator; bool mHasButtonUnderPad; std::vector mMotionRanges; }; /* Types of input device configuration files. */ enum InputDeviceConfigurationFileType { INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */ INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */ }; /* * Gets the path of an input device configuration file, if one is available. * Considers both system provided and user installed configuration files. * * The device identifier is used to construct several default configuration file * names to try based on the device name, vendor, product, and version. * * Returns an empty string if not found. */ extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type); /* * Gets the path of an input device configuration file, if one is available. * Considers both system provided and user installed configuration files. * * The name is case-sensitive and is used to construct the filename to resolve. * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. * * Returns an empty string if not found. */ extern std::string getInputDeviceConfigurationFilePathByName( const std::string& name, InputDeviceConfigurationFileType type); enum ReservedInputDeviceId : int32_t { // Device id of a special "virtual" keyboard that is always present. VIRTUAL_KEYBOARD_ID = -1, // Device id of the "built-in" keyboard if there is one. BUILT_IN_KEYBOARD_ID = 0, }; } // namespace android #endif // _LIBINPUT_INPUT_DEVICE_H include/input/InputEventLabels.h0100644 0000000 0000000 00000032447 13756501735 016012 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_INPUT_EVENT_LABELS_H #define _LIBINPUT_INPUT_EVENT_LABELS_H #include #include #define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } #define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } #define DEFINE_LED(led) { #led, ALED_##led } #define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag } namespace android { template size_t size(T (&)[N]) { return N; } struct InputEventLabel { const char *literal; int value; }; static const InputEventLabel KEYCODES[] = { // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. DEFINE_KEYCODE(UNKNOWN), DEFINE_KEYCODE(SOFT_LEFT), DEFINE_KEYCODE(SOFT_RIGHT), DEFINE_KEYCODE(HOME), DEFINE_KEYCODE(BACK), DEFINE_KEYCODE(CALL), DEFINE_KEYCODE(ENDCALL), DEFINE_KEYCODE(0), DEFINE_KEYCODE(1), DEFINE_KEYCODE(2), DEFINE_KEYCODE(3), DEFINE_KEYCODE(4), DEFINE_KEYCODE(5), DEFINE_KEYCODE(6), DEFINE_KEYCODE(7), DEFINE_KEYCODE(8), DEFINE_KEYCODE(9), DEFINE_KEYCODE(STAR), DEFINE_KEYCODE(POUND), DEFINE_KEYCODE(DPAD_UP), DEFINE_KEYCODE(DPAD_DOWN), DEFINE_KEYCODE(DPAD_LEFT), DEFINE_KEYCODE(DPAD_RIGHT), DEFINE_KEYCODE(DPAD_CENTER), DEFINE_KEYCODE(VOLUME_UP), DEFINE_KEYCODE(VOLUME_DOWN), DEFINE_KEYCODE(POWER), DEFINE_KEYCODE(CAMERA), DEFINE_KEYCODE(CLEAR), DEFINE_KEYCODE(A), DEFINE_KEYCODE(B), DEFINE_KEYCODE(C), DEFINE_KEYCODE(D), DEFINE_KEYCODE(E), DEFINE_KEYCODE(F), DEFINE_KEYCODE(G), DEFINE_KEYCODE(H), DEFINE_KEYCODE(I), DEFINE_KEYCODE(J), DEFINE_KEYCODE(K), DEFINE_KEYCODE(L), DEFINE_KEYCODE(M), DEFINE_KEYCODE(N), DEFINE_KEYCODE(O), DEFINE_KEYCODE(P), DEFINE_KEYCODE(Q), DEFINE_KEYCODE(R), DEFINE_KEYCODE(S), DEFINE_KEYCODE(T), DEFINE_KEYCODE(U), DEFINE_KEYCODE(V), DEFINE_KEYCODE(W), DEFINE_KEYCODE(X), DEFINE_KEYCODE(Y), DEFINE_KEYCODE(Z), DEFINE_KEYCODE(COMMA), DEFINE_KEYCODE(PERIOD), DEFINE_KEYCODE(ALT_LEFT), DEFINE_KEYCODE(ALT_RIGHT), DEFINE_KEYCODE(SHIFT_LEFT), DEFINE_KEYCODE(SHIFT_RIGHT), DEFINE_KEYCODE(TAB), DEFINE_KEYCODE(SPACE), DEFINE_KEYCODE(SYM), DEFINE_KEYCODE(EXPLORER), DEFINE_KEYCODE(ENVELOPE), DEFINE_KEYCODE(ENTER), DEFINE_KEYCODE(DEL), DEFINE_KEYCODE(GRAVE), DEFINE_KEYCODE(MINUS), DEFINE_KEYCODE(EQUALS), DEFINE_KEYCODE(LEFT_BRACKET), DEFINE_KEYCODE(RIGHT_BRACKET), DEFINE_KEYCODE(BACKSLASH), DEFINE_KEYCODE(SEMICOLON), DEFINE_KEYCODE(APOSTROPHE), DEFINE_KEYCODE(SLASH), DEFINE_KEYCODE(AT), DEFINE_KEYCODE(NUM), DEFINE_KEYCODE(HEADSETHOOK), DEFINE_KEYCODE(FOCUS), // *Camera* focus DEFINE_KEYCODE(PLUS), DEFINE_KEYCODE(MENU), DEFINE_KEYCODE(NOTIFICATION), DEFINE_KEYCODE(SEARCH), DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), DEFINE_KEYCODE(MEDIA_STOP), DEFINE_KEYCODE(MEDIA_NEXT), DEFINE_KEYCODE(MEDIA_PREVIOUS), DEFINE_KEYCODE(MEDIA_REWIND), DEFINE_KEYCODE(MEDIA_FAST_FORWARD), DEFINE_KEYCODE(MUTE), DEFINE_KEYCODE(PAGE_UP), DEFINE_KEYCODE(PAGE_DOWN), DEFINE_KEYCODE(PICTSYMBOLS), DEFINE_KEYCODE(SWITCH_CHARSET), DEFINE_KEYCODE(BUTTON_A), DEFINE_KEYCODE(BUTTON_B), DEFINE_KEYCODE(BUTTON_C), DEFINE_KEYCODE(BUTTON_X), DEFINE_KEYCODE(BUTTON_Y), DEFINE_KEYCODE(BUTTON_Z), DEFINE_KEYCODE(BUTTON_L1), DEFINE_KEYCODE(BUTTON_R1), DEFINE_KEYCODE(BUTTON_L2), DEFINE_KEYCODE(BUTTON_R2), DEFINE_KEYCODE(BUTTON_THUMBL), DEFINE_KEYCODE(BUTTON_THUMBR), DEFINE_KEYCODE(BUTTON_START), DEFINE_KEYCODE(BUTTON_SELECT), DEFINE_KEYCODE(BUTTON_MODE), DEFINE_KEYCODE(ESCAPE), DEFINE_KEYCODE(FORWARD_DEL), DEFINE_KEYCODE(CTRL_LEFT), DEFINE_KEYCODE(CTRL_RIGHT), DEFINE_KEYCODE(CAPS_LOCK), DEFINE_KEYCODE(SCROLL_LOCK), DEFINE_KEYCODE(META_LEFT), DEFINE_KEYCODE(META_RIGHT), DEFINE_KEYCODE(FUNCTION), DEFINE_KEYCODE(SYSRQ), DEFINE_KEYCODE(BREAK), DEFINE_KEYCODE(MOVE_HOME), DEFINE_KEYCODE(MOVE_END), DEFINE_KEYCODE(INSERT), DEFINE_KEYCODE(FORWARD), DEFINE_KEYCODE(MEDIA_PLAY), DEFINE_KEYCODE(MEDIA_PAUSE), DEFINE_KEYCODE(MEDIA_CLOSE), DEFINE_KEYCODE(MEDIA_EJECT), DEFINE_KEYCODE(MEDIA_RECORD), DEFINE_KEYCODE(F1), DEFINE_KEYCODE(F2), DEFINE_KEYCODE(F3), DEFINE_KEYCODE(F4), DEFINE_KEYCODE(F5), DEFINE_KEYCODE(F6), DEFINE_KEYCODE(F7), DEFINE_KEYCODE(F8), DEFINE_KEYCODE(F9), DEFINE_KEYCODE(F10), DEFINE_KEYCODE(F11), DEFINE_KEYCODE(F12), DEFINE_KEYCODE(NUM_LOCK), DEFINE_KEYCODE(NUMPAD_0), DEFINE_KEYCODE(NUMPAD_1), DEFINE_KEYCODE(NUMPAD_2), DEFINE_KEYCODE(NUMPAD_3), DEFINE_KEYCODE(NUMPAD_4), DEFINE_KEYCODE(NUMPAD_5), DEFINE_KEYCODE(NUMPAD_6), DEFINE_KEYCODE(NUMPAD_7), DEFINE_KEYCODE(NUMPAD_8), DEFINE_KEYCODE(NUMPAD_9), DEFINE_KEYCODE(NUMPAD_DIVIDE), DEFINE_KEYCODE(NUMPAD_MULTIPLY), DEFINE_KEYCODE(NUMPAD_SUBTRACT), DEFINE_KEYCODE(NUMPAD_ADD), DEFINE_KEYCODE(NUMPAD_DOT), DEFINE_KEYCODE(NUMPAD_COMMA), DEFINE_KEYCODE(NUMPAD_ENTER), DEFINE_KEYCODE(NUMPAD_EQUALS), DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), DEFINE_KEYCODE(VOLUME_MUTE), DEFINE_KEYCODE(INFO), DEFINE_KEYCODE(CHANNEL_UP), DEFINE_KEYCODE(CHANNEL_DOWN), DEFINE_KEYCODE(ZOOM_IN), DEFINE_KEYCODE(ZOOM_OUT), DEFINE_KEYCODE(TV), DEFINE_KEYCODE(WINDOW), DEFINE_KEYCODE(GUIDE), DEFINE_KEYCODE(DVR), DEFINE_KEYCODE(BOOKMARK), DEFINE_KEYCODE(CAPTIONS), DEFINE_KEYCODE(SETTINGS), DEFINE_KEYCODE(TV_POWER), DEFINE_KEYCODE(TV_INPUT), DEFINE_KEYCODE(STB_POWER), DEFINE_KEYCODE(STB_INPUT), DEFINE_KEYCODE(AVR_POWER), DEFINE_KEYCODE(AVR_INPUT), DEFINE_KEYCODE(PROG_RED), DEFINE_KEYCODE(PROG_GREEN), DEFINE_KEYCODE(PROG_YELLOW), DEFINE_KEYCODE(PROG_BLUE), DEFINE_KEYCODE(APP_SWITCH), DEFINE_KEYCODE(BUTTON_1), DEFINE_KEYCODE(BUTTON_2), DEFINE_KEYCODE(BUTTON_3), DEFINE_KEYCODE(BUTTON_4), DEFINE_KEYCODE(BUTTON_5), DEFINE_KEYCODE(BUTTON_6), DEFINE_KEYCODE(BUTTON_7), DEFINE_KEYCODE(BUTTON_8), DEFINE_KEYCODE(BUTTON_9), DEFINE_KEYCODE(BUTTON_10), DEFINE_KEYCODE(BUTTON_11), DEFINE_KEYCODE(BUTTON_12), DEFINE_KEYCODE(BUTTON_13), DEFINE_KEYCODE(BUTTON_14), DEFINE_KEYCODE(BUTTON_15), DEFINE_KEYCODE(BUTTON_16), DEFINE_KEYCODE(LANGUAGE_SWITCH), DEFINE_KEYCODE(MANNER_MODE), DEFINE_KEYCODE(3D_MODE), DEFINE_KEYCODE(CONTACTS), DEFINE_KEYCODE(CALENDAR), DEFINE_KEYCODE(MUSIC), DEFINE_KEYCODE(CALCULATOR), DEFINE_KEYCODE(ZENKAKU_HANKAKU), DEFINE_KEYCODE(EISU), DEFINE_KEYCODE(MUHENKAN), DEFINE_KEYCODE(HENKAN), DEFINE_KEYCODE(KATAKANA_HIRAGANA), DEFINE_KEYCODE(YEN), DEFINE_KEYCODE(RO), DEFINE_KEYCODE(KANA), DEFINE_KEYCODE(ASSIST), DEFINE_KEYCODE(BRIGHTNESS_DOWN), DEFINE_KEYCODE(BRIGHTNESS_UP), DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), DEFINE_KEYCODE(SLEEP), DEFINE_KEYCODE(WAKEUP), DEFINE_KEYCODE(PAIRING), DEFINE_KEYCODE(MEDIA_TOP_MENU), DEFINE_KEYCODE(11), DEFINE_KEYCODE(12), DEFINE_KEYCODE(LAST_CHANNEL), DEFINE_KEYCODE(TV_DATA_SERVICE), DEFINE_KEYCODE(VOICE_ASSIST), DEFINE_KEYCODE(TV_RADIO_SERVICE), DEFINE_KEYCODE(TV_TELETEXT), DEFINE_KEYCODE(TV_NUMBER_ENTRY), DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), DEFINE_KEYCODE(TV_SATELLITE), DEFINE_KEYCODE(TV_SATELLITE_BS), DEFINE_KEYCODE(TV_SATELLITE_CS), DEFINE_KEYCODE(TV_SATELLITE_SERVICE), DEFINE_KEYCODE(TV_NETWORK), DEFINE_KEYCODE(TV_ANTENNA_CABLE), DEFINE_KEYCODE(TV_INPUT_HDMI_1), DEFINE_KEYCODE(TV_INPUT_HDMI_2), DEFINE_KEYCODE(TV_INPUT_HDMI_3), DEFINE_KEYCODE(TV_INPUT_HDMI_4), DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), DEFINE_KEYCODE(TV_INPUT_VGA_1), DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), DEFINE_KEYCODE(TV_ZOOM_MODE), DEFINE_KEYCODE(TV_CONTENTS_MENU), DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), DEFINE_KEYCODE(HELP), DEFINE_KEYCODE(NAVIGATE_PREVIOUS), DEFINE_KEYCODE(NAVIGATE_NEXT), DEFINE_KEYCODE(NAVIGATE_IN), DEFINE_KEYCODE(NAVIGATE_OUT), DEFINE_KEYCODE(STEM_PRIMARY), DEFINE_KEYCODE(STEM_1), DEFINE_KEYCODE(STEM_2), DEFINE_KEYCODE(STEM_3), DEFINE_KEYCODE(DPAD_UP_LEFT), DEFINE_KEYCODE(DPAD_DOWN_LEFT), DEFINE_KEYCODE(DPAD_UP_RIGHT), DEFINE_KEYCODE(DPAD_DOWN_RIGHT), DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), DEFINE_KEYCODE(MEDIA_STEP_FORWARD), DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), DEFINE_KEYCODE(SOFT_SLEEP), DEFINE_KEYCODE(CUT), DEFINE_KEYCODE(COPY), DEFINE_KEYCODE(PASTE), DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), DEFINE_KEYCODE(ALL_APPS), DEFINE_KEYCODE(REFRESH), DEFINE_KEYCODE(THUMBS_UP), DEFINE_KEYCODE(THUMBS_DOWN), DEFINE_KEYCODE(PROFILE_SWITCH), { nullptr, 0 } }; static const InputEventLabel AXES[] = { DEFINE_AXIS(X), DEFINE_AXIS(Y), DEFINE_AXIS(PRESSURE), DEFINE_AXIS(SIZE), DEFINE_AXIS(TOUCH_MAJOR), DEFINE_AXIS(TOUCH_MINOR), DEFINE_AXIS(TOOL_MAJOR), DEFINE_AXIS(TOOL_MINOR), DEFINE_AXIS(ORIENTATION), DEFINE_AXIS(VSCROLL), DEFINE_AXIS(HSCROLL), DEFINE_AXIS(Z), DEFINE_AXIS(RX), DEFINE_AXIS(RY), DEFINE_AXIS(RZ), DEFINE_AXIS(HAT_X), DEFINE_AXIS(HAT_Y), DEFINE_AXIS(LTRIGGER), DEFINE_AXIS(RTRIGGER), DEFINE_AXIS(THROTTLE), DEFINE_AXIS(RUDDER), DEFINE_AXIS(WHEEL), DEFINE_AXIS(GAS), DEFINE_AXIS(BRAKE), DEFINE_AXIS(DISTANCE), DEFINE_AXIS(TILT), DEFINE_AXIS(GENERIC_1), DEFINE_AXIS(GENERIC_2), DEFINE_AXIS(GENERIC_3), DEFINE_AXIS(GENERIC_4), DEFINE_AXIS(GENERIC_5), DEFINE_AXIS(GENERIC_6), DEFINE_AXIS(GENERIC_7), DEFINE_AXIS(GENERIC_8), DEFINE_AXIS(GENERIC_9), DEFINE_AXIS(GENERIC_10), DEFINE_AXIS(GENERIC_11), DEFINE_AXIS(GENERIC_12), DEFINE_AXIS(GENERIC_13), DEFINE_AXIS(GENERIC_14), DEFINE_AXIS(GENERIC_15), DEFINE_AXIS(GENERIC_16), // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. { nullptr, 0 } }; static const InputEventLabel LEDS[] = { DEFINE_LED(NUM_LOCK), DEFINE_LED(CAPS_LOCK), DEFINE_LED(SCROLL_LOCK), DEFINE_LED(COMPOSE), DEFINE_LED(KANA), DEFINE_LED(SLEEP), DEFINE_LED(SUSPEND), DEFINE_LED(MUTE), DEFINE_LED(MISC), DEFINE_LED(MAIL), DEFINE_LED(CHARGING), DEFINE_LED(CONTROLLER_1), DEFINE_LED(CONTROLLER_2), DEFINE_LED(CONTROLLER_3), DEFINE_LED(CONTROLLER_4), // NOTE: If you add new LEDs here, you must also add them to Input.h { nullptr, 0 } }; static const InputEventLabel FLAGS[] = { DEFINE_FLAG(VIRTUAL), DEFINE_FLAG(FUNCTION), DEFINE_FLAG(GESTURE), { nullptr, 0 } }; static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { while (list->literal) { if (strcmp(literal, list->literal) == 0) { return list->value; } list++; } return list->value; } static const char* lookupLabelByValue(int value, const InputEventLabel* list) { while (list->literal) { if (list->value == value) { return list->literal; } list++; } return nullptr; } static inline int32_t getKeyCodeByLabel(const char* label) { return int32_t(lookupValueByLabel(label, KEYCODES)); } static inline const char* getLabelByKeyCode(int32_t keyCode) { if (keyCode >= 0 && keyCode < static_cast(size(KEYCODES))) { return KEYCODES[keyCode].literal; } return nullptr; } static inline uint32_t getKeyFlagByLabel(const char* label) { return uint32_t(lookupValueByLabel(label, FLAGS)); } static inline int32_t getAxisByLabel(const char* label) { return int32_t(lookupValueByLabel(label, AXES)); } static inline const char* getAxisLabel(int32_t axisId) { return lookupLabelByValue(axisId, AXES); } static inline int32_t getLedByLabel(const char* label) { return int32_t(lookupValueByLabel(label, LEDS)); } } // namespace android #endif // _LIBINPUT_INPUT_EVENT_LABELS_H include/input/InputTransport.h0100644 0000000 0000000 00000044767 13756501735 015612 0ustar000000000 0000000 /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_INPUT_TRANSPORT_H #define _LIBINPUT_INPUT_TRANSPORT_H #pragma GCC system_header /** * Native input transport. * * The InputChannel provides a mechanism for exchanging InputMessage structures across processes. * * The InputPublisher and InputConsumer each handle one end-point of an input channel. * The InputPublisher is used by the input dispatcher to send events to the application. * The InputConsumer is used by the application to receive events from the input dispatcher. */ #include #include #include #include #include #include #include #include namespace android { class Parcel; /* * Intermediate representation used to send input events and related signals. * * Note that this structure is used for IPCs so its layout must be identical * on 64 and 32 bit processes. This is tested in StructLayout_test.cpp. * * Since the struct must be aligned to an 8-byte boundary, there could be uninitialized bytes * in-between the defined fields. This padding data should be explicitly accounted for by adding * "empty" fields into the struct. This data is memset to zero before sending the struct across * the socket. Adding the explicit fields ensures that the memset is not optimized away by the * compiler. When a new field is added to the struct, the corresponding change * in StructLayout_test should be made. */ struct InputMessage { enum { TYPE_KEY = 1, TYPE_MOTION = 2, TYPE_FINISHED = 3, }; struct Header { uint32_t type; // We don't need this field in order to align the body below but we // leave it here because InputMessage::size() and other functions // compute the size of this structure as sizeof(Header) + sizeof(Body). uint32_t padding; } header; // Body *must* be 8 byte aligned. union Body { struct Key { uint32_t seq; uint32_t empty1; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; int32_t displayId; int32_t action; int32_t flags; int32_t keyCode; int32_t scanCode; int32_t metaState; int32_t repeatCount; uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); inline size_t size() const { return sizeof(Key); } } key; struct Motion { uint32_t seq; uint32_t empty1; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; int32_t displayId; int32_t action; int32_t actionButton; int32_t flags; int32_t metaState; int32_t buttonState; MotionClassification classification; // base type: uint8_t uint8_t empty2[3]; int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); float xOffset; float yOffset; float xPrecision; float yPrecision; uint32_t pointerCount; uint32_t empty3; // Note that PointerCoords requires 8 byte alignment. struct Pointer { PointerProperties properties; PointerCoords coords; } pointers[MAX_POINTERS]; int32_t getActionId() const { uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; return pointers[index].properties.id; } inline size_t size() const { return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS + sizeof(Pointer) * pointerCount; } } motion; struct Finished { uint32_t seq; bool handled; inline size_t size() const { return sizeof(Finished); } } finished; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; size_t size() const; void getSanitizedCopy(InputMessage* msg) const; }; /* * An input channel consists of a local unix domain socket used to send and receive * input messages across processes. Each channel has a descriptive name for debugging purposes. * * Each endpoint has its own InputChannel object that specifies its file descriptor. * * The input channel is closed when all references to it are released. */ class InputChannel : public RefBase { protected: virtual ~InputChannel(); public: InputChannel() = default; InputChannel(const std::string& name, int fd); /* Creates a pair of input channels. * * Returns OK on success. */ static status_t openInputChannelPair(const std::string& name, sp& outServerChannel, sp& outClientChannel); inline std::string getName() const { return mName; } inline int getFd() const { return mFd; } /* Sends a message to the other endpoint. * * If the channel is full then the message is guaranteed not to have been sent at all. * Try again after the consumer has sent a finished signal indicating that it has * consumed some of the pending messages from the channel. * * Returns OK on success. * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t sendMessage(const InputMessage* msg); /* Receives a message sent by the other endpoint. * * If there is no message present, try again after poll() indicates that the fd * is readable. * * Returns OK on success. * Returns WOULD_BLOCK if there is no message present. * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t receiveMessage(InputMessage* msg); /* Returns a new object that has a duplicate of this channel's fd. */ sp dup() const; status_t write(Parcel& out) const; status_t read(const Parcel& from); sp getToken() const; void setToken(const sp& token); private: void setFd(int fd); std::string mName; int mFd = -1; sp mToken = nullptr; }; /* * Publishes input events to an input channel. */ class InputPublisher { public: /* Creates a publisher associated with an input channel. */ explicit InputPublisher(const sp& channel); /* Destroys the publisher and releases its input channel. */ ~InputPublisher(); /* Gets the underlying input channel. */ inline sp getChannel() { return mChannel; } /* Publishes a key event to the input channel. * * Returns OK on success. * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ status_t publishKeyEvent( uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); /* Publishes a motion event to the input channel. * * Returns OK on success. * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent( uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, * and whether the consumer handled the message. * * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. * Returns WOULD_BLOCK if there is no signal present. * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: sp mChannel; }; /* * Consumes input events from an input channel. */ class InputConsumer { public: /* Creates a consumer associated with an input channel. */ explicit InputConsumer(const sp& channel); /* Destroys the consumer and releases its input channel. */ ~InputConsumer(); /* Gets the underlying input channel. */ inline sp getChannel() { return mChannel; } /* Consumes an input event from the input channel and copies its contents into * an InputEvent object created using the specified factory. * * Tries to combine a series of move events into larger batches whenever possible. * * If consumeBatches is false, then defers consuming pending batched events if it * is possible for additional samples to be added to them later. Call hasPendingBatch() * to determine whether a pending batch is available to be consumed. * * If consumeBatches is true, then events are still batched but they are consumed * immediately as soon as the input channel is exhausted. * * The frameTime parameter specifies the time when the current display frame started * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. * * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. * Returns WOULD_BLOCK if there is no event present. * Returns DEAD_OBJECT if the channel's peer has been closed. * Returns NO_MEMORY if the event could not be created. * Other errors probably indicate that the channel is broken. */ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether * the message was handled by the consumer. * * Returns OK on success. * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ status_t sendFinishedSignal(uint32_t seq, bool handled); /* Returns true if there is a deferred event waiting. * * Should be called after calling consume() to determine whether the consumer * has a deferred event to be processed. Deferred events are somewhat special in * that they have already been removed from the input channel. If the input channel * becomes empty, the client may need to do extra work to ensure that it processes * the deferred event despite the fact that the input channel's file descriptor * is not readable. * * One option is simply to call consume() in a loop until it returns WOULD_BLOCK. * This guarantees that all deferred events will be processed. * * Alternately, the caller can call hasDeferredEvent() to determine whether there is * a deferred event waiting and then ensure that its event loop wakes up at least * one more time to consume the deferred event. */ bool hasDeferredEvent() const; /* Returns true if there is a pending batch. * * Should be called after calling consume() with consumeBatches == false to determine * whether consume() should be called again later on with consumeBatches == true. */ bool hasPendingBatch() const; private: // True if touch resampling is enabled. const bool mResampleTouch; // The input channel. sp mChannel; // The current input message. InputMessage mMsg; // True if mMsg contains a valid input message that was deferred from the previous // call to consume and that still needs to be handled. bool mMsgDeferred; // Batched motion events per device and source. struct Batch { Vector samples; }; Vector mBatches; // Touch state per device and source, only for sources of class pointer. struct History { nsecs_t eventTime; BitSet32 idBits; int32_t idToIndex[MAX_POINTER_ID + 1]; PointerCoords pointers[MAX_POINTERS]; void initializeFrom(const InputMessage& msg) { eventTime = msg.body.motion.eventTime; idBits.clear(); for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { uint32_t id = msg.body.motion.pointers[i].properties.id; idBits.markBit(id); idToIndex[id] = i; pointers[i].copyFrom(msg.body.motion.pointers[i].coords); } } void initializeFrom(const History& other) { eventTime = other.eventTime; idBits = other.idBits; // temporary copy for (size_t i = 0; i < other.idBits.count(); i++) { uint32_t id = idBits.clearFirstMarkedBit(); int32_t index = other.idToIndex[id]; idToIndex[id] = index; pointers[index].copyFrom(other.pointers[index]); } idBits = other.idBits; // final copy } const PointerCoords& getPointerById(uint32_t id) const { return pointers[idToIndex[id]]; } bool hasPointerId(uint32_t id) const { return idBits.hasBit(id); } }; struct TouchState { int32_t deviceId; int32_t source; size_t historyCurrent; size_t historySize; History history[2]; History lastResample; void initialize(int32_t deviceId, int32_t source) { this->deviceId = deviceId; this->source = source; historyCurrent = 0; historySize = 0; lastResample.eventTime = 0; lastResample.idBits.clear(); } void addHistory(const InputMessage& msg) { historyCurrent ^= 1; if (historySize < 2) { historySize += 1; } history[historyCurrent].initializeFrom(msg); } const History* getHistory(size_t index) const { return &history[(historyCurrent + index) & 1]; } bool recentCoordinatesAreIdentical(uint32_t id) const { // Return true if the two most recently received "raw" coordinates are identical if (historySize < 2) { return false; } if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) { return false; } float currentX = getHistory(0)->getPointerById(id).getX(); float currentY = getHistory(0)->getPointerById(id).getY(); float previousX = getHistory(1)->getPointerById(id).getX(); float previousY = getHistory(1)->getPointerById(id).getY(); if (currentX == previousX && currentY == previousY) { return true; } return false; } }; Vector mTouchStates; // Chain of batched sequence numbers. When multiple input messages are combined into // a batch, we append a record here that associates the last sequence number in the // batch with the previous one. When the finished signal is sent, we traverse the // chain to individually finish all input messages that were part of the batch. struct SeqChain { uint32_t seq; // sequence number of batched input message uint32_t chain; // sequence number of previous batched input message }; Vector mSeqChains; status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); status_t consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); void updateTouchState(InputMessage& msg); void resampleTouchState(nsecs_t frameTime, MotionEvent* event, const InputMessage *next); ssize_t findBatch(int32_t deviceId, int32_t source) const; ssize_t findTouchState(int32_t deviceId, int32_t source) const; status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); static void rewriteMessage(TouchState& state, InputMessage& msg); static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); static void addSample(MotionEvent* event, const InputMessage* msg); static bool canAddSample(const Batch& batch, const InputMessage* msg); static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); static bool shouldResampleTool(int32_t toolType); static bool isTouchResamplingEnabled(); }; } // namespace android #endif // _LIBINPUT_INPUT_TRANSPORT_H include/input/InputWindow.h0100644 0000000 0000000 00000020717 13756501735 015052 0ustar000000000 0000000 /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _UI_INPUT_WINDOW_H #define _UI_INPUT_WINDOW_H #include #include #include #include #include #include #include "InputApplication.h" namespace android { class Parcel; /* * Describes the properties of a window that can receive input. */ struct InputWindowInfo { InputWindowInfo() = default; InputWindowInfo(const Parcel& from); // Window flags from WindowManager.LayoutParams enum { FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, FLAG_DIM_BEHIND = 0x00000002, FLAG_BLUR_BEHIND = 0x00000004, FLAG_NOT_FOCUSABLE = 0x00000008, FLAG_NOT_TOUCHABLE = 0x00000010, FLAG_NOT_TOUCH_MODAL = 0x00000020, FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, FLAG_KEEP_SCREEN_ON = 0x00000080, FLAG_LAYOUT_IN_SCREEN = 0x00000100, FLAG_LAYOUT_NO_LIMITS = 0x00000200, FLAG_FULLSCREEN = 0x00000400, FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, FLAG_DITHER = 0x00001000, FLAG_SECURE = 0x00002000, FLAG_SCALED = 0x00004000, FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, FLAG_LAYOUT_INSET_DECOR = 0x00010000, FLAG_ALT_FOCUSABLE_IM = 0x00020000, FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, FLAG_SHOW_WHEN_LOCKED = 0x00080000, FLAG_SHOW_WALLPAPER = 0x00100000, FLAG_TURN_SCREEN_ON = 0x00200000, FLAG_DISMISS_KEYGUARD = 0x00400000, FLAG_SPLIT_TOUCH = 0x00800000, FLAG_SLIPPERY = 0x20000000, FLAG_NEEDS_MENU_KEY = 0x40000000, }; // Window types from WindowManager.LayoutParams enum { FIRST_APPLICATION_WINDOW = 1, TYPE_BASE_APPLICATION = 1, TYPE_APPLICATION = 2, TYPE_APPLICATION_STARTING = 3, LAST_APPLICATION_WINDOW = 99, FIRST_SUB_WINDOW = 1000, TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1, TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2, TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3, TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4, LAST_SUB_WINDOW = 1999, FIRST_SYSTEM_WINDOW = 2000, TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1, TYPE_PHONE = FIRST_SYSTEM_WINDOW+2, TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3, TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4, TYPE_TOAST = FIRST_SYSTEM_WINDOW+5, TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6, TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7, TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8, TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9, TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10, TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15, TYPE_DRAG = FIRST_SYSTEM_WINDOW+16, TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17, TYPE_POINTER = FIRST_SYSTEM_WINDOW+18, TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19, TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20, TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21, TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22, TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24, TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27, TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32, TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34, LAST_SYSTEM_WINDOW = 2999, }; enum { INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001, INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004, }; /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ sp token; std::string name; int32_t layoutParamsFlags; int32_t layoutParamsType; nsecs_t dispatchingTimeout; /* These values are filled in by SurfaceFlinger. */ int32_t frameLeft; int32_t frameTop; int32_t frameRight; int32_t frameBottom; /* * SurfaceFlinger consumes this value to shrink the computed frame. This is * different from shrinking the touchable region in that it DOES shift the coordinate * space where-as the touchable region does not and is more like "cropping". This * is used for window shadows. */ int32_t surfaceInset = 0; // A global scaling factor for all windows. Unlike windowScaleX/Y this results // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis. float globalScaleFactor; // Scaling factors applied to individual windows. float windowXScale = 1.0f; float windowYScale = 1.0f; /* * This is filled in by the WM relative to the frame and then translated * to absolute coordinates by SurfaceFlinger once the frame is computed. */ Region touchableRegion; bool visible; bool canReceiveKeys; bool hasFocus; bool hasWallpaper; bool paused; int32_t layer; int32_t ownerPid; int32_t ownerUid; int32_t inputFeatures; int32_t displayId; int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; bool replaceTouchableRegionWithCrop; wp touchableRegionCropHandle; void addTouchableRegion(const Rect& region); bool touchableRegionContainsPoint(int32_t x, int32_t y) const; bool frameContainsPoint(int32_t x, int32_t y) const; /* Returns true if the window is of a trusted type that is allowed to silently * overlay other windows for the purpose of implementing the secure views feature. * Trusted overlays, such as IME windows, can partly obscure other windows without causing * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ bool isTrustedOverlay() const; bool supportsSplitTouch() const; bool overlaps(const InputWindowInfo* other) const; status_t write(Parcel& output) const; static InputWindowInfo read(const Parcel& from); }; /* * Handle for a window that can receive input. * * Used by the native input dispatcher to indirectly refer to the window manager objects * that describe a window. */ class InputWindowHandle : public RefBase { public: inline const InputWindowInfo* getInfo() const { return &mInfo; } sp getToken() const; sp getApplicationToken() { return mInfo.applicationInfo.token; } inline std::string getName() const { return mInfo.token ? mInfo.name : ""; } inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } /** * Requests that the state of this object be updated to reflect * the most current available information about the application. * * This method should only be called from within the input dispatcher's * critical section. * * Returns true on success, or false if the handle is no longer valid. */ virtual bool updateInfo() = 0; /** * Updates from another input window handle. */ void updateFrom(const sp handle); /** * Releases the channel used by the associated information when it is * no longer needed. */ void releaseChannel(); protected: explicit InputWindowHandle(); virtual ~InputWindowHandle(); InputWindowInfo mInfo; }; } // namespace android #endif // _UI_INPUT_WINDOW_H include/input/KeyCharacterMap.h0100644 0000000 0000000 00000022022 13756501735 015555 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_KEY_CHARACTER_MAP_H #define _LIBINPUT_KEY_CHARACTER_MAP_H #include #ifdef __ANDROID__ #include #endif #include #include #include #include #include #include // Maximum number of keys supported by KeyCharacterMaps #define MAX_KEYS 8192 namespace android { /** * Describes a mapping from Android key codes to characters. * Also specifies other functions of the keyboard such as the keyboard type * and key modifier semantics. * * This object is immutable after it has been loaded. */ class KeyCharacterMap : public RefBase { public: enum KeyboardType { KEYBOARD_TYPE_UNKNOWN = 0, KEYBOARD_TYPE_NUMERIC = 1, KEYBOARD_TYPE_PREDICTIVE = 2, KEYBOARD_TYPE_ALPHA = 3, KEYBOARD_TYPE_FULL = 4, /** * Deprecated. Set 'keyboard.specialFunction' to '1' in the device's IDC file instead. */ KEYBOARD_TYPE_SPECIAL_FUNCTION = 5, KEYBOARD_TYPE_OVERLAY = 6, }; enum Format { // Base keyboard layout, may contain device-specific options, such as "type" declaration. FORMAT_BASE = 0, // Overlay keyboard layout, more restrictive, may be published by applications, // cannot override device-specific options. FORMAT_OVERLAY = 1, // Either base or overlay layout ok. FORMAT_ANY = 2, }; // Substitute key code and meta state for fallback action. struct FallbackAction { int32_t keyCode; int32_t metaState; }; /* Loads a key character map from a file. */ static status_t load(const std::string& filename, Format format, sp* outMap); /* Loads a key character map from its string contents. */ static status_t loadContents(const std::string& filename, const char* contents, Format format, sp* outMap); /* Combines a base key character map and an overlay. */ static sp combine(const sp& base, const sp& overlay); /* Returns an empty key character map. */ static sp empty(); /* Gets the keyboard type. */ int32_t getKeyboardType() const; /* Gets the primary character for this key as in the label physically printed on it. * Returns 0 if none (eg. for non-printing keys). */ char16_t getDisplayLabel(int32_t keyCode) const; /* Gets the Unicode character for the number or symbol generated by the key * when the keyboard is used as a dialing pad. * Returns 0 if no number or symbol is generated. */ char16_t getNumber(int32_t keyCode) const; /* Gets the Unicode character generated by the key and meta key modifiers. * Returns 0 if no character is generated. */ char16_t getCharacter(int32_t keyCode, int32_t metaState) const; /* Gets the fallback action to use by default if the application does not * handle the specified key. * Returns true if an action was available, false if none. */ bool getFallbackAction(int32_t keyCode, int32_t metaState, FallbackAction* outFallbackAction) const; /* Gets the first matching Unicode character that can be generated by the key, * preferring the one with the specified meta key modifiers. * Returns 0 if no matching character is generated. */ char16_t getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, int32_t metaState) const; /* Gets a sequence of key events that could plausibly generate the specified * character sequence. Returns false if some of the characters cannot be generated. */ bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector& outEvents) const; /* Maps a scan code and usage code to a key code, in case this key map overrides * the mapping in some way. */ status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const; /* Tries to find a replacement key code for a given key code and meta state * in character map. */ void tryRemapKey(int32_t scanCode, int32_t metaState, int32_t* outKeyCode, int32_t* outMetaState) const; #ifdef __ANDROID__ /* Reads a key map from a parcel. */ static sp readFromParcel(Parcel* parcel); /* Writes a key map to a parcel. */ void writeToParcel(Parcel* parcel) const; #endif protected: virtual ~KeyCharacterMap(); private: struct Behavior { Behavior(); Behavior(const Behavior& other); /* The next behavior in the list, or NULL if none. */ Behavior* next; /* The meta key modifiers for this behavior. */ int32_t metaState; /* The character to insert. */ char16_t character; /* The fallback keycode if the key is not handled. */ int32_t fallbackKeyCode; /* The replacement keycode if the key has to be replaced outright. */ int32_t replacementKeyCode; }; struct Key { Key(); Key(const Key& other); ~Key(); /* The single character label printed on the key, or 0 if none. */ char16_t label; /* The number or symbol character generated by the key, or 0 if none. */ char16_t number; /* The list of key behaviors sorted from most specific to least specific * meta key binding. */ Behavior* firstBehavior; }; class Parser { enum State { STATE_TOP = 0, STATE_KEY = 1, }; enum { PROPERTY_LABEL = 1, PROPERTY_NUMBER = 2, PROPERTY_META = 3, }; struct Property { inline explicit Property(int32_t property = 0, int32_t metaState = 0) : property(property), metaState(metaState) { } int32_t property; int32_t metaState; }; KeyCharacterMap* mMap; Tokenizer* mTokenizer; Format mFormat; State mState; int32_t mKeyCode; public: Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format); ~Parser(); status_t parse(); private: status_t parseType(); status_t parseMap(); status_t parseMapKey(); status_t parseKey(); status_t parseKeyProperty(); status_t finishKey(Key* key); status_t parseModifier(const std::string& token, int32_t* outMetaState); status_t parseCharacterLiteral(char16_t* outCharacter); }; static sp sEmpty; KeyedVector mKeys; int mType; KeyedVector mKeysByScanCode; KeyedVector mKeysByUsageCode; KeyCharacterMap(); KeyCharacterMap(const KeyCharacterMap& other); bool getKey(int32_t keyCode, const Key** outKey) const; bool getKeyBehavior(int32_t keyCode, int32_t metaState, const Key** outKey, const Behavior** outBehavior) const; static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState); bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; static status_t load(Tokenizer* tokenizer, Format format, sp* outMap); static void addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); static void addMetaKeys(Vector& outEvents, int32_t deviceId, int32_t metaState, bool down, nsecs_t time, int32_t* currentMetaState); static bool addSingleEphemeralMetaKey(Vector& outEvents, int32_t deviceId, int32_t metaState, bool down, nsecs_t time, int32_t keyCode, int32_t keyMetaState, int32_t* currentMetaState); static void addDoubleEphemeralMetaKey(Vector& outEvents, int32_t deviceId, int32_t metaState, bool down, nsecs_t time, int32_t leftKeyCode, int32_t leftKeyMetaState, int32_t rightKeyCode, int32_t rightKeyMetaState, int32_t eitherKeyMetaState, int32_t* currentMetaState); static void addLockedMetaKey(Vector& outEvents, int32_t deviceId, int32_t metaState, nsecs_t time, int32_t keyCode, int32_t keyMetaState, int32_t* currentMetaState); }; } // namespace android #endif // _LIBINPUT_KEY_CHARACTER_MAP_H include/input/KeyLayoutMap.h0100644 0000000 0000000 00000006216 13756501735 015145 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_KEY_LAYOUT_MAP_H #define _LIBINPUT_KEY_LAYOUT_MAP_H #include #include #include #include #include namespace android { struct AxisInfo { enum Mode { // Axis value is reported directly. MODE_NORMAL = 0, // Axis value should be inverted before reporting. MODE_INVERT = 1, // Axis value should be split into two axes MODE_SPLIT = 2, }; // Axis mode. Mode mode; // Axis id. // When split, this is the axis used for values smaller than the split position. int32_t axis; // When split, this is the axis used for values after higher than the split position. int32_t highAxis; // The split value, or 0 if not split. int32_t splitValue; // The flat value, or -1 if none. int32_t flatOverride; AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) { } }; /** * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes. * * This object is immutable after it has been loaded. */ class KeyLayoutMap : public RefBase { public: static status_t load(const std::string& filename, sp* outMap); status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; status_t findScanCodesForKey(int32_t keyCode, std::vector* outScanCodes) const; status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; protected: virtual ~KeyLayoutMap(); private: struct Key { int32_t keyCode; uint32_t flags; }; struct Led { int32_t ledCode; }; KeyedVector mKeysByScanCode; KeyedVector mKeysByUsageCode; KeyedVector mAxes; KeyedVector mLedsByScanCode; KeyedVector mLedsByUsageCode; KeyLayoutMap(); const Key* getKey(int32_t scanCode, int32_t usageCode) const; class Parser { KeyLayoutMap* mMap; Tokenizer* mTokenizer; public: Parser(KeyLayoutMap* map, Tokenizer* tokenizer); ~Parser(); status_t parse(); private: status_t parseKey(); status_t parseAxis(); status_t parseLed(); }; }; } // namespace android #endif // _LIBINPUT_KEY_LAYOUT_MAP_H include/input/Keyboard.h0100644 0000000 0000000 00000005450 13756501735 014320 0ustar000000000 0000000 /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_KEYBOARD_H #define _LIBINPUT_KEYBOARD_H #include #include #include #include #include namespace android { class KeyLayoutMap; class KeyCharacterMap; /** * Loads the key layout map and key character map for a keyboard device. */ class KeyMap { public: std::string keyLayoutFile; sp keyLayoutMap; std::string keyCharacterMapFile; sp keyCharacterMap; KeyMap(); ~KeyMap(); status_t load(const InputDeviceIdentifier& deviceIdenfier, const PropertyMap* deviceConfiguration); inline bool haveKeyLayout() const { return !keyLayoutFile.empty(); } inline bool haveKeyCharacterMap() const { return !keyCharacterMapFile.empty(); } inline bool isComplete() const { return haveKeyLayout() && haveKeyCharacterMap(); } private: bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name, InputDeviceConfigurationFileType type); }; /** * Returns true if the keyboard is eligible for use as a built-in keyboard. */ extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration, const KeyMap* keyMap); /** * Updates a meta state field when a key is pressed or released. */ extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState); /** * Normalizes the meta state such that if either the left or right modifier * meta state bits are set then the result will also include the universal * bit for that modifier. */ extern int32_t normalizeMetaState(int32_t oldMetaState); /** * Returns true if a key is a meta key like ALT or CAPS_LOCK. */ extern bool isMetaKey(int32_t keyCode); } // namespace android #endif // _LIBINPUT_KEYBOARD_H include/input/TouchVideoFrame.h0100644 0000000 0000000 00000004225 13756501735 015603 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_TOUCHVIDEOFRAME_H #define _LIBINPUT_TOUCHVIDEOFRAME_H #include #include #include #include namespace android { /** * Represents data from a single scan of the touchscreen device. * Similar in concept to a video frame, but the touch strength is used as * the values instead. */ class TouchVideoFrame { public: TouchVideoFrame(uint32_t height, uint32_t width, std::vector data, const struct timeval& timestamp); bool operator==(const TouchVideoFrame& rhs) const; /** * Height of the frame */ uint32_t getHeight() const; /** * Width of the frame */ uint32_t getWidth() const; /** * The touch strength data. * The array is a 2-D row-major matrix, with dimensions (height, width). * Total size of the array should equal getHeight() * getWidth(). * Data is allowed to be negative. */ const std::vector& getData() const; /** * Time at which the heatmap was taken. */ const struct timeval& getTimestamp() const; /** * Rotate the video frame. * The rotation value is an enum from ui/DisplayInfo.h */ void rotate(int32_t orientation); private: uint32_t mHeight; uint32_t mWidth; std::vector mData; struct timeval mTimestamp; /** * Common method for 90 degree and 270 degree rotation */ void rotateQuarterTurn(bool clockwise); void rotate180(); }; } // namespace android #endif // _LIBINPUT_TOUCHVIDEOFRAME_H include/input/VelocityControl.h0100644 0000000 0000000 00000007232 13756501735 015717 0ustar000000000 0000000 /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_VELOCITY_CONTROL_H #define _LIBINPUT_VELOCITY_CONTROL_H #include #include #include namespace android { /* * Specifies parameters that govern pointer or wheel acceleration. */ struct VelocityControlParameters { // A scale factor that is multiplied with the raw velocity deltas // prior to applying any other velocity control factors. The scale // factor should be used to adapt the input device resolution // (eg. counts per inch) to the output device resolution (eg. pixels per inch). // // Must be a positive value. // Default is 1.0 (no scaling). float scale; // The scaled speed at which acceleration begins to be applied. // This value establishes the upper bound of a low speed regime for // small precise motions that are performed without any acceleration. // // Must be a non-negative value. // Default is 0.0 (no low threshold). float lowThreshold; // The scaled speed at which maximum acceleration is applied. // The difference between highThreshold and lowThreshold controls // the range of speeds over which the acceleration factor is interpolated. // The wider the range, the smoother the acceleration. // // Must be a non-negative value greater than or equal to lowThreshold. // Default is 0.0 (no high threshold). float highThreshold; // The acceleration factor. // When the speed is above the low speed threshold, the velocity will scaled // by an interpolated value between 1.0 and this amount. // // Must be a positive greater than or equal to 1.0. // Default is 1.0 (no acceleration). float acceleration; VelocityControlParameters() : scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) { } VelocityControlParameters(float scale, float lowThreshold, float highThreshold, float acceleration) : scale(scale), lowThreshold(lowThreshold), highThreshold(highThreshold), acceleration(acceleration) { } }; /* * Implements mouse pointer and wheel speed control and acceleration. */ class VelocityControl { public: VelocityControl(); /* Sets the various parameters. */ void setParameters(const VelocityControlParameters& parameters); /* Resets the current movement counters to zero. * This has the effect of nullifying any acceleration. */ void reset(); /* Translates a raw movement delta into an appropriately * scaled / accelerated delta based on the current velocity. */ void move(nsecs_t eventTime, float* deltaX, float* deltaY); private: // If no movements are received within this amount of time, // we assume the movement has stopped and reset the movement counters. static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms VelocityControlParameters mParameters; nsecs_t mLastMovementTime; VelocityTracker::Position mRawPosition; VelocityTracker mVelocityTracker; }; } // namespace android #endif // _LIBINPUT_VELOCITY_CONTROL_H include/input/VelocityTracker.h0100644 0000000 0000000 00000023261 13756501735 015672 0ustar000000000 0000000 /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_VELOCITY_TRACKER_H #define _LIBINPUT_VELOCITY_TRACKER_H #include #include #include namespace android { class VelocityTrackerStrategy; /* * Calculates the velocity of pointer movements over time. */ class VelocityTracker { public: struct Position { float x, y; }; struct Estimator { static const size_t MAX_DEGREE = 4; // Estimator time base. nsecs_t time; // Polynomial coefficients describing motion in X and Y. float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1]; // Polynomial degree (number of coefficients), or zero if no information is // available. uint32_t degree; // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). float confidence; inline void clear() { time = 0; degree = 0; confidence = 0; for (size_t i = 0; i <= MAX_DEGREE; i++) { xCoeff[i] = 0; yCoeff[i] = 0; } } }; // Creates a velocity tracker using the specified strategy. // If strategy is NULL, uses the default strategy for the platform. VelocityTracker(const char* strategy = nullptr); ~VelocityTracker(); // Resets the velocity tracker state. void clear(); // Resets the velocity tracker state for specific pointers. // Call this method when some pointers have changed and may be reusing // an id that was assigned to a different pointer earlier. void clearPointers(BitSet32 idBits); // Adds movement information for a set of pointers. // The idBits bitfield specifies the pointer ids of the pointers whose positions // are included in the movement. // The positions array contains position information for each pointer in order by // increasing id. Its size should be equal to the number of one bits in idBits. void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); // Gets the velocity of the specified pointer id in position units per second. // Returns false and sets the velocity components to zero if there is // insufficient movement information for the pointer. bool getVelocity(uint32_t id, float* outVx, float* outVy) const; // Gets an estimator for the recent movements of the specified pointer id. // Returns false and clears the estimator if there is no information available // about the pointer. bool getEstimator(uint32_t id, Estimator* outEstimator) const; // Gets the active pointer id, or -1 if none. inline int32_t getActivePointerId() const { return mActivePointerId; } // Gets a bitset containing all pointer ids from the most recent movement. inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } private: static const char* DEFAULT_STRATEGY; nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; int32_t mActivePointerId; VelocityTrackerStrategy* mStrategy; bool configureStrategy(const char* strategy); static VelocityTrackerStrategy* createStrategy(const char* strategy); }; /* * Implements a particular velocity tracker algorithm. */ class VelocityTrackerStrategy { protected: VelocityTrackerStrategy() { } public: virtual ~VelocityTrackerStrategy() { } virtual void clear() = 0; virtual void clearPointers(BitSet32 idBits) = 0; virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) = 0; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; }; /* * Velocity tracker algorithm based on least-squares linear regression. */ class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { public: enum Weighting { // No weights applied. All data points are equally reliable. WEIGHTING_NONE, // Weight by time delta. Data points clustered together are weighted less. WEIGHTING_DELTA, // Weight such that points within a certain horizon are weighed more than those // outside of that horizon. WEIGHTING_CENTRAL, // Weight such that points older than a certain amount are weighed less. WEIGHTING_RECENT, }; // Degree must be no greater than Estimator::MAX_DEGREE. LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE); virtual ~LeastSquaresVelocityTrackerStrategy(); virtual void clear(); virtual void clearPointers(BitSet32 idBits); virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions); virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: // Sample horizon. // We don't use too much history by default since we want to react to quick // changes in direction. static const nsecs_t HORIZON = 100 * 1000000; // 100 ms // Number of samples to keep. static const uint32_t HISTORY_SIZE = 20; struct Movement { nsecs_t eventTime; BitSet32 idBits; VelocityTracker::Position positions[MAX_POINTERS]; inline const VelocityTracker::Position& getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; float chooseWeight(uint32_t index) const; const uint32_t mDegree; const Weighting mWeighting; uint32_t mIndex; Movement mMovements[HISTORY_SIZE]; }; /* * Velocity tracker algorithm that uses an IIR filter. */ class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy { public: // Degree must be 1 or 2. IntegratingVelocityTrackerStrategy(uint32_t degree); ~IntegratingVelocityTrackerStrategy(); virtual void clear(); virtual void clearPointers(BitSet32 idBits); virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions); virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: // Current state estimate for a particular pointer. struct State { nsecs_t updateTime; uint32_t degree; float xpos, xvel, xaccel; float ypos, yvel, yaccel; }; const uint32_t mDegree; BitSet32 mPointerIdBits; State mPointerState[MAX_POINTER_ID + 1]; void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const; void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const; void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; }; /* * Velocity tracker strategy used prior to ICS. */ class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { public: LegacyVelocityTrackerStrategy(); virtual ~LegacyVelocityTrackerStrategy(); virtual void clear(); virtual void clearPointers(BitSet32 idBits); virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions); virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: // Oldest sample to consider when calculating the velocity. static const nsecs_t HORIZON = 200 * 1000000; // 100 ms // Number of samples to keep. static const uint32_t HISTORY_SIZE = 20; // The minimum duration between samples when estimating velocity. static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms struct Movement { nsecs_t eventTime; BitSet32 idBits; VelocityTracker::Position positions[MAX_POINTERS]; inline const VelocityTracker::Position& getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; uint32_t mIndex; Movement mMovements[HISTORY_SIZE]; }; class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { public: ImpulseVelocityTrackerStrategy(); virtual ~ImpulseVelocityTrackerStrategy(); virtual void clear(); virtual void clearPointers(BitSet32 idBits); virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions); virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: // Sample horizon. // We don't use too much history by default since we want to react to quick // changes in direction. static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms // Number of samples to keep. static constexpr size_t HISTORY_SIZE = 20; struct Movement { nsecs_t eventTime; BitSet32 idBits; VelocityTracker::Position positions[MAX_POINTERS]; inline const VelocityTracker::Position& getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; } }; size_t mIndex; Movement mMovements[HISTORY_SIZE]; }; } // namespace android #endif // _LIBINPUT_VELOCITY_TRACKER_H include/input/VirtualKeyMap.h0100644 0000000 0000000 00000003745 13756501735 015322 0ustar000000000 0000000 /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _LIBINPUT_VIRTUAL_KEY_MAP_H #define _LIBINPUT_VIRTUAL_KEY_MAP_H #include #include #include #include #include #include #include namespace android { /* Describes a virtual key. */ struct VirtualKeyDefinition { int32_t scanCode; // configured position data, specified in display coords int32_t centerX; int32_t centerY; int32_t width; int32_t height; }; /** * Describes a collection of virtual keys on a touch screen in terms of * virtual scan codes and hit rectangles. * * This object is immutable after it has been loaded. */ class VirtualKeyMap { public: ~VirtualKeyMap(); static std::unique_ptr load(const std::string& filename); inline const std::vector& getVirtualKeys() const { return mVirtualKeys; } private: class Parser { VirtualKeyMap* mMap; Tokenizer* mTokenizer; public: Parser(VirtualKeyMap* map, Tokenizer* tokenizer); ~Parser(); status_t parse(); private: bool consumeFieldDelimiterAndSkipWhitespace(); bool parseNextIntField(int32_t* outValue); }; std::vector mVirtualKeys; VirtualKeyMap(); }; } // namespace android #endif // _LIBINPUT_KEY_CHARACTER_MAP_H include/layerproto0100644 0000000 0000000 00000000000 13756501735 026437 2../services/surfaceflinger/layerproto/include/layerproto/ustar000000000 0000000 include/media0100644 0000000 0000000 00000000000 13756501735 017521 2../headers/media_plugin/mediaustar000000000 0000000 include/powermanager/0040755 0000000 0000000 00000000000 13756501735 013736 5ustar000000000 0000000 include/powermanager/IPowerManager.h0100644 0000000 0000000 00000007370 13756501735 016613 0ustar000000000 0000000 /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_IPOWERMANAGER_H #define ANDROID_IPOWERMANAGER_H #include #include #include namespace android { // ---------------------------------------------------------------------------- class IPowerManager : public IInterface { public: // These transaction IDs must be kept in sync with the method order from // IPowerManager.aidl. enum { ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION, ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1, RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2, UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3, POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4, UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5, IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6, USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7, WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8, GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9, NAP = IBinder::FIRST_CALL_TRANSACTION + 10, IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17, REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18, SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19, CRASH = IBinder::FIRST_CALL_TRANSACTION + 20, }; DECLARE_META_INTERFACE(PowerManager) // The parcels created by these methods must be kept in sync with the // corresponding methods from IPowerManager.aidl. // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl virtual status_t acquireWakeLock(int flags, const sp& lock, const String16& tag, const String16& packageName, bool isOneWay = false) = 0; virtual status_t acquireWakeLockWithUid(int flags, const sp& lock, const String16& tag, const String16& packageName, int uid, bool isOneWay = false) = 0; virtual status_t releaseWakeLock(const sp& lock, int flags, bool isOneWay = false) = 0; virtual status_t updateWakeLockUids(const sp& lock, int len, const int *uids, bool isOneWay = false) = 0; virtual status_t powerHint(int hintId, int data) = 0; virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0; virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0; virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0; virtual status_t crash(const String16& message) = 0; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IPOWERMANAGER_H include/powermanager/PowerManager.h0100644 0000000 0000000 00000002233 13756501735 016473 0ustar000000000 0000000 /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_POWERMANAGER_H #define ANDROID_POWERMANAGER_H namespace android { // must be kept in sync with definitions in PowerManager.java enum { POWERMANAGER_PARTIAL_WAKE_LOCK = 1, // equals PowerManager.PARTIAL_WAKE_LOCK constant }; enum { USER_ACTIVITY_EVENT_OTHER = 0, USER_ACTIVITY_EVENT_BUTTON = 1, USER_ACTIVITY_EVENT_TOUCH = 2, USER_ACTIVITY_EVENT_ACCESSIBILITY = 3, USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_ACCESSIBILITY, // Last valid event code. }; }; // namespace android #endif // ANDROID_POWERMANAGER_H include/private/0040755 0000000 0000000 00000000000 13756501735 012721 5ustar000000000 0000000 include/private/binder0100644 0000000 0000000 00000000000 13756501735 023177 2../../libs/binder/include/private/binderustar000000000 0000000 include/private/gui0100644 0000000 0000000 00000000000 13756501735 021362 2../../libs/gui/include/private/guiustar000000000 0000000 include/private/ui/0040755 0000000 0000000 00000000000 13756501735 013336 5ustar000000000 0000000 include/private/ui/RegionHelper.h0100644 0000000 0000000 00000022627 13756501735 016100 0ustar000000000 0000000 /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H #define ANDROID_UI_PRIVATE_REGION_HELPER_H #include #include #include #include namespace android { // ---------------------------------------------------------------------------- template class region_operator { public: typedef typename RECT::value_type TYPE; static const TYPE max_value = std::numeric_limits::max(); /* * Common boolean operations: * value is computed as 0b101 op 0b110 * other boolean operation are possible, simply compute * their corresponding value with the above formulae and use * it when instantiating a region_operator. */ static const uint32_t LHS = 0x5; // 0b101 static const uint32_t RHS = 0x6; // 0b110 enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS }; struct region { RECT const* rects; size_t count; TYPE dx; TYPE dy; inline region(const region& rhs) : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { } inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() { } inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy) : rects(_r), count(_c), dx(_dx), dy(_dy) { } }; class region_rasterizer { friend class region_operator; virtual void operator()(const RECT& rect) = 0; public: virtual ~region_rasterizer() { } }; inline region_operator(uint32_t op, const region& lhs, const region& rhs) : op_mask(op), spanner(lhs, rhs) { } void operator()(region_rasterizer& rasterizer) { RECT current(Rect::EMPTY_RECT); do { SpannerInner spannerInner(spanner.lhs, spanner.rhs); int inside = spanner.next(current.top, current.bottom); spannerInner.prepare(inside); do { int inner_inside = spannerInner.next(current.left, current.right); if ((op_mask >> inner_inside) & 1) { if (current.left < current.right && current.top < current.bottom) { rasterizer(current); } } } while(!spannerInner.isDone()); } while(!spanner.isDone()); } private: uint32_t op_mask; class SpannerBase { public: SpannerBase() : lhs_head(max_value), lhs_tail(max_value), rhs_head(max_value), rhs_tail(max_value) { } enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 }; protected: TYPE lhs_head; TYPE lhs_tail; TYPE rhs_head; TYPE rhs_tail; inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) { int inside; more_lhs = false; more_rhs = false; if (lhs_head < rhs_head) { inside = lhs_before_rhs; head = lhs_head; if (lhs_tail <= rhs_head) { tail = lhs_tail; more_lhs = true; } else { lhs_head = rhs_head; tail = rhs_head; } } else if (rhs_head < lhs_head) { inside = lhs_after_rhs; head = rhs_head; if (rhs_tail <= lhs_head) { tail = rhs_tail; more_rhs = true; } else { rhs_head = lhs_head; tail = lhs_head; } } else { inside = lhs_coincide_rhs; head = lhs_head; if (lhs_tail <= rhs_tail) { tail = rhs_head = lhs_tail; more_lhs = true; } if (rhs_tail <= lhs_tail) { tail = lhs_head = rhs_tail; more_rhs = true; } } return inside; } }; class Spanner : protected SpannerBase { friend class region_operator; region lhs; region rhs; public: inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) { if (lhs.count) { SpannerBase::lhs_head = lhs.rects->top + lhs.dy; SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy; } if (rhs.count) { SpannerBase::rhs_head = rhs.rects->top + rhs.dy; SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy; } } inline bool isDone() const { return !rhs.count && !lhs.count; } inline int next(TYPE& top, TYPE& bottom) { bool more_lhs = false; bool more_rhs = false; int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs); if (more_lhs) { advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); } if (more_rhs) { advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); } return inside; } private: static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) { // got to next span size_t count = reg.count; RECT const * rects = reg.rects; RECT const * const end = rects + count; const int top = rects->top; while (rects != end && rects->top == top) { rects++; count--; } if (rects != end) { aTop = rects->top + reg.dy; aBottom = rects->bottom + reg.dy; } else { aTop = max_value; aBottom = max_value; } reg.rects = rects; reg.count = count; } }; class SpannerInner : protected SpannerBase { region lhs; region rhs; public: inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) { } inline void prepare(int inside) { if (inside == SpannerBase::lhs_before_rhs) { if (lhs.count) { SpannerBase::lhs_head = lhs.rects->left + lhs.dx; SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; } SpannerBase::rhs_head = max_value; SpannerBase::rhs_tail = max_value; } else if (inside == SpannerBase::lhs_after_rhs) { SpannerBase::lhs_head = max_value; SpannerBase::lhs_tail = max_value; if (rhs.count) { SpannerBase::rhs_head = rhs.rects->left + rhs.dx; SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; } } else { if (lhs.count) { SpannerBase::lhs_head = lhs.rects->left + lhs.dx; SpannerBase::lhs_tail = lhs.rects->right + lhs.dx; } if (rhs.count) { SpannerBase::rhs_head = rhs.rects->left + rhs.dx; SpannerBase::rhs_tail = rhs.rects->right + rhs.dx; } } } inline bool isDone() const { return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value; } inline int next(TYPE& left, TYPE& right) { bool more_lhs = false; bool more_rhs = false; int inside = SpannerBase::next(left, right, more_lhs, more_rhs); if (more_lhs) { advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail); } if (more_rhs) { advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail); } return inside; } private: static inline void advance(region& reg, TYPE& left, TYPE& right) { if (reg.rects && reg.count) { const int cur_span_top = reg.rects->top; reg.rects++; reg.count--; if (!reg.count || reg.rects->top != cur_span_top) { left = max_value; right = max_value; } else { left = reg.rects->left + reg.dx; right = reg.rects->right + reg.dx; } } } }; Spanner spanner; }; // ---------------------------------------------------------------------------- }; #endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */ include/ui0100644 0000000 0000000 00000000000 13756501735 015336 2../libs/ui/include/uiustar000000000 0000000 include/vr/0040755 0000000 0000000 00000000000 13756501735 011676 5ustar000000000 0000000 include/vr/vr_manager/0040755 0000000 0000000 00000000000 13756501735 014017 5ustar000000000 0000000 include/vr/vr_manager/vr_manager.h0100644 0000000 0000000 00000005270 13756501735 016312 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_VR_MANAGER_H #define ANDROID_VR_MANAGER_H #include namespace android { // Must be kept in sync with interface defined in IVrStateCallbacks.aidl. class IVrStateCallbacks : public IInterface { public: DECLARE_META_INTERFACE(VrStateCallbacks) virtual void onVrStateChanged(bool enabled) = 0; }; enum VrStateCallbacksTransaction { ON_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION, }; class BnVrStateCallbacks : public BnInterface { public: status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; }; // Must be kept in sync with interface defined in // IPersistentVrStateCallbacks.aidl. class IPersistentVrStateCallbacks : public IInterface { public: DECLARE_META_INTERFACE(PersistentVrStateCallbacks) virtual void onPersistentVrStateChanged(bool enabled) = 0; }; enum PersistentVrStateCallbacksTransaction { ON_PERSISTENT_VR_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION, }; class BnPersistentVrStateCallbacks : public BnInterface { public: status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; }; // Must be kept in sync with interface defined in IVrManager.aidl. class IVrManager : public IInterface { public: DECLARE_META_INTERFACE(VrManager) virtual void registerListener(const sp& cb) = 0; virtual void unregisterListener(const sp& cb) = 0; virtual void registerPersistentVrStateListener( const sp& cb) = 0; virtual void unregisterPersistentVrStateListener( const sp& cb) = 0; virtual bool getVrModeState() = 0; }; enum VrManagerTransaction { REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION, UNREGISTER_LISTENER, REGISTER_PERSISTENT_VR_STATE_LISTENER, UNREGISTER_PERSISTENT_VR_STATE_LISTENER, GET_VR_MODE_STATE, }; }; // namespace android #endif // ANDROID_VR_MANAGER_H include_sensor/0040755 0000000 0000000 00000000000 13756501735 012640 5ustar000000000 0000000 include_sensor/android/0040755 0000000 0000000 00000000000 13756501735 014260 5ustar000000000 0000000 include_sensor/android/looper.h0100644 0000000 0000000 00000000000 13756501735 023056 2../../include/android/looper.hustar000000000 0000000 include_sensor/android/sensor.h0100644 0000000 0000000 00000000000 13756501735 023100 2../../include/android/sensor.hustar000000000 0000000 libs/0040755 0000000 0000000 00000000000 13756501735 010555 5ustar000000000 0000000 libs/android_runtime_lazy/0040755 0000000 0000000 00000000000 13756501735 014777 5ustar000000000 0000000 libs/android_runtime_lazy/Android.bp0100644 0000000 0000000 00000003661 13756501735 016705 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ // libandroid_runtime_lazy is a shim library. // This provides very limited small set of APIs from libandroid_runtime. // // By depending on this instead of libandroid_runtime, // a library can be loaded without paying the cost of libandroid_runtime // which is quite huge. The cost will be paid when libandroid_runtime is actually used. // // For Partial-source PDK build, there is a constraint that // frameworks/native modules should not depend on frameworks/base. // This library can be used to cut down the dependency between them. // (e.g. libbinder_ndk) // // Some libraries which serve as LL-NDK and NDK as well may depend on this // instead of libandroid_runtime. When they are used by a vendor process, // depending on libandroid_runtime is meaningless. In this case, // they can depend on libandroid_runtime_lazy. cc_library { name: "libandroid_runtime_lazy", vendor_available: true, double_loadable: true, cflags: [ "-Wall", "-Werror", "-Wunused", "-Wunreachable-code", ], srcs: [ "android_runtime_lazy.cpp", ], shared_libs: [ "liblog", "libutils", ], required: [ "libandroid_runtime", ], export_include_dirs: [ "include", ], header_libs: [ "jni_headers", "libbinder_headers", ], } libs/android_runtime_lazy/android_runtime_lazy.cpp0100644 0000000 0000000 00000005535 13756501735 021732 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #define LOG_TAG "ANDROID_RUNTIME_LAZY" #include "android_runtime/AndroidRuntime.h" #include "android_util_Binder.h" #include #include #include namespace android { namespace { std::once_flag loadFlag; typedef JNIEnv* (*getJNIEnv_t)(); typedef sp (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj); typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp& val); getJNIEnv_t _getJNIEnv; ibinderForJavaObject_t _ibinderForJavaObject; javaObjectForIBinder_t _javaObjectForIBinder; void load() { std::call_once(loadFlag, []() { void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY); if (handle == nullptr) { ALOGE("Could not open libandroid_runtime."); return; } _getJNIEnv = reinterpret_cast( dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv")); if (_getJNIEnv == nullptr) { ALOGW("Could not find getJNIEnv."); // no return } _ibinderForJavaObject = reinterpret_cast( dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject")); if (_ibinderForJavaObject == nullptr) { ALOGW("Could not find ibinderForJavaObject."); // no return } _javaObjectForIBinder = reinterpret_cast( dlsym(handle, "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE")); if (_javaObjectForIBinder == nullptr) { ALOGW("Could not find javaObjectForIBinder."); // no return } }); } } // namespace // exports delegate functions JNIEnv* AndroidRuntime::getJNIEnv() { load(); if (_getJNIEnv == nullptr) { return nullptr; } return _getJNIEnv(); } sp ibinderForJavaObject(JNIEnv* env, jobject obj) { load(); if (_ibinderForJavaObject == nullptr) { return nullptr; } return _ibinderForJavaObject(env, obj); } jobject javaObjectForIBinder(JNIEnv* env, const sp& val) { load(); if (_javaObjectForIBinder == nullptr) { return nullptr; } return _javaObjectForIBinder(env, val); } } // namespace android libs/android_runtime_lazy/include/0040755 0000000 0000000 00000000000 13756501735 016422 5ustar000000000 0000000 libs/android_runtime_lazy/include/android_runtime/0040755 0000000 0000000 00000000000 13756501735 021605 5ustar000000000 0000000 libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h0100644 0000000 0000000 00000001630 13756501735 024677 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #pragma once #include "jni.h" namespace android { // Intentionally use the same name with AndroidRuntime in frameworks/base/core/jni/ // to make the client use this in the same way with the original class. class AndroidRuntime { public: static JNIEnv* getJNIEnv(); }; } // namespace android libs/android_runtime_lazy/include/android_util_Binder.h0100644 0000000 0000000 00000002017 13756501735 022530 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #pragma once #include #include "jni.h" namespace android { // The name of this file is same with the file in frameworks/base/core/jni/ // This is intentional to make the client use these exported functions // in the same way with the original. jobject javaObjectForIBinder(JNIEnv* env, const sp& val); sp ibinderForJavaObject(JNIEnv* env, jobject obj); } // namespace android libs/arect/0040755 0000000 0000000 00000000000 13756501735 011653 5ustar000000000 0000000 libs/arect/Android.bp0100644 0000000 0000000 00000001725 13756501735 013560 0ustar000000000 0000000 // Copyright (C) 2017 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. ndk_headers { name: "libarect_headers", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } cc_library_static { name: "libarect", host_supported: true, vendor_available: true, export_include_dirs: ["include"], target: { windows: { enabled: true, }, }, } libs/arect/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501735 014773 0ustar000000000 0000000 libs/arect/NOTICE0100644 0000000 0000000 00000024707 13756501735 012566 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS libs/arect/include/0040755 0000000 0000000 00000000000 13756501735 013276 5ustar000000000 0000000 libs/arect/include/android/0040755 0000000 0000000 00000000000 13756501735 014716 5ustar000000000 0000000 libs/arect/include/android/rect.h0100644 0000000 0000000 00000003126 13756501735 016023 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ /** * @addtogroup NativeActivity Native Activity * @{ */ /** * @file rect.h */ #ifndef ANDROID_RECT_H #define ANDROID_RECT_H #include #ifdef __cplusplus extern "C" { #endif /** * Rectangular window area. * * This is the NDK equivalent of the android.graphics.Rect class in Java. It is * used with {@link ANativeActivityCallbacks::onContentRectChanged} event * callback and the ANativeWindow_lock() function. * * In a valid ARect, left <= right and top <= bottom. ARect with left=0, top=10, * right=1, bottom=11 contains only one pixel at x=0, y=10. */ typedef struct ARect { #ifdef __cplusplus typedef int32_t value_type; #endif /// Minimum X coordinate of the rectangle. int32_t left; /// Minimum Y coordinate of the rectangle. int32_t top; /// Maximum X coordinate of the rectangle. int32_t right; /// Maximum Y coordinate of the rectangle. int32_t bottom; } ARect; #ifdef __cplusplus }; #endif #endif // ANDROID_RECT_H /** @} */ libs/binder/0040755 0000000 0000000 00000000000 13756501735 012020 5ustar000000000 0000000 libs/binder/ActivityManager.cpp0100644 0000000 0000000 00000007305 13756501735 015615 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include #include #include #include #include #include namespace android { ActivityManager::ActivityManager() { } sp ActivityManager::getService() { std::lock_guard scoped_lock(mLock); int64_t startTime = 0; sp service = mService; while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { sp binder = defaultServiceManager()->checkService(String16("activity")); if (binder == nullptr) { // Wait for the activity service to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting for activity service"); } else if ((uptimeMillis() - startTime) > 1000000) { ALOGW("Waiting too long for activity service, giving up"); service = nullptr; break; } usleep(25000); } else { service = interface_cast(binder); mService = service; } } return service; } int ActivityManager::openContentUri(const String16& stringUri) { sp service = getService(); return service != nullptr ? service->openContentUri(stringUri) : -1; } void ActivityManager::registerUidObserver(const sp& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage) { sp service = getService(); if (service != nullptr) { service->registerUidObserver(observer, event, cutpoint, callingPackage); } } void ActivityManager::unregisterUidObserver(const sp& observer) { sp service = getService(); if (service != nullptr) { service->unregisterUidObserver(observer); } } bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage) { sp service = getService(); if (service != nullptr) { return service->isUidActive(uid, callingPackage); } return false; } int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& callingPackage) { sp service = getService(); if (service != nullptr) { return service->getUidProcessState(uid, callingPackage); } return PROCESS_STATE_UNKNOWN; } status_t ActivityManager::linkToDeath(const sp& recipient) { sp service = getService(); if (service != nullptr) { return IInterface::asBinder(service)->linkToDeath(recipient); } return INVALID_OPERATION; } status_t ActivityManager::unlinkToDeath(const sp& recipient) { sp service = getService(); if (service != nullptr) { return IInterface::asBinder(service)->unlinkToDeath(recipient); } return INVALID_OPERATION; } }; // namespace android libs/binder/Android.bp0100644 0000000 0000000 00000007166 13756501735 013732 0ustar000000000 0000000 // Copyright (C) 2009 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. cc_library_headers { name: "libbinder_headers", export_include_dirs: ["include"], vendor_available: true, header_libs: [ "libbase_headers", "libcutils_headers", "libutils_headers", ], export_header_lib_headers: [ "libbase_headers", "libcutils_headers", "libutils_headers", ], } cc_library_shared { name: "libbinder", // for vndbinder vendor_available: true, vndk: { enabled: true, }, double_loadable: true, srcs: [ "ActivityManager.cpp", "AppOpsManager.cpp", "Binder.cpp", "BpBinder.cpp", "BufferedTextOutput.cpp", "Debug.cpp", "IActivityManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", "IBatteryStats.cpp", "IInterface.cpp", "IMediaResourceMonitor.cpp", "IMemory.cpp", "IPCThreadState.cpp", "IPermissionController.cpp", "IProcessInfoService.cpp", "IResultReceiver.cpp", "IServiceManager.cpp", "IShellCallback.cpp", "IUidObserver.cpp", "MemoryBase.cpp", "MemoryDealer.cpp", "MemoryHeapBase.cpp", "Parcel.cpp", "ParcelFileDescriptor.cpp", "PermissionCache.cpp", "PermissionController.cpp", "PersistableBundle.cpp", "ProcessInfoService.cpp", "ProcessState.cpp", "Static.cpp", "Status.cpp", "TextOutput.cpp", "IpPrefix.cpp", "Value.cpp", ":libbinder_aidl", ], target: { vendor: { exclude_srcs: [ "ActivityManager.cpp", "AppOpsManager.cpp", "IActivityManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", "IBatteryStats.cpp", "IMediaResourceMonitor.cpp", "IPermissionController.cpp", "IProcessInfoService.cpp", "IUidObserver.cpp", "PermissionCache.cpp", "PermissionController.cpp", "ProcessInfoService.cpp", "IpPrefix.cpp", ":libbinder_aidl", ], }, }, aidl: { export_aidl_headers: true, }, cflags: [ "-Wall", "-Wextra", "-Werror", "-Wzero-as-null-pointer-constant", ], product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, shared_libs: [ "libbase", "liblog", "libcutils", "libutils", "libbinderthreadstate", ], header_libs: [ "libbinder_headers", ], export_header_lib_headers: [ "libbinder_headers", ], clang: true, sanitize: { misc_undefined: ["integer"], }, } // AIDL interface between libbinder and framework.jar filegroup { name: "libbinder_aidl", srcs: [ "aidl/android/content/pm/IPackageManagerNative.aidl", ], } subdirs = ["tests"] libs/binder/AppOpsManager.cpp0100644 0000000 0000000 00000011627 13756501735 015225 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include namespace android { namespace { #if defined(__BRILLO__) // Because Brillo has no application model, security policy is managed // statically (at build time) with SELinux controls. // As a consequence, it also never runs the AppOpsManager service. const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_ALLOWED; #else const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED; #endif // defined(__BRILLO__) } // namespace static String16 _appops("appops"); static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER; static sp gToken; static const sp& getToken(const sp& service) { pthread_mutex_lock(&gTokenMutex); if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) { gToken = service->getToken(new BBinder()); } pthread_mutex_unlock(&gTokenMutex); return gToken; } AppOpsManager::AppOpsManager() { } #if defined(__BRILLO__) // There is no AppOpsService on Brillo sp AppOpsManager::getService() { return NULL; } #else sp AppOpsManager::getService() { std::lock_guard scoped_lock(mLock); int64_t startTime = 0; sp service = mService; while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { sp binder = defaultServiceManager()->checkService(_appops); if (binder == nullptr) { // Wait for the app ops service to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting for app ops service"); } else if ((uptimeMillis()-startTime) > 10000) { ALOGW("Waiting too long for app ops service, giving up"); service = nullptr; break; } sleep(1); } else { service = interface_cast(binder); mService = service; } } return service; } #endif // defined(__BRILLO__) int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage) { sp service = getService(); return service != nullptr ? service->checkOperation(op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, const String16& callingPackage) { sp service = getService(); return service != nullptr ? service->checkAudioOperation(op, usage, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) { sp service = getService(); return service != nullptr ? service->noteOperation(op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault) { sp service = getService(); return service != nullptr ? service->startOperation(getToken(service), op, uid, callingPackage, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { sp service = getService(); if (service != nullptr) { service->finishOperation(getToken(service), op, uid, callingPackage); } } void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, const sp& callback) { sp service = getService(); if (service != nullptr) { service->startWatchingMode(op, packageName, callback); } } void AppOpsManager::stopWatchingMode(const sp& callback) { sp service = getService(); if (service != nullptr) { service->stopWatchingMode(callback); } } int32_t AppOpsManager::permissionToOpCode(const String16& permission) { sp service = getService(); if (service != nullptr) { return service->permissionToOpCode(permission); } return -1; } }; // namespace android libs/binder/Binder.cpp0100644 0000000 0000000 00000021612 13756501735 013726 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #include #include #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- IBinder::IBinder() : RefBase() { } IBinder::~IBinder() { } // --------------------------------------------------------------------------- sp IBinder::queryLocalInterface(const String16& /*descriptor*/) { return nullptr; } BBinder* IBinder::localBinder() { return nullptr; } BpBinder* IBinder::remoteBinder() { return nullptr; } bool IBinder::checkSubclass(const void* /*subclassID*/) const { return false; } status_t IBinder::shellCommand(const sp& target, int in, int out, int err, Vector& args, const sp& callback, const sp& resultReceiver) { Parcel send; Parcel reply; send.writeFileDescriptor(in); send.writeFileDescriptor(out); send.writeFileDescriptor(err); const size_t numArgs = args.size(); send.writeInt32(numArgs); for (size_t i = 0; i < numArgs; i++) { send.writeString16(args[i]); } send.writeStrongBinder(callback != nullptr ? IInterface::asBinder(callback) : nullptr); send.writeStrongBinder(resultReceiver != nullptr ? IInterface::asBinder(resultReceiver) : nullptr); return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply); } // --------------------------------------------------------------------------- class BBinder::Extras { public: // unlocked objects bool mRequestingSid = false; // for below objects Mutex mLock; BpBinder::ObjectManager mObjects; }; // --------------------------------------------------------------------------- BBinder::BBinder() : mExtras(nullptr) { } bool BBinder::isBinderAlive() const { return true; } status_t BBinder::pingBinder() { return NO_ERROR; } const String16& BBinder::getInterfaceDescriptor() const { // This is a local static rather than a global static, // to avoid static initializer ordering issues. static String16 sEmptyDescriptor; ALOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); return sEmptyDescriptor; } // NOLINTNEXTLINE(google-default-arguments) status_t BBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { data.setDataPosition(0); status_t err = NO_ERROR; switch (code) { case PING_TRANSACTION: reply->writeInt32(pingBinder()); break; default: err = onTransact(code, data, reply, flags); break; } if (reply != nullptr) { reply->setDataPosition(0); } return err; } // NOLINTNEXTLINE(google-default-arguments) status_t BBinder::linkToDeath( const sp& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/) { return INVALID_OPERATION; } // NOLINTNEXTLINE(google-default-arguments) status_t BBinder::unlinkToDeath( const wp& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/, wp* /*outRecipient*/) { return INVALID_OPERATION; } status_t BBinder::dump(int /*fd*/, const Vector& /*args*/) { return NO_ERROR; } void BBinder::attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { Extras* e = getOrCreateExtras(); if (!e) return; // out of memory AutoMutex _l(e->mLock); e->mObjects.attach(objectID, object, cleanupCookie, func); } void* BBinder::findObject(const void* objectID) const { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return nullptr; AutoMutex _l(e->mLock); return e->mObjects.find(objectID); } void BBinder::detachObject(const void* objectID) { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return; AutoMutex _l(e->mLock); e->mObjects.detach(objectID); } BBinder* BBinder::localBinder() { return this; } bool BBinder::isRequestingSid() { Extras* e = mExtras.load(std::memory_order_acquire); return e && e->mRequestingSid; } void BBinder::setRequestingSid(bool requestingSid) { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) { // default is false. Most things don't need sids, so avoiding allocations when possible. if (!requestingSid) { return; } e = getOrCreateExtras(); if (!e) return; // out of memory } e->mRequestingSid = requestingSid; } BBinder::~BBinder() { Extras* e = mExtras.load(std::memory_order_relaxed); if (e) delete e; } // NOLINTNEXTLINE(google-default-arguments) status_t BBinder::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/) { switch (code) { case INTERFACE_TRANSACTION: reply->writeString16(getInterfaceDescriptor()); return NO_ERROR; case DUMP_TRANSACTION: { int fd = data.readFileDescriptor(); int argc = data.readInt32(); Vector args; for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } return dump(fd, args); } case SHELL_COMMAND_TRANSACTION: { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); int argc = data.readInt32(); Vector args; for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } sp shellCallback = IShellCallback::asInterface( data.readStrongBinder()); sp resultReceiver = IResultReceiver::asInterface( data.readStrongBinder()); // XXX can't add virtuals until binaries are updated. //return shellCommand(in, out, err, args, resultReceiver); (void)in; (void)out; (void)err; if (resultReceiver != nullptr) { resultReceiver->send(INVALID_OPERATION); } return NO_ERROR; } case SYSPROPS_TRANSACTION: { report_sysprop_change(); return NO_ERROR; } default: return UNKNOWN_TRANSACTION; } } BBinder::Extras* BBinder::getOrCreateExtras() { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) { e = new Extras; Extras* expected = nullptr; if (!mExtras.compare_exchange_strong(expected, e, std::memory_order_release, std::memory_order_acquire)) { delete e; e = expected; // Filled in by CAS } if (e == nullptr) return nullptr; // out of memory } return e; } // --------------------------------------------------------------------------- enum { // This is used to transfer ownership of the remote binder from // the BpRefBase object holding it (when it is constructed), to the // owner of the BpRefBase object when it first acquires that BpRefBase. kRemoteAcquired = 0x00000001 }; BpRefBase::BpRefBase(const sp& o) : mRemote(o.get()), mRefs(nullptr), mState(0) { extendObjectLifetime(OBJECT_LIFETIME_WEAK); if (mRemote) { mRemote->incStrong(this); // Removed on first IncStrong(). mRefs = mRemote->createWeak(this); // Held for our entire lifetime. } } BpRefBase::~BpRefBase() { if (mRemote) { if (!(mState.load(std::memory_order_relaxed)&kRemoteAcquired)) { mRemote->decStrong(this); } mRefs->decWeak(this); } } void BpRefBase::onFirstRef() { mState.fetch_or(kRemoteAcquired, std::memory_order_relaxed); } void BpRefBase::onLastStrongRef(const void* /*id*/) { if (mRemote) { mRemote->decStrong(this); } } bool BpRefBase::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) { return mRemote ? mRefs->attemptIncStrong(this) : false; } // --------------------------------------------------------------------------- }; // namespace android libs/binder/BpBinder.cpp0100644 0000000 0000000 00000034072 13756501735 014214 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "BpBinder" //#define LOG_NDEBUG 0 #include #include #include #include #include #include //#undef ALOGV //#define ALOGV(...) fprintf(stderr, __VA_ARGS__) namespace android { // --------------------------------------------------------------------------- Mutex BpBinder::sTrackingLock; std::unordered_map BpBinder::sTrackingMap; int BpBinder::sNumTrackedUids = 0; std::atomic_bool BpBinder::sCountByUidEnabled(false); binder_proxy_limit_callback BpBinder::sLimitCallback; bool BpBinder::sBinderProxyThrottleCreate = false; // Arbitrarily high value that probably distinguishes a bad behaving app uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; // Another arbitrary value a binder count needs to drop below before another callback will be called uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000; enum { LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value }; BpBinder::ObjectManager::ObjectManager() { } BpBinder::ObjectManager::~ObjectManager() { kill(); } void BpBinder::ObjectManager::attach( const void* objectID, void* object, void* cleanupCookie, IBinder::object_cleanup_func func) { entry_t e; e.object = object; e.cleanupCookie = cleanupCookie; e.func = func; if (mObjects.indexOfKey(objectID) >= 0) { ALOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", objectID, this, object); return; } mObjects.add(objectID, e); } void* BpBinder::ObjectManager::find(const void* objectID) const { const ssize_t i = mObjects.indexOfKey(objectID); if (i < 0) return nullptr; return mObjects.valueAt(i).object; } void BpBinder::ObjectManager::detach(const void* objectID) { mObjects.removeItem(objectID); } void BpBinder::ObjectManager::kill() { const size_t N = mObjects.size(); ALOGV("Killing %zu objects in manager %p", N, this); for (size_t i=0; igetCallingUid(); AutoMutex _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[trackedUid]; if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) { if (sBinderProxyThrottleCreate) { return nullptr; } } else { if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) { ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)", getuid(), trackedUid, trackedValue); sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK; if (sLimitCallback) sLimitCallback(trackedUid); if (sBinderProxyThrottleCreate) { ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy" " count drops below %d", trackedUid, getuid(), sBinderProxyCountLowWatermark); return nullptr; } } } sTrackingMap[trackedUid]++; } return new BpBinder(handle, trackedUid); } BpBinder::BpBinder(int32_t handle, int32_t trackedUid) : mHandle(handle) , mAlive(1) , mObitsSent(0) , mObituaries(nullptr) , mTrackedUid(trackedUid) { ALOGV("Creating BpBinder %p handle %d\n", this, mHandle); extendObjectLifetime(OBJECT_LIFETIME_WEAK); IPCThreadState::self()->incWeakHandle(handle, this); } bool BpBinder::isDescriptorCached() const { Mutex::Autolock _l(mLock); return mDescriptorCache.size() ? true : false; } const String16& BpBinder::getInterfaceDescriptor() const { if (isDescriptorCached() == false) { Parcel send, reply; // do the IPC without a lock held. status_t err = const_cast(this)->transact( INTERFACE_TRANSACTION, send, &reply); if (err == NO_ERROR) { String16 res(reply.readString16()); Mutex::Autolock _l(mLock); // mDescriptorCache could have been assigned while the lock was // released. if (mDescriptorCache.size() == 0) mDescriptorCache = res; } } // we're returning a reference to a non-static object here. Usually this // is not something smart to do, however, with binder objects it is // (usually) safe because they are reference-counted. return mDescriptorCache; } bool BpBinder::isBinderAlive() const { return mAlive != 0; } status_t BpBinder::pingBinder() { Parcel send; Parcel reply; status_t err = transact(PING_TRANSACTION, send, &reply); if (err != NO_ERROR) return err; if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; return (status_t)reply.readInt32(); } status_t BpBinder::dump(int fd, const Vector& args) { Parcel send; Parcel reply; send.writeFileDescriptor(fd); const size_t numArgs = args.size(); send.writeInt32(numArgs); for (size_t i = 0; i < numArgs; i++) { send.writeString16(args[i]); } status_t err = transact(DUMP_TRANSACTION, send, &reply); return err; } // NOLINTNEXTLINE(google-default-arguments) status_t BpBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { // Once a binder has died, it will never come back to life. if (mAlive) { status_t status = IPCThreadState::self()->transact( mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0; return status; } return DEAD_OBJECT; } // NOLINTNEXTLINE(google-default-arguments) status_t BpBinder::linkToDeath( const sp& recipient, void* cookie, uint32_t flags) { Obituary ob; ob.recipient = recipient; ob.cookie = cookie; ob.flags = flags; LOG_ALWAYS_FATAL_IF(recipient == nullptr, "linkToDeath(): recipient must be non-NULL"); { AutoMutex _l(mLock); if (!mObitsSent) { if (!mObituaries) { mObituaries = new Vector; if (!mObituaries) { return NO_MEMORY; } ALOGV("Requesting death notification: %p handle %d\n", this, mHandle); getWeakRefs()->incWeak(this); IPCThreadState* self = IPCThreadState::self(); self->requestDeathNotification(mHandle, this); self->flushCommands(); } ssize_t res = mObituaries->add(ob); return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; } } return DEAD_OBJECT; } // NOLINTNEXTLINE(google-default-arguments) status_t BpBinder::unlinkToDeath( const wp& recipient, void* cookie, uint32_t flags, wp* outRecipient) { AutoMutex _l(mLock); if (mObitsSent) { return DEAD_OBJECT; } const size_t N = mObituaries ? mObituaries->size() : 0; for (size_t i=0; iitemAt(i); if ((obit.recipient == recipient || (recipient == nullptr && obit.cookie == cookie)) && obit.flags == flags) { if (outRecipient != nullptr) { *outRecipient = mObituaries->itemAt(i).recipient; } mObituaries->removeAt(i); if (mObituaries->size() == 0) { ALOGV("Clearing death notification: %p handle %d\n", this, mHandle); IPCThreadState* self = IPCThreadState::self(); self->clearDeathNotification(mHandle, this); self->flushCommands(); delete mObituaries; mObituaries = nullptr; } return NO_ERROR; } } return NAME_NOT_FOUND; } void BpBinder::sendObituary() { ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, mHandle, mObitsSent ? "true" : "false"); mAlive = 0; if (mObitsSent) return; mLock.lock(); Vector* obits = mObituaries; if(obits != nullptr) { ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); IPCThreadState* self = IPCThreadState::self(); self->clearDeathNotification(mHandle, this); self->flushCommands(); mObituaries = nullptr; } mObitsSent = 1; mLock.unlock(); ALOGV("Reporting death of proxy %p for %zu recipients\n", this, obits ? obits->size() : 0U); if (obits != nullptr) { const size_t N = obits->size(); for (size_t i=0; iitemAt(i)); } delete obits; } } void BpBinder::reportOneDeath(const Obituary& obit) { sp recipient = obit.recipient.promote(); ALOGV("Reporting death to recipient: %p\n", recipient.get()); if (recipient == nullptr) return; recipient->binderDied(this); } void BpBinder::attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { AutoMutex _l(mLock); ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); mObjects.attach(objectID, object, cleanupCookie, func); } void* BpBinder::findObject(const void* objectID) const { AutoMutex _l(mLock); return mObjects.find(objectID); } void BpBinder::detachObject(const void* objectID) { AutoMutex _l(mLock); mObjects.detach(objectID); } BpBinder* BpBinder::remoteBinder() { return this; } BpBinder::~BpBinder() { ALOGV("Destroying BpBinder %p handle %d\n", this, mHandle); IPCThreadState* ipc = IPCThreadState::self(); if (mTrackedUid >= 0) { AutoMutex _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[mTrackedUid]; if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) { ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, mHandle); } else { if (CC_UNLIKELY( (trackedValue & LIMIT_REACHED_MASK) && ((trackedValue & COUNTING_VALUE_MASK) <= sBinderProxyCountLowWatermark) )) { ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)", getuid(), mTrackedUid, sBinderProxyCountLowWatermark); sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK; } if (--sTrackingMap[mTrackedUid] == 0) { sTrackingMap.erase(mTrackedUid); } } } mLock.lock(); Vector* obits = mObituaries; if(obits != nullptr) { if (ipc) ipc->clearDeathNotification(mHandle, this); mObituaries = nullptr; } mLock.unlock(); if (obits != nullptr) { // XXX Should we tell any remaining DeathRecipient // objects that the last strong ref has gone away, so they // are no longer linked? delete obits; } if (ipc) { ipc->expungeHandle(mHandle, this); ipc->decWeakHandle(mHandle); } } void BpBinder::onFirstRef() { ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->incStrongHandle(mHandle, this); } void BpBinder::onLastStrongRef(const void* /*id*/) { ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); IF_ALOGV() { printRefs(); } IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->decStrongHandle(mHandle); } bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) { ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); IPCThreadState* ipc = IPCThreadState::self(); return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; } uint32_t BpBinder::getBinderProxyCount(uint32_t uid) { AutoMutex _l(sTrackingLock); auto it = sTrackingMap.find(uid); if (it != sTrackingMap.end()) { return it->second & COUNTING_VALUE_MASK; } return 0; } void BpBinder::getCountByUid(Vector& uids, Vector& counts) { AutoMutex _l(sTrackingLock); uids.setCapacity(sTrackingMap.size()); counts.setCapacity(sTrackingMap.size()); for (const auto& it : sTrackingMap) { uids.push_back(it.first); counts.push_back(it.second & COUNTING_VALUE_MASK); } } void BpBinder::enableCountByUid() { sCountByUidEnabled.store(true); } void BpBinder::disableCountByUid() { sCountByUidEnabled.store(false); } void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); } void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) { AutoMutex _l(sTrackingLock); sLimitCallback = cb; } void BpBinder::setBinderProxyCountWatermarks(int high, int low) { AutoMutex _l(sTrackingLock); sBinderProxyCountHighWatermark = high; sBinderProxyCountLowWatermark = low; } // --------------------------------------------------------------------------- }; // namespace android libs/binder/BufferedTextOutput.cpp0100644 0000000 0000000 00000017307 13756501735 016341 0ustar000000000 0000000 /* * Copyright (C) 2006 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. */ #include #include #include #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { struct BufferedTextOutput::BufferState : public RefBase { explicit BufferState(int32_t _seq) : seq(_seq) , buffer(nullptr) , bufferPos(0) , bufferSize(0) , atFront(true) , indent(0) , bundle(0) { } ~BufferState() { free(buffer); } status_t append(const char* txt, size_t len) { if ((len+bufferPos) > bufferSize) { size_t newSize = ((len+bufferPos)*3)/2; if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow void* b = realloc(buffer, newSize); if (!b) return NO_MEMORY; buffer = (char*)b; bufferSize = newSize; } memcpy(buffer+bufferPos, txt, len); bufferPos += len; return NO_ERROR; } void restart() { bufferPos = 0; atFront = true; if (bufferSize > 256) { void* b = realloc(buffer, 256); if (b) { buffer = (char*)b; bufferSize = 256; } } } const int32_t seq; char* buffer; size_t bufferPos; size_t bufferSize; bool atFront; int32_t indent; int32_t bundle; }; struct BufferedTextOutput::ThreadState { Vector > states; }; static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER; static thread_store_t tls; BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() { ThreadState* ts = (ThreadState*) thread_store_get( &tls ); if (ts) return ts; ts = new ThreadState; thread_store_set( &tls, ts, threadDestructor ); return ts; } void BufferedTextOutput::threadDestructor(void *st) { delete ((ThreadState*)st); } static volatile int32_t gSequence = 0; static volatile int32_t gFreeBufferIndex = -1; static int32_t allocBufferIndex() { int32_t res = -1; pthread_mutex_lock(&gMutex); if (gFreeBufferIndex >= 0) { res = gFreeBufferIndex; gFreeBufferIndex = gTextBuffers[res]; gTextBuffers.editItemAt(res) = -1; } else { res = gTextBuffers.size(); gTextBuffers.add(-1); } pthread_mutex_unlock(&gMutex); return res; } static void freeBufferIndex(int32_t idx) { pthread_mutex_lock(&gMutex); gTextBuffers.editItemAt(idx) = gFreeBufferIndex; gFreeBufferIndex = idx; pthread_mutex_unlock(&gMutex); } // --------------------------------------------------------------------------- BufferedTextOutput::BufferedTextOutput(uint32_t flags) : mFlags(flags) , mSeq(android_atomic_inc(&gSequence)) , mIndex(allocBufferIndex()) { mGlobalState = new BufferState(mSeq); if (mGlobalState) mGlobalState->incStrong(this); } BufferedTextOutput::~BufferedTextOutput() { if (mGlobalState) mGlobalState->decStrong(this); freeBufferIndex(mIndex); } status_t BufferedTextOutput::print(const char* txt, size_t len) { //printf("BufferedTextOutput: printing %d\n", len); AutoMutex _l(mLock); BufferState* b = getBuffer(); const char* const end = txt+len; status_t err; while (txt < end) { // Find the next line. const char* first = txt; while (txt < end && *txt != '\n') txt++; // Include this and all following empty lines. while (txt < end && *txt == '\n') txt++; // Special cases for first data on a line. if (b->atFront) { if (b->indent > 0) { // If this is the start of a line, add the indent. const char* prefix = stringForIndent(b->indent); err = b->append(prefix, strlen(prefix)); if (err != NO_ERROR) return err; } else if (*(txt-1) == '\n' && !b->bundle) { // Fast path: if we are not indenting or bundling, and // have been given one or more complete lines, just write // them out without going through the buffer. // Slurp up all of the lines. const char* lastLine = txt; while (txt < end) { if (*txt++ == '\n') lastLine = txt; } struct iovec vec; vec.iov_base = (void*)first; vec.iov_len = lastLine-first; //printf("Writing %d bytes of data!\n", vec.iov_len); writeLines(vec, 1); txt = lastLine; continue; } } // Append the new text to the buffer. err = b->append(first, txt-first); if (err != NO_ERROR) return err; b->atFront = *(txt-1) == '\n'; // If we have finished a line and are not bundling, write // it out. //printf("Buffer is now %d bytes\n", b->bufferPos); if (b->atFront && !b->bundle) { struct iovec vec; vec.iov_base = b->buffer; vec.iov_len = b->bufferPos; //printf("Writing %d bytes of data!\n", vec.iov_len); writeLines(vec, 1); b->restart(); } } return NO_ERROR; } void BufferedTextOutput::moveIndent(int delta) { AutoMutex _l(mLock); BufferState* b = getBuffer(); b->indent += delta; if (b->indent < 0) b->indent = 0; } void BufferedTextOutput::pushBundle() { AutoMutex _l(mLock); BufferState* b = getBuffer(); b->bundle++; } void BufferedTextOutput::popBundle() { AutoMutex _l(mLock); BufferState* b = getBuffer(); b->bundle--; LOG_FATAL_IF(b->bundle < 0, "TextOutput::popBundle() called more times than pushBundle()"); if (b->bundle < 0) b->bundle = 0; if (b->bundle == 0) { // Last bundle, write out data if it is complete. If it is not // complete, don't write until the last line is done... this may // or may not be the write thing to do, but it's the easiest. if (b->bufferPos > 0 && b->atFront) { struct iovec vec; vec.iov_base = b->buffer; vec.iov_len = b->bufferPos; writeLines(vec, 1); b->restart(); } } } BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const { if ((mFlags&MULTITHREADED) != 0) { ThreadState* ts = getThreadState(); if (ts) { while (ts->states.size() <= (size_t)mIndex) ts->states.add(nullptr); BufferState* bs = ts->states[mIndex].get(); if (bs != nullptr && bs->seq == mSeq) return bs; ts->states.editItemAt(mIndex) = new BufferState(mIndex); bs = ts->states[mIndex].get(); if (bs != nullptr) return bs; } } return mGlobalState; } }; // namespace android libs/binder/Debug.cpp0100644 0000000 0000000 00000020610 13756501735 013546 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------- static const char indentStr[] = " " " "; const char* stringForIndent(int32_t indentLevel) { ssize_t off = sizeof(indentStr)-1-(indentLevel*2); return indentStr + (off < 0 ? 0 : off); } // --------------------------------------------------------------------- static void defaultPrintFunc(void* /*cookie*/, const char* txt) { printf("%s", txt); } // --------------------------------------------------------------------- static inline int isident(int c) { return isalnum(c) || c == '_'; } static inline bool isasciitype(char c) { if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; return false; } static inline char makehexdigit(uint32_t val) { return "0123456789abcdef"[val&0xF]; } static char* appendhexnum(uint32_t val, char* out) { for( int32_t i=28; i>=0; i-=4 ) { *out++ = makehexdigit( val>>i ); } *out = 0; return out; } static char* appendcharornum(char c, char* out, bool skipzero = true) { if (skipzero && c == 0) return out; if (isasciitype(c)) { *out++ = c; return out; } *out++ = '\\'; *out++ = 'x'; *out++ = makehexdigit(c>>4); *out++ = makehexdigit(c); return out; } static char* typetostring(uint32_t type, char* out, bool fullContext = true, bool strict = false) { char* pos = out; char c[4]; c[0] = (char)((type>>24)&0xFF); c[1] = (char)((type>>16)&0xFF); c[2] = (char)((type>>8)&0xFF); c[3] = (char)(type&0xFF); bool valid; if( !strict ) { // now even less strict! // valid = isasciitype(c[3]); valid = true; int32_t i = 0; bool zero = true; while (valid && i<3) { if (c[i] == 0) { if (!zero) valid = false; } else { zero = false; //if (!isasciitype(c[i])) valid = false; } i++; } // if all zeros, not a valid type code. if (zero) valid = false; } else { valid = isident(c[3]) ? true : false; int32_t i = 0; bool zero = true; while (valid && i<3) { if (c[i] == 0) { if (!zero) valid = false; } else { zero = false; if (!isident(c[i])) valid = false; } i++; } } if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { if( fullContext ) *pos++ = '\''; pos = appendcharornum(c[0], pos); pos = appendcharornum(c[1], pos); pos = appendcharornum(c[2], pos); pos = appendcharornum(c[3], pos); if( fullContext ) *pos++ = '\''; *pos = 0; return pos; } if( fullContext ) { *pos++ = '0'; *pos++ = 'x'; } return appendhexnum(type, pos); } void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) { char buffer[32]; char* end = typetostring(typeCode, buffer); *end = 0; func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); } void printHexData(int32_t indent, const void *buf, size_t length, size_t bytesPerLine, int32_t singleLineBytesCutoff, size_t alignment, bool cStyle, debugPrintFunc func, void* cookie) { if (alignment == 0) { if (bytesPerLine >= 16) alignment = 4; else if (bytesPerLine >= 8) alignment = 2; else alignment = 1; } if (func == nullptr) func = defaultPrintFunc; size_t offset; unsigned char *pos = (unsigned char *)buf; if (pos == nullptr) { if (singleLineBytesCutoff < 0) func(cookie, "\n"); func(cookie, "(NULL)"); return; } if (length == 0) { if (singleLineBytesCutoff < 0) func(cookie, "\n"); func(cookie, "(empty)"); return; } if ((int32_t)length < 0) { if (singleLineBytesCutoff < 0) func(cookie, "\n"); char buf[64]; sprintf(buf, "(bad length: %zu)", length); func(cookie, buf); return; } char buffer[256]; static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; const bool oneLine = (int32_t)length <= singleLineBytesCutoff; bool newLine = false; if (cStyle) { indent++; func(cookie, "{\n"); newLine = true; } else if (!oneLine) { func(cookie, "\n"); newLine = true; } for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { long remain = length; char* c = buffer; if (!oneLine && !cStyle) { sprintf(c, "0x%08x: ", (int)offset); c += 12; } size_t index; size_t word; for (word = 0; word < bytesPerLine; ) { size_t align_offset = alignment-(alignment?1:0); if (remain > 0 && (size_t)remain <= align_offset) { align_offset = remain - 1; } const size_t startIndex = word+align_offset; for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { if (!cStyle) { if (index == 0 && word > 0 && alignment > 0) { *c++ = ' '; } if (remain-- > 0) { const unsigned char val = *(pos+startIndex-index); *c++ = makehexdigit(val>>4); *c++ = makehexdigit(val); } else if (!oneLine) { *c++ = ' '; *c++ = ' '; } } else { if (remain > 0) { if (index == 0 && word > 0) { *c++ = ','; *c++ = ' '; } if (index == 0) { *c++ = '0'; *c++ = 'x'; } const unsigned char val = *(pos+startIndex-index); *c++ = makehexdigit(val>>4); *c++ = makehexdigit(val); remain--; } } } word += index; } if (!cStyle) { remain = length; *c++ = ' '; *c++ = '\''; for (index = 0; index < bytesPerLine; index++) { if (remain-- > 0) { const unsigned char val = pos[index]; *c++ = (val >= ' ' && val < 127) ? val : '.'; } else if (!oneLine) { *c++ = ' '; } } *c++ = '\''; if (length > bytesPerLine) *c++ = '\n'; } else { if (remain > 0) *c++ = ','; *c++ = '\n'; } if (newLine && indent) func(cookie, stringForIndent(indent)); *c = 0; func(cookie, buffer); newLine = true; if (length <= bytesPerLine) break; length -= bytesPerLine; } if (cStyle) { if (indent > 0) func(cookie, stringForIndent(indent-1)); func(cookie, "};"); } } ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) { sp proc = ProcessState::selfOrNull(); if (proc.get() == nullptr) { return 0; } return proc->getKernelReferences(count, buf); } }; // namespace android libs/binder/IActivityManager.cpp0100644 0000000 0000000 00000010253 13756501735 015722 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include namespace android { // ------------------------------------------------------------------------------------ class BpActivityManager : public BpInterface { public: explicit BpActivityManager(const sp& impl) : BpInterface(impl) { } virtual int openContentUri(const String16& stringUri) { Parcel data, reply; data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeString16(stringUri); status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply); int fd = -1; if (ret == NO_ERROR) { int32_t exceptionCode = reply.readExceptionCode(); if (!exceptionCode) { // Success is indicated here by a nonzero int followed by the fd; // failure by a zero int with no data following. if (reply.readInt32() != 0) { fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0); } } else { // An exception was thrown back; fall through to return failure ALOGD("openContentUri(%s) caught exception %d\n", String8(stringUri).string(), exceptionCode); } } return fd; } virtual void registerUidObserver(const sp& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage) { Parcel data, reply; data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(observer)); data.writeInt32(event); data.writeInt32(cutpoint); data.writeString16(callingPackage); remote()->transact(REGISTER_UID_OBSERVER_TRANSACTION, data, &reply); } virtual void unregisterUidObserver(const sp& observer) { Parcel data, reply; data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(observer)); remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply); } virtual bool isUidActive(const uid_t uid, const String16& callingPackage) { Parcel data, reply; data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeInt32(uid); data.writeString16(callingPackage); remote()->transact(IS_UID_ACTIVE_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return false; return reply.readInt32() == 1; } virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) { Parcel data, reply; data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); data.writeInt32(uid); data.writeString16(callingPackage); remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) { return ActivityManager::PROCESS_STATE_UNKNOWN; } return reply.readInt32(); } }; // ------------------------------------------------------------------------------------ IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager"); }; // namespace android libs/binder/IAppOpsCallback.cpp0100644 0000000 0000000 00000004242 13756501735 015453 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "AppOpsCallback" #include #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpAppOpsCallback : public BpInterface { public: explicit BpAppOpsCallback(const sp& impl) : BpInterface(impl) { } virtual void opChanged(int32_t op, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor()); data.writeInt32(op); data.writeString16(packageName); remote()->transact(OP_CHANGED_TRANSACTION, data, &reply); } }; IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback"); // ---------------------------------------------------------------------- // NOLINTNEXTLINE(google-default-arguments) status_t BnAppOpsCallback::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case OP_CHANGED_TRANSACTION: { CHECK_INTERFACE(IAppOpsCallback, data, reply); int32_t op = data.readInt32(); String16 packageName; (void)data.readString16(&packageName); opChanged(op, packageName); reply->writeNoException(); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IAppOpsService.cpp0100644 0000000 0000000 00000023130 13756501735 015354 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "AppOpsService" #include #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpAppOpsService : public BpInterface { public: explicit BpAppOpsService(const sp& impl) : BpInterface(impl) { } virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; return reply.readInt32(); } virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; return reply.readInt32(); } virtual int32_t startOperation(const sp& token, int32_t code, int32_t uid, const String16& packageName, bool startIfModeDefault) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); data.writeInt32(startIfModeDefault ? 1 : 0); remote()->transact(START_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; return reply.readInt32(); } virtual void finishOperation(const sp& token, int32_t code, int32_t uid, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply); } virtual void startWatchingMode(int32_t op, const String16& packageName, const sp& callback) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(op); data.writeString16(packageName); data.writeStrongBinder(IInterface::asBinder(callback)); remote()->transact(START_WATCHING_MODE_TRANSACTION, data, &reply); } virtual void stopWatchingMode(const sp& callback) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(callback)); remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply); } virtual sp getToken(const sp& clientToken) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(clientToken); remote()->transact(GET_TOKEN_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return nullptr; return reply.readStrongBinder(); } virtual int32_t permissionToOpCode(const String16& permission) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeString16(permission); remote()->transact(PERMISSION_TO_OP_CODE_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return -1; return reply.readInt32(); } virtual int32_t checkAudioOperation(int32_t code, int32_t usage, int32_t uid, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); data.writeInt32(usage); data.writeInt32(uid); data.writeString16(packageName); remote()->transact(CHECK_AUDIO_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) { return MODE_ERRORED; } return reply.readInt32(); } }; IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService"); // ---------------------------------------------------------------------- // NOLINTNEXTLINE(google-default-arguments) status_t BnAppOpsService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { //printf("AppOpsService received: "); data.print(); switch(code) { case CHECK_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); int32_t res = checkOperation(code, uid, packageName); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; case NOTE_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); int32_t res = noteOperation(code, uid, packageName); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; case START_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp token = data.readStrongBinder(); int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); bool startIfModeDefault = data.readInt32() == 1; int32_t res = startOperation(token, code, uid, packageName, startIfModeDefault); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; case FINISH_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp token = data.readStrongBinder(); int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); finishOperation(token, code, uid, packageName); reply->writeNoException(); return NO_ERROR; } break; case START_WATCHING_MODE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t op = data.readInt32(); String16 packageName = data.readString16(); sp callback = interface_cast(data.readStrongBinder()); startWatchingMode(op, packageName, callback); reply->writeNoException(); return NO_ERROR; } break; case STOP_WATCHING_MODE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp callback = interface_cast(data.readStrongBinder()); stopWatchingMode(callback); reply->writeNoException(); return NO_ERROR; } break; case GET_TOKEN_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp clientToken = data.readStrongBinder(); sp token = getToken(clientToken); reply->writeNoException(); reply->writeStrongBinder(token); return NO_ERROR; } break; case PERMISSION_TO_OP_CODE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); String16 permission = data.readString16(); const int32_t opCode = permissionToOpCode(permission); reply->writeNoException(); reply->writeInt32(opCode); return NO_ERROR; } break; case CHECK_AUDIO_OPERATION_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); const int32_t code = data.readInt32(); const int32_t usage = data.readInt32(); const int32_t uid = data.readInt32(); const String16 packageName = data.readString16(); const int32_t res = checkAudioOperation(code, usage, uid, packageName); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IBatteryStats.cpp0100644 0000000 0000000 00000020652 13756501735 015270 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpBatteryStats : public BpInterface { public: explicit BpBatteryStats(const sp& impl) : BpInterface(impl) { } virtual void noteStartSensor(int uid, int sensor) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); data.writeInt32(sensor); remote()->transact(NOTE_START_SENSOR_TRANSACTION, data, &reply); } virtual void noteStopSensor(int uid, int sensor) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); data.writeInt32(sensor); remote()->transact(NOTE_STOP_SENSOR_TRANSACTION, data, &reply); } virtual void noteStartVideo(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_START_VIDEO_TRANSACTION, data, &reply); } virtual void noteStopVideo(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_STOP_VIDEO_TRANSACTION, data, &reply); } virtual void noteStartAudio(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_START_AUDIO_TRANSACTION, data, &reply); } virtual void noteStopAudio(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_STOP_AUDIO_TRANSACTION, data, &reply); } virtual void noteResetVideo() { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); remote()->transact(NOTE_RESET_VIDEO_TRANSACTION, data, &reply); } virtual void noteResetAudio() { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); remote()->transact(NOTE_RESET_AUDIO_TRANSACTION, data, &reply); } virtual void noteFlashlightOn(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_FLASHLIGHT_ON_TRANSACTION, data, &reply); } virtual void noteFlashlightOff(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_FLASHLIGHT_OFF_TRANSACTION, data, &reply); } virtual void noteStartCamera(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_START_CAMERA_TRANSACTION, data, &reply); } virtual void noteStopCamera(int uid) { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(NOTE_STOP_CAMERA_TRANSACTION, data, &reply); } virtual void noteResetCamera() { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); remote()->transact(NOTE_RESET_CAMERA_TRANSACTION, data, &reply); } virtual void noteResetFlashlight() { Parcel data, reply; data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); remote()->transact(NOTE_RESET_FLASHLIGHT_TRANSACTION, data, &reply); } }; IMPLEMENT_META_INTERFACE(BatteryStats, "com.android.internal.app.IBatteryStats"); // ---------------------------------------------------------------------- // NOLINTNEXTLINE(google-default-arguments) status_t BnBatteryStats::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case NOTE_START_SENSOR_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); int sensor = data.readInt32(); noteStartSensor(uid, sensor); reply->writeNoException(); return NO_ERROR; } break; case NOTE_STOP_SENSOR_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); int sensor = data.readInt32(); noteStopSensor(uid, sensor); reply->writeNoException(); return NO_ERROR; } break; case NOTE_START_VIDEO_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteStartVideo(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_STOP_VIDEO_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteStopVideo(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_START_AUDIO_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteStartAudio(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_STOP_AUDIO_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteStopAudio(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_RESET_VIDEO_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); noteResetVideo(); reply->writeNoException(); return NO_ERROR; } break; case NOTE_RESET_AUDIO_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); noteResetAudio(); reply->writeNoException(); return NO_ERROR; } break; case NOTE_FLASHLIGHT_ON_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteFlashlightOn(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_FLASHLIGHT_OFF_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteFlashlightOff(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_START_CAMERA_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteStartCamera(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_STOP_CAMERA_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); int uid = data.readInt32(); noteStopCamera(uid); reply->writeNoException(); return NO_ERROR; } break; case NOTE_RESET_CAMERA_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); noteResetCamera(); reply->writeNoException(); return NO_ERROR; } break; case NOTE_RESET_FLASHLIGHT_TRANSACTION: { CHECK_INTERFACE(IBatteryStats, data, reply); noteResetFlashlight(); reply->writeNoException(); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IInterface.cpp0100644 0000000 0000000 00000002440 13756501735 014532 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "IInterface" #include #include namespace android { // --------------------------------------------------------------------------- IInterface::IInterface() : RefBase() { } IInterface::~IInterface() { } // static sp IInterface::asBinder(const IInterface* iface) { if (iface == nullptr) return nullptr; return const_cast(iface)->onAsBinder(); } // static sp IInterface::asBinder(const sp& iface) { if (iface == nullptr) return nullptr; return iface->onAsBinder(); } // --------------------------------------------------------------------------- }; // namespace android libs/binder/IMediaResourceMonitor.cpp0100644 0000000 0000000 00000004222 13756501735 016731 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpMediaResourceMonitor : public BpInterface { public: explicit BpMediaResourceMonitor(const sp& impl) : BpInterface(impl) {} virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) { Parcel data, reply; data.writeInterfaceToken(IMediaResourceMonitor::getInterfaceDescriptor()); data.writeInt32(pid); data.writeInt32(type); remote()->transact(NOTIFY_RESOURCE_GRANTED, data, &reply, IBinder::FLAG_ONEWAY); } }; IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor"); // ---------------------------------------------------------------------- status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case NOTIFY_RESOURCE_GRANTED: { CHECK_INTERFACE(IMediaResourceMonitor, data, reply); int32_t pid = data.readInt32(); const int32_t type = data.readInt32(); notifyResourceGranted(/*in*/ pid, /*in*/ type); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } // ---------------------------------------------------------------------- }; // namespace android libs/binder/IMemory.cpp0100644 0000000 0000000 00000036701 13756501735 014111 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #define LOG_TAG "IMemory" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VERBOSE 0 namespace android { // --------------------------------------------------------------------------- class HeapCache : public IBinder::DeathRecipient { public: HeapCache(); virtual ~HeapCache(); virtual void binderDied(const wp& who); sp find_heap(const sp& binder); void free_heap(const sp& binder); sp get_heap(const sp& binder); void dump_heaps(); private: // For IMemory.cpp struct heap_info_t { sp heap; int32_t count; // Note that this cannot be meaningfully copied. }; void free_heap(const wp& binder); Mutex mHeapCacheLock; // Protects entire vector below. KeyedVector< wp, heap_info_t > mHeapCache; // We do not use the copy-on-write capabilities of KeyedVector. // TODO: Reimplemement based on standard C++ container? }; static sp gHeapCache = new HeapCache(); /******************************************************************************/ enum { HEAP_ID = IBinder::FIRST_CALL_TRANSACTION }; class BpMemoryHeap : public BpInterface { public: explicit BpMemoryHeap(const sp& impl); virtual ~BpMemoryHeap(); virtual int getHeapID() const; virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; off_t getOffset() const override; private: friend class IMemory; friend class HeapCache; // for debugging in this module static inline sp find_heap(const sp& binder) { return gHeapCache->find_heap(binder); } static inline void free_heap(const sp& binder) { gHeapCache->free_heap(binder); } static inline sp get_heap(const sp& binder) { return gHeapCache->get_heap(binder); } static inline void dump_heaps() { gHeapCache->dump_heaps(); } void assertMapped() const; void assertReallyMapped() const; mutable std::atomic mHeapId; mutable void* mBase; mutable size_t mSize; mutable uint32_t mFlags; mutable off_t mOffset; mutable bool mRealHeap; mutable Mutex mLock; }; // ---------------------------------------------------------------------------- enum { GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION }; class BpMemory : public BpInterface { public: explicit BpMemory(const sp& impl); virtual ~BpMemory(); // NOLINTNEXTLINE(google-default-arguments) virtual sp getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const; private: mutable sp mHeap; mutable ssize_t mOffset; mutable size_t mSize; }; /******************************************************************************/ void* IMemory::fastPointer(const sp& binder, ssize_t offset) const { sp realHeap = BpMemoryHeap::get_heap(binder); void* const base = realHeap->base(); if (base == MAP_FAILED) return nullptr; return static_cast(base) + offset; } void* IMemory::pointer() const { ssize_t offset; sp heap = getMemory(&offset); void* const base = heap!=nullptr ? heap->base() : MAP_FAILED; if (base == MAP_FAILED) return nullptr; return static_cast(base) + offset; } size_t IMemory::size() const { size_t size; getMemory(nullptr, &size); return size; } ssize_t IMemory::offset() const { ssize_t offset; getMemory(&offset); return offset; } /******************************************************************************/ BpMemory::BpMemory(const sp& impl) : BpInterface(impl), mOffset(0), mSize(0) { } BpMemory::~BpMemory() { } // NOLINTNEXTLINE(google-default-arguments) sp BpMemory::getMemory(ssize_t* offset, size_t* size) const { if (mHeap == nullptr) { Parcel data, reply; data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp heap = reply.readStrongBinder(); if (heap != nullptr) { mHeap = interface_cast(heap); if (mHeap != nullptr) { const int64_t offset64 = reply.readInt64(); const uint64_t size64 = reply.readUint64(); const ssize_t o = (ssize_t)offset64; const size_t s = (size_t)size64; size_t heapSize = mHeap->getSize(); if (s == size64 && o == offset64 // ILP32 bounds check && s <= heapSize && o >= 0 && (static_cast(o) <= heapSize - s)) { mOffset = o; mSize = s; } else { // Hm. android_errorWriteWithInfoLog(0x534e4554, "26877992", -1, nullptr, 0); mOffset = 0; mSize = 0; } } } } } if (offset) *offset = mOffset; if (size) *size = mSize; return (mSize > 0) ? mHeap : nullptr; } // --------------------------------------------------------------------------- IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); BnMemory::BnMemory() { } BnMemory::~BnMemory() { } // NOLINTNEXTLINE(google-default-arguments) status_t BnMemory::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case GET_MEMORY: { CHECK_INTERFACE(IMemory, data, reply); ssize_t offset; size_t size; reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) ); reply->writeInt64(offset); reply->writeUint64(size); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } /******************************************************************************/ BpMemoryHeap::BpMemoryHeap(const sp& impl) : BpInterface(impl), mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false) { } BpMemoryHeap::~BpMemoryHeap() { int32_t heapId = mHeapId.load(memory_order_relaxed); if (heapId != -1) { close(heapId); if (mRealHeap) { // by construction we're the last one if (mBase != MAP_FAILED) { sp binder = IInterface::asBinder(this); if (VERBOSE) { ALOGD("UNMAPPING binder=%p, heap=%p, size=%zu, fd=%d", binder.get(), this, mSize, heapId); } munmap(mBase, mSize); } } else { // remove from list only if it was mapped before sp binder = IInterface::asBinder(this); free_heap(binder); } } } void BpMemoryHeap::assertMapped() const { int32_t heapId = mHeapId.load(memory_order_acquire); if (heapId == -1) { sp binder(IInterface::asBinder(const_cast(this))); sp heap(static_cast(find_heap(binder).get())); heap->assertReallyMapped(); if (heap->mBase != MAP_FAILED) { Mutex::Autolock _l(mLock); if (mHeapId.load(memory_order_relaxed) == -1) { mBase = heap->mBase; mSize = heap->mSize; mOffset = heap->mOffset; int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0); ALOGE_IF(fd==-1, "cannot dup fd=%d", heap->mHeapId.load(memory_order_relaxed)); mHeapId.store(fd, memory_order_release); } } else { // something went wrong free_heap(binder); } } } void BpMemoryHeap::assertReallyMapped() const { int32_t heapId = mHeapId.load(memory_order_acquire); if (heapId == -1) { // remote call without mLock held, worse case scenario, we end up // calling transact() from multiple threads, but that's not a problem, // only mmap below must be in the critical section. Parcel data, reply; data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); status_t err = remote()->transact(HEAP_ID, data, &reply); int parcel_fd = reply.readFileDescriptor(); const uint64_t size64 = reply.readUint64(); const int64_t offset64 = reply.readInt64(); const uint32_t flags = reply.readUint32(); const size_t size = (size_t)size64; const off_t offset = (off_t)offset64; if (err != NO_ERROR || // failed transaction size != size64 || offset != offset64) { // ILP32 size check ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)", IInterface::asBinder(this).get(), parcel_fd, size, err, strerror(-err)); return; } Mutex::Autolock _l(mLock); if (mHeapId.load(memory_order_relaxed) == -1) { int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0); ALOGE_IF(fd == -1, "cannot dup fd=%d, size=%zu, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; if (!(flags & READ_ONLY)) { access |= PROT_WRITE; } mRealHeap = true; mBase = mmap(nullptr, size, access, MAP_SHARED, fd, offset); if (mBase == MAP_FAILED) { ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zu, fd=%d (%s)", IInterface::asBinder(this).get(), size, fd, strerror(errno)); close(fd); } else { mSize = size; mFlags = flags; mOffset = offset; mHeapId.store(fd, memory_order_release); } } } } int BpMemoryHeap::getHeapID() const { assertMapped(); // We either stored mHeapId ourselves, or loaded it with acquire semantics. return mHeapId.load(memory_order_relaxed); } void* BpMemoryHeap::getBase() const { assertMapped(); return mBase; } size_t BpMemoryHeap::getSize() const { assertMapped(); return mSize; } uint32_t BpMemoryHeap::getFlags() const { assertMapped(); return mFlags; } off_t BpMemoryHeap::getOffset() const { assertMapped(); return mOffset; } // --------------------------------------------------------------------------- IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); BnMemoryHeap::BnMemoryHeap() { } BnMemoryHeap::~BnMemoryHeap() { } // NOLINTNEXTLINE(google-default-arguments) status_t BnMemoryHeap::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case HEAP_ID: { CHECK_INTERFACE(IMemoryHeap, data, reply); reply->writeFileDescriptor(getHeapID()); reply->writeUint64(getSize()); reply->writeInt64(getOffset()); reply->writeUint32(getFlags()); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } /*****************************************************************************/ HeapCache::HeapCache() : DeathRecipient() { } HeapCache::~HeapCache() { } void HeapCache::binderDied(const wp& binder) { //ALOGD("binderDied binder=%p", binder.unsafe_get()); free_heap(binder); } sp HeapCache::find_heap(const sp& binder) { Mutex::Autolock _l(mHeapCacheLock); ssize_t i = mHeapCache.indexOfKey(binder); if (i>=0) { heap_info_t& info = mHeapCache.editValueAt(i); ALOGD_IF(VERBOSE, "found binder=%p, heap=%p, size=%zu, fd=%d, count=%d", binder.get(), info.heap.get(), static_cast(info.heap.get())->mSize, static_cast(info.heap.get()) ->mHeapId.load(memory_order_relaxed), info.count); ++info.count; return info.heap; } else { heap_info_t info; info.heap = interface_cast(binder); info.count = 1; //ALOGD("adding binder=%p, heap=%p, count=%d", // binder.get(), info.heap.get(), info.count); mHeapCache.add(binder, info); return info.heap; } } void HeapCache::free_heap(const sp& binder) { free_heap( wp(binder) ); } void HeapCache::free_heap(const wp& binder) { sp rel; { Mutex::Autolock _l(mHeapCacheLock); ssize_t i = mHeapCache.indexOfKey(binder); if (i>=0) { heap_info_t& info(mHeapCache.editValueAt(i)); if (--info.count == 0) { ALOGD_IF(VERBOSE, "removing binder=%p, heap=%p, size=%zu, fd=%d, count=%d", binder.unsafe_get(), info.heap.get(), static_cast(info.heap.get())->mSize, static_cast(info.heap.get()) ->mHeapId.load(memory_order_relaxed), info.count); rel = mHeapCache.valueAt(i).heap; mHeapCache.removeItemsAt(i); } } else { ALOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); } } } sp HeapCache::get_heap(const sp& binder) { sp realHeap; Mutex::Autolock _l(mHeapCacheLock); ssize_t i = mHeapCache.indexOfKey(binder); if (i>=0) realHeap = mHeapCache.valueAt(i).heap; else realHeap = interface_cast(binder); return realHeap; } void HeapCache::dump_heaps() { Mutex::Autolock _l(mHeapCacheLock); int c = mHeapCache.size(); for (int i=0 ; i(info.heap.get())); ALOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%zu)", mHeapCache.keyAt(i).unsafe_get(), info.heap.get(), info.count, h->mHeapId.load(memory_order_relaxed), h->mBase, h->mSize); } } // --------------------------------------------------------------------------- }; // namespace android libs/binder/IPCThreadState.cpp0100644 0000000 0000000 00000121275 13756501735 015275 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "IPCThreadState" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LOG_NDEBUG #define IF_LOG_TRANSACTIONS() if (false) #define IF_LOG_COMMANDS() if (false) #define LOG_REMOTEREFS(...) #define IF_LOG_REMOTEREFS() if (false) #define LOG_THREADPOOL(...) #define LOG_ONEWAY(...) #else #define IF_LOG_TRANSACTIONS() IF_ALOG(LOG_VERBOSE, "transact") #define IF_LOG_COMMANDS() IF_ALOG(LOG_VERBOSE, "ipc") #define LOG_REMOTEREFS(...) ALOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) #define IF_LOG_REMOTEREFS() IF_ALOG(LOG_DEBUG, "remoterefs") #define LOG_THREADPOOL(...) ALOG(LOG_DEBUG, "threadpool", __VA_ARGS__) #define LOG_ONEWAY(...) ALOG(LOG_DEBUG, "ipc", __VA_ARGS__) #endif // --------------------------------------------------------------------------- namespace android { // Static const and functions will be optimized out if not used, // when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out. static const char *kReturnStrings[] = { "BR_ERROR", "BR_OK", "BR_TRANSACTION", "BR_REPLY", "BR_ACQUIRE_RESULT", "BR_DEAD_REPLY", "BR_TRANSACTION_COMPLETE", "BR_INCREFS", "BR_ACQUIRE", "BR_RELEASE", "BR_DECREFS", "BR_ATTEMPT_ACQUIRE", "BR_NOOP", "BR_SPAWN_LOOPER", "BR_FINISHED", "BR_DEAD_BINDER", "BR_CLEAR_DEATH_NOTIFICATION_DONE", "BR_FAILED_REPLY", "BR_TRANSACTION_SEC_CTX", }; static const char *kCommandStrings[] = { "BC_TRANSACTION", "BC_REPLY", "BC_ACQUIRE_RESULT", "BC_FREE_BUFFER", "BC_INCREFS", "BC_ACQUIRE", "BC_RELEASE", "BC_DECREFS", "BC_INCREFS_DONE", "BC_ACQUIRE_DONE", "BC_ATTEMPT_ACQUIRE", "BC_REGISTER_LOOPER", "BC_ENTER_LOOPER", "BC_EXIT_LOOPER", "BC_REQUEST_DEATH_NOTIFICATION", "BC_CLEAR_DEATH_NOTIFICATION", "BC_DEAD_BINDER_DONE" }; static const int64_t kWorkSourcePropagatedBitIndex = 32; static const char* getReturnString(uint32_t cmd) { size_t idx = cmd & 0xff; if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) return kReturnStrings[idx]; else return "unknown"; } static const void* printBinderTransactionData(TextOutput& out, const void* data) { const binder_transaction_data* btd = (const binder_transaction_data*)data; if (btd->target.handle < 1024) { /* want to print descriptors in decimal; guess based on value */ out << "target.desc=" << btd->target.handle; } else { out << "target.ptr=" << btd->target.ptr; } out << " (cookie " << btd->cookie << ")" << endl << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(long)btd->flags << endl << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)" << endl << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)"; return btd+1; } static const void* printReturnCommand(TextOutput& out, const void* _cmd) { static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); const int32_t* cmd = (const int32_t*)_cmd; uint32_t code = (uint32_t)*cmd++; size_t cmdIndex = code & 0xff; if (code == BR_ERROR) { out << "BR_ERROR: " << (void*)(long)(*cmd++) << endl; return cmd; } else if (cmdIndex >= N) { out << "Unknown reply: " << code << endl; return cmd; } out << kReturnStrings[cmdIndex]; switch (code) { case BR_TRANSACTION: case BR_REPLY: { out << ": " << indent; cmd = (const int32_t *)printBinderTransactionData(out, cmd); out << dedent; } break; case BR_ACQUIRE_RESULT: { const int32_t res = *cmd++; out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); } break; case BR_INCREFS: case BR_ACQUIRE: case BR_RELEASE: case BR_DECREFS: { const int32_t b = *cmd++; const int32_t c = *cmd++; out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")"; } break; case BR_ATTEMPT_ACQUIRE: { const int32_t p = *cmd++; const int32_t b = *cmd++; const int32_t c = *cmd++; out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << "), pri=" << p; } break; case BR_DEAD_BINDER: case BR_CLEAR_DEATH_NOTIFICATION_DONE: { const int32_t c = *cmd++; out << ": death cookie " << (void*)(long)c; } break; default: // no details to show for: BR_OK, BR_DEAD_REPLY, // BR_TRANSACTION_COMPLETE, BR_FINISHED break; } out << endl; return cmd; } static const void* printCommand(TextOutput& out, const void* _cmd) { static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); const int32_t* cmd = (const int32_t*)_cmd; uint32_t code = (uint32_t)*cmd++; size_t cmdIndex = code & 0xff; if (cmdIndex >= N) { out << "Unknown command: " << code << endl; return cmd; } out << kCommandStrings[cmdIndex]; switch (code) { case BC_TRANSACTION: case BC_REPLY: { out << ": " << indent; cmd = (const int32_t *)printBinderTransactionData(out, cmd); out << dedent; } break; case BC_ACQUIRE_RESULT: { const int32_t res = *cmd++; out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); } break; case BC_FREE_BUFFER: { const int32_t buf = *cmd++; out << ": buffer=" << (void*)(long)buf; } break; case BC_INCREFS: case BC_ACQUIRE: case BC_RELEASE: case BC_DECREFS: { const int32_t d = *cmd++; out << ": desc=" << d; } break; case BC_INCREFS_DONE: case BC_ACQUIRE_DONE: { const int32_t b = *cmd++; const int32_t c = *cmd++; out << ": target=" << (void*)(long)b << " (cookie " << (void*)(long)c << ")"; } break; case BC_ATTEMPT_ACQUIRE: { const int32_t p = *cmd++; const int32_t d = *cmd++; out << ": desc=" << d << ", pri=" << p; } break; case BC_REQUEST_DEATH_NOTIFICATION: case BC_CLEAR_DEATH_NOTIFICATION: { const int32_t h = *cmd++; const int32_t c = *cmd++; out << ": handle=" << h << " (death cookie " << (void*)(long)c << ")"; } break; case BC_DEAD_BINDER_DONE: { const int32_t c = *cmd++; out << ": death cookie " << (void*)(long)c; } break; default: // no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER, // BC_EXIT_LOOPER break; } out << endl; return cmd; } static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; static bool gHaveTLS = false; static pthread_key_t gTLS = 0; static bool gShutdown = false; static bool gDisableBackgroundScheduling = false; IPCThreadState* IPCThreadState::self() { if (gHaveTLS) { restart: const pthread_key_t k = gTLS; IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); if (st) return st; return new IPCThreadState; } if (gShutdown) { ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n"); return nullptr; } pthread_mutex_lock(&gTLSMutex); if (!gHaveTLS) { int key_create_value = pthread_key_create(&gTLS, threadDestructor); if (key_create_value != 0) { pthread_mutex_unlock(&gTLSMutex); ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n", strerror(key_create_value)); return nullptr; } gHaveTLS = true; } pthread_mutex_unlock(&gTLSMutex); goto restart; } IPCThreadState* IPCThreadState::selfOrNull() { if (gHaveTLS) { const pthread_key_t k = gTLS; IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); return st; } return nullptr; } void IPCThreadState::shutdown() { gShutdown = true; if (gHaveTLS) { // XXX Need to wait for all thread pool threads to exit! IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); if (st) { delete st; pthread_setspecific(gTLS, nullptr); } pthread_key_delete(gTLS); gHaveTLS = false; } } void IPCThreadState::disableBackgroundScheduling(bool disable) { gDisableBackgroundScheduling = disable; } bool IPCThreadState::backgroundSchedulingDisabled() { return gDisableBackgroundScheduling; } sp IPCThreadState::process() { return mProcess; } status_t IPCThreadState::clearLastError() { const status_t err = mLastError; mLastError = NO_ERROR; return err; } pid_t IPCThreadState::getCallingPid() const { return mCallingPid; } const char* IPCThreadState::getCallingSid() const { return mCallingSid; } uid_t IPCThreadState::getCallingUid() const { return mCallingUid; } int64_t IPCThreadState::clearCallingIdentity() { // ignore mCallingSid for legacy reasons int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; clearCaller(); return token; } void IPCThreadState::setStrictModePolicy(int32_t policy) { mStrictModePolicy = policy; } int32_t IPCThreadState::getStrictModePolicy() const { return mStrictModePolicy; } int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid) { int64_t token = setCallingWorkSourceUidWithoutPropagation(uid); mPropagateWorkSource = true; return token; } int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid) { const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex; int64_t token = propagatedBit | mWorkSource; mWorkSource = uid; return token; } void IPCThreadState::clearPropagateWorkSource() { mPropagateWorkSource = false; } bool IPCThreadState::shouldPropagateWorkSource() const { return mPropagateWorkSource; } uid_t IPCThreadState::getCallingWorkSourceUid() const { return mWorkSource; } int64_t IPCThreadState::clearCallingWorkSource() { return setCallingWorkSourceUid(kUnsetWorkSource); } void IPCThreadState::restoreCallingWorkSource(int64_t token) { uid_t uid = (int)token; setCallingWorkSourceUidWithoutPropagation(uid); mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1; } void IPCThreadState::setLastTransactionBinderFlags(int32_t flags) { mLastTransactionBinderFlags = flags; } int32_t IPCThreadState::getLastTransactionBinderFlags() const { return mLastTransactionBinderFlags; } void IPCThreadState::restoreCallingIdentity(int64_t token) { mCallingUid = (int)(token>>32); mCallingSid = nullptr; // not enough data to restore mCallingPid = (int)token; } void IPCThreadState::clearCaller() { mCallingPid = getpid(); mCallingSid = nullptr; // expensive to lookup mCallingUid = getuid(); } void IPCThreadState::flushCommands() { if (mProcess->mDriverFD <= 0) return; talkWithDriver(false); // The flush could have caused post-write refcount decrements to have // been executed, which in turn could result in BC_RELEASE/BC_DECREFS // being queued in mOut. So flush again, if we need to. if (mOut.dataSize() > 0) { talkWithDriver(false); } if (mOut.dataSize() > 0) { ALOGW("mOut.dataSize() > 0 after flushCommands()"); } } void IPCThreadState::blockUntilThreadAvailable() { pthread_mutex_lock(&mProcess->mThreadCountLock); while (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads) { ALOGW("Waiting for thread to be free. mExecutingThreadsCount=%lu mMaxThreads=%lu\n", static_cast(mProcess->mExecutingThreadsCount), static_cast(mProcess->mMaxThreads)); pthread_cond_wait(&mProcess->mThreadCountDecrement, &mProcess->mThreadCountLock); } pthread_mutex_unlock(&mProcess->mThreadCountLock); } status_t IPCThreadState::getAndExecuteCommand() { status_t result; int32_t cmd; result = talkWithDriver(); if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); if (IN < sizeof(int32_t)) return result; cmd = mIn.readInt32(); IF_LOG_COMMANDS() { alog << "Processing top-level Command: " << getReturnString(cmd) << endl; } pthread_mutex_lock(&mProcess->mThreadCountLock); mProcess->mExecutingThreadsCount++; if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads && mProcess->mStarvationStartTimeMs == 0) { mProcess->mStarvationStartTimeMs = uptimeMillis(); } pthread_mutex_unlock(&mProcess->mThreadCountLock); result = executeCommand(cmd); pthread_mutex_lock(&mProcess->mThreadCountLock); mProcess->mExecutingThreadsCount--; if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads && mProcess->mStarvationStartTimeMs != 0) { int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs; if (starvationTimeMs > 100) { ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms", mProcess->mMaxThreads, starvationTimeMs); } mProcess->mStarvationStartTimeMs = 0; } pthread_cond_broadcast(&mProcess->mThreadCountDecrement); pthread_mutex_unlock(&mProcess->mThreadCountLock); } return result; } // When we've cleared the incoming command queue, process any pending derefs void IPCThreadState::processPendingDerefs() { if (mIn.dataPosition() >= mIn.dataSize()) { /* * The decWeak()/decStrong() calls may cause a destructor to run, * which in turn could have initiated an outgoing transaction, * which in turn could cause us to add to the pending refs * vectors; so instead of simply iterating, loop until they're empty. * * We do this in an outer loop, because calling decStrong() * may result in something being added to mPendingWeakDerefs, * which could be delayed until the next incoming command * from the driver if we don't process it now. */ while (mPendingWeakDerefs.size() > 0 || mPendingStrongDerefs.size() > 0) { while (mPendingWeakDerefs.size() > 0) { RefBase::weakref_type* refs = mPendingWeakDerefs[0]; mPendingWeakDerefs.removeAt(0); refs->decWeak(mProcess.get()); } if (mPendingStrongDerefs.size() > 0) { // We don't use while() here because we don't want to re-order // strong and weak decs at all; if this decStrong() causes both a // decWeak() and a decStrong() to be queued, we want to process // the decWeak() first. BBinder* obj = mPendingStrongDerefs[0]; mPendingStrongDerefs.removeAt(0); obj->decStrong(mProcess.get()); } } } } void IPCThreadState::processPostWriteDerefs() { for (size_t i = 0; i < mPostWriteWeakDerefs.size(); i++) { RefBase::weakref_type* refs = mPostWriteWeakDerefs[i]; refs->decWeak(mProcess.get()); } mPostWriteWeakDerefs.clear(); for (size_t i = 0; i < mPostWriteStrongDerefs.size(); i++) { RefBase* obj = mPostWriteStrongDerefs[i]; obj->decStrong(mProcess.get()); } mPostWriteStrongDerefs.clear(); } void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); status_t result; do { processPendingDerefs(); // now get the next command to be processed, waiting if necessary result = getAndExecuteCommand(); if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting", mProcess->mDriverFD, result); abort(); } // Let this thread exit the thread pool if it is no longer // needed and it is not the main process thread. if(result == TIMED_OUT && !isMain) { break; } } while (result != -ECONNREFUSED && result != -EBADF); LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n", (void*)pthread_self(), getpid(), result); mOut.writeInt32(BC_EXIT_LOOPER); talkWithDriver(false); } int IPCThreadState::setupPolling(int* fd) { if (mProcess->mDriverFD <= 0) { return -EBADF; } mOut.writeInt32(BC_ENTER_LOOPER); *fd = mProcess->mDriverFD; return 0; } status_t IPCThreadState::handlePolledCommands() { status_t result; do { result = getAndExecuteCommand(); } while (mIn.dataPosition() < mIn.dataSize()); processPendingDerefs(); flushCommands(); return result; } void IPCThreadState::stopProcess(bool /*immediate*/) { //ALOGI("**** STOPPING PROCESS"); flushCommands(); int fd = mProcess->mDriverFD; mProcess->mDriverFD = -1; close(fd); //kill(getpid(), SIGKILL); } status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { status_t err; flags |= TF_ACCEPT_FDS; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " << handle << " / code " << TypeCode(code) << ": " << indent << data << dedent << endl; } LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr); if (err != NO_ERROR) { if (reply) reply->setError(err); return (mLastError = err); } if ((flags & TF_ONE_WAY) == 0) { if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) { if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) { ALOGE("Process making non-oneway call but is restricted."); CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), ANDROID_LOG_ERROR); } else /* FATAL_IF_NOT_ONEWAY */ { LOG_ALWAYS_FATAL("Process may not make oneway calls."); } } #if 0 if (code == 4) { // relayout ALOGI(">>>>>> CALLING transaction 4"); } else { ALOGI(">>>>>> CALLING transaction %d", code); } #endif if (reply) { err = waitForResponse(reply); } else { Parcel fakeReply; err = waitForResponse(&fakeReply); } #if 0 if (code == 4) { // relayout ALOGI("<<<<<< RETURNING transaction 4"); } else { ALOGI("<<<<<< RETURNING transaction %d", code); } #endif IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " << handle << ": "; if (reply) alog << indent << *reply << dedent << endl; else alog << "(none requested)" << endl; } } else { err = waitForResponse(nullptr, nullptr); } return err; } void IPCThreadState::incStrongHandle(int32_t handle, BpBinder *proxy) { LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); mOut.writeInt32(BC_ACQUIRE); mOut.writeInt32(handle); // Create a temp reference until the driver has handled this command. proxy->incStrong(mProcess.get()); mPostWriteStrongDerefs.push(proxy); } void IPCThreadState::decStrongHandle(int32_t handle) { LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); mOut.writeInt32(BC_RELEASE); mOut.writeInt32(handle); } void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy) { LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); mOut.writeInt32(BC_INCREFS); mOut.writeInt32(handle); // Create a temp reference until the driver has handled this command. proxy->getWeakRefs()->incWeak(mProcess.get()); mPostWriteWeakDerefs.push(proxy->getWeakRefs()); } void IPCThreadState::decWeakHandle(int32_t handle) { LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); mOut.writeInt32(BC_DECREFS); mOut.writeInt32(handle); } status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) { #if HAS_BC_ATTEMPT_ACQUIRE LOG_REMOTEREFS("IPCThreadState::attemptIncStrongHandle(%d)\n", handle); mOut.writeInt32(BC_ATTEMPT_ACQUIRE); mOut.writeInt32(0); // xxx was thread priority mOut.writeInt32(handle); status_t result = UNKNOWN_ERROR; waitForResponse(NULL, &result); #if LOG_REFCOUNTS ALOGV("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); #endif return result; #else (void)handle; ALOGE("%s(%d): Not supported\n", __func__, handle); return INVALID_OPERATION; #endif } void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) { #if LOG_REFCOUNTS ALOGV("IPCThreadState::expungeHandle(%ld)\n", handle); #endif self()->mProcess->expungeHandle(handle, binder); // NOLINT } status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) { mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); mOut.writeInt32((int32_t)handle); mOut.writePointer((uintptr_t)proxy); return NO_ERROR; } status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) { mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); mOut.writeInt32((int32_t)handle); mOut.writePointer((uintptr_t)proxy); return NO_ERROR; } IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), mWorkSource(kUnsetWorkSource), mPropagateWorkSource(false), mStrictModePolicy(0), mLastTransactionBinderFlags(0), mCallRestriction(mProcess->mCallRestriction) { pthread_setspecific(gTLS, this); clearCaller(); mIn.setDataCapacity(256); mOut.setDataCapacity(256); mIPCThreadStateBase = IPCThreadStateBase::self(); } IPCThreadState::~IPCThreadState() { } status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) { status_t err; status_t statusBuffer; err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); if (err < NO_ERROR) return err; return waitForResponse(nullptr, nullptr); } status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { uint32_t cmd; int32_t err; while (1) { if ((err=talkWithDriver()) < NO_ERROR) break; err = mIn.errorCheck(); if (err < NO_ERROR) break; if (mIn.dataAvail() == 0) continue; cmd = (uint32_t)mIn.readInt32(); IF_LOG_COMMANDS() { alog << "Processing waitForResponse Command: " << getReturnString(cmd) << endl; } switch (cmd) { case BR_TRANSACTION_COMPLETE: if (!reply && !acquireResult) goto finish; break; case BR_DEAD_REPLY: err = DEAD_OBJECT; goto finish; case BR_FAILED_REPLY: err = FAILED_TRANSACTION; goto finish; case BR_ACQUIRE_RESULT: { ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); const int32_t result = mIn.readInt32(); if (!acquireResult) continue; *acquireResult = result ? NO_ERROR : INVALID_OPERATION; } goto finish; case BR_REPLY: { binder_transaction_data tr; err = mIn.read(&tr, sizeof(tr)); ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); if (err != NO_ERROR) goto finish; if (reply) { if ((tr.flags & TF_STATUS_CODE) == 0) { reply->ipcSetDataReference( reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), freeBuffer, this); } else { err = *reinterpret_cast(tr.data.ptr.buffer); freeBuffer(nullptr, reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), this); } } else { freeBuffer(nullptr, reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), this); continue; } } goto finish; default: err = executeCommand(cmd); if (err != NO_ERROR) goto finish; break; } } finish: if (err != NO_ERROR) { if (acquireResult) *acquireResult = err; if (reply) reply->setError(err); mLastError = err; } return err; } status_t IPCThreadState::talkWithDriver(bool doReceive) { if (mProcess->mDriverFD <= 0) { return -EBADF; } binder_write_read bwr; // Is the read buffer empty? const bool needRead = mIn.dataPosition() >= mIn.dataSize(); // We don't want to write anything if we are still reading // from data left in the input buffer and the caller // has requested to read the next data. const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; bwr.write_size = outAvail; bwr.write_buffer = (uintptr_t)mOut.data(); // This is what we'll read. if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity(); bwr.read_buffer = (uintptr_t)mIn.data(); } else { bwr.read_size = 0; bwr.read_buffer = 0; } IF_LOG_COMMANDS() { TextOutput::Bundle _b(alog); if (outAvail != 0) { alog << "Sending commands to driver: " << indent; const void* cmds = (const void*)bwr.write_buffer; const void* end = ((const uint8_t*)cmds)+bwr.write_size; alog << HexDump(cmds, bwr.write_size) << endl; while (cmds < end) cmds = printCommand(alog, cmds); alog << dedent; } alog << "Size of receive buffer: " << bwr.read_size << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; } // Return immediately if there is nothing to do. if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; bwr.write_consumed = 0; bwr.read_consumed = 0; status_t err; do { IF_LOG_COMMANDS() { alog << "About to read/write, write size = " << mOut.dataSize() << endl; } #if defined(__ANDROID__) if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; else err = -errno; #else err = INVALID_OPERATION; #endif if (mProcess->mDriverFD <= 0) { err = -EBADF; } IF_LOG_COMMANDS() { alog << "Finished read/write, write size = " << mOut.dataSize() << endl; } } while (err == -EINTR); IF_LOG_COMMANDS() { alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: " << bwr.write_consumed << " (of " << mOut.dataSize() << "), read consumed: " << bwr.read_consumed << endl; } if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { if (bwr.write_consumed < mOut.dataSize()) mOut.remove(0, bwr.write_consumed); else { mOut.setDataSize(0); processPostWriteDerefs(); } } if (bwr.read_consumed > 0) { mIn.setDataSize(bwr.read_consumed); mIn.setDataPosition(0); } IF_LOG_COMMANDS() { TextOutput::Bundle _b(alog); alog << "Remaining data size: " << mOut.dataSize() << endl; alog << "Received commands from driver: " << indent; const void* cmds = mIn.data(); const void* end = mIn.data() + mIn.dataSize(); alog << HexDump(cmds, mIn.dataSize()) << endl; while (cmds < end) cmds = printReturnCommand(alog, cmds); alog << dedent; } return NO_ERROR; } return err; } status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) { binder_transaction_data tr; tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */ tr.target.handle = handle; tr.code = code; tr.flags = binderFlags; tr.cookie = 0; tr.sender_pid = 0; tr.sender_euid = 0; const status_t err = data.errorCheck(); if (err == NO_ERROR) { tr.data_size = data.ipcDataSize(); tr.data.ptr.buffer = data.ipcData(); tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); tr.data.ptr.offsets = data.ipcObjects(); } else if (statusBuffer) { tr.flags |= TF_STATUS_CODE; *statusBuffer = err; tr.data_size = sizeof(status_t); tr.data.ptr.buffer = reinterpret_cast(statusBuffer); tr.offsets_size = 0; tr.data.ptr.offsets = 0; } else { return (mLastError = err); } mOut.writeInt32(cmd); mOut.write(&tr, sizeof(tr)); return NO_ERROR; } sp the_context_object; void setTheContextObject(sp obj) { the_context_object = obj; } status_t IPCThreadState::executeCommand(int32_t cmd) { BBinder* obj; RefBase::weakref_type* refs; status_t result = NO_ERROR; switch ((uint32_t)cmd) { case BR_ERROR: result = mIn.readInt32(); break; case BR_OK: break; case BR_ACQUIRE: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); ALOG_ASSERT(refs->refBase() == obj, "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", refs, obj, refs->refBase()); obj->incStrong(mProcess.get()); IF_LOG_REMOTEREFS() { LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); obj->printRefs(); } mOut.writeInt32(BC_ACQUIRE_DONE); mOut.writePointer((uintptr_t)refs); mOut.writePointer((uintptr_t)obj); break; case BR_RELEASE: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); ALOG_ASSERT(refs->refBase() == obj, "BR_RELEASE: object %p does not match cookie %p (expected %p)", refs, obj, refs->refBase()); IF_LOG_REMOTEREFS() { LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); obj->printRefs(); } mPendingStrongDerefs.push(obj); break; case BR_INCREFS: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); refs->incWeak(mProcess.get()); mOut.writeInt32(BC_INCREFS_DONE); mOut.writePointer((uintptr_t)refs); mOut.writePointer((uintptr_t)obj); break; case BR_DECREFS: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); // NOTE: This assertion is not valid, because the object may no // longer exist (thus the (BBinder*)cast above resulting in a different // memory address). //ALOG_ASSERT(refs->refBase() == obj, // "BR_DECREFS: object %p does not match cookie %p (expected %p)", // refs, obj, refs->refBase()); mPendingWeakDerefs.push(refs); break; case BR_ATTEMPT_ACQUIRE: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); { const bool success = refs->attemptIncStrong(mProcess.get()); ALOG_ASSERT(success && refs->refBase() == obj, "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", refs, obj, refs->refBase()); mOut.writeInt32(BC_ACQUIRE_RESULT); mOut.writeInt32((int32_t)success); } break; case BR_TRANSACTION_SEC_CTX: case BR_TRANSACTION: { binder_transaction_data_secctx tr_secctx; binder_transaction_data& tr = tr_secctx.transaction_data; if (cmd == (int) BR_TRANSACTION_SEC_CTX) { result = mIn.read(&tr_secctx, sizeof(tr_secctx)); } else { result = mIn.read(&tr, sizeof(tr)); tr_secctx.secctx = 0; } ALOG_ASSERT(result == NO_ERROR, "Not enough command data for brTRANSACTION"); if (result != NO_ERROR) break; //Record the fact that we're in a binder call. mIPCThreadStateBase->pushCurrentState( IPCThreadStateBase::CallState::BINDER); Parcel buffer; buffer.ipcSetDataReference( reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), freeBuffer, this); const pid_t origPid = mCallingPid; const char* origSid = mCallingSid; const uid_t origUid = mCallingUid; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; const int32_t origWorkSource = mWorkSource; const bool origPropagateWorkSet = mPropagateWorkSource; // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface // is only guaranteed to be called for AIDL-generated stubs so we reset the work source // here to never propagate it. clearCallingWorkSource(); clearPropagateWorkSource(); mCallingPid = tr.sender_pid; mCallingSid = reinterpret_cast(tr_secctx.secctx); mCallingUid = tr.sender_euid; mLastTransactionBinderFlags = tr.flags; // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid, // (mCallingSid ? mCallingSid : ""), mCallingUid); Parcel reply; status_t error; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); alog << "BR_TRANSACTION thr " << (void*)pthread_self() << " / obj " << tr.target.ptr << " / code " << TypeCode(tr.code) << ": " << indent << buffer << dedent << endl << "Data addr = " << reinterpret_cast(tr.data.ptr.buffer) << ", offsets addr=" << reinterpret_cast(tr.data.ptr.offsets) << endl; } if (tr.target.ptr) { // We only have a weak reference on the target object, so we must first try to // safely acquire a strong reference before doing anything else with it. if (reinterpret_cast( tr.target.ptr)->attemptIncStrong(this)) { error = reinterpret_cast(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags); reinterpret_cast(tr.cookie)->decStrong(this); } else { error = UNKNOWN_TRANSACTION; } } else { error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); } mIPCThreadStateBase->popCurrentState(); //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n", // mCallingPid, origPid, (origSid ? origSid : ""), origUid); if ((tr.flags & TF_ONE_WAY) == 0) { LOG_ONEWAY("Sending reply to %d!", mCallingPid); if (error < NO_ERROR) reply.setError(error); sendReply(reply, 0); } else { LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } mCallingPid = origPid; mCallingSid = origSid; mCallingUid = origUid; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; mWorkSource = origWorkSource; mPropagateWorkSource = origPropagateWorkSet; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " << tr.target.ptr << ": " << indent << reply << dedent << endl; } } break; case BR_DEAD_BINDER: { BpBinder *proxy = (BpBinder*)mIn.readPointer(); proxy->sendObituary(); mOut.writeInt32(BC_DEAD_BINDER_DONE); mOut.writePointer((uintptr_t)proxy); } break; case BR_CLEAR_DEATH_NOTIFICATION_DONE: { BpBinder *proxy = (BpBinder*)mIn.readPointer(); proxy->getWeakRefs()->decWeak(proxy); } break; case BR_FINISHED: result = TIMED_OUT; break; case BR_NOOP: break; case BR_SPAWN_LOOPER: mProcess->spawnPooledThread(false); break; default: ALOGE("*** BAD COMMAND %d received from Binder driver\n", cmd); result = UNKNOWN_ERROR; break; } if (result != NO_ERROR) { mLastError = result; } return result; } bool IPCThreadState::isServingCall() const { return mIPCThreadStateBase->getCurrentBinderCallState() == IPCThreadStateBase::CallState::BINDER; } void IPCThreadState::threadDestructor(void *st) { IPCThreadState* const self = static_cast(st); if (self) { self->flushCommands(); #if defined(__ANDROID__) if (self->mProcess->mDriverFD > 0) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } #endif delete self; } } void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t /*dataSize*/, const binder_size_t* /*objects*/, size_t /*objectsSize*/, void* /*cookie*/) { //ALOGI("Freeing parcel %p", &parcel); IF_LOG_COMMANDS() { alog << "Writing BC_FREE_BUFFER for " << data << endl; } ALOG_ASSERT(data != NULL, "Called with NULL data"); if (parcel != nullptr) parcel->closeFileDescriptors(); IPCThreadState* state = self(); state->mOut.writeInt32(BC_FREE_BUFFER); state->mOut.writePointer((uintptr_t)data); } }; // namespace android libs/binder/IPermissionController.cpp0100644 0000000 0000000 00000014136 13756501735 017033 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "PermissionController" #include #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpPermissionController : public BpInterface { public: explicit BpPermissionController(const sp& impl) : BpInterface(impl) { } virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) { Parcel data, reply; data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); data.writeString16(permission); data.writeInt32(pid); data.writeInt32(uid); remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return 0; return reply.readInt32() != 0; } virtual int32_t noteOp(const String16& op, int32_t uid, const String16& packageName) { Parcel data, reply; data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); data.writeString16(op); data.writeInt32(uid); data.writeString16(packageName); remote()->transact(NOTE_OP_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return 2; // MODE_ERRORED return reply.readInt32(); } virtual void getPackagesForUid(const uid_t uid, Vector& packages) { Parcel data, reply; data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); data.writeInt32(uid); remote()->transact(GET_PACKAGES_FOR_UID_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) { return; } const int32_t size = reply.readInt32(); if (size <= 0) { return; } for (int i = 0; i < size; i++) { packages.push(reply.readString16()); } } virtual bool isRuntimePermission(const String16& permission) { Parcel data, reply; data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); data.writeString16(permission); remote()->transact(IS_RUNTIME_PERMISSION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return false; return reply.readInt32() != 0; } virtual int getPackageUid(const String16& package, int flags) { Parcel data, reply; data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); data.writeString16(package); data.writeInt32(flags); remote()->transact(GET_PACKAGE_UID_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return false; return reply.readInt32(); } }; IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); // ---------------------------------------------------------------------- // NOLINTNEXTLINE(google-default-arguments) status_t BnPermissionController::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case CHECK_PERMISSION_TRANSACTION: { CHECK_INTERFACE(IPermissionController, data, reply); String16 permission = data.readString16(); int32_t pid = data.readInt32(); int32_t uid = data.readInt32(); bool res = checkPermission(permission, pid, uid); reply->writeNoException(); reply->writeInt32(res ? 1 : 0); return NO_ERROR; } break; case NOTE_OP_TRANSACTION: { CHECK_INTERFACE(IPermissionController, data, reply); String16 op = data.readString16(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); int32_t res = noteOp(op, uid, packageName); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; case GET_PACKAGES_FOR_UID_TRANSACTION: { CHECK_INTERFACE(IPermissionController, data, reply); int32_t uid = data.readInt32(); Vector packages; getPackagesForUid(uid, packages); reply->writeNoException(); size_t size = packages.size(); reply->writeInt32(size); for (size_t i = 0; i < size; i++) { reply->writeString16(packages[i]); } return NO_ERROR; } break; case IS_RUNTIME_PERMISSION_TRANSACTION: { CHECK_INTERFACE(IPermissionController, data, reply); String16 permission = data.readString16(); const bool res = isRuntimePermission(permission); reply->writeNoException(); reply->writeInt32(res ? 1 : 0); return NO_ERROR; } break; case GET_PACKAGE_UID_TRANSACTION: { CHECK_INTERFACE(IPermissionController, data, reply); String16 package = data.readString16(); int flags = data.readInt32(); const int uid = getPackageUid(package, flags); reply->writeNoException(); reply->writeInt32(uid); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IProcessInfoService.cpp0100644 0000000 0000000 00000006526 13756501735 016416 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpProcessInfoService : public BpInterface { public: explicit BpProcessInfoService(const sp& impl) : BpInterface(impl) {} virtual status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states) { Parcel data, reply; data.writeInterfaceToken(IProcessInfoService::getInterfaceDescriptor()); data.writeInt32Array(length, pids); data.writeInt32(length); // write length of output array, used by java AIDL stubs status_t err = remote()->transact(GET_PROCESS_STATES_FROM_PIDS, data, &reply); if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { return err; } int32_t replyLen = reply.readInt32(); if (static_cast(replyLen) != length) { return NOT_ENOUGH_DATA; } if (replyLen > 0 && (err = reply.read(states, length * sizeof(*states))) != NO_ERROR) { return err; } return reply.readInt32(); } virtual status_t getProcessStatesAndOomScoresFromPids(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states, /*out*/ int32_t* scores) { Parcel data, reply; data.writeInterfaceToken(IProcessInfoService::getInterfaceDescriptor()); data.writeInt32Array(length, pids); // write length of output arrays, used by java AIDL stubs data.writeInt32(length); data.writeInt32(length); status_t err = remote()->transact( GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS, data, &reply); if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { return err; } int32_t replyLen = reply.readInt32(); if (static_cast(replyLen) != length) { return NOT_ENOUGH_DATA; } if (replyLen > 0 && (err = reply.read( states, length * sizeof(*states))) != NO_ERROR) { return err; } replyLen = reply.readInt32(); if (static_cast(replyLen) != length) { return NOT_ENOUGH_DATA; } if (replyLen > 0 && (err = reply.read( scores, length * sizeof(*scores))) != NO_ERROR) { return err; } return reply.readInt32(); } }; IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService"); // ---------------------------------------------------------------------- }; // namespace android libs/binder/IResultReceiver.cpp0100644 0000000 0000000 00000004067 13756501735 015604 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "ResultReceiver" #include #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpResultReceiver : public BpInterface { public: explicit BpResultReceiver(const sp& impl) : BpInterface(impl) { } virtual void send(int32_t resultCode) { Parcel data; data.writeInterfaceToken(IResultReceiver::getInterfaceDescriptor()); data.writeInt32(resultCode); remote()->transact(OP_SEND, data, nullptr, IBinder::FLAG_ONEWAY); } }; IMPLEMENT_META_INTERFACE(ResultReceiver, "com.android.internal.os.IResultReceiver"); // ---------------------------------------------------------------------- // NOLINTNEXTLINE(google-default-arguments) status_t BnResultReceiver::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case OP_SEND: { CHECK_INTERFACE(IResultReceiver, data, reply); int32_t resultCode = data.readInt32(); send(resultCode); if (reply != nullptr) { reply->writeNoException(); } return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IServiceManager.cpp0100644 0000000 0000000 00000016222 13756501735 015530 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "ServiceManager" #include #include #include #ifndef __ANDROID_VNDK__ #include #endif #include #include #include #include #include #include namespace android { sp defaultServiceManager() { if (gDefaultServiceManager != nullptr) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); while (gDefaultServiceManager == nullptr) { gDefaultServiceManager = interface_cast( ProcessState::self()->getContextObject(nullptr)); if (gDefaultServiceManager == nullptr) sleep(1); } } return gDefaultServiceManager; } #ifndef __ANDROID_VNDK__ // IPermissionController is not accessible to vendors bool checkCallingPermission(const String16& permission) { return checkCallingPermission(permission, nullptr, nullptr); } static String16 _permission("permission"); bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) { IPCThreadState* ipcState = IPCThreadState::self(); pid_t pid = ipcState->getCallingPid(); uid_t uid = ipcState->getCallingUid(); if (outPid) *outPid = pid; if (outUid) *outUid = uid; return checkPermission(permission, pid, uid); } bool checkPermission(const String16& permission, pid_t pid, uid_t uid) { sp pc; gDefaultServiceManagerLock.lock(); pc = gPermissionController; gDefaultServiceManagerLock.unlock(); int64_t startTime = 0; while (true) { if (pc != nullptr) { bool res = pc->checkPermission(permission, pid, uid); if (res) { if (startTime != 0) { ALOGI("Check passed after %d seconds for %s from uid=%d pid=%d", (int)((uptimeMillis()-startTime)/1000), String8(permission).string(), uid, pid); } return res; } // Is this a permission failure, or did the controller go away? if (IInterface::asBinder(pc)->isBinderAlive()) { ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).string(), uid, pid); return false; } // Object is dead! gDefaultServiceManagerLock.lock(); if (gPermissionController == pc) { gPermissionController = nullptr; } gDefaultServiceManagerLock.unlock(); } // Need to retrieve the permission controller. sp binder = defaultServiceManager()->checkService(_permission); if (binder == nullptr) { // Wait for the permission controller to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting to check permission %s from uid=%d pid=%d", String8(permission).string(), uid, pid); } sleep(1); } else { pc = interface_cast(binder); // Install the new permission controller, and try again. gDefaultServiceManagerLock.lock(); gPermissionController = pc; gDefaultServiceManagerLock.unlock(); } } } #endif //__ANDROID_VNDK__ // ---------------------------------------------------------------------- class BpServiceManager : public BpInterface { public: explicit BpServiceManager(const sp& impl) : BpInterface(impl) { } virtual sp getService(const String16& name) const { sp svc = checkService(name); if (svc != nullptr) return svc; const bool isVendorService = strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0; const long timeout = uptimeMillis() + 5000; if (!gSystemBootCompleted && !isVendorService) { // Vendor code can't access system properties char bootCompleted[PROPERTY_VALUE_MAX]; property_get("sys.boot_completed", bootCompleted, "0"); gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false; } // retry interval in millisecond; note that vendor services stay at 100ms const long sleepTime = gSystemBootCompleted ? 1000 : 100; int n = 0; while (uptimeMillis() < timeout) { n++; ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), ProcessState::self()->getDriverName().c_str()); usleep(1000*sleepTime); sp svc = checkService(name); if (svc != nullptr) return svc; } ALOGW("Service %s didn't start. Returning NULL", String8(name).string()); return nullptr; } virtual sp checkService( const String16& name) const { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); return reply.readStrongBinder(); } virtual status_t addService(const String16& name, const sp& service, bool allowIsolated, int dumpsysPriority) { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); data.writeStrongBinder(service); data.writeInt32(allowIsolated ? 1 : 0); data.writeInt32(dumpsysPriority); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); return err == NO_ERROR ? reply.readExceptionCode() : err; } virtual Vector listServices(int dumpsysPriority) { Vector res; int n = 0; for (;;) { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeInt32(n++); data.writeInt32(dumpsysPriority); status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); if (err != NO_ERROR) break; res.add(reply.readString16()); } return res; } }; IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); }; // namespace android libs/binder/IShellCallback.cpp0100644 0000000 0000000 00000005444 13756501735 015325 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "ShellCallback" #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------- class BpShellCallback : public BpInterface { public: explicit BpShellCallback(const sp& impl) : BpInterface(impl) { } virtual int openFile(const String16& path, const String16& seLinuxContext, const String16& mode) { Parcel data, reply; data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor()); data.writeString16(path); data.writeString16(seLinuxContext); data.writeString16(mode); remote()->transact(OP_OPEN_OUTPUT_FILE, data, &reply, 0); reply.readExceptionCode(); int fd = reply.readParcelFileDescriptor(); return fd >= 0 ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd; } }; IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback"); // ---------------------------------------------------------------------- // NOLINTNEXTLINE(google-default-arguments) status_t BnShellCallback::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case OP_OPEN_OUTPUT_FILE: { CHECK_INTERFACE(IShellCallback, data, reply); String16 path(data.readString16()); String16 seLinuxContext(data.readString16()); String16 mode(data.readString16()); int fd = openFile(path, seLinuxContext, mode); if (reply != nullptr) { reply->writeNoException(); if (fd >= 0) { reply->writeInt32(1); reply->writeParcelFileDescriptor(fd, true); } else { reply->writeInt32(0); } } else if (fd >= 0) { close(fd); } return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IUidObserver.cpp0100644 0000000 0000000 00000007533 13756501735 015073 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #include #include namespace android { // ------------------------------------------------------------------------------------ class BpUidObserver : public BpInterface { public: explicit BpUidObserver(const sp& impl) : BpInterface(impl) { } virtual void onUidGone(uid_t uid, bool disabled) { Parcel data, reply; data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor()); data.writeInt32((int32_t) uid); data.writeInt32(disabled ? 1 : 0); remote()->transact(ON_UID_GONE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void onUidActive(uid_t uid) { Parcel data, reply; data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor()); data.writeInt32((int32_t) uid); remote()->transact(ON_UID_ACTIVE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void onUidIdle(uid_t uid, bool disabled) { Parcel data, reply; data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor()); data.writeInt32((int32_t) uid); data.writeInt32(disabled ? 1 : 0); remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) { Parcel data, reply; data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor()); data.writeInt32((int32_t) uid); data.writeInt32(procState); data.writeInt64(procStateSeq); remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } }; // ---------------------------------------------------------------------- IMPLEMENT_META_INTERFACE(UidObserver, "android.app.IUidObserver"); // ---------------------------------------------------------------------- status_t BnUidObserver::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case ON_UID_GONE_TRANSACTION: { CHECK_INTERFACE(IUidObserver, data, reply); uid_t uid = data.readInt32(); bool disabled = data.readInt32() == 1; onUidGone(uid, disabled); return NO_ERROR; } break; case ON_UID_ACTIVE_TRANSACTION: { CHECK_INTERFACE(IUidObserver, data, reply); uid_t uid = data.readInt32(); onUidActive(uid); return NO_ERROR; } break; case ON_UID_IDLE_TRANSACTION: { CHECK_INTERFACE(IUidObserver, data, reply); uid_t uid = data.readInt32(); bool disabled = data.readInt32() == 1; onUidIdle(uid, disabled); return NO_ERROR; } break; case ON_UID_STATE_CHANGED_TRANSACTION: { CHECK_INTERFACE(IUidObserver, data, reply); uid_t uid = data.readInt32(); int32_t procState = data.readInt32(); int64_t procStateSeq = data.readInt64(); onUidStateChanged(uid, procState, procStateSeq); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IpPrefix.cpp0100644 0000000 0000000 00000010762 13756501735 014255 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "IpPrefix" #include #include #include #include #include #include using android::BAD_TYPE; using android::BAD_VALUE; using android::NO_ERROR; using android::Parcel; using android::status_t; using android::UNEXPECTED_NULL; using namespace ::android::binder; namespace android { namespace net { #define RETURN_IF_FAILED(calledOnce) \ { \ status_t returnStatus = calledOnce; \ if (returnStatus) { \ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ return returnStatus; \ } \ } status_t IpPrefix::writeToParcel(Parcel* parcel) const { /* * Keep implementation in sync with writeToParcel() in * frameworks/base/core/java/android/net/IpPrefix.java. */ std::vector byte_vector; if (mIsIpv6) { const uint8_t* bytes = reinterpret_cast(&mUnion.mIn6Addr); byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr)); } else { const uint8_t* bytes = reinterpret_cast(&mUnion.mInAddr); byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr)); } RETURN_IF_FAILED(parcel->writeByteVector(byte_vector)); RETURN_IF_FAILED(parcel->writeInt32(static_cast(mPrefixLength))); return NO_ERROR; } status_t IpPrefix::readFromParcel(const Parcel* parcel) { /* * Keep implementation in sync with readFromParcel() in * frameworks/base/core/java/android/net/IpPrefix.java. */ std::vector byte_vector; RETURN_IF_FAILED(parcel->readByteVector(&byte_vector)); RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength)); if (byte_vector.size() == 16) { mIsIpv6 = true; memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr)); } else if (byte_vector.size() == 4) { mIsIpv6 = false; memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr)); } else { ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ return BAD_VALUE; } return NO_ERROR; } const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const { return mUnion.mIn6Addr; } const struct in_addr& IpPrefix::getAddressAsInAddr() const { return mUnion.mInAddr; } bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const { if (isIpv6()) { *addr = mUnion.mIn6Addr; return true; } return false; } bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const { if (isIpv4()) { *addr = mUnion.mInAddr; return true; } return false; } bool IpPrefix::isIpv6() const { return mIsIpv6; } bool IpPrefix::isIpv4() const { return !mIsIpv6; } int32_t IpPrefix::getPrefixLength() const { return mPrefixLength; } void IpPrefix::setAddress(const struct in6_addr& addr) { mUnion.mIn6Addr = addr; mIsIpv6 = true; } void IpPrefix::setAddress(const struct in_addr& addr) { mUnion.mInAddr = addr; mIsIpv6 = false; } void IpPrefix::setPrefixLength(int32_t prefix) { mPrefixLength = prefix; } bool operator==(const IpPrefix& lhs, const IpPrefix& rhs) { if (lhs.mIsIpv6 != rhs.mIsIpv6) { return false; } if (lhs.mPrefixLength != rhs.mPrefixLength) { return false; } if (lhs.mIsIpv6) { return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr)); } return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr)); } } // namespace net } // namespace android libs/binder/MemoryBase.cpp0100644 0000000 0000000 00000002325 13756501735 014566 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #include #include #include namespace android { // --------------------------------------------------------------------------- MemoryBase::MemoryBase(const sp& heap, ssize_t offset, size_t size) : mSize(size), mOffset(offset), mHeap(heap) { } sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = mOffset; if (size) *size = mSize; return mHeap; } MemoryBase::~MemoryBase() { } // --------------------------------------------------------------------------- }; // namespace android libs/binder/MemoryDealer.cpp0100644 0000000 0000000 00000033127 13756501735 015114 0ustar000000000 0000000 /* * Copyright (C) 2007 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. */ #define LOG_TAG "MemoryDealer" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- /* * A simple templatized doubly linked-list implementation */ template class LinkedList { NODE* mFirst; NODE* mLast; public: LinkedList() : mFirst(nullptr), mLast(nullptr) { } bool isEmpty() const { return mFirst == nullptr; } NODE const* head() const { return mFirst; } NODE* head() { return mFirst; } NODE const* tail() const { return mLast; } NODE* tail() { return mLast; } void insertAfter(NODE* node, NODE* newNode) { newNode->prev = node; newNode->next = node->next; if (node->next == nullptr) mLast = newNode; else node->next->prev = newNode; node->next = newNode; } void insertBefore(NODE* node, NODE* newNode) { newNode->prev = node->prev; newNode->next = node; if (node->prev == nullptr) mFirst = newNode; else node->prev->next = newNode; node->prev = newNode; } void insertHead(NODE* newNode) { if (mFirst == nullptr) { mFirst = mLast = newNode; newNode->prev = newNode->next = nullptr; } else { newNode->prev = nullptr; newNode->next = mFirst; mFirst->prev = newNode; mFirst = newNode; } } void insertTail(NODE* newNode) { if (mLast == 0) { insertHead(newNode); } else { newNode->prev = mLast; newNode->next = 0; mLast->next = newNode; mLast = newNode; } } NODE* remove(NODE* node) { if (node->prev == nullptr) mFirst = node->next; else node->prev->next = node->next; if (node->next == nullptr) mLast = node->prev; else node->next->prev = node->prev; return node; } }; // ---------------------------------------------------------------------------- class Allocation : public MemoryBase { public: Allocation(const sp& dealer, const sp& heap, ssize_t offset, size_t size); virtual ~Allocation(); private: sp mDealer; }; // ---------------------------------------------------------------------------- class SimpleBestFitAllocator { enum { PAGE_ALIGNED = 0x00000001 }; public: explicit SimpleBestFitAllocator(size_t size); ~SimpleBestFitAllocator(); size_t allocate(size_t size, uint32_t flags = 0); status_t deallocate(size_t offset); size_t size() const; void dump(const char* what) const; void dump(String8& res, const char* what) const; static size_t getAllocationAlignment() { return kMemoryAlign; } private: struct chunk_t { chunk_t(size_t start, size_t size) : start(start), size(size), free(1), prev(nullptr), next(nullptr) { } size_t start; size_t size : 28; int free : 4; mutable chunk_t* prev; mutable chunk_t* next; }; ssize_t alloc(size_t size, uint32_t flags); chunk_t* dealloc(size_t start); void dump_l(const char* what) const; void dump_l(String8& res, const char* what) const; static const int kMemoryAlign; mutable Mutex mLock; LinkedList mList; size_t mHeapSize; }; // ---------------------------------------------------------------------------- Allocation::Allocation( const sp& dealer, const sp& heap, ssize_t offset, size_t size) : MemoryBase(heap, offset, size), mDealer(dealer) { #ifndef NDEBUG void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); memset(start_ptr, 0xda, size); #endif } Allocation::~Allocation() { size_t freedOffset = getOffset(); size_t freedSize = getSize(); if (freedSize) { /* NOTE: it's VERY important to not free allocations of size 0 because * they're special as they don't have any record in the allocator * and could alias some real allocation (their offset is zero). */ // keep the size to unmap in excess size_t pagesize = getpagesize(); size_t start = freedOffset; size_t end = start + freedSize; start &= ~(pagesize-1); end = (end + pagesize-1) & ~(pagesize-1); // give back to the kernel the pages we don't need size_t free_start = freedOffset; size_t free_end = free_start + freedSize; if (start < free_start) start = free_start; if (end > free_end) end = free_end; start = (start + pagesize-1) & ~(pagesize-1); end &= ~(pagesize-1); if (start < end) { void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); size_t size = end-start; #ifndef NDEBUG memset(start_ptr, 0xdf, size); #endif // MADV_REMOVE is not defined on Dapper based Goobuntu #ifdef MADV_REMOVE if (size) { int err = madvise(start_ptr, size, MADV_REMOVE); ALOGW_IF(err, "madvise(%p, %zu, MADV_REMOVE) returned %s", start_ptr, size, err<0 ? strerror(errno) : "Ok"); } #endif } // This should be done after madvise(MADV_REMOVE), otherwise madvise() // might kick out the memory region that's allocated and/or written // right after the deallocation. mDealer->deallocate(freedOffset); } } // ---------------------------------------------------------------------------- MemoryDealer::MemoryDealer(size_t size, const char* name, uint32_t flags) : mHeap(new MemoryHeapBase(size, flags, name)), mAllocator(new SimpleBestFitAllocator(size)) { } MemoryDealer::~MemoryDealer() { delete mAllocator; } sp MemoryDealer::allocate(size_t size) { sp memory; const ssize_t offset = allocator()->allocate(size); if (offset >= 0) { memory = new Allocation(this, heap(), offset, size); } return memory; } void MemoryDealer::deallocate(size_t offset) { allocator()->deallocate(offset); } void MemoryDealer::dump(const char* what) const { allocator()->dump(what); } const sp& MemoryDealer::heap() const { return mHeap; } SimpleBestFitAllocator* MemoryDealer::allocator() const { return mAllocator; } // static size_t MemoryDealer::getAllocationAlignment() { return SimpleBestFitAllocator::getAllocationAlignment(); } // ---------------------------------------------------------------------------- // align all the memory blocks on a cache-line boundary const int SimpleBestFitAllocator::kMemoryAlign = 32; SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) { size_t pagesize = getpagesize(); mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); mList.insertHead(node); } SimpleBestFitAllocator::~SimpleBestFitAllocator() { while(!mList.isEmpty()) { chunk_t* removed = mList.remove(mList.head()); #ifdef __clang_analyzer__ // Clang static analyzer gets confused in this loop // and generates a false positive warning about accessing // memory that is already freed. // Add an "assert" to avoid the confusion. LOG_ALWAYS_FATAL_IF(mList.head() == removed); #endif delete removed; } } size_t SimpleBestFitAllocator::size() const { return mHeapSize; } size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) { Mutex::Autolock _l(mLock); ssize_t offset = alloc(size, flags); return offset; } status_t SimpleBestFitAllocator::deallocate(size_t offset) { Mutex::Autolock _l(mLock); chunk_t const * const freed = dealloc(offset); if (freed) { return NO_ERROR; } return NAME_NOT_FOUND; } ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) { if (size == 0) { return 0; } size = (size + kMemoryAlign-1) / kMemoryAlign; chunk_t* free_chunk = nullptr; chunk_t* cur = mList.head(); size_t pagesize = getpagesize(); while (cur) { int extra = 0; if (flags & PAGE_ALIGNED) extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; // best fit if (cur->free && (cur->size >= (size+extra))) { if ((!free_chunk) || (cur->size < free_chunk->size)) { free_chunk = cur; } if (cur->size == size) { break; } } cur = cur->next; } if (free_chunk) { const size_t free_size = free_chunk->size; free_chunk->free = 0; free_chunk->size = size; if (free_size > size) { int extra = 0; if (flags & PAGE_ALIGNED) extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; if (extra) { chunk_t* split = new chunk_t(free_chunk->start, extra); free_chunk->start += extra; mList.insertBefore(free_chunk, split); } ALOGE_IF((flags&PAGE_ALIGNED) && ((free_chunk->start*kMemoryAlign)&(pagesize-1)), "PAGE_ALIGNED requested, but page is not aligned!!!"); const ssize_t tail_free = free_size - (size+extra); if (tail_free > 0) { chunk_t* split = new chunk_t( free_chunk->start + free_chunk->size, tail_free); mList.insertAfter(free_chunk, split); } } return (free_chunk->start)*kMemoryAlign; } return NO_MEMORY; } SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) { start = start / kMemoryAlign; chunk_t* cur = mList.head(); while (cur) { if (cur->start == start) { LOG_FATAL_IF(cur->free, "block at offset 0x%08lX of size 0x%08lX already freed", cur->start*kMemoryAlign, cur->size*kMemoryAlign); // merge freed blocks together chunk_t* freed = cur; cur->free = 1; do { chunk_t* const p = cur->prev; chunk_t* const n = cur->next; if (p && (p->free || !cur->size)) { freed = p; p->size += cur->size; mList.remove(cur); delete cur; } cur = n; } while (cur && cur->free); #ifndef NDEBUG if (!freed->free) { dump_l("dealloc (!freed->free)"); } #endif LOG_FATAL_IF(!freed->free, "freed block at offset 0x%08lX of size 0x%08lX is not free!", freed->start * kMemoryAlign, freed->size * kMemoryAlign); return freed; } cur = cur->next; } return nullptr; } void SimpleBestFitAllocator::dump(const char* what) const { Mutex::Autolock _l(mLock); dump_l(what); } void SimpleBestFitAllocator::dump_l(const char* what) const { String8 result; dump_l(result, what); ALOGD("%s", result.string()); } void SimpleBestFitAllocator::dump(String8& result, const char* what) const { Mutex::Autolock _l(mLock); dump_l(result, what); } void SimpleBestFitAllocator::dump_l(String8& result, const char* what) const { size_t size = 0; int32_t i = 0; chunk_t const* cur = mList.head(); const size_t SIZE = 256; char buffer[SIZE]; snprintf(buffer, SIZE, " %s (%p, size=%u)\n", what, this, (unsigned int)mHeapSize); result.append(buffer); while (cur) { const char* errs[] = {"", "| link bogus NP", "| link bogus PN", "| link bogus NP+PN" }; int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; snprintf(buffer, SIZE, " %3u: %p | 0x%08X | 0x%08X | %s %s\n", i, cur, int(cur->start*kMemoryAlign), int(cur->size*kMemoryAlign), int(cur->free) ? "F" : "A", errs[np|pn]); result.append(buffer); if (!cur->free) size += cur->size*kMemoryAlign; i++; cur = cur->next; } snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); result.append(buffer); } }; // namespace android libs/binder/MemoryHeapBase.cpp0100644 0000000 0000000 00000012022 13756501735 015357 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #define LOG_TAG "MemoryHeapBase" #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- MemoryHeapBase::MemoryHeapBase() : mFD(-1), mSize(0), mBase(MAP_FAILED), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { } MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size); ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); if (fd >= 0) { if (mapfd(fd, size) == NO_ERROR) { if (flags & READ_ONLY) { ashmem_set_prot_region(fd, PROT_READ); } } } } MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { int open_flags = O_RDWR; if (flags & NO_CACHING) open_flags |= O_SYNC; int fd = open(device, open_flags); ALOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); if (fd >= 0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); if (mapfd(fd, size) == NO_ERROR) { mDevice = device; } } } MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset); } status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device) { if (mFD != -1) { return INVALID_OPERATION; } mFD = fd; mBase = base; mSize = size; mFlags = flags; mDevice = device; return NO_ERROR; } status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset) { if (size == 0) { // try to figure out the size automatically struct stat sb; if (fstat(fd, &sb) == 0) { size = (size_t)sb.st_size; // sb.st_size is off_t which on ILP32 may be 64 bits while size_t is 32 bits. if ((off_t)size != sb.st_size) { ALOGE("%s: size of file %lld cannot fit in memory", __func__, (long long)sb.st_size); return INVALID_OPERATION; } } // if it didn't work, let mmap() fail. } if ((mFlags & DONT_MAP_LOCALLY) == 0) { void* base = (uint8_t*)mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (base == MAP_FAILED) { ALOGE("mmap(fd=%d, size=%zu) failed (%s)", fd, size, strerror(errno)); close(fd); return -errno; } //ALOGD("mmap(fd=%d, base=%p, size=%zu)", fd, base, size); mBase = base; mNeedUnmap = true; } else { mBase = nullptr; // not MAP_FAILED mNeedUnmap = false; } mFD = fd; mSize = size; mOffset = offset; return NO_ERROR; } MemoryHeapBase::~MemoryHeapBase() { dispose(); } void MemoryHeapBase::dispose() { int fd = android_atomic_or(-1, &mFD); if (fd >= 0) { if (mNeedUnmap) { //ALOGD("munmap(fd=%d, base=%p, size=%zu)", fd, mBase, mSize); munmap(mBase, mSize); } mBase = nullptr; mSize = 0; close(fd); } } int MemoryHeapBase::getHeapID() const { return mFD; } void* MemoryHeapBase::getBase() const { return mBase; } size_t MemoryHeapBase::getSize() const { return mSize; } uint32_t MemoryHeapBase::getFlags() const { return mFlags; } const char* MemoryHeapBase::getDevice() const { return mDevice; } off_t MemoryHeapBase::getOffset() const { return mOffset; } // --------------------------------------------------------------------------- }; // namespace android libs/binder/OWNERS0100644 0000000 0000000 00000000131 13756501735 012750 0ustar000000000 0000000 arve@google.com ctate@google.com hackbod@google.com maco@google.com smoreland@google.com libs/binder/Parcel.cpp0100644 0000000 0000000 00000243407 13756501735 013741 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "Parcel" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef INT32_MAX #define INT32_MAX ((int32_t)(2147483647)) #endif #define LOG_REFS(...) //#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOG_ALLOC(...) //#define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) // --------------------------------------------------------------------------- // This macro should never be used at runtime, as a too large value // of s could cause an integer overflow. Instead, you should always // use the wrapper function pad_size() #define PAD_SIZE_UNSAFE(s) (((s)+3)&~3) static size_t pad_size(size_t s) { if (s > (SIZE_T_MAX - 3)) { abort(); } return PAD_SIZE_UNSAFE(s); } // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER #define STRICT_MODE_PENALTY_GATHER (1 << 31) namespace android { static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER; static size_t gParcelGlobalAllocSize = 0; static size_t gParcelGlobalAllocCount = 0; static size_t gMaxFds = 0; // Maximum size of a blob to transfer in-place. static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; enum { BLOB_INPLACE = 0, BLOB_ASHMEM_IMMUTABLE = 1, BLOB_ASHMEM_MUTABLE = 2, }; void acquire_object(const sp& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); reinterpret_cast(obj.cookie)->incStrong(who); } return; case BINDER_TYPE_WEAK_BINDER: if (obj.binder) reinterpret_cast(obj.binder)->incWeak(who); return; case BINDER_TYPE_HANDLE: { const sp b = proc->getStrongProxyForHandle(obj.handle); if (b != nullptr) { LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); b->incStrong(who); } return; } case BINDER_TYPE_WEAK_HANDLE: { const wp b = proc->getWeakProxyForHandle(obj.handle); if (b != nullptr) b.get_refs()->incWeak(who); return; } case BINDER_TYPE_FD: { if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { // If we own an ashmem fd, keep track of how much memory it refers to. int size = ashmem_get_size_region(obj.handle); if (size > 0) { *outAshmemSize += size; } } return; } } ALOGD("Invalid object type 0x%08x", obj.hdr.type); } void acquire_object(const sp& proc, const flat_binder_object& obj, const void* who) { acquire_object(proc, obj, who, nullptr); } static void release_object(const sp& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); reinterpret_cast(obj.cookie)->decStrong(who); } return; case BINDER_TYPE_WEAK_BINDER: if (obj.binder) reinterpret_cast(obj.binder)->decWeak(who); return; case BINDER_TYPE_HANDLE: { const sp b = proc->getStrongProxyForHandle(obj.handle); if (b != nullptr) { LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); b->decStrong(who); } return; } case BINDER_TYPE_WEAK_HANDLE: { const wp b = proc->getWeakProxyForHandle(obj.handle); if (b != nullptr) b.get_refs()->decWeak(who); return; } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { int size = ashmem_get_size_region(obj.handle); if (size > 0) { // ashmem size might have changed since last time it was accounted for, e.g. // in acquire_object(). Value of *outAshmemSize is not critical since we are // releasing the object anyway. Check for integer overflow condition. *outAshmemSize -= std::min(*outAshmemSize, static_cast(size)); } } close(obj.handle); } return; } } ALOGE("Invalid object type 0x%08x", obj.hdr.type); } void release_object(const sp& proc, const flat_binder_object& obj, const void* who) { release_object(proc, obj, who, nullptr); } inline static status_t finish_flatten_binder( const sp& /*binder*/, const flat_binder_object& flat, Parcel* out) { return out->writeObject(flat, false); } status_t flatten_binder(const sp& /*proc*/, const sp& binder, Parcel* out) { flat_binder_object obj; if (IPCThreadState::self()->backgroundSchedulingDisabled()) { /* minimum priority for all nodes is nice 0 */ obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; } else { /* minimum priority for all nodes is MAX_NICE(19) */ obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS; } if (binder != nullptr) { BBinder *local = binder->localBinder(); if (!local) { BpBinder *proxy = binder->remoteBinder(); if (proxy == nullptr) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; obj.hdr.type = BINDER_TYPE_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { if (local->isRequestingSid()) { obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX; } obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = reinterpret_cast(local->getWeakRefs()); obj.cookie = reinterpret_cast(local); } } else { obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; } return finish_flatten_binder(binder, obj, out); } status_t flatten_binder(const sp& /*proc*/, const wp& binder, Parcel* out) { flat_binder_object obj; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; if (binder != nullptr) { sp real = binder.promote(); if (real != nullptr) { IBinder *local = real->localBinder(); if (!local) { BpBinder *proxy = real->remoteBinder(); if (proxy == nullptr) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; obj.hdr.type = BINDER_TYPE_WEAK_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { obj.hdr.type = BINDER_TYPE_WEAK_BINDER; obj.binder = reinterpret_cast(binder.get_refs()); obj.cookie = reinterpret_cast(binder.unsafe_get()); } return finish_flatten_binder(real, obj, out); } // XXX How to deal? In order to flatten the given binder, // we need to probe it for information, which requires a primary // reference... but we don't have one. // // The OpenBinder implementation uses a dynamic_cast<> here, // but we can't do that with the different reference counting // implementation we are using. ALOGE("Unable to unflatten Binder weak reference!"); obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(nullptr, obj, out); } else { obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(nullptr, obj, out); } } inline static status_t finish_unflatten_binder( BpBinder* /*proxy*/, const flat_binder_object& /*flat*/, const Parcel& /*in*/) { return NO_ERROR; } status_t unflatten_binder(const sp& proc, const Parcel& in, sp* out) { const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast(flat->cookie); return finish_unflatten_binder(nullptr, *flat, in); case BINDER_TYPE_HANDLE: *out = proc->getStrongProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast(out->get()), *flat, in); } } return BAD_TYPE; } status_t unflatten_binder(const sp& proc, const Parcel& in, wp* out) { const flat_binder_object* flat = in.readObject(false); if (flat) { switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast(flat->cookie); return finish_unflatten_binder(nullptr, *flat, in); case BINDER_TYPE_WEAK_BINDER: if (flat->binder != 0) { out->set_object_and_refs( reinterpret_cast(flat->cookie), reinterpret_cast(flat->binder)); } else { *out = nullptr; } return finish_unflatten_binder(nullptr, *flat, in); case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: *out = proc->getWeakProxyForHandle(flat->handle); return finish_unflatten_binder( static_cast(out->unsafe_get()), *flat, in); } } return BAD_TYPE; } // --------------------------------------------------------------------------- Parcel::Parcel() { LOG_ALLOC("Parcel %p: constructing", this); initState(); } Parcel::~Parcel() { freeDataNoInit(); LOG_ALLOC("Parcel %p: destroyed", this); } size_t Parcel::getGlobalAllocSize() { pthread_mutex_lock(&gParcelGlobalAllocSizeLock); size_t size = gParcelGlobalAllocSize; pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); return size; } size_t Parcel::getGlobalAllocCount() { pthread_mutex_lock(&gParcelGlobalAllocSizeLock); size_t count = gParcelGlobalAllocCount; pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); return count; } const uint8_t* Parcel::data() const { return mData; } size_t Parcel::dataSize() const { return (mDataSize > mDataPos ? mDataSize : mDataPos); } size_t Parcel::dataAvail() const { size_t result = dataSize() - dataPosition(); if (result > INT32_MAX) { abort(); } return result; } size_t Parcel::dataPosition() const { return mDataPos; } size_t Parcel::dataCapacity() const { return mDataCapacity; } status_t Parcel::setDataSize(size_t size) { if (size > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } status_t err; err = continueWrite(size); if (err == NO_ERROR) { mDataSize = size; ALOGV("setDataSize Setting data size of %p to %zu", this, mDataSize); } return err; } void Parcel::setDataPosition(size_t pos) const { if (pos > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. abort(); } mDataPos = pos; mNextObjectHint = 0; mObjectsSorted = false; } status_t Parcel::setDataCapacity(size_t size) { if (size > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } if (size > mDataCapacity) return continueWrite(size); return NO_ERROR; } status_t Parcel::setData(const uint8_t* buffer, size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } status_t err = restartWrite(len); if (err == NO_ERROR) { memcpy(const_cast(data()), buffer, len); mDataSize = len; mFdsKnown = false; } return err; } status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) { status_t err; const uint8_t *data = parcel->mData; const binder_size_t *objects = parcel->mObjects; size_t size = parcel->mObjectsSize; int startPos = mDataPos; int firstIndex = -1, lastIndex = -2; if (len == 0) { return NO_ERROR; } if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } // range checks against the source parcel size if ((offset > parcel->mDataSize) || (len > parcel->mDataSize) || (offset + len > parcel->mDataSize)) { return BAD_VALUE; } // Count objects in range for (int i = 0; i < (int) size; i++) { size_t off = objects[i]; if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) { if (firstIndex == -1) { firstIndex = i; } lastIndex = i; } } int numObjects = lastIndex - firstIndex + 1; if ((mDataSize+len) > mDataCapacity) { // grow data err = growData(len); if (err != NO_ERROR) { return err; } } // append data memcpy(mData + mDataPos, data + offset, len); mDataPos += len; mDataSize += len; err = NO_ERROR; if (numObjects > 0) { const sp proc(ProcessState::self()); // grow objects if (mObjectsCapacity < mObjectsSize + numObjects) { size_t newSize = ((mObjectsSize + numObjects)*3)/2; if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow binder_size_t *objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); if (objects == (binder_size_t*)nullptr) { return NO_MEMORY; } mObjects = objects; mObjectsCapacity = newSize; } // append and acquire objects int idx = mObjectsSize; for (int i = firstIndex; i <= lastIndex; i++) { size_t off = objects[i] - offset + startPos; mObjects[idx++] = off; mObjectsSize++; flat_binder_object* flat = reinterpret_cast(mData + off); acquire_object(proc, *flat, this, &mOpenAshmemSize); if (flat->hdr.type == BINDER_TYPE_FD) { // If this is a file descriptor, we need to dup it so the // new Parcel now owns its own fd, and can declare that we // officially know we have fds. flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); flat->cookie = 1; mHasFds = mFdsKnown = true; if (!mAllowFds) { err = FDS_NOT_ALLOWED; } } } } return err; } int Parcel::compareData(const Parcel& other) { size_t size = dataSize(); if (size != other.dataSize()) { return size < other.dataSize() ? -1 : 1; } return memcmp(data(), other.data(), size); } bool Parcel::allowFds() const { return mAllowFds; } bool Parcel::pushAllowFds(bool allowFds) { const bool origValue = mAllowFds; if (!allowFds) { mAllowFds = false; } return origValue; } void Parcel::restoreAllowFds(bool lastValue) { mAllowFds = lastValue; } bool Parcel::hasFileDescriptors() const { if (!mFdsKnown) { scanForFds(); } return mHasFds; } void Parcel::updateWorkSourceRequestHeaderPosition() const { // Only update the request headers once. We only want to point // to the first headers read/written. if (!mRequestHeaderPresent) { mWorkSourceRequestHeaderPosition = dataPosition(); mRequestHeaderPresent = true; } } // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { const IPCThreadState* threadState = IPCThreadState::self(); writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); updateWorkSourceRequestHeaderPosition(); writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); // currently the interface identification token is just its name as a string return writeString16(interface); } bool Parcel::replaceCallingWorkSourceUid(uid_t uid) { if (!mRequestHeaderPresent) { return false; } const size_t initialPosition = dataPosition(); setDataPosition(mWorkSourceRequestHeaderPosition); status_t err = writeInt32(uid); setDataPosition(initialPosition); return err == NO_ERROR; } uid_t Parcel::readCallingWorkSourceUid() { if (!mRequestHeaderPresent) { return IPCThreadState::kUnsetWorkSource; } const size_t initialPosition = dataPosition(); setDataPosition(mWorkSourceRequestHeaderPosition); uid_t uid = readInt32(); setDataPosition(initialPosition); return uid; } bool Parcel::checkInterface(IBinder* binder) const { return enforceInterface(binder->getInterfaceDescriptor()); } bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { threadState = IPCThreadState::self(); } if ((threadState->getLastTransactionBinderFlags() & IBinder::FLAG_ONEWAY) != 0) { // For one-way calls, the callee is running entirely // disconnected from the caller, so disable StrictMode entirely. // Not only does disk/network usage not impact the caller, but // there's no way to commuicate back any violations anyway. threadState->setStrictModePolicy(0); } else { threadState->setStrictModePolicy(strictPolicy); } // WorkSource. updateWorkSourceRequestHeaderPosition(); int32_t workSource = readInt32(); threadState->setCallingWorkSourceUidWithoutPropagation(workSource); // Interface descriptor. const String16 str(readString16()); if (str == interface) { return true; } else { ALOGW("**** enforceInterface() expected '%s' but read '%s'", String8(interface).string(), String8(str).string()); return false; } } const binder_size_t* Parcel::objects() const { return mObjects; } size_t Parcel::objectsCount() const { return mObjectsSize; } status_t Parcel::errorCheck() const { return mError; } void Parcel::setError(status_t err) { mError = err; } status_t Parcel::finishWrite(size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } //printf("Finish write of %d\n", len); mDataPos += len; ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos); if (mDataPos > mDataSize) { mDataSize = mDataPos; ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize); } //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); return NO_ERROR; } status_t Parcel::writeUnpadded(const void* data, size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } size_t end = mDataPos + len; if (end < mDataPos) { // integer overflow return BAD_VALUE; } if (end <= mDataCapacity) { restart_write: memcpy(mData+mDataPos, data, len); return finishWrite(len); } status_t err = growData(len); if (err == NO_ERROR) goto restart_write; return err; } status_t Parcel::write(const void* data, size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } void* const d = writeInplace(len); if (d) { memcpy(d, data, len); return NO_ERROR; } return mError; } void* Parcel::writeInplace(size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return nullptr; } const size_t padded = pad_size(len); // sanity check for integer overflow if (mDataPos+padded < mDataPos) { return nullptr; } if ((mDataPos+padded) <= mDataCapacity) { restart_write: //printf("Writing %ld bytes, padded to %ld\n", len, padded); uint8_t* const data = mData+mDataPos; // Need to pad at end? if (padded != len) { #if BYTE_ORDER == BIG_ENDIAN static const uint32_t mask[4] = { 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 }; #endif #if BYTE_ORDER == LITTLE_ENDIAN static const uint32_t mask[4] = { 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff }; #endif //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], // *reinterpret_cast(data+padded-4)); *reinterpret_cast(data+padded-4) &= mask[padded-len]; } finishWrite(padded); return data; } status_t err = growData(padded); if (err == NO_ERROR) goto restart_write; return nullptr; } status_t Parcel::writeUtf8AsUtf16(const std::string& str) { const uint8_t* strData = (uint8_t*)str.data(); const size_t strLen= str.length(); const ssize_t utf16Len = utf8_to_utf16_length(strData, strLen); if (utf16Len < 0 || utf16Len > std::numeric_limits::max()) { return BAD_VALUE; } status_t err = writeInt32(utf16Len); if (err) { return err; } // Allocate enough bytes to hold our converted string and its terminating NULL. void* dst = writeInplace((utf16Len + 1) * sizeof(char16_t)); if (!dst) { return NO_MEMORY; } utf8_to_utf16(strData, strLen, (char16_t*)dst, (size_t) utf16Len + 1); return NO_ERROR; } status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr& str) { if (!str) { return writeInt32(-1); } return writeUtf8AsUtf16(*str); } namespace { template status_t writeByteVectorInternal(Parcel* parcel, const std::vector& val) { status_t status; if (val.size() > std::numeric_limits::max()) { status = BAD_VALUE; return status; } status = parcel->writeInt32(val.size()); if (status != OK) { return status; } void* data = parcel->writeInplace(val.size()); if (!data) { status = BAD_VALUE; return status; } memcpy(data, val.data(), val.size()); return status; } template status_t writeByteVectorInternalPtr(Parcel* parcel, const std::unique_ptr>& val) { if (!val) { return parcel->writeInt32(-1); } return writeByteVectorInternal(parcel, *val); } } // namespace status_t Parcel::writeByteVector(const std::vector& val) { return writeByteVectorInternal(this, val); } status_t Parcel::writeByteVector(const std::unique_ptr>& val) { return writeByteVectorInternalPtr(this, val); } status_t Parcel::writeByteVector(const std::vector& val) { return writeByteVectorInternal(this, val); } status_t Parcel::writeByteVector(const std::unique_ptr>& val) { return writeByteVectorInternalPtr(this, val); } status_t Parcel::writeInt32Vector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeInt32); } status_t Parcel::writeInt32Vector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeInt32); } status_t Parcel::writeInt64Vector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeInt64); } status_t Parcel::writeInt64Vector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeInt64); } status_t Parcel::writeUint64Vector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeUint64); } status_t Parcel::writeUint64Vector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeUint64); } status_t Parcel::writeFloatVector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeFloat); } status_t Parcel::writeFloatVector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeFloat); } status_t Parcel::writeDoubleVector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeDouble); } status_t Parcel::writeDoubleVector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeDouble); } status_t Parcel::writeBoolVector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeBool); } status_t Parcel::writeBoolVector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeBool); } status_t Parcel::writeCharVector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeChar); } status_t Parcel::writeCharVector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeChar); } status_t Parcel::writeString16Vector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeString16); } status_t Parcel::writeString16Vector( const std::unique_ptr>>& val) { return writeNullableTypedVector(val, &Parcel::writeString16); } status_t Parcel::writeUtf8VectorAsUtf16Vector( const std::unique_ptr>>& val) { return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16); } status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeUtf8AsUtf16); } status_t Parcel::writeInt32(int32_t val) { return writeAligned(val); } status_t Parcel::writeUint32(uint32_t val) { return writeAligned(val); } status_t Parcel::writeInt32Array(size_t len, const int32_t *val) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } if (!val) { return writeInt32(-1); } status_t ret = writeInt32(static_cast(len)); if (ret == NO_ERROR) { ret = write(val, len * sizeof(*val)); } return ret; } status_t Parcel::writeByteArray(size_t len, const uint8_t *val) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } if (!val) { return writeInt32(-1); } status_t ret = writeInt32(static_cast(len)); if (ret == NO_ERROR) { ret = write(val, len * sizeof(*val)); } return ret; } status_t Parcel::writeBool(bool val) { return writeInt32(int32_t(val)); } status_t Parcel::writeChar(char16_t val) { return writeInt32(int32_t(val)); } status_t Parcel::writeByte(int8_t val) { return writeInt32(int32_t(val)); } status_t Parcel::writeInt64(int64_t val) { return writeAligned(val); } status_t Parcel::writeUint64(uint64_t val) { return writeAligned(val); } status_t Parcel::writePointer(uintptr_t val) { return writeAligned(val); } status_t Parcel::writeFloat(float val) { return writeAligned(val); } #if defined(__mips__) && defined(__mips_hard_float) status_t Parcel::writeDouble(double val) { union { double d; unsigned long long ll; } u; u.d = val; return writeAligned(u.ll); } #else status_t Parcel::writeDouble(double val) { return writeAligned(val); } #endif status_t Parcel::writeCString(const char* str) { return write(str, strlen(str)+1); } status_t Parcel::writeString8(const String8& str) { status_t err = writeInt32(str.bytes()); // only write string if its length is more than zero characters, // as readString8 will only read if the length field is non-zero. // this is slightly different from how writeString16 works. if (str.bytes() > 0 && err == NO_ERROR) { err = write(str.string(), str.bytes()+1); } return err; } status_t Parcel::writeString16(const std::unique_ptr& str) { if (!str) { return writeInt32(-1); } return writeString16(*str); } status_t Parcel::writeString16(const String16& str) { return writeString16(str.string(), str.size()); } status_t Parcel::writeString16(const char16_t* str, size_t len) { if (str == nullptr) return writeInt32(-1); status_t err = writeInt32(len); if (err == NO_ERROR) { len *= sizeof(char16_t); uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); if (data) { memcpy(data, str, len); *reinterpret_cast(data+len) = 0; return NO_ERROR; } err = mError; } return err; } status_t Parcel::writeStrongBinder(const sp& val) { return flatten_binder(ProcessState::self(), val, this); } status_t Parcel::writeStrongBinderVector(const std::vector>& val) { return writeTypedVector(val, &Parcel::writeStrongBinder); } status_t Parcel::writeStrongBinderVector(const std::unique_ptr>>& val) { return writeNullableTypedVector(val, &Parcel::writeStrongBinder); } status_t Parcel::readStrongBinderVector(std::unique_ptr>>* val) const { return readNullableTypedVector(val, &Parcel::readNullableStrongBinder); } status_t Parcel::readStrongBinderVector(std::vector>* val) const { return readTypedVector(val, &Parcel::readStrongBinder); } status_t Parcel::writeWeakBinder(const wp& val) { return flatten_binder(ProcessState::self(), val, this); } status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) { if (!parcelable) { return writeInt32(0); } return writeParcelable(*parcelable); } status_t Parcel::writeParcelable(const Parcelable& parcelable) { status_t status = writeInt32(1); // parcelable is not null. if (status != OK) { return status; } return parcelable.writeToParcel(this); } status_t Parcel::writeValue(const binder::Value& value) { return value.writeToParcel(this); } status_t Parcel::writeNativeHandle(const native_handle* handle) { if (!handle || handle->version != sizeof(native_handle)) return BAD_TYPE; status_t err; err = writeInt32(handle->numFds); if (err != NO_ERROR) return err; err = writeInt32(handle->numInts); if (err != NO_ERROR) return err; for (int i=0 ; err==NO_ERROR && inumFds ; i++) err = writeDupFileDescriptor(handle->data[i]); if (err != NO_ERROR) { ALOGD("write native handle, write dup fd failed"); return err; } err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts); return err; } status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { flat_binder_object obj; obj.hdr.type = BINDER_TYPE_FD; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = fd; obj.cookie = takeOwnership ? 1 : 0; return writeObject(obj, true); } status_t Parcel::writeDupFileDescriptor(int fd) { int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (dupFd < 0) { return -errno; } status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/); if (err != OK) { close(dupFd); } return err; } status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership) { writeInt32(0); return writeFileDescriptor(fd, takeOwnership); } status_t Parcel::writeDupParcelFileDescriptor(int fd) { int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (dupFd < 0) { return -errno; } status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/); if (err != OK) { close(dupFd); } return err; } status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) { return writeDupFileDescriptor(fd.get()); } status_t Parcel::writeUniqueFileDescriptorVector(const std::vector& val) { return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor); } status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr>& val) { return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor); } status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } status_t status; if (!mAllowFds || len <= BLOB_INPLACE_LIMIT) { ALOGV("writeBlob: write in place"); status = writeInt32(BLOB_INPLACE); if (status) return status; void* ptr = writeInplace(len); if (!ptr) return NO_MEMORY; outBlob->init(-1, ptr, len, false); return NO_ERROR; } ALOGV("writeBlob: write to ashmem"); int fd = ashmem_create_region("Parcel Blob", len); if (fd < 0) return NO_MEMORY; int result = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE); if (result < 0) { status = result; } else { void* ptr = ::mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { status = -errno; } else { if (!mutableCopy) { result = ashmem_set_prot_region(fd, PROT_READ); } if (result < 0) { status = result; } else { status = writeInt32(mutableCopy ? BLOB_ASHMEM_MUTABLE : BLOB_ASHMEM_IMMUTABLE); if (!status) { status = writeFileDescriptor(fd, true /*takeOwnership*/); if (!status) { outBlob->init(fd, ptr, len, mutableCopy); return NO_ERROR; } } } } ::munmap(ptr, len); } ::close(fd); return status; } status_t Parcel::writeDupImmutableBlobFileDescriptor(int fd) { // Must match up with what's done in writeBlob. if (!mAllowFds) return FDS_NOT_ALLOWED; status_t status = writeInt32(BLOB_ASHMEM_IMMUTABLE); if (status) return status; return writeDupFileDescriptor(fd); } status_t Parcel::write(const FlattenableHelperInterface& val) { status_t err; // size if needed const size_t len = val.getFlattenedSize(); const size_t fd_count = val.getFdCount(); if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } err = this->writeInt32(len); if (err) return err; err = this->writeInt32(fd_count); if (err) return err; // payload void* const buf = this->writeInplace(len); if (buf == nullptr) return BAD_VALUE; int* fds = nullptr; if (fd_count) { fds = new (std::nothrow) int[fd_count]; if (fds == nullptr) { ALOGE("write: failed to allocate requested %zu fds", fd_count); return BAD_VALUE; } } err = val.flatten(buf, len, fds, fd_count); for (size_t i=0 ; iwriteDupFileDescriptor( fds[i] ); } if (fd_count) { delete [] fds; } return err; } status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) { const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; const bool enoughObjects = mObjectsSize < mObjectsCapacity; if (enoughData && enoughObjects) { restart_write: *reinterpret_cast(mData+mDataPos) = val; // remember if it's a file descriptor if (val.hdr.type == BINDER_TYPE_FD) { if (!mAllowFds) { // fail before modifying our object index return FDS_NOT_ALLOWED; } mHasFds = mFdsKnown = true; } // Need to write meta-data? if (nullMetaData || val.binder != 0) { mObjects[mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize); mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; } if (!enoughObjects) { size_t newSize = ((mObjectsSize+2)*3)/2; if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); if (objects == nullptr) return NO_MEMORY; mObjects = objects; mObjectsCapacity = newSize; } goto restart_write; } status_t Parcel::writeNoException() { binder::Status status; return status.writeToParcel(this); } status_t Parcel::writeMap(const ::android::binder::Map& map_in) { using ::std::map; using ::android::binder::Value; using ::android::binder::Map; Map::const_iterator iter; status_t ret; ret = writeInt32(map_in.size()); if (ret != NO_ERROR) { return ret; } for (iter = map_in.begin(); iter != map_in.end(); ++iter) { ret = writeValue(Value(iter->first)); if (ret != NO_ERROR) { return ret; } ret = writeValue(iter->second); if (ret != NO_ERROR) { return ret; } } return ret; } status_t Parcel::writeNullableMap(const std::unique_ptr& map) { if (map == nullptr) { return writeInt32(-1); } return writeMap(*map.get()); } status_t Parcel::readMap(::android::binder::Map* map_out)const { using ::std::map; using ::android::String16; using ::android::String8; using ::android::binder::Value; using ::android::binder::Map; status_t ret = NO_ERROR; int32_t count; ret = readInt32(&count); if (ret != NO_ERROR) { return ret; } if (count < 0) { ALOGE("readMap: Unexpected count: %d", count); return (count == -1) ? UNEXPECTED_NULL : BAD_VALUE; } map_out->clear(); while (count--) { Map::key_type key; Value value; ret = readValue(&value); if (ret != NO_ERROR) { return ret; } if (!value.getString(&key)) { ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType()); return BAD_VALUE; } ret = readValue(&value); if (ret != NO_ERROR) { return ret; } (*map_out)[key] = value; } return ret; } status_t Parcel::readNullableMap(std::unique_ptr* map) const { const size_t start = dataPosition(); int32_t count; status_t status = readInt32(&count); map->reset(); if (status != OK || count == -1) { return status; } setDataPosition(start); map->reset(new binder::Map()); status = readMap(map->get()); if (status != OK) { map->reset(); } return status; } void Parcel::remove(size_t /*start*/, size_t /*amt*/) { LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); } status_t Parcel::validateReadData(size_t upperBound) const { // Don't allow non-object reads on object data if (mObjectsSorted || mObjectsSize <= 1) { data_sorted: // Expect to check only against the next object if (mNextObjectHint < mObjectsSize && upperBound > mObjects[mNextObjectHint]) { // For some reason the current read position is greater than the next object // hint. Iterate until we find the right object size_t nextObject = mNextObjectHint; do { if (mDataPos < mObjects[nextObject] + sizeof(flat_binder_object)) { // Requested info overlaps with an object ALOGE("Attempt to read from protected data in Parcel %p", this); return PERMISSION_DENIED; } nextObject++; } while (nextObject < mObjectsSize && upperBound > mObjects[nextObject]); mNextObjectHint = nextObject; } return NO_ERROR; } // Quickly determine if mObjects is sorted. binder_size_t* currObj = mObjects + mObjectsSize - 1; binder_size_t* prevObj = currObj; while (currObj > mObjects) { prevObj--; if(*prevObj > *currObj) { goto data_unsorted; } currObj--; } mObjectsSorted = true; goto data_sorted; data_unsorted: // Insertion Sort mObjects // Great for mostly sorted lists. If randomly sorted or reverse ordered mObjects become common, // switch to std::sort(mObjects, mObjects + mObjectsSize); for (binder_size_t* iter0 = mObjects + 1; iter0 < mObjects + mObjectsSize; iter0++) { binder_size_t temp = *iter0; binder_size_t* iter1 = iter0 - 1; while (iter1 >= mObjects && *iter1 > temp) { *(iter1 + 1) = *iter1; iter1--; } *(iter1 + 1) = temp; } mNextObjectHint = 0; mObjectsSorted = true; goto data_sorted; } status_t Parcel::read(void* outData, size_t len) const { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { if (mObjectsSize > 0) { status_t err = validateReadData(mDataPos + pad_size(len)); if(err != NO_ERROR) { // Still increment the data position by the expected length mDataPos += pad_size(len); ALOGV("read Setting data pos of %p to %zu", this, mDataPos); return err; } } memcpy(outData, mData+mDataPos, len); mDataPos += pad_size(len); ALOGV("read Setting data pos of %p to %zu", this, mDataPos); return NO_ERROR; } return NOT_ENOUGH_DATA; } const void* Parcel::readInplace(size_t len) const { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return nullptr; } if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { if (mObjectsSize > 0) { status_t err = validateReadData(mDataPos + pad_size(len)); if(err != NO_ERROR) { // Still increment the data position by the expected length mDataPos += pad_size(len); ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos); return nullptr; } } const void* data = mData+mDataPos; mDataPos += pad_size(len); ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos); return data; } return nullptr; } template status_t Parcel::readAligned(T *pArg) const { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(T)) <= mDataSize) { if (mObjectsSize > 0) { status_t err = validateReadData(mDataPos + sizeof(T)); if(err != NO_ERROR) { // Still increment the data position by the expected length mDataPos += sizeof(T); return err; } } const void* data = mData+mDataPos; mDataPos += sizeof(T); *pArg = *reinterpret_cast(data); return NO_ERROR; } else { return NOT_ENOUGH_DATA; } } template T Parcel::readAligned() const { T result; if (readAligned(&result) != NO_ERROR) { result = 0; } return result; } template status_t Parcel::writeAligned(T val) { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: *reinterpret_cast(mData+mDataPos) = val; return finishWrite(sizeof(val)); } status_t err = growData(sizeof(val)); if (err == NO_ERROR) goto restart_write; return err; } namespace { template status_t readByteVectorInternal(const Parcel* parcel, std::vector* val) { val->clear(); int32_t size; status_t status = parcel->readInt32(&size); if (status != OK) { return status; } if (size < 0) { status = UNEXPECTED_NULL; return status; } if (size_t(size) > parcel->dataAvail()) { status = BAD_VALUE; return status; } T* data = const_cast(reinterpret_cast(parcel->readInplace(size))); if (!data) { status = BAD_VALUE; return status; } val->reserve(size); val->insert(val->end(), data, data + size); return status; } template status_t readByteVectorInternalPtr( const Parcel* parcel, std::unique_ptr>* val) { const int32_t start = parcel->dataPosition(); int32_t size; status_t status = parcel->readInt32(&size); val->reset(); if (status != OK || size < 0) { return status; } parcel->setDataPosition(start); val->reset(new (std::nothrow) std::vector()); status = readByteVectorInternal(parcel, val->get()); if (status != OK) { val->reset(); } return status; } } // namespace status_t Parcel::readByteVector(std::vector* val) const { return readByteVectorInternal(this, val); } status_t Parcel::readByteVector(std::vector* val) const { return readByteVectorInternal(this, val); } status_t Parcel::readByteVector(std::unique_ptr>* val) const { return readByteVectorInternalPtr(this, val); } status_t Parcel::readByteVector(std::unique_ptr>* val) const { return readByteVectorInternalPtr(this, val); } status_t Parcel::readInt32Vector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readInt32); } status_t Parcel::readInt32Vector(std::vector* val) const { return readTypedVector(val, &Parcel::readInt32); } status_t Parcel::readInt64Vector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readInt64); } status_t Parcel::readInt64Vector(std::vector* val) const { return readTypedVector(val, &Parcel::readInt64); } status_t Parcel::readUint64Vector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readUint64); } status_t Parcel::readUint64Vector(std::vector* val) const { return readTypedVector(val, &Parcel::readUint64); } status_t Parcel::readFloatVector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readFloat); } status_t Parcel::readFloatVector(std::vector* val) const { return readTypedVector(val, &Parcel::readFloat); } status_t Parcel::readDoubleVector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readDouble); } status_t Parcel::readDoubleVector(std::vector* val) const { return readTypedVector(val, &Parcel::readDouble); } status_t Parcel::readBoolVector(std::unique_ptr>* val) const { const int32_t start = dataPosition(); int32_t size; status_t status = readInt32(&size); val->reset(); if (status != OK || size < 0) { return status; } setDataPosition(start); val->reset(new (std::nothrow) std::vector()); status = readBoolVector(val->get()); if (status != OK) { val->reset(); } return status; } status_t Parcel::readBoolVector(std::vector* val) const { int32_t size; status_t status = readInt32(&size); if (status != OK) { return status; } if (size < 0) { return UNEXPECTED_NULL; } val->resize(size); /* C++ bool handling means a vector of bools isn't necessarily addressable * (we might use individual bits) */ bool data; for (int32_t i = 0; i < size; ++i) { status = readBool(&data); (*val)[i] = data; if (status != OK) { return status; } } return OK; } status_t Parcel::readCharVector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readChar); } status_t Parcel::readCharVector(std::vector* val) const { return readTypedVector(val, &Parcel::readChar); } status_t Parcel::readString16Vector( std::unique_ptr>>* val) const { return readNullableTypedVector(val, &Parcel::readString16); } status_t Parcel::readString16Vector(std::vector* val) const { return readTypedVector(val, &Parcel::readString16); } status_t Parcel::readUtf8VectorFromUtf16Vector( std::unique_ptr>>* val) const { return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16); } status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector* val) const { return readTypedVector(val, &Parcel::readUtf8FromUtf16); } status_t Parcel::readInt32(int32_t *pArg) const { return readAligned(pArg); } int32_t Parcel::readInt32() const { return readAligned(); } status_t Parcel::readUint32(uint32_t *pArg) const { return readAligned(pArg); } uint32_t Parcel::readUint32() const { return readAligned(); } status_t Parcel::readInt64(int64_t *pArg) const { return readAligned(pArg); } int64_t Parcel::readInt64() const { return readAligned(); } status_t Parcel::readUint64(uint64_t *pArg) const { return readAligned(pArg); } uint64_t Parcel::readUint64() const { return readAligned(); } status_t Parcel::readPointer(uintptr_t *pArg) const { status_t ret; binder_uintptr_t ptr; ret = readAligned(&ptr); if (!ret) *pArg = ptr; return ret; } uintptr_t Parcel::readPointer() const { return readAligned(); } status_t Parcel::readFloat(float *pArg) const { return readAligned(pArg); } float Parcel::readFloat() const { return readAligned(); } #if defined(__mips__) && defined(__mips_hard_float) status_t Parcel::readDouble(double *pArg) const { union { double d; unsigned long long ll; } u; u.d = 0; status_t status; status = readAligned(&u.ll); *pArg = u.d; return status; } double Parcel::readDouble() const { union { double d; unsigned long long ll; } u; u.ll = readAligned(); return u.d; } #else status_t Parcel::readDouble(double *pArg) const { return readAligned(pArg); } double Parcel::readDouble() const { return readAligned(); } #endif status_t Parcel::readIntPtr(intptr_t *pArg) const { return readAligned(pArg); } intptr_t Parcel::readIntPtr() const { return readAligned(); } status_t Parcel::readBool(bool *pArg) const { int32_t tmp = 0; status_t ret = readInt32(&tmp); *pArg = (tmp != 0); return ret; } bool Parcel::readBool() const { return readInt32() != 0; } status_t Parcel::readChar(char16_t *pArg) const { int32_t tmp = 0; status_t ret = readInt32(&tmp); *pArg = char16_t(tmp); return ret; } char16_t Parcel::readChar() const { return char16_t(readInt32()); } status_t Parcel::readByte(int8_t *pArg) const { int32_t tmp = 0; status_t ret = readInt32(&tmp); *pArg = int8_t(tmp); return ret; } int8_t Parcel::readByte() const { return int8_t(readInt32()); } status_t Parcel::readUtf8FromUtf16(std::string* str) const { size_t utf16Size = 0; const char16_t* src = readString16Inplace(&utf16Size); if (!src) { return UNEXPECTED_NULL; } // Save ourselves the trouble, we're done. if (utf16Size == 0u) { str->clear(); return NO_ERROR; } // Allow for closing '\0' ssize_t utf8Size = utf16_to_utf8_length(src, utf16Size) + 1; if (utf8Size < 1) { return BAD_VALUE; } // Note that while it is probably safe to assume string::resize keeps a // spare byte around for the trailing null, we still pass the size including the trailing null str->resize(utf8Size); utf16_to_utf8(src, utf16Size, &((*str)[0]), utf8Size); str->resize(utf8Size - 1); return NO_ERROR; } status_t Parcel::readUtf8FromUtf16(std::unique_ptr* str) const { const int32_t start = dataPosition(); int32_t size; status_t status = readInt32(&size); str->reset(); if (status != OK || size < 0) { return status; } setDataPosition(start); str->reset(new (std::nothrow) std::string()); return readUtf8FromUtf16(str->get()); } const char* Parcel::readCString() const { if (mDataPos < mDataSize) { const size_t avail = mDataSize-mDataPos; const char* str = reinterpret_cast(mData+mDataPos); // is the string's trailing NUL within the parcel's valid bounds? const char* eos = reinterpret_cast(memchr(str, 0, avail)); if (eos) { const size_t len = eos - str; mDataPos += pad_size(len+1); ALOGV("readCString Setting data pos of %p to %zu", this, mDataPos); return str; } } return nullptr; } String8 Parcel::readString8() const { String8 retString; status_t status = readString8(&retString); if (status != OK) { // We don't care about errors here, so just return an empty string. return String8(); } return retString; } status_t Parcel::readString8(String8* pArg) const { int32_t size; status_t status = readInt32(&size); if (status != OK) { return status; } // watch for potential int overflow from size+1 if (size < 0 || size >= INT32_MAX) { return BAD_VALUE; } // |writeString8| writes nothing for empty string. if (size == 0) { *pArg = String8(); return OK; } const char* str = (const char*)readInplace(size + 1); if (str == nullptr) { return BAD_VALUE; } pArg->setTo(str, size); return OK; } String16 Parcel::readString16() const { size_t len; const char16_t* str = readString16Inplace(&len); if (str) return String16(str, len); ALOGE("Reading a NULL string not supported here."); return String16(); } status_t Parcel::readString16(std::unique_ptr* pArg) const { const int32_t start = dataPosition(); int32_t size; status_t status = readInt32(&size); pArg->reset(); if (status != OK || size < 0) { return status; } setDataPosition(start); pArg->reset(new (std::nothrow) String16()); status = readString16(pArg->get()); if (status != OK) { pArg->reset(); } return status; } status_t Parcel::readString16(String16* pArg) const { size_t len; const char16_t* str = readString16Inplace(&len); if (str) { pArg->setTo(str, len); return 0; } else { *pArg = String16(); return UNEXPECTED_NULL; } } const char16_t* Parcel::readString16Inplace(size_t* outLen) const { int32_t size = readInt32(); // watch for potential int overflow from size+1 if (size >= 0 && size < INT32_MAX) { *outLen = size; const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); if (str != nullptr) { return str; } } *outLen = 0; return nullptr; } status_t Parcel::readStrongBinder(sp* val) const { status_t status = readNullableStrongBinder(val); if (status == OK && !val->get()) { status = UNEXPECTED_NULL; } return status; } status_t Parcel::readNullableStrongBinder(sp* val) const { return unflatten_binder(ProcessState::self(), *this, val); } sp Parcel::readStrongBinder() const { sp val; // Note that a lot of code in Android reads binders by hand with this // method, and that code has historically been ok with getting nullptr // back (while ignoring error codes). readNullableStrongBinder(&val); return val; } wp Parcel::readWeakBinder() const { wp val; unflatten_binder(ProcessState::self(), *this, &val); return val; } status_t Parcel::readParcelable(Parcelable* parcelable) const { int32_t have_parcelable = 0; status_t status = readInt32(&have_parcelable); if (status != OK) { return status; } if (!have_parcelable) { return UNEXPECTED_NULL; } return parcelable->readFromParcel(this); } status_t Parcel::readValue(binder::Value* value) const { return value->readFromParcel(this); } int32_t Parcel::readExceptionCode() const { binder::Status status; status.readFromParcel(*this); return status.exceptionCode(); } native_handle* Parcel::readNativeHandle() const { int numFds, numInts; status_t err; err = readInt32(&numFds); if (err != NO_ERROR) return nullptr; err = readInt32(&numInts); if (err != NO_ERROR) return nullptr; native_handle* h = native_handle_create(numFds, numInts); if (!h) { return nullptr; } for (int i=0 ; err==NO_ERROR && idata[i] = fcntl(readFileDescriptor(), F_DUPFD_CLOEXEC, 0); if (h->data[i] < 0) { for (int j = 0; j < i; j++) { close(h->data[j]); } native_handle_delete(h); return nullptr; } } err = read(h->data + numFds, sizeof(int)*numInts); if (err != NO_ERROR) { native_handle_close(h); native_handle_delete(h); h = nullptr; } return h; } int Parcel::readFileDescriptor() const { const flat_binder_object* flat = readObject(true); if (flat && flat->hdr.type == BINDER_TYPE_FD) { return flat->handle; } return BAD_TYPE; } int Parcel::readParcelFileDescriptor() const { int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { // detach (owned by the binder driver) int comm = readFileDescriptor(); // warning: this must be kept in sync with: // frameworks/base/core/java/android/os/ParcelFileDescriptor.java enum ParcelFileDescriptorStatus { DETACHED = 2, }; #if BYTE_ORDER == BIG_ENDIAN const int32_t message = ParcelFileDescriptorStatus::DETACHED; #endif #if BYTE_ORDER == LITTLE_ENDIAN const int32_t message = __builtin_bswap32(ParcelFileDescriptorStatus::DETACHED); #endif ssize_t written = TEMP_FAILURE_RETRY( ::write(comm, &message, sizeof(message))); if (written == -1 || written != sizeof(message)) { ALOGW("Failed to detach ParcelFileDescriptor written: %zd err: %s", written, strerror(errno)); return BAD_TYPE; } } return fd; } status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const { int got = readFileDescriptor(); if (got == BAD_TYPE) { return BAD_TYPE; } val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); if (val->get() < 0) { return BAD_VALUE; } return OK; } status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const { int got = readParcelFileDescriptor(); if (got == BAD_TYPE) { return BAD_TYPE; } val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); if (val->get() < 0) { return BAD_VALUE; } return OK; } status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr>* val) const { return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor); } status_t Parcel::readUniqueFileDescriptorVector(std::vector* val) const { return readTypedVector(val, &Parcel::readUniqueFileDescriptor); } status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const { int32_t blobType; status_t status = readInt32(&blobType); if (status) return status; if (blobType == BLOB_INPLACE) { ALOGV("readBlob: read in place"); const void* ptr = readInplace(len); if (!ptr) return BAD_VALUE; outBlob->init(-1, const_cast(ptr), len, false); return NO_ERROR; } ALOGV("readBlob: read from ashmem"); bool isMutable = (blobType == BLOB_ASHMEM_MUTABLE); int fd = readFileDescriptor(); if (fd == int(BAD_TYPE)) return BAD_VALUE; if (!ashmem_valid(fd)) { ALOGE("invalid fd"); return BAD_VALUE; } int size = ashmem_get_size_region(fd); if (size < 0 || size_t(size) < len) { ALOGE("request size %zu does not match fd size %d", len, size); return BAD_VALUE; } void* ptr = ::mmap(nullptr, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) return NO_MEMORY; outBlob->init(fd, ptr, len, isMutable); return NO_ERROR; } status_t Parcel::read(FlattenableHelperInterface& val) const { // size const size_t len = this->readInt32(); const size_t fd_count = this->readInt32(); if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } // payload void const* const buf = this->readInplace(pad_size(len)); if (buf == nullptr) return BAD_VALUE; int* fds = nullptr; if (fd_count) { fds = new (std::nothrow) int[fd_count]; if (fds == nullptr) { ALOGE("read: failed to allocate requested %zu fds", fd_count); return BAD_VALUE; } } status_t err = NO_ERROR; for (size_t i=0 ; ireadFileDescriptor(); if (fd < 0 || ((fds[i] = fcntl(fd, F_DUPFD_CLOEXEC, 0)) < 0)) { err = BAD_VALUE; ALOGE("fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s", i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno)); // Close all the file descriptors that were dup-ed. for (size_t j=0; j(mData+DPOS); mDataPos = DPOS + sizeof(flat_binder_object); if (!nullMetaData && (obj->cookie == 0 && obj->binder == 0)) { // When transferring a NULL object, we don't write it into // the object list, so we don't want to check for it when // reading. ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } // Ensure that this object is valid... binder_size_t* const OBJS = mObjects; const size_t N = mObjectsSize; size_t opos = mNextObjectHint; if (N > 0) { ALOGV("Parcel %p looking for obj at %zu, hint=%zu", this, DPOS, opos); // Start at the current hint position, looking for an object at // the current data position. if (opos < N) { while (opos < (N-1) && OBJS[opos] < DPOS) { opos++; } } else { opos = N-1; } if (OBJS[opos] == DPOS) { // Found it! ALOGV("Parcel %p found obj %zu at index %zu with forward search", this, DPOS, opos); mNextObjectHint = opos+1; ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } // Look backwards for it... while (opos > 0 && OBJS[opos] > DPOS) { opos--; } if (OBJS[opos] == DPOS) { // Found it! ALOGV("Parcel %p found obj %zu at index %zu with backward search", this, DPOS, opos); mNextObjectHint = opos+1; ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } } ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object list", this, DPOS); } return nullptr; } void Parcel::closeFileDescriptors() { size_t i = mObjectsSize; if (i > 0) { //ALOGI("Closing file descriptors for %zu objects...", i); } while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast(mData+mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { //ALOGI("Closing fd: %ld", flat->handle); close(flat->handle); } } } uintptr_t Parcel::ipcData() const { return reinterpret_cast(mData); } size_t Parcel::ipcDataSize() const { return (mDataSize > mDataPos ? mDataSize : mDataPos); } uintptr_t Parcel::ipcObjects() const { return reinterpret_cast(mObjects); } size_t Parcel::ipcObjectsCount() const { return mObjectsSize; } void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) { binder_size_t minOffset = 0; freeDataNoInit(); mError = NO_ERROR; mData = const_cast(data); mDataSize = mDataCapacity = dataSize; //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid()); mDataPos = 0; ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos); mObjects = const_cast(objects); mObjectsSize = mObjectsCapacity = objectsCount; mNextObjectHint = 0; mObjectsSorted = false; mOwner = relFunc; mOwnerCookie = relCookie; for (size_t i = 0; i < mObjectsSize; i++) { binder_size_t offset = mObjects[i]; if (offset < minOffset) { ALOGE("%s: bad object offset %" PRIu64 " < %" PRIu64 "\n", __func__, (uint64_t)offset, (uint64_t)minOffset); mObjectsSize = 0; break; } minOffset = offset + sizeof(flat_binder_object); } scanForFds(); } void Parcel::print(TextOutput& to, uint32_t /*flags*/) const { to << "Parcel("; if (errorCheck() != NO_ERROR) { const status_t err = errorCheck(); to << "Error: " << (void*)(intptr_t)err << " \"" << strerror(-err) << "\""; } else if (dataSize() > 0) { const uint8_t* DATA = data(); to << indent << HexDump(DATA, dataSize()) << dedent; const binder_size_t* OBJS = objects(); const size_t N = objectsCount(); for (size_t i=0; i(DATA+OBJS[i]); to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder; } } else { to << "NULL"; } to << ")"; } void Parcel::releaseObjects() { size_t i = mObjectsSize; if (i == 0) { return; } sp proc(ProcessState::self()); uint8_t* const data = mData; binder_size_t* const objects = mObjects; while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast(data+objects[i]); release_object(proc, *flat, this, &mOpenAshmemSize); } } void Parcel::acquireObjects() { size_t i = mObjectsSize; if (i == 0) { return; } const sp proc(ProcessState::self()); uint8_t* const data = mData; binder_size_t* const objects = mObjects; while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast(data+objects[i]); acquire_object(proc, *flat, this, &mOpenAshmemSize); } } void Parcel::freeData() { freeDataNoInit(); initState(); } void Parcel::freeDataNoInit() { if (mOwner) { LOG_ALLOC("Parcel %p: freeing other owner data", this); //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); } else { LOG_ALLOC("Parcel %p: freeing allocated data", this); releaseObjects(); if (mData) { LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); if (mDataCapacity <= gParcelGlobalAllocSize) { gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity; } else { gParcelGlobalAllocSize = 0; } if (gParcelGlobalAllocCount > 0) { gParcelGlobalAllocCount--; } pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); free(mData); } if (mObjects) free(mObjects); } } status_t Parcel::growData(size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } size_t newSize = ((mDataSize+len)*3)/2; return (newSize <= mDataSize) ? (status_t) NO_MEMORY : continueWrite(newSize); } status_t Parcel::restartWrite(size_t desired) { if (desired > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } if (mOwner) { freeData(); return continueWrite(desired); } uint8_t* data = (uint8_t*)realloc(mData, desired); if (!data && desired > mDataCapacity) { mError = NO_MEMORY; return NO_MEMORY; } releaseObjects(); if (data) { LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); gParcelGlobalAllocSize += desired; gParcelGlobalAllocSize -= mDataCapacity; if (!mData) { gParcelGlobalAllocCount++; } pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); mData = data; mDataCapacity = desired; } mDataSize = mDataPos = 0; ALOGV("restartWrite Setting data size of %p to %zu", this, mDataSize); ALOGV("restartWrite Setting data pos of %p to %zu", this, mDataPos); free(mObjects); mObjects = nullptr; mObjectsSize = mObjectsCapacity = 0; mNextObjectHint = 0; mObjectsSorted = false; mHasFds = false; mFdsKnown = true; mAllowFds = true; return NO_ERROR; } status_t Parcel::continueWrite(size_t desired) { if (desired > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } // If shrinking, first adjust for any objects that appear // after the new data size. size_t objectsSize = mObjectsSize; if (desired < mDataSize) { if (desired == 0) { objectsSize = 0; } else { while (objectsSize > 0) { if (mObjects[objectsSize-1] < desired) break; objectsSize--; } } } if (mOwner) { // If the size is going to zero, just release the owner's data. if (desired == 0) { freeData(); return NO_ERROR; } // If there is a different owner, we need to take // posession. uint8_t* data = (uint8_t*)malloc(desired); if (!data) { mError = NO_MEMORY; return NO_MEMORY; } binder_size_t* objects = nullptr; if (objectsSize) { objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)); if (!objects) { free(data); mError = NO_MEMORY; return NO_MEMORY; } // Little hack to only acquire references on objects // we will be keeping. size_t oldObjectsSize = mObjectsSize; mObjectsSize = objectsSize; acquireObjects(); mObjectsSize = oldObjectsSize; } if (mData) { memcpy(data, mData, mDataSize < desired ? mDataSize : desired); } if (objects && mObjects) { memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t)); } //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); mOwner = nullptr; LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); gParcelGlobalAllocSize += desired; gParcelGlobalAllocCount++; pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); mData = data; mObjects = objects; mDataSize = (mDataSize < desired) ? mDataSize : desired; ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize); mDataCapacity = desired; mObjectsSize = mObjectsCapacity = objectsSize; mNextObjectHint = 0; mObjectsSorted = false; } else if (mData) { if (objectsSize < mObjectsSize) { // Need to release refs on any objects we are dropping. const sp proc(ProcessState::self()); for (size_t i=objectsSize; i(mData+mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { // will need to rescan because we may have lopped off the only FDs mFdsKnown = false; } release_object(proc, *flat, this, &mOpenAshmemSize); } if (objectsSize == 0) { free(mObjects); mObjects = nullptr; mObjectsCapacity = 0; } else { binder_size_t* objects = (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t)); if (objects) { mObjects = objects; mObjectsCapacity = objectsSize; } } mObjectsSize = objectsSize; mNextObjectHint = 0; mObjectsSorted = false; } // We own the data, so we can just do a realloc(). if (desired > mDataCapacity) { uint8_t* data = (uint8_t*)realloc(mData, desired); if (data) { LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity, desired); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); gParcelGlobalAllocSize += desired; gParcelGlobalAllocSize -= mDataCapacity; pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); mData = data; mDataCapacity = desired; } else { mError = NO_MEMORY; return NO_MEMORY; } } else { if (mDataSize > desired) { mDataSize = desired; ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize); } if (mDataPos > desired) { mDataPos = desired; ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos); } } } else { // This is the first data. Easy! uint8_t* data = (uint8_t*)malloc(desired); if (!data) { mError = NO_MEMORY; return NO_MEMORY; } if(!(mDataCapacity == 0 && mObjects == nullptr && mObjectsCapacity == 0)) { ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired); } LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); gParcelGlobalAllocSize += desired; gParcelGlobalAllocCount++; pthread_mutex_unlock(&gParcelGlobalAllocSizeLock); mData = data; mDataSize = mDataPos = 0; ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize); ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos); mDataCapacity = desired; } return NO_ERROR; } void Parcel::initState() { LOG_ALLOC("Parcel %p: initState", this); mError = NO_ERROR; mData = nullptr; mDataSize = 0; mDataCapacity = 0; mDataPos = 0; ALOGV("initState Setting data size of %p to %zu", this, mDataSize); ALOGV("initState Setting data pos of %p to %zu", this, mDataPos); mObjects = nullptr; mObjectsSize = 0; mObjectsCapacity = 0; mNextObjectHint = 0; mObjectsSorted = false; mHasFds = false; mFdsKnown = true; mAllowFds = true; mOwner = nullptr; mOpenAshmemSize = 0; mWorkSourceRequestHeaderPosition = 0; mRequestHeaderPresent = false; // racing multiple init leads only to multiple identical write if (gMaxFds == 0) { struct rlimit result; if (!getrlimit(RLIMIT_NOFILE, &result)) { gMaxFds = (size_t)result.rlim_cur; //ALOGI("parcel fd limit set to %zu", gMaxFds); } else { ALOGW("Unable to getrlimit: %s", strerror(errno)); gMaxFds = 1024; } } } void Parcel::scanForFds() const { bool hasFds = false; for (size_t i=0; i(mData + mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { hasFds = true; break; } } mHasFds = hasFds; mFdsKnown = true; } size_t Parcel::getBlobAshmemSize() const { // This used to return the size of all blobs that were written to ashmem, now we're returning // the ashmem currently referenced by this Parcel, which should be equivalent. // TODO: Remove method once ABI can be changed. return mOpenAshmemSize; } size_t Parcel::getOpenAshmemSize() const { return mOpenAshmemSize; } // --- Parcel::Blob --- Parcel::Blob::Blob() : mFd(-1), mData(nullptr), mSize(0), mMutable(false) { } Parcel::Blob::~Blob() { release(); } void Parcel::Blob::release() { if (mFd != -1 && mData) { ::munmap(mData, mSize); } clear(); } void Parcel::Blob::init(int fd, void* data, size_t size, bool isMutable) { mFd = fd; mData = data; mSize = size; mMutable = isMutable; } void Parcel::Blob::clear() { mFd = -1; mData = nullptr; mSize = 0; mMutable = false; } }; // namespace android libs/binder/ParcelFileDescriptor.cpp0100644 0000000 0000000 00000002276 13756501735 016575 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include namespace android { namespace os { ParcelFileDescriptor::ParcelFileDescriptor() = default; ParcelFileDescriptor::ParcelFileDescriptor(android::base::unique_fd fd) : mFd(std::move(fd)) {} ParcelFileDescriptor::~ParcelFileDescriptor() = default; status_t ParcelFileDescriptor::writeToParcel(Parcel* parcel) const { return parcel->writeDupParcelFileDescriptor(mFd.get()); } status_t ParcelFileDescriptor::readFromParcel(const Parcel* parcel) { return parcel->readUniqueParcelFileDescriptor(&mFd); } } // namespace os } // namespace android libs/binder/PermissionCache.cpp0100644 0000000 0000000 00000006706 13756501735 015606 0ustar000000000 0000000 /* * Copyright (C) 2009 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. */ #define LOG_TAG "PermissionCache" #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- ANDROID_SINGLETON_STATIC_INSTANCE(PermissionCache) ; // ---------------------------------------------------------------------------- PermissionCache::PermissionCache() { } status_t PermissionCache::check(bool* granted, const String16& permission, uid_t uid) const { Mutex::Autolock _l(mLock); Entry e; e.name = permission; e.uid = uid; ssize_t index = mCache.indexOf(e); if (index >= 0) { *granted = mCache.itemAt(index).granted; return NO_ERROR; } return NAME_NOT_FOUND; } void PermissionCache::cache(const String16& permission, uid_t uid, bool granted) { Mutex::Autolock _l(mLock); Entry e; ssize_t index = mPermissionNamesPool.indexOf(permission); if (index > 0) { e.name = mPermissionNamesPool.itemAt(index); } else { mPermissionNamesPool.add(permission); e.name = permission; } // note, we don't need to store the pid, which is not actually used in // permission checks e.uid = uid; e.granted = granted; index = mCache.indexOf(e); if (index < 0) { mCache.add(e); } } void PermissionCache::purge() { Mutex::Autolock _l(mLock); mCache.clear(); } bool PermissionCache::checkCallingPermission(const String16& permission) { return PermissionCache::checkCallingPermission(permission, nullptr, nullptr); } bool PermissionCache::checkCallingPermission( const String16& permission, int32_t* outPid, int32_t* outUid) { IPCThreadState* ipcState = IPCThreadState::self(); pid_t pid = ipcState->getCallingPid(); uid_t uid = ipcState->getCallingUid(); if (outPid) *outPid = pid; if (outUid) *outUid = uid; return PermissionCache::checkPermission(permission, pid, uid); } bool PermissionCache::checkPermission( const String16& permission, pid_t pid, uid_t uid) { if ((uid == 0) || (pid == getpid())) { // root and ourselves is always okay return true; } PermissionCache& pc(PermissionCache::getInstance()); bool granted = false; if (pc.check(&granted, permission, uid) != NO_ERROR) { nsecs_t t = -systemTime(); granted = android::checkPermission(permission, pid, uid); t += systemTime(); ALOGD("checking %s for uid=%d => %s (%d us)", String8(permission).string(), uid, granted?"granted":"denied", (int)ns2us(t)); pc.cache(permission, uid, granted); } return granted; } // --------------------------------------------------------------------------- }; // namespace android libs/binder/PermissionController.cpp0100644 0000000 0000000 00000005675 13756501735 016732 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include #include #include namespace android { PermissionController::PermissionController() { } sp PermissionController::getService() { std::lock_guard scoped_lock(mLock); int64_t startTime = 0; sp service = mService; while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { sp binder = defaultServiceManager()->checkService(String16("permission")); if (binder == nullptr) { // Wait for the activity service to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting for permission service"); } else if ((uptimeMillis() - startTime) > 10000) { ALOGW("Waiting too long for permission service, giving up"); service = nullptr; break; } sleep(1); } else { service = interface_cast(binder); mService = service; } } return service; } bool PermissionController::checkPermission(const String16& permission, int32_t pid, int32_t uid) { sp service = getService(); return service != nullptr ? service->checkPermission(permission, pid, uid) : false; } int32_t PermissionController::noteOp(const String16& op, int32_t uid, const String16& packageName) { sp service = getService(); return service != nullptr ? service->noteOp(op, uid, packageName) : MODE_ERRORED; } void PermissionController::getPackagesForUid(const uid_t uid, Vector &packages) { sp service = getService(); if (service != nullptr) { service->getPackagesForUid(uid, packages); } } bool PermissionController::isRuntimePermission(const String16& permission) { sp service = getService(); return service != nullptr ? service->isRuntimePermission(permission) : false; } int PermissionController::getPackageUid(const String16& package, int flags) { sp service = getService(); return service != nullptr ? service->getPackageUid(package, flags) : -1; } }; // namespace android libs/binder/PersistableBundle.cpp0100644 0000000 0000000 00000037765 13756501735 016152 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "PersistableBundle" #include #include #include #include #include #include #include using android::BAD_TYPE; using android::BAD_VALUE; using android::NO_ERROR; using android::Parcel; using android::sp; using android::status_t; using android::UNEXPECTED_NULL; using std::map; using std::set; using std::vector; using namespace ::android::binder; enum { // Keep them in sync with BUNDLE_MAGIC* in frameworks/base/core/java/android/os/BaseBundle.java. BUNDLE_MAGIC = 0x4C444E42, BUNDLE_MAGIC_NATIVE = 0x4C444E44, }; namespace { template bool getValue(const android::String16& key, T* out, const map& map) { const auto& it = map.find(key); if (it == map.end()) return false; *out = it->second; return true; } template set getKeys(const map& map) { if (map.empty()) return set(); set keys; for (const auto& key_value_pair : map) { keys.emplace(key_value_pair.first); } return keys; } } // namespace namespace android { namespace os { #define RETURN_IF_FAILED(calledOnce) \ { \ status_t returnStatus = calledOnce; \ if (returnStatus) { \ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ return returnStatus; \ } \ } #define RETURN_IF_ENTRY_ERASED(map, key) \ { \ size_t num_erased = (map).erase(key); \ if (num_erased) { \ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ return num_erased; \ } \ } status_t PersistableBundle::writeToParcel(Parcel* parcel) const { /* * Keep implementation in sync with writeToParcelInner() in * frameworks/base/core/java/android/os/BaseBundle.java. */ // Special case for empty bundles. if (empty()) { RETURN_IF_FAILED(parcel->writeInt32(0)); return NO_ERROR; } size_t length_pos = parcel->dataPosition(); RETURN_IF_FAILED(parcel->writeInt32(1)); // dummy, will hold length RETURN_IF_FAILED(parcel->writeInt32(BUNDLE_MAGIC_NATIVE)); size_t start_pos = parcel->dataPosition(); RETURN_IF_FAILED(writeToParcelInner(parcel)); size_t end_pos = parcel->dataPosition(); // Backpatch length. This length value includes the length header. parcel->setDataPosition(length_pos); size_t length = end_pos - start_pos; if (length > std::numeric_limits::max()) { ALOGE("Parcel length (%zu) too large to store in 32-bit signed int", length); return BAD_VALUE; } RETURN_IF_FAILED(parcel->writeInt32(static_cast(length))); parcel->setDataPosition(end_pos); return NO_ERROR; } status_t PersistableBundle::readFromParcel(const Parcel* parcel) { /* * Keep implementation in sync with readFromParcelInner() in * frameworks/base/core/java/android/os/BaseBundle.java. */ int32_t length = parcel->readInt32(); if (length < 0) { ALOGE("Bad length in parcel: %d", length); return UNEXPECTED_NULL; } return readFromParcelInner(parcel, static_cast(length)); } bool PersistableBundle::empty() const { return size() == 0u; } size_t PersistableBundle::size() const { return (mBoolMap.size() + mIntMap.size() + mLongMap.size() + mDoubleMap.size() + mStringMap.size() + mBoolVectorMap.size() + mIntVectorMap.size() + mLongVectorMap.size() + mDoubleVectorMap.size() + mStringVectorMap.size() + mPersistableBundleMap.size()); } size_t PersistableBundle::erase(const String16& key) { RETURN_IF_ENTRY_ERASED(mBoolMap, key); RETURN_IF_ENTRY_ERASED(mIntMap, key); RETURN_IF_ENTRY_ERASED(mLongMap, key); RETURN_IF_ENTRY_ERASED(mDoubleMap, key); RETURN_IF_ENTRY_ERASED(mStringMap, key); RETURN_IF_ENTRY_ERASED(mBoolVectorMap, key); RETURN_IF_ENTRY_ERASED(mIntVectorMap, key); RETURN_IF_ENTRY_ERASED(mLongVectorMap, key); RETURN_IF_ENTRY_ERASED(mDoubleVectorMap, key); RETURN_IF_ENTRY_ERASED(mStringVectorMap, key); return mPersistableBundleMap.erase(key); } void PersistableBundle::putBoolean(const String16& key, bool value) { erase(key); mBoolMap[key] = value; } void PersistableBundle::putInt(const String16& key, int32_t value) { erase(key); mIntMap[key] = value; } void PersistableBundle::putLong(const String16& key, int64_t value) { erase(key); mLongMap[key] = value; } void PersistableBundle::putDouble(const String16& key, double value) { erase(key); mDoubleMap[key] = value; } void PersistableBundle::putString(const String16& key, const String16& value) { erase(key); mStringMap[key] = value; } void PersistableBundle::putBooleanVector(const String16& key, const vector& value) { erase(key); mBoolVectorMap[key] = value; } void PersistableBundle::putIntVector(const String16& key, const vector& value) { erase(key); mIntVectorMap[key] = value; } void PersistableBundle::putLongVector(const String16& key, const vector& value) { erase(key); mLongVectorMap[key] = value; } void PersistableBundle::putDoubleVector(const String16& key, const vector& value) { erase(key); mDoubleVectorMap[key] = value; } void PersistableBundle::putStringVector(const String16& key, const vector& value) { erase(key); mStringVectorMap[key] = value; } void PersistableBundle::putPersistableBundle(const String16& key, const PersistableBundle& value) { erase(key); mPersistableBundleMap[key] = value; } bool PersistableBundle::getBoolean(const String16& key, bool* out) const { return getValue(key, out, mBoolMap); } bool PersistableBundle::getInt(const String16& key, int32_t* out) const { return getValue(key, out, mIntMap); } bool PersistableBundle::getLong(const String16& key, int64_t* out) const { return getValue(key, out, mLongMap); } bool PersistableBundle::getDouble(const String16& key, double* out) const { return getValue(key, out, mDoubleMap); } bool PersistableBundle::getString(const String16& key, String16* out) const { return getValue(key, out, mStringMap); } bool PersistableBundle::getBooleanVector(const String16& key, vector* out) const { return getValue(key, out, mBoolVectorMap); } bool PersistableBundle::getIntVector(const String16& key, vector* out) const { return getValue(key, out, mIntVectorMap); } bool PersistableBundle::getLongVector(const String16& key, vector* out) const { return getValue(key, out, mLongVectorMap); } bool PersistableBundle::getDoubleVector(const String16& key, vector* out) const { return getValue(key, out, mDoubleVectorMap); } bool PersistableBundle::getStringVector(const String16& key, vector* out) const { return getValue(key, out, mStringVectorMap); } bool PersistableBundle::getPersistableBundle(const String16& key, PersistableBundle* out) const { return getValue(key, out, mPersistableBundleMap); } set PersistableBundle::getBooleanKeys() const { return getKeys(mBoolMap); } set PersistableBundle::getIntKeys() const { return getKeys(mIntMap); } set PersistableBundle::getLongKeys() const { return getKeys(mLongMap); } set PersistableBundle::getDoubleKeys() const { return getKeys(mDoubleMap); } set PersistableBundle::getStringKeys() const { return getKeys(mStringMap); } set PersistableBundle::getBooleanVectorKeys() const { return getKeys(mBoolVectorMap); } set PersistableBundle::getIntVectorKeys() const { return getKeys(mIntVectorMap); } set PersistableBundle::getLongVectorKeys() const { return getKeys(mLongVectorMap); } set PersistableBundle::getDoubleVectorKeys() const { return getKeys(mDoubleVectorMap); } set PersistableBundle::getStringVectorKeys() const { return getKeys(mStringVectorMap); } set PersistableBundle::getPersistableBundleKeys() const { return getKeys(mPersistableBundleMap); } status_t PersistableBundle::writeToParcelInner(Parcel* parcel) const { /* * To keep this implementation in sync with writeArrayMapInternal() in * frameworks/base/core/java/android/os/Parcel.java, the number of key * value pairs must be written into the parcel before writing the key-value * pairs themselves. */ size_t num_entries = size(); if (num_entries > std::numeric_limits::max()) { ALOGE("The size of this PersistableBundle (%zu) too large to store in 32-bit signed int", num_entries); return BAD_VALUE; } RETURN_IF_FAILED(parcel->writeInt32(static_cast(num_entries))); for (const auto& key_val_pair : mBoolMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_BOOLEAN)); RETURN_IF_FAILED(parcel->writeBool(key_val_pair.second)); } for (const auto& key_val_pair : mIntMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_INTEGER)); RETURN_IF_FAILED(parcel->writeInt32(key_val_pair.second)); } for (const auto& key_val_pair : mLongMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_LONG)); RETURN_IF_FAILED(parcel->writeInt64(key_val_pair.second)); } for (const auto& key_val_pair : mDoubleMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_DOUBLE)); RETURN_IF_FAILED(parcel->writeDouble(key_val_pair.second)); } for (const auto& key_val_pair : mStringMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_STRING)); RETURN_IF_FAILED(parcel->writeString16(key_val_pair.second)); } for (const auto& key_val_pair : mBoolVectorMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_BOOLEANARRAY)); RETURN_IF_FAILED(parcel->writeBoolVector(key_val_pair.second)); } for (const auto& key_val_pair : mIntVectorMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_INTARRAY)); RETURN_IF_FAILED(parcel->writeInt32Vector(key_val_pair.second)); } for (const auto& key_val_pair : mLongVectorMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_LONGARRAY)); RETURN_IF_FAILED(parcel->writeInt64Vector(key_val_pair.second)); } for (const auto& key_val_pair : mDoubleVectorMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_DOUBLEARRAY)); RETURN_IF_FAILED(parcel->writeDoubleVector(key_val_pair.second)); } for (const auto& key_val_pair : mStringVectorMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_STRINGARRAY)); RETURN_IF_FAILED(parcel->writeString16Vector(key_val_pair.second)); } for (const auto& key_val_pair : mPersistableBundleMap) { RETURN_IF_FAILED(parcel->writeString16(key_val_pair.first)); RETURN_IF_FAILED(parcel->writeInt32(VAL_PERSISTABLEBUNDLE)); RETURN_IF_FAILED(key_val_pair.second.writeToParcel(parcel)); } return NO_ERROR; } status_t PersistableBundle::readFromParcelInner(const Parcel* parcel, size_t length) { /* * Note: we don't actually use length for anything other than an empty PersistableBundle * check, since we do not actually need to copy in an entire Parcel, unlike in the Java * implementation. */ if (length == 0) { // Empty PersistableBundle or end of data. return NO_ERROR; } int32_t magic; RETURN_IF_FAILED(parcel->readInt32(&magic)); if (magic != BUNDLE_MAGIC && magic != BUNDLE_MAGIC_NATIVE) { ALOGE("Bad magic number for PersistableBundle: 0x%08x", magic); return BAD_VALUE; } /* * To keep this implementation in sync with unparcel() in * frameworks/base/core/java/android/os/BaseBundle.java, the number of * key-value pairs must be read from the parcel before reading the key-value * pairs themselves. */ int32_t num_entries; RETURN_IF_FAILED(parcel->readInt32(&num_entries)); for (; num_entries > 0; --num_entries) { String16 key; int32_t value_type; RETURN_IF_FAILED(parcel->readString16(&key)); RETURN_IF_FAILED(parcel->readInt32(&value_type)); /* * We assume that both the C++ and Java APIs ensure that all keys in a PersistableBundle * are unique. */ switch (value_type) { case VAL_STRING: { RETURN_IF_FAILED(parcel->readString16(&mStringMap[key])); break; } case VAL_INTEGER: { RETURN_IF_FAILED(parcel->readInt32(&mIntMap[key])); break; } case VAL_LONG: { RETURN_IF_FAILED(parcel->readInt64(&mLongMap[key])); break; } case VAL_DOUBLE: { RETURN_IF_FAILED(parcel->readDouble(&mDoubleMap[key])); break; } case VAL_BOOLEAN: { RETURN_IF_FAILED(parcel->readBool(&mBoolMap[key])); break; } case VAL_STRINGARRAY: { RETURN_IF_FAILED(parcel->readString16Vector(&mStringVectorMap[key])); break; } case VAL_INTARRAY: { RETURN_IF_FAILED(parcel->readInt32Vector(&mIntVectorMap[key])); break; } case VAL_LONGARRAY: { RETURN_IF_FAILED(parcel->readInt64Vector(&mLongVectorMap[key])); break; } case VAL_BOOLEANARRAY: { RETURN_IF_FAILED(parcel->readBoolVector(&mBoolVectorMap[key])); break; } case VAL_PERSISTABLEBUNDLE: { RETURN_IF_FAILED(mPersistableBundleMap[key].readFromParcel(parcel)); break; } case VAL_DOUBLEARRAY: { RETURN_IF_FAILED(parcel->readDoubleVector(&mDoubleVectorMap[key])); break; } default: { ALOGE("Unrecognized type: %d", value_type); return BAD_TYPE; break; } } } return NO_ERROR; } } // namespace os } // namespace android libs/binder/ProcessInfoService.cpp0100644 0000000 0000000 00000006263 13756501735 016303 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #include namespace android { ProcessInfoService::ProcessInfoService() { updateBinderLocked(); } status_t ProcessInfoService::getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states) { status_t err = NO_ERROR; sp pis; mProcessInfoLock.lock(); pis = mProcessInfoService; mProcessInfoLock.unlock(); for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) { if (pis != nullptr) { err = pis->getProcessStatesFromPids(length, /*in*/ pids, /*out*/ states); if (err == NO_ERROR) return NO_ERROR; // success if (IInterface::asBinder(pis)->isBinderAlive()) return err; } sleep(1); mProcessInfoLock.lock(); if (pis == mProcessInfoService) { updateBinderLocked(); } pis = mProcessInfoService; mProcessInfoLock.unlock(); } ALOGW("%s: Could not retrieve process states from ProcessInfoService after %d retries.", __FUNCTION__, BINDER_ATTEMPT_LIMIT); return TIMED_OUT; } status_t ProcessInfoService::getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states, /*out*/ int32_t *scores) { status_t err = NO_ERROR; sp pis; mProcessInfoLock.lock(); pis = mProcessInfoService; mProcessInfoLock.unlock(); for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) { if (pis != nullptr) { err = pis->getProcessStatesAndOomScoresFromPids(length, /*in*/ pids, /*out*/ states, /*out*/ scores); if (err == NO_ERROR) return NO_ERROR; // success if (IInterface::asBinder(pis)->isBinderAlive()) return err; } sleep(1); mProcessInfoLock.lock(); if (pis == mProcessInfoService) { updateBinderLocked(); } pis = mProcessInfoService; mProcessInfoLock.unlock(); } ALOGW("%s: Could not retrieve process states and scores " "from ProcessInfoService after %d retries.", __FUNCTION__, BINDER_ATTEMPT_LIMIT); return TIMED_OUT; } void ProcessInfoService::updateBinderLocked() { const sp sm(defaultServiceManager()); if (sm != nullptr) { const String16 name("processinfo"); mProcessInfoService = interface_cast(sm->checkService(name)); } } ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService); }; // namespace android libs/binder/ProcessState.cpp0100644 0000000 0000000 00000034133 13756501735 015144 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #define LOG_TAG "ProcessState" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) #define DEFAULT_MAX_BINDER_THREADS 15 #ifdef __ANDROID_VNDK__ const char* kDefaultDriver = "/dev/vndbinder"; #else const char* kDefaultDriver = "/dev/binder"; #endif // ------------------------------------------------------------------------- namespace android { class PoolThread : public Thread { public: explicit PoolThread(bool isMain) : mIsMain(isMain) { } protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; } const bool mIsMain; }; sp ProcessState::self() { Mutex::Autolock _l(gProcessMutex); if (gProcess != nullptr) { return gProcess; } gProcess = new ProcessState(kDefaultDriver); return gProcess; } sp ProcessState::initWithDriver(const char* driver) { Mutex::Autolock _l(gProcessMutex); if (gProcess != nullptr) { // Allow for initWithDriver to be called repeatedly with the same // driver. if (!strcmp(gProcess->getDriverName().c_str(), driver)) { return gProcess; } LOG_ALWAYS_FATAL("ProcessState was already initialized."); } if (access(driver, R_OK) == -1) { ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver); driver = "/dev/binder"; } gProcess = new ProcessState(driver); return gProcess; } sp ProcessState::selfOrNull() { Mutex::Autolock _l(gProcessMutex); return gProcess; } void ProcessState::setContextObject(const sp& object) { setContextObject(object, String16("default")); } sp ProcessState::getContextObject(const sp& /*caller*/) { return getStrongProxyForHandle(0); } void ProcessState::setContextObject(const sp& object, const String16& name) { AutoMutex _l(mLock); mContexts.add(name, object); } sp ProcessState::getContextObject(const String16& name, const sp& caller) { mLock.lock(); sp object( mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : nullptr); mLock.unlock(); //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); if (object != nullptr) return object; // Don't attempt to retrieve contexts if we manage them if (mManagesContexts) { ALOGE("getContextObject(%s) failed, but we manage the contexts!\n", String8(name).string()); return nullptr; } IPCThreadState* ipc = IPCThreadState::self(); { Parcel data, reply; // no interface token on this magic transaction data.writeString16(name); data.writeStrongBinder(caller); status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); if (result == NO_ERROR) { object = reply.readStrongBinder(); } } ipc->flushCommands(); if (object != nullptr) setContextObject(object, name); return object; } void ProcessState::startThreadPool() { AutoMutex _l(mLock); if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true); } } bool ProcessState::isContextManager(void) const { return mManagesContexts; } bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) { if (!mManagesContexts) { AutoMutex _l(mLock); mBinderContextCheckFunc = checkFunc; mBinderContextUserData = userData; flat_binder_object obj { .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, }; status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj); // fallback to original method if (result != 0) { android_errorWriteLog(0x534e4554, "121035042"); int dummy = 0; result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); } if (result == 0) { mManagesContexts = true; } else if (result == -1) { mBinderContextCheckFunc = nullptr; mBinderContextUserData = nullptr; ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); } } return mManagesContexts; } // Get references to userspace objects held by the kernel binder driver // Writes up to count elements into buf, and returns the total number // of references the kernel has, which may be larger than count. // buf may be NULL if count is 0. The pointers returned by this method // should only be used for debugging and not dereferenced, they may // already be invalid. ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf) { binder_node_debug_info info = {}; uintptr_t* end = buf ? buf + buf_count : nullptr; size_t count = 0; do { status_t result = ioctl(mDriverFD, BINDER_GET_NODE_DEBUG_INFO, &info); if (result < 0) { return -1; } if (info.ptr != 0) { if (buf && buf < end) *buf++ = info.ptr; count++; if (buf && buf < end) *buf++ = info.cookie; count++; } } while (info.ptr != 0); return count; } void ProcessState::setCallRestriction(CallRestriction restriction) { LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull(), "Call restrictions must be set before the threadpool is started."); mCallRestriction = restriction; } ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) { const size_t N=mHandleToObject.size(); if (N <= (size_t)handle) { handle_entry e; e.binder = nullptr; e.refs = nullptr; status_t err = mHandleToObject.insertAt(e, N, handle+1-N); if (err < NO_ERROR) return nullptr; } return &mHandleToObject.editItemAt(handle); } sp ProcessState::getStrongProxyForHandle(int32_t handle) { sp result; AutoMutex _l(mLock); handle_entry* e = lookupHandleLocked(handle); if (e != nullptr) { // We need to create a new BpBinder if there isn't currently one, OR we // are unable to acquire a weak reference on this current one. See comment // in getWeakProxyForHandle() for more info about this. IBinder* b = e->binder; if (b == nullptr || !e->refs->attemptIncWeak(this)) { if (handle == 0) { // Special case for context manager... // The context manager is the only object for which we create // a BpBinder proxy without already holding a reference. // Perform a dummy transaction to ensure the context manager // is registered before we create the first local reference // to it (which will occur when creating the BpBinder). // If a local reference is created for the BpBinder when the // context manager is not present, the driver will fail to // provide a reference to the context manager, but the // driver API does not return status. // // Note that this is not race-free if the context manager // dies while this code runs. // // TODO: add a driver API to wait for context manager, or // stop special casing handle 0 for context manager and add // a driver API to get a handle to the context manager with // proper reference counting. Parcel data; status_t status = IPCThreadState::self()->transact( 0, IBinder::PING_TRANSACTION, data, nullptr, 0); if (status == DEAD_OBJECT) return nullptr; } b = BpBinder::create(handle); e->binder = b; if (b) e->refs = b->getWeakRefs(); result = b; } else { // This little bit of nastyness is to allow us to add a primary // reference to the remote proxy when this team doesn't have one // but another team is sending the handle to us. result.force_set(b); e->refs->decWeak(this); } } return result; } wp ProcessState::getWeakProxyForHandle(int32_t handle) { wp result; AutoMutex _l(mLock); handle_entry* e = lookupHandleLocked(handle); if (e != nullptr) { // We need to create a new BpBinder if there isn't currently one, OR we // are unable to acquire a weak reference on this current one. The // attemptIncWeak() is safe because we know the BpBinder destructor will always // call expungeHandle(), which acquires the same lock we are holding now. // We need to do this because there is a race condition between someone // releasing a reference on this BpBinder, and a new reference on its handle // arriving from the driver. IBinder* b = e->binder; if (b == nullptr || !e->refs->attemptIncWeak(this)) { b = BpBinder::create(handle); result = b; e->binder = b; if (b) e->refs = b->getWeakRefs(); } else { result = b; e->refs->decWeak(this); } } return result; } void ProcessState::expungeHandle(int32_t handle, IBinder* binder) { AutoMutex _l(mLock); handle_entry* e = lookupHandleLocked(handle); // This handle may have already been replaced with a new BpBinder // (if someone failed the AttemptIncWeak() above); we don't want // to overwrite it. if (e && e->binder == binder) e->binder = nullptr; } String8 ProcessState::makeBinderThreadName() { int32_t s = android_atomic_add(1, &mThreadPoolSeq); pid_t pid = getpid(); String8 name; name.appendFormat("Binder:%d_%X", pid, s); return name; } void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp t = new PoolThread(isMain); t->run(name.string()); } } status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { status_t result = NO_ERROR; if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) { mMaxThreads = maxThreads; } else { result = -errno; ALOGE("Binder ioctl to set max threads failed: %s", strerror(-result)); } return result; } void ProcessState::giveThreadPoolName() { androidSetThreadName( makeBinderThreadName().string() ); } String8 ProcessState::getDriverName() { return mDriverName; } static int open_driver(const char *driver) { int fd = open(driver, O_RDWR | O_CLOEXEC); if (fd >= 0) { int vers = 0; status_t result = ioctl(fd, BINDER_VERSION, &vers); if (result == -1) { ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); close(fd); fd = -1; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d", vers, BINDER_CURRENT_PROTOCOL_VERSION, result); close(fd); fd = -1; } size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } } else { ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno)); } return fd; } ProcessState::ProcessState(const char *driver) : mDriverName(String8(driver)) , mDriverFD(open_driver(driver)) , mVMStart(MAP_FAILED) , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER) , mThreadCountDecrement(PTHREAD_COND_INITIALIZER) , mExecutingThreadsCount(0) , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs(0) , mManagesContexts(false) , mBinderContextCheckFunc(nullptr) , mBinderContextUserData(nullptr) , mThreadPoolStarted(false) , mThreadPoolSeq(1) , mCallRestriction(CallRestriction::NONE) { if (mDriverFD >= 0) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str()); close(mDriverFD); mDriverFD = -1; mDriverName.clear(); } } LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); } ProcessState::~ProcessState() { if (mDriverFD >= 0) { if (mVMStart != MAP_FAILED) { munmap(mVMStart, BINDER_VM_SIZE); } close(mDriverFD); } mDriverFD = -1; } }; // namespace android libs/binder/Static.cpp0100644 0000000 0000000 00000004502 13756501735 013751 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ // All static variables go here, to control initialization and // destruction order in the library. #include #include #include #include namespace android { // ------------ Text output streams Vector gTextBuffers; class LogTextOutput : public BufferedTextOutput { public: LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } virtual ~LogTextOutput() { }; protected: virtual status_t writeLines(const struct iovec& vec, size_t N) { //android_writevLog(&vec, N); <-- this is now a no-op if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N); ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base); return NO_ERROR; } }; class FdTextOutput : public BufferedTextOutput { public: explicit FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } virtual ~FdTextOutput() { }; protected: virtual status_t writeLines(const struct iovec& vec, size_t N) { writev(mFD, &vec, N); return NO_ERROR; } private: int mFD; }; static LogTextOutput gLogTextOutput; static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); static FdTextOutput gStderrTextOutput(STDERR_FILENO); TextOutput& alog(gLogTextOutput); TextOutput& aout(gStdoutTextOutput); TextOutput& aerr(gStderrTextOutput); // ------------ ProcessState.cpp Mutex& gProcessMutex = *new Mutex; sp gProcess; // ------------ IServiceManager.cpp Mutex gDefaultServiceManagerLock; sp gDefaultServiceManager; #ifndef __ANDROID_VNDK__ sp gPermissionController; #endif bool gSystemBootCompleted = false; } // namespace android libs/binder/Status.cpp0100644 0000000 0000000 00000020121 13756501735 014000 0ustar000000000 0000000 /* * 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. */ #include namespace android { namespace binder { Status Status::ok() { return Status(); } Status Status::fromExceptionCode(int32_t exceptionCode) { if (exceptionCode == EX_TRANSACTION_FAILED) { return Status(exceptionCode, FAILED_TRANSACTION); } return Status(exceptionCode, OK); } Status Status::fromExceptionCode(int32_t exceptionCode, const String8& message) { if (exceptionCode == EX_TRANSACTION_FAILED) { return Status(exceptionCode, FAILED_TRANSACTION, message); } return Status(exceptionCode, OK, message); } Status Status::fromExceptionCode(int32_t exceptionCode, const char* message) { return fromExceptionCode(exceptionCode, String8(message)); } Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode) { return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode); } Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode, const String8& message) { return Status(EX_SERVICE_SPECIFIC, serviceSpecificErrorCode, message); } Status Status::fromServiceSpecificError(int32_t serviceSpecificErrorCode, const char* message) { return fromServiceSpecificError(serviceSpecificErrorCode, String8(message)); } Status Status::fromStatusT(status_t status) { Status ret; ret.setFromStatusT(status); return ret; } std::string Status::exceptionToString(int32_t exceptionCode) { switch (exceptionCode) { #define EXCEPTION_TO_CASE(EXCEPTION) case EXCEPTION: return #EXCEPTION; EXCEPTION_TO_CASE(EX_NONE) EXCEPTION_TO_CASE(EX_SECURITY) EXCEPTION_TO_CASE(EX_BAD_PARCELABLE) EXCEPTION_TO_CASE(EX_ILLEGAL_ARGUMENT) EXCEPTION_TO_CASE(EX_NULL_POINTER) EXCEPTION_TO_CASE(EX_ILLEGAL_STATE) EXCEPTION_TO_CASE(EX_NETWORK_MAIN_THREAD) EXCEPTION_TO_CASE(EX_UNSUPPORTED_OPERATION) EXCEPTION_TO_CASE(EX_SERVICE_SPECIFIC) EXCEPTION_TO_CASE(EX_PARCELABLE) EXCEPTION_TO_CASE(EX_HAS_REPLY_HEADER) EXCEPTION_TO_CASE(EX_TRANSACTION_FAILED) #undef EXCEPTION_TO_CASE default: return std::to_string(exceptionCode); } } Status::Status(int32_t exceptionCode, int32_t errorCode) : mException(exceptionCode), mErrorCode(errorCode) {} Status::Status(int32_t exceptionCode, int32_t errorCode, const String8& message) : mException(exceptionCode), mErrorCode(errorCode), mMessage(message) {} status_t Status::readFromParcel(const Parcel& parcel) { status_t status = parcel.readInt32(&mException); if (status != OK) { setFromStatusT(status); return status; } // Skip over fat response headers. Not used (or propagated) in native code. if (mException == EX_HAS_REPLY_HEADER) { // Note that the header size includes the 4 byte size field. const size_t header_start = parcel.dataPosition(); // Get available size before reading more const size_t header_avail = parcel.dataAvail(); int32_t header_size; status = parcel.readInt32(&header_size); if (status != OK) { setFromStatusT(status); return status; } if (header_size < 0 || static_cast(header_size) > header_avail) { android_errorWriteLog(0x534e4554, "132650049"); setFromStatusT(UNKNOWN_ERROR); return UNKNOWN_ERROR; } parcel.setDataPosition(header_start + header_size); // And fat response headers are currently only used when there are no // exceptions, so act like there was no error. mException = EX_NONE; } if (mException == EX_NONE) { return status; } // The remote threw an exception. Get the message back. String16 message; status = parcel.readString16(&message); if (status != OK) { setFromStatusT(status); return status; } mMessage = String8(message); // Skip over the remote stack trace data int32_t remote_stack_trace_header_size; status = parcel.readInt32(&remote_stack_trace_header_size); if (status != OK) { setFromStatusT(status); return status; } if (remote_stack_trace_header_size < 0 || static_cast(remote_stack_trace_header_size) > parcel.dataAvail()) { android_errorWriteLog(0x534e4554, "132650049"); setFromStatusT(UNKNOWN_ERROR); return UNKNOWN_ERROR; } parcel.setDataPosition(parcel.dataPosition() + remote_stack_trace_header_size); if (mException == EX_SERVICE_SPECIFIC) { status = parcel.readInt32(&mErrorCode); } else if (mException == EX_PARCELABLE) { // Skip over the blob of Parcelable data const size_t header_start = parcel.dataPosition(); // Get available size before reading more const size_t header_avail = parcel.dataAvail(); int32_t header_size; status = parcel.readInt32(&header_size); if (status != OK) { setFromStatusT(status); return status; } if (header_size < 0 || static_cast(header_size) > header_avail) { android_errorWriteLog(0x534e4554, "132650049"); setFromStatusT(UNKNOWN_ERROR); return UNKNOWN_ERROR; } parcel.setDataPosition(header_start + header_size); } if (status != OK) { setFromStatusT(status); return status; } return status; } status_t Status::writeToParcel(Parcel* parcel) const { // Something really bad has happened, and we're not going to even // try returning rich error data. if (mException == EX_TRANSACTION_FAILED) { return mErrorCode; } status_t status = parcel->writeInt32(mException); if (status != OK) { return status; } if (mException == EX_NONE) { // We have no more information to write. return status; } status = parcel->writeString16(String16(mMessage)); status = parcel->writeInt32(0); // Empty remote stack trace header if (mException == EX_SERVICE_SPECIFIC) { status = parcel->writeInt32(mErrorCode); } else if (mException == EX_PARCELABLE) { // Sending Parcelable blobs currently not supported status = parcel->writeInt32(0); } return status; } void Status::setException(int32_t ex, const String8& message) { mException = ex; mErrorCode = ex == EX_TRANSACTION_FAILED ? FAILED_TRANSACTION : NO_ERROR; mMessage.setTo(message); } void Status::setServiceSpecificError(int32_t errorCode, const String8& message) { setException(EX_SERVICE_SPECIFIC, message); mErrorCode = errorCode; } void Status::setFromStatusT(status_t status) { mException = (status == NO_ERROR) ? EX_NONE : EX_TRANSACTION_FAILED; mErrorCode = status; mMessage.clear(); } String8 Status::toString8() const { String8 ret; if (mException == EX_NONE) { ret.append("No error"); } else { ret.appendFormat("Status(%d, %s): '", mException, exceptionToString(mException).c_str()); if (mException == EX_SERVICE_SPECIFIC || mException == EX_TRANSACTION_FAILED) { ret.appendFormat("%d: ", mErrorCode); } ret.append(String8(mMessage)); ret.append("'"); } return ret; } std::stringstream& operator<< (std::stringstream& stream, const Status& s) { stream << s.toString8().string(); return stream; } } // namespace binder } // namespace android libs/binder/TextOutput.cpp0100644 0000000 0000000 00000003572 13756501735 014675 0ustar000000000 0000000 /* * Copyright (C) 2005 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. */ #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- TextOutput::TextOutput() { } TextOutput::~TextOutput() { } // --------------------------------------------------------------------------- static void textOutputPrinter(void* cookie, const char* txt) { ((TextOutput*)cookie)->print(txt, strlen(txt)); } TextOutput& operator<<(TextOutput& to, const TypeCode& val) { printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); return to; } HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) : mBuffer(buf) , mSize(size) , mBytesPerLine(bytesPerLine) , mSingleLineCutoff(16) , mAlignment(4) , mCArrayStyle(false) { if (bytesPerLine >= 16) mAlignment = 4; else if (bytesPerLine >= 8) mAlignment = 2; else mAlignment = 1; } TextOutput& operator<<(TextOutput& to, const HexDump& val) { printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), val.singleLineCutoff(), val.alignment(), val.carrayStyle(), textOutputPrinter, (void*)&to); return to; } }; // namespace android libs/binder/Value.cpp0100644 0000000 0000000 00000034714 13756501735 013606 0ustar000000000 0000000 /* * 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. */ #define LOG_TAG "Value" #include #include #include #include #include #include #include #include using android::BAD_TYPE; using android::BAD_VALUE; using android::NO_ERROR; using android::UNEXPECTED_NULL; using android::Parcel; using android::sp; using android::status_t; using std::map; using std::set; using std::vector; using android::binder::Value; using android::IBinder; using android::os::PersistableBundle; using namespace android::binder; // ==================================================================== #define RETURN_IF_FAILED(calledOnce) \ do { \ status_t returnStatus = calledOnce; \ if (returnStatus) { \ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ return returnStatus; \ } \ } while(false) // ==================================================================== /* These `internal_type_ptr()` functions allow this * class to work without C++ RTTI support. This technique * only works properly when called directly from this file, * but that is OK because that is the only place we will * be calling them from. */ template const void* internal_type_ptr() { static const T *marker; return (void*)▮ } /* Allows the type to be specified by the argument * instead of inside angle brackets. */ template const void* internal_type_ptr(const T&) { return internal_type_ptr(); } // ==================================================================== namespace android { namespace binder { class Value::ContentBase { public: virtual ~ContentBase() = default; virtual const void* type_ptr() const = 0; virtual ContentBase * clone() const = 0; virtual bool operator==(const ContentBase& rhs) const = 0; #ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO virtual const std::type_info &type() const = 0; #endif template bool get(T* out) const; }; /* This is the actual class that holds the value. */ template class Value::Content : public Value::ContentBase { public: Content() = default; explicit Content(const T & value) : mValue(value) { } virtual ~Content() = default; #ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO virtual const std::type_info &type() const override { return typeid(T); } #endif virtual const void* type_ptr() const override { return internal_type_ptr(); } virtual ContentBase * clone() const override { return new Content(mValue); }; virtual bool operator==(const ContentBase& rhs) const override { if (type_ptr() != rhs.type_ptr()) { return false; } return mValue == static_cast* >(&rhs)->mValue; } T mValue; }; template bool Value::ContentBase::get(T* out) const { if (internal_type_ptr(*out) != type_ptr()) { return false; } *out = static_cast*>(this)->mValue; return true; } // ==================================================================== Value::Value() : mContent(nullptr) { } Value::Value(const Value& value) : mContent(value.mContent ? value.mContent->clone() : nullptr) { } Value::~Value() { delete mContent; } bool Value::operator==(const Value& rhs) const { const Value& lhs(*this); if (lhs.empty() && rhs.empty()) { return true; } if ( (lhs.mContent == nullptr) || (rhs.mContent == nullptr) ) { return false; } return *lhs.mContent == *rhs.mContent; } Value& Value::swap(Value &rhs) { std::swap(mContent, rhs.mContent); return *this; } Value& Value::operator=(const Value& rhs) { if (this != &rhs) { delete mContent; mContent = rhs.mContent ? rhs.mContent->clone() : nullptr; } return *this; } bool Value::empty() const { return mContent == nullptr; } void Value::clear() { delete mContent; mContent = nullptr; } int32_t Value::parcelType() const { const void* t_info(mContent ? mContent->type_ptr() : nullptr); if (t_info == internal_type_ptr()) return VAL_BOOLEAN; if (t_info == internal_type_ptr()) return VAL_BYTE; if (t_info == internal_type_ptr()) return VAL_INTEGER; if (t_info == internal_type_ptr()) return VAL_LONG; if (t_info == internal_type_ptr()) return VAL_DOUBLE; if (t_info == internal_type_ptr()) return VAL_STRING; if (t_info == internal_type_ptr>()) return VAL_BOOLEANARRAY; if (t_info == internal_type_ptr>()) return VAL_BYTEARRAY; if (t_info == internal_type_ptr>()) return VAL_INTARRAY; if (t_info == internal_type_ptr>()) return VAL_LONGARRAY; if (t_info == internal_type_ptr>()) return VAL_DOUBLEARRAY; if (t_info == internal_type_ptr>()) return VAL_STRINGARRAY; if (t_info == internal_type_ptr()) return VAL_MAP; if (t_info == internal_type_ptr()) return VAL_PERSISTABLEBUNDLE; return VAL_NULL; } #ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO const std::type_info& Value::type() const { return mContent != nullptr ? mContent->type() : typeid(void); } #endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO #define DEF_TYPE_ACCESSORS(T, TYPENAME) \ bool Value::is ## TYPENAME() const \ { \ return mContent \ ? internal_type_ptr() == mContent->type_ptr() \ : false; \ } \ bool Value::get ## TYPENAME(T* out) const \ { \ return mContent \ ? mContent->get(out) \ : false; \ } \ void Value::put ## TYPENAME(const T& in) \ { \ *this = in; \ } \ Value& Value::operator=(const T& rhs) \ { \ delete mContent; \ mContent = new Content< T >(rhs); \ return *this; \ } \ Value::Value(const T& value) \ : mContent(new Content< T >(value)) \ { } DEF_TYPE_ACCESSORS(bool, Boolean) DEF_TYPE_ACCESSORS(int8_t, Byte) DEF_TYPE_ACCESSORS(int32_t, Int) DEF_TYPE_ACCESSORS(int64_t, Long) DEF_TYPE_ACCESSORS(double, Double) DEF_TYPE_ACCESSORS(String16, String) DEF_TYPE_ACCESSORS(std::vector, BooleanVector) DEF_TYPE_ACCESSORS(std::vector, ByteVector) DEF_TYPE_ACCESSORS(std::vector, IntVector) DEF_TYPE_ACCESSORS(std::vector, LongVector) DEF_TYPE_ACCESSORS(std::vector, DoubleVector) DEF_TYPE_ACCESSORS(std::vector, StringVector) DEF_TYPE_ACCESSORS(::android::binder::Map, Map) DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle) bool Value::getString(String8* out) const { String16 val; bool ret = getString(&val); if (ret) { *out = String8(val); } return ret; } bool Value::getString(::std::string* out) const { String8 val; bool ret = getString(&val); if (ret) { *out = val.string(); } return ret; } status_t Value::writeToParcel(Parcel* parcel) const { // This implementation needs to be kept in sync with the writeValue // implementation in frameworks/base/core/java/android/os/Parcel.java #define BEGIN_HANDLE_WRITE() \ do { \ const void* t_info(mContent?mContent->type_ptr():nullptr); \ if (false) { } #define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \ else if (t_info == internal_type_ptr()) { \ RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast*>(mContent)->mValue)); \ } #define HANDLE_WRITE_PARCELABLE(T, TYPEVAL) \ else if (t_info == internal_type_ptr()) { \ RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ RETURN_IF_FAILED(static_cast*>(mContent)->mValue.writeToParcel(parcel)); \ } #define END_HANDLE_WRITE() \ else { \ ALOGE("writeToParcel: Type not supported"); \ return BAD_TYPE; \ } \ } while (false); BEGIN_HANDLE_WRITE() HANDLE_WRITE_TYPE(bool, VAL_BOOLEAN, writeBool) HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) HANDLE_WRITE_TYPE(int32_t, VAL_INTEGER, writeInt32) HANDLE_WRITE_TYPE(int64_t, VAL_LONG, writeInt64) HANDLE_WRITE_TYPE(double, VAL_DOUBLE, writeDouble) HANDLE_WRITE_TYPE(String16, VAL_STRING, writeString16) HANDLE_WRITE_TYPE(vector, VAL_BOOLEANARRAY, writeBoolVector) HANDLE_WRITE_TYPE(vector, VAL_BYTEARRAY, writeByteVector) HANDLE_WRITE_TYPE(vector, VAL_BYTEARRAY, writeByteVector) HANDLE_WRITE_TYPE(vector, VAL_INTARRAY, writeInt32Vector) HANDLE_WRITE_TYPE(vector, VAL_LONGARRAY, writeInt64Vector) HANDLE_WRITE_TYPE(vector, VAL_DOUBLEARRAY, writeDoubleVector) HANDLE_WRITE_TYPE(vector, VAL_STRINGARRAY, writeString16Vector) HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) END_HANDLE_WRITE() return NO_ERROR; #undef BEGIN_HANDLE_WRITE #undef HANDLE_WRITE_TYPE #undef HANDLE_WRITE_PARCELABLE #undef END_HANDLE_WRITE } status_t Value::readFromParcel(const Parcel* parcel) { // This implementation needs to be kept in sync with the readValue // implementation in frameworks/base/core/java/android/os/Parcel.javai #define BEGIN_HANDLE_READ() \ switch(value_type) { \ default: \ ALOGE("readFromParcel: Parcel type %d is not supported", value_type); \ return BAD_TYPE; #define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD) \ case TYPEVAL: \ mContent = new Content(); \ RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast*>(mContent)->mValue)); \ break; #define HANDLE_READ_PARCELABLE(T, TYPEVAL) \ case TYPEVAL: \ mContent = new Content(); \ RETURN_IF_FAILED(static_cast*>(mContent)->mValue.readFromParcel(parcel)); \ break; #define END_HANDLE_READ() \ } int32_t value_type = VAL_NULL; delete mContent; mContent = nullptr; RETURN_IF_FAILED(parcel->readInt32(&value_type)); BEGIN_HANDLE_READ() HANDLE_READ_TYPE(bool, VAL_BOOLEAN, readBool) HANDLE_READ_TYPE(int8_t, VAL_BYTE, readByte) HANDLE_READ_TYPE(int32_t, VAL_INTEGER, readInt32) HANDLE_READ_TYPE(int64_t, VAL_LONG, readInt64) HANDLE_READ_TYPE(double, VAL_DOUBLE, readDouble) HANDLE_READ_TYPE(String16, VAL_STRING, readString16) HANDLE_READ_TYPE(vector, VAL_BOOLEANARRAY, readBoolVector) HANDLE_READ_TYPE(vector, VAL_BYTEARRAY, readByteVector) HANDLE_READ_TYPE(vector, VAL_INTARRAY, readInt32Vector) HANDLE_READ_TYPE(vector, VAL_LONGARRAY, readInt64Vector) HANDLE_READ_TYPE(vector, VAL_DOUBLEARRAY, readDoubleVector) HANDLE_READ_TYPE(vector, VAL_STRINGARRAY, readString16Vector) HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) END_HANDLE_READ() return NO_ERROR; #undef BEGIN_HANDLE_READ #undef HANDLE_READ_TYPE #undef HANDLE_READ_PARCELABLE #undef END_HANDLE_READ } } // namespace binder } // namespace android /* vim: set ts=4 sw=4 tw=0 et :*/ libs/binder/aidl/0040755 0000000 0000000 00000000000 13756501735 012731 5ustar000000000 0000000 libs/binder/aidl/android/0040755 0000000 0000000 00000000000 13756501735 014351 5ustar000000000 0000000 libs/binder/aidl/android/content/0040755 0000000 0000000 00000000000 13756501735 016023 5ustar000000000 0000000 libs/binder/aidl/android/content/pm/0040755 0000000 0000000 00000000000 13756501735 016437 5ustar000000000 0000000 libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl0100644 0000000 0000000 00000006457 13756501735 023411 0ustar000000000 0000000 /* ** ** Copyright 2017, 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.content.pm; /** * Parallel implementation of certain {@link PackageManager} APIs that need to * be exposed to native code. *

These APIs are a parallel definition to the APIs in PackageManager, so, * they can technically diverge. However, it's good practice to keep these * APIs in sync with each other. *

Because these APIs are exposed to native code, it's possible they will * be exposed to privileged components [such as UID 0]. Care should be taken * to avoid exposing potential security holes for methods where permission * checks are bypassed based upon UID alone. * * @hide */ interface IPackageManagerNative { /** * Returns a set of names for the given UIDs. * IMPORTANT: Unlike the Java version of this API, unknown UIDs are * not represented by 'null's. Instead, they are represented by empty * strings. */ @utf8InCpp String[] getNamesForUids(in int[] uids); /** * Returns the name of the installer (a package) which installed the named * package. Preloaded packages return the string "preload". Sideloaded packages * return an empty string. Unknown or unknowable are returned as empty strings. */ @utf8InCpp String getInstallerForPackage(in String packageName); /** * Returns the version code of the named package. * Unknown or unknowable versions are returned as 0. */ long getVersionCodeForPackage(in String packageName); /** * Return if each app, identified by its package name allows its audio to be recorded. * Unknown packages are mapped to false. */ boolean[] isAudioPlaybackCaptureAllowed(in @utf8InCpp String[] packageNames); /* ApplicationInfo.isSystemApp() == true */ const int LOCATION_SYSTEM = 0x1; /* ApplicationInfo.isVendor() == true */ const int LOCATION_VENDOR = 0x2; /* ApplicationInfo.isProduct() == true */ const int LOCATION_PRODUCT = 0x4; /** * Returns a set of bitflags about package location. * LOCATION_SYSTEM: getApplicationInfo(packageName).isSystemApp() * LOCATION_VENDOR: getApplicationInfo(packageName).isVendor() * LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct() */ int getLocationFlags(in @utf8InCpp String packageName); /** * Returns the target SDK version for the given package. * Unknown packages will cause the call to fail. The caller must check the * returned Status before using the result of this function. */ int getTargetSdkVersionForPackage(in String packageName); /** * Returns the name of module metadata package, or empty string if device doesn't have such * package. */ @utf8InCpp String getModuleMetadataPackageName(); } libs/binder/aidl/android/content/pm/OWNERS0100644 0000000 0000000 00000000116 13756501735 017372 0ustar000000000 0000000 narayan@google.com patb@google.com svetoslavganov@google.com toddke@google.comlibs/binder/include/0040755 0000000 0000000 00000000000 13756501735 013443 5ustar000000000 0000000 libs/binder/include/binder/0040755 0000000 0000000 00000000000 13756501735 014706 5ustar000000000 0000000 libs/binder/include/binder/ActivityManager.h0100644 0000000 0000000 00000006403 13756501735 020146 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_ACTIVITY_MANAGER_H #define ANDROID_ACTIVITY_MANAGER_H #ifndef __ANDROID_VNDK__ #include #include // --------------------------------------------------------------------------- namespace android { class ActivityManager { public: enum { // Flag for registerUidObserver: report uid state changed UID_OBSERVER_PROCSTATE = 1<<0, // Flag for registerUidObserver: report uid gone UID_OBSERVER_GONE = 1<<1, // Flag for registerUidObserver: report uid has become idle UID_OBSERVER_IDLE = 1<<2, // Flag for registerUidObserver: report uid has become active UID_OBSERVER_ACTIVE = 1<<3 }; enum { PROCESS_STATE_UNKNOWN = -1, PROCESS_STATE_PERSISTENT = 0, PROCESS_STATE_PERSISTENT_UI = 1, PROCESS_STATE_TOP = 2, PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3, PROCESS_STATE_BOUND_TOP = 4, PROCESS_STATE_FOREGROUND_SERVICE = 5, PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6, PROCESS_STATE_IMPORTANT_FOREGROUND = 7, PROCESS_STATE_IMPORTANT_BACKGROUND = 8, PROCESS_STATE_TRANSIENT_BACKGROUND = 9, PROCESS_STATE_BACKUP = 10, PROCESS_STATE_SERVICE = 11, PROCESS_STATE_RECEIVER = 12, PROCESS_STATE_TOP_SLEEPING = 13, PROCESS_STATE_HEAVY_WEIGHT = 14, PROCESS_STATE_HOME = 15, PROCESS_STATE_LAST_ACTIVITY = 16, PROCESS_STATE_CACHED_ACTIVITY = 17, PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18, PROCESS_STATE_CACHED_RECENT = 19, PROCESS_STATE_CACHED_EMPTY = 20, PROCESS_STATE_NONEXISTENT = 21, }; ActivityManager(); int openContentUri(const String16& stringUri); void registerUidObserver(const sp& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage); void unregisterUidObserver(const sp& observer); bool isUidActive(const uid_t uid, const String16& callingPackage); int getUidProcessState(const uid_t uid, const String16& callingPackage); status_t linkToDeath(const sp& recipient); status_t unlinkToDeath(const sp& recipient); private: Mutex mLock; sp mService; sp getService(); }; }; // namespace android // --------------------------------------------------------------------------- #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_ACTIVITY_MANAGER_H libs/binder/include/binder/AppOpsManager.h0100644 0000000 0000000 00000010771 13756501735 017557 0ustar000000000 0000000 /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_APP_OPS_MANAGER_H #define ANDROID_APP_OPS_MANAGER_H #ifndef __ANDROID_VNDK__ #include #include // --------------------------------------------------------------------------- namespace android { class AppOpsManager { public: enum { MODE_ALLOWED = IAppOpsService::MODE_ALLOWED, MODE_IGNORED = IAppOpsService::MODE_IGNORED, MODE_ERRORED = IAppOpsService::MODE_ERRORED }; enum { OP_NONE = -1, OP_COARSE_LOCATION = 0, OP_FINE_LOCATION = 1, OP_GPS = 2, OP_VIBRATE = 3, OP_READ_CONTACTS = 4, OP_WRITE_CONTACTS = 5, OP_READ_CALL_LOG = 6, OP_WRITE_CALL_LOG = 7, OP_READ_CALENDAR = 8, OP_WRITE_CALENDAR = 9, OP_WIFI_SCAN = 10, OP_POST_NOTIFICATION = 11, OP_NEIGHBORING_CELLS = 12, OP_CALL_PHONE = 13, OP_READ_SMS = 14, OP_WRITE_SMS = 15, OP_RECEIVE_SMS = 16, OP_RECEIVE_EMERGECY_SMS = 17, OP_RECEIVE_MMS = 18, OP_RECEIVE_WAP_PUSH = 19, OP_SEND_SMS = 20, OP_READ_ICC_SMS = 21, OP_WRITE_ICC_SMS = 22, OP_WRITE_SETTINGS = 23, OP_SYSTEM_ALERT_WINDOW = 24, OP_ACCESS_NOTIFICATIONS = 25, OP_CAMERA = 26, OP_RECORD_AUDIO = 27, OP_PLAY_AUDIO = 28, OP_READ_CLIPBOARD = 29, OP_WRITE_CLIPBOARD = 30, OP_TAKE_MEDIA_BUTTONS = 31, OP_TAKE_AUDIO_FOCUS = 32, OP_AUDIO_MASTER_VOLUME = 33, OP_AUDIO_VOICE_VOLUME = 34, OP_AUDIO_RING_VOLUME = 35, OP_AUDIO_MEDIA_VOLUME = 36, OP_AUDIO_ALARM_VOLUME = 37, OP_AUDIO_NOTIFICATION_VOLUME = 38, OP_AUDIO_BLUETOOTH_VOLUME = 39, OP_WAKE_LOCK = 40, OP_MONITOR_LOCATION = 41, OP_MONITOR_HIGH_POWER_LOCATION = 42, OP_GET_USAGE_STATS = 43, OP_MUTE_MICROPHONE = 44, OP_TOAST_WINDOW = 45, OP_PROJECT_MEDIA = 46, OP_ACTIVATE_VPN = 47, OP_WRITE_WALLPAPER = 48, OP_ASSIST_STRUCTURE = 49, OP_ASSIST_SCREENSHOT = 50, OP_READ_PHONE_STATE = 51, OP_ADD_VOICEMAIL = 52, OP_USE_SIP = 53, OP_PROCESS_OUTGOING_CALLS = 54, OP_USE_FINGERPRINT = 55, OP_BODY_SENSORS = 56, OP_AUDIO_ACCESSIBILITY_VOLUME = 64, OP_READ_PHONE_NUMBERS = 65, OP_REQUEST_INSTALL_PACKAGES = 66, OP_PICTURE_IN_PICTURE = 67, OP_INSTANT_APP_START_FOREGROUND = 68, OP_ANSWER_PHONE_CALLS = 69, OP_RUN_ANY_IN_BACKGROUND = 70, OP_CHANGE_WIFI_STATE = 71, OP_REQUEST_DELETE_PACKAGES = 72, OP_BIND_ACCESSIBILITY_SERVICE = 73, OP_ACCEPT_HANDOVER = 74, OP_MANAGE_IPSEC_TUNNELS = 75, OP_START_FOREGROUND = 76, OP_BLUETOOTH_SCAN = 77, OP_USE_BIOMETRIC = 78, }; AppOpsManager(); int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, const String16& callingPackage); int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault); void finishOp(int32_t op, int32_t uid, const String16& callingPackage); void startWatchingMode(int32_t op, const String16& packageName, const sp& callback); void stopWatchingMode(const sp& callback); int32_t permissionToOpCode(const String16& permission); private: Mutex mLock; sp mService; sp getService(); }; }; // namespace android // --------------------------------------------------------------------------- #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_APP_OPS_MANAGER_H libs/binder/include/binder/Binder.h0100644 0000000 0000000 00000007753 13756501735 016273 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BINDER_H #define ANDROID_BINDER_H #include #include #include // --------------------------------------------------------------------------- namespace android { class BBinder : public IBinder { public: BBinder(); virtual const String16& getInterfaceDescriptor() const; virtual bool isBinderAlive() const; virtual status_t pingBinder(); virtual status_t dump(int fd, const Vector& args); // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp& recipient, void* cookie = nullptr, uint32_t flags = 0); // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp& recipient, void* cookie = nullptr, uint32_t flags = 0, wp* outRecipient = nullptr); virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func); virtual void* findObject(const void* objectID) const; virtual void detachObject(const void* objectID); virtual BBinder* localBinder(); bool isRequestingSid(); // This must be called before the object is sent to another process. Not thread safe. void setRequestingSid(bool requestSid); protected: virtual ~BBinder(); // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); private: BBinder(const BBinder& o); BBinder& operator=(const BBinder& o); class Extras; Extras* getOrCreateExtras(); std::atomic mExtras; void* mReserved0; }; // --------------------------------------------------------------------------- class BpRefBase : public virtual RefBase { protected: explicit BpRefBase(const sp& o); virtual ~BpRefBase(); virtual void onFirstRef(); virtual void onLastStrongRef(const void* id); virtual bool onIncStrongAttempted(uint32_t flags, const void* id); inline IBinder* remote() { return mRemote; } inline IBinder* remote() const { return mRemote; } private: BpRefBase(const BpRefBase& o); BpRefBase& operator=(const BpRefBase& o); IBinder* const mRemote; RefBase::weakref_type* mRefs; std::atomic mState; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_BINDER_H libs/binder/include/binder/BinderService.h0100644 0000000 0000000 00000004067 13756501735 017607 0ustar000000000 0000000 /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BINDER_SERVICE_H #define ANDROID_BINDER_SERVICE_H #include #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { template class BinderService { public: static status_t publish(bool allowIsolated = false, int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) { sp sm(defaultServiceManager()); return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated, dumpFlags); } static void publishAndJoinThreadPool( bool allowIsolated = false, int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) { publish(allowIsolated, dumpFlags); joinThreadPool(); } static void instantiate() { publish(); } static status_t shutdown() { return NO_ERROR; } private: static void joinThreadPool() { sp ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); IPCThreadState::self()->joinThreadPool(); } }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_BINDER_SERVICE_H libs/binder/include/binder/BpBinder.h0100644 0000000 0000000 00000012701 13756501735 016542 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BPBINDER_H #define ANDROID_BPBINDER_H #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { using binder_proxy_limit_callback = void(*)(int); class BpBinder : public IBinder { public: static BpBinder* create(int32_t handle); inline int32_t handle() const { return mHandle; } virtual const String16& getInterfaceDescriptor() const; virtual bool isBinderAlive() const; virtual status_t pingBinder(); virtual status_t dump(int fd, const Vector& args); // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp& recipient, void* cookie = nullptr, uint32_t flags = 0); // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp& recipient, void* cookie = nullptr, uint32_t flags = 0, wp* outRecipient = nullptr); virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func); virtual void* findObject(const void* objectID) const; virtual void detachObject(const void* objectID); virtual BpBinder* remoteBinder(); status_t setConstantData(const void* data, size_t size); void sendObituary(); static uint32_t getBinderProxyCount(uint32_t uid); static void getCountByUid(Vector& uids, Vector& counts); static void enableCountByUid(); static void disableCountByUid(); static void setCountByUidEnabled(bool enable); static void setLimitCallback(binder_proxy_limit_callback cb); static void setBinderProxyCountWatermarks(int high, int low); class ObjectManager { public: ObjectManager(); ~ObjectManager(); void attach( const void* objectID, void* object, void* cleanupCookie, IBinder::object_cleanup_func func); void* find(const void* objectID) const; void detach(const void* objectID); void kill(); private: ObjectManager(const ObjectManager&); ObjectManager& operator=(const ObjectManager&); struct entry_t { void* object; void* cleanupCookie; IBinder::object_cleanup_func func; }; KeyedVector mObjects; }; protected: BpBinder(int32_t handle,int32_t trackedUid); virtual ~BpBinder(); virtual void onFirstRef(); virtual void onLastStrongRef(const void* id); virtual bool onIncStrongAttempted(uint32_t flags, const void* id); private: const int32_t mHandle; struct Obituary { wp recipient; void* cookie; uint32_t flags; }; void reportOneDeath(const Obituary& obit); bool isDescriptorCached() const; mutable Mutex mLock; volatile int32_t mAlive; volatile int32_t mObitsSent; Vector* mObituaries; ObjectManager mObjects; Parcel* mConstantData; mutable String16 mDescriptorCache; int32_t mTrackedUid; static Mutex sTrackingLock; static std::unordered_map sTrackingMap; static int sNumTrackedUids; static std::atomic_bool sCountByUidEnabled; static binder_proxy_limit_callback sLimitCallback; static uint32_t sBinderProxyCountHighWatermark; static uint32_t sBinderProxyCountLowWatermark; static bool sBinderProxyThrottleCreate; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_BPBINDER_H libs/binder/include/binder/BufferedTextOutput.h0100644 0000000 0000000 00000003632 13756501735 020670 0ustar000000000 0000000 /* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BUFFEREDTEXTOUTPUT_H #define ANDROID_BUFFEREDTEXTOUTPUT_H #include #include #include // --------------------------------------------------------------------------- namespace android { class BufferedTextOutput : public TextOutput { public: //** Flags for constructor */ enum { MULTITHREADED = 0x0001 }; explicit BufferedTextOutput(uint32_t flags = 0); virtual ~BufferedTextOutput(); virtual status_t print(const char* txt, size_t len); virtual void moveIndent(int delta); virtual void pushBundle(); virtual void popBundle(); protected: virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; private: struct BufferState; struct ThreadState; static ThreadState*getThreadState(); static void threadDestructor(void *st); BufferState*getBuffer() const; uint32_t mFlags; const int32_t mSeq; const int32_t mIndex; Mutex mLock; BufferState* mGlobalState; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_BUFFEREDTEXTOUTPUT_H libs/binder/include/binder/Debug.h0100644 0000000 0000000 00000002776 13756501735 016116 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BINDER_DEBUG_H #define ANDROID_BINDER_DEBUG_H #include #include #include namespace android { // --------------------------------------------------------------------------- __BEGIN_DECLS const char* stringForIndent(int32_t indentLevel); typedef void (*debugPrintFunc)(void* cookie, const char* txt); void printTypeCode(uint32_t typeCode, debugPrintFunc func = nullptr, void* cookie = nullptr); void printHexData(int32_t indent, const void *buf, size_t length, size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, size_t alignment=0, bool cArrayStyle=false, debugPrintFunc func = nullptr, void* cookie = nullptr); ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf); __END_DECLS // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_BINDER_DEBUG_H libs/binder/include/binder/IActivityManager.h0100644 0000000 0000000 00000004047 13756501735 020261 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_IACTIVITY_MANAGER_H #define ANDROID_IACTIVITY_MANAGER_H #ifndef __ANDROID_VNDK__ #include #include namespace android { // ------------------------------------------------------------------------------------ class IActivityManager : public IInterface { public: DECLARE_META_INTERFACE(ActivityManager) virtual int openContentUri(const String16& stringUri) = 0; virtual void registerUidObserver(const sp& observer, const int32_t event, const int32_t cutpoint, const String16& callingPackage) = 0; virtual void unregisterUidObserver(const sp& observer) = 0; virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0; virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_UID_OBSERVER_TRANSACTION, UNREGISTER_UID_OBSERVER_TRANSACTION, IS_UID_ACTIVE_TRANSACTION, GET_UID_PROCESS_STATE_TRANSACTION }; }; // ------------------------------------------------------------------------------------ }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IACTIVITY_MANAGER_H libs/binder/include/binder/IAppOpsCallback.h0100644 0000000 0000000 00000003341 13756501735 020005 0ustar000000000 0000000 /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #ifndef ANDROID_IAPP_OPS_CALLBACK_H #define ANDROID_IAPP_OPS_CALLBACK_H #ifndef __ANDROID_VNDK__ #include namespace android { // ---------------------------------------------------------------------- class IAppOpsCallback : public IInterface { public: DECLARE_META_INTERFACE(AppOpsCallback) virtual void opChanged(int32_t op, const String16& packageName) = 0; enum { OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION }; }; // ---------------------------------------------------------------------- class BnAppOpsCallback : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IAPP_OPS_CALLBACK_H libs/binder/include/binder/IAppOpsService.h0100644 0000000 0000000 00000006454 13756501735 017721 0ustar000000000 0000000 /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #ifndef ANDROID_IAPP_OPS_SERVICE_H #define ANDROID_IAPP_OPS_SERVICE_H #ifndef __ANDROID_VNDK__ #include #include namespace android { // ---------------------------------------------------------------------- class IAppOpsService : public IInterface { public: DECLARE_META_INTERFACE(AppOpsService) virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0; virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0; virtual int32_t startOperation(const sp& token, int32_t code, int32_t uid, const String16& packageName, bool startIfModeDefault) = 0; virtual void finishOperation(const sp& token, int32_t code, int32_t uid, const String16& packageName) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp& callback) = 0; virtual void stopWatchingMode(const sp& callback) = 0; virtual sp getToken(const sp& clientToken) = 0; virtual int32_t permissionToOpCode(const String16& permission) = 0; virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid, const String16& packageName) = 0; enum { CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1, START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2, FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3, START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4, STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5, GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6, PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7, CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8, }; enum { MODE_ALLOWED = 0, MODE_IGNORED = 1, MODE_ERRORED = 2 }; }; // ---------------------------------------------------------------------- class BnAppOpsService : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IAPP_OPS_SERVICE_H libs/binder/include/binder/IBatteryStats.h0100644 0000000 0000000 00000005412 13756501735 017620 0ustar000000000 0000000 /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_IBATTERYSTATS_H #define ANDROID_IBATTERYSTATS_H #ifndef __ANDROID_VNDK__ #include namespace android { // ---------------------------------------------------------------------- class IBatteryStats : public IInterface { public: DECLARE_META_INTERFACE(BatteryStats) virtual void noteStartSensor(int uid, int sensor) = 0; virtual void noteStopSensor(int uid, int sensor) = 0; virtual void noteStartVideo(int uid) = 0; virtual void noteStopVideo(int uid) = 0; virtual void noteStartAudio(int uid) = 0; virtual void noteStopAudio(int uid) = 0; virtual void noteResetVideo() = 0; virtual void noteResetAudio() = 0; virtual void noteFlashlightOn(int uid) = 0; virtual void noteFlashlightOff(int uid) = 0; virtual void noteStartCamera(int uid) = 0; virtual void noteStopCamera(int uid) = 0; virtual void noteResetCamera() = 0; virtual void noteResetFlashlight() = 0; enum { NOTE_START_SENSOR_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, NOTE_STOP_SENSOR_TRANSACTION, NOTE_START_VIDEO_TRANSACTION, NOTE_STOP_VIDEO_TRANSACTION, NOTE_START_AUDIO_TRANSACTION, NOTE_STOP_AUDIO_TRANSACTION, NOTE_RESET_VIDEO_TRANSACTION, NOTE_RESET_AUDIO_TRANSACTION, NOTE_FLASHLIGHT_ON_TRANSACTION, NOTE_FLASHLIGHT_OFF_TRANSACTION, NOTE_START_CAMERA_TRANSACTION, NOTE_STOP_CAMERA_TRANSACTION, NOTE_RESET_CAMERA_TRANSACTION, NOTE_RESET_FLASHLIGHT_TRANSACTION }; }; // ---------------------------------------------------------------------- class BnBatteryStats : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IBATTERYSTATS_H libs/binder/include/binder/IBinder.h0100644 0000000 0000000 00000017502 13756501735 016375 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_IBINDER_H #define ANDROID_IBINDER_H #include #include #include #include // linux/binder.h already defines this, but we can't just include it from there // because there are host builds that include this file. #ifndef B_PACK_CHARS #define B_PACK_CHARS(c1, c2, c3, c4) \ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) #endif // B_PACK_CHARS // --------------------------------------------------------------------------- namespace android { class BBinder; class BpBinder; class IInterface; class Parcel; class IResultReceiver; class IShellCallback; /** * Base class and low-level protocol for a remotable object. * You can derive from this class to create an object for which other * processes can hold references to it. Communication between processes * (method calls, property get and set) is down through a low-level * protocol implemented on top of the transact() API. */ class [[clang::lto_visibility_public]] IBinder : public virtual RefBase { public: enum { FIRST_CALL_TRANSACTION = 0x00000001, LAST_CALL_TRANSACTION = 0x00ffffff, PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'), INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'), // Corresponds to TF_ONE_WAY -- an asynchronous call. FLAG_ONEWAY = 0x00000001 }; IBinder(); /** * Check if this IBinder implements the interface named by * @a descriptor. If it does, the base pointer to it is returned, * which you can safely static_cast<> to the concrete C++ interface. */ virtual sp queryLocalInterface(const String16& descriptor); /** * Return the canonical name of the interface provided by this IBinder * object. */ virtual const String16& getInterfaceDescriptor() const = 0; virtual bool isBinderAlive() const = 0; virtual status_t pingBinder() = 0; virtual status_t dump(int fd, const Vector& args) = 0; static status_t shellCommand(const sp& target, int in, int out, int err, Vector& args, const sp& callback, const sp& resultReceiver); // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) = 0; // DeathRecipient is pure abstract, there is no virtual method // implementation to put in a translation unit in order to silence the // weak vtables warning. #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif class DeathRecipient : public virtual RefBase { public: virtual void binderDied(const wp& who) = 0; }; #if defined(__clang__) #pragma clang diagnostic pop #endif /** * Register the @a recipient for a notification if this binder * goes away. If this binder object unexpectedly goes away * (typically because its hosting process has been killed), * then DeathRecipient::binderDied() will be called with a reference * to this. * * The @a cookie is optional -- if non-NULL, it should be a * memory address that you own (that is, you know it is unique). * * @note You will only receive death notifications for remote binders, * as local binders by definition can't die without you dying as well. * Trying to use this function on a local binder will result in an * INVALID_OPERATION code being returned and nothing happening. * * @note This link always holds a weak reference to its recipient. * * @note You will only receive a weak reference to the dead * binder. You should not try to promote this to a strong reference. * (Nor should you need to, as there is nothing useful you can * directly do with it now that it has passed on.) */ // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp& recipient, void* cookie = nullptr, uint32_t flags = 0) = 0; /** * Remove a previously registered death notification. * The @a recipient will no longer be called if this object * dies. The @a cookie is optional. If non-NULL, you can * supply a NULL @a recipient, and the recipient previously * added with that cookie will be unlinked. * * If the binder is dead, this will return DEAD_OBJECT. Deleting * the object will also unlink all death recipients. */ // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp& recipient, void* cookie = nullptr, uint32_t flags = 0, wp* outRecipient = nullptr) = 0; virtual bool checkSubclass(const void* subclassID) const; typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); /** * This object is attached for the lifetime of this binder object. When * this binder object is destructed, the cleanup function of all attached * objects are invoked with their respective objectID, object, and * cleanupCookie. Access to these APIs can be made from multiple threads, * but calls from different threads are allowed to be interleaved. */ virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) = 0; /** * Returns object attached with attachObject. */ virtual void* findObject(const void* objectID) const = 0; /** * WARNING: this API does not call the cleanup function for legacy reasons. * It also does not return void* for legacy reasons. If you need to detach * an object and destroy it, there are two options: * - if you can, don't call detachObject and instead wait for the destructor * to clean it up. * - manually retrieve and destruct the object (if multiple of your threads * are accessing these APIs, you must guarantee that attachObject isn't * called after findObject and before detachObject is called). */ virtual void detachObject(const void* objectID) = 0; virtual BBinder* localBinder(); virtual BpBinder* remoteBinder(); protected: virtual ~IBinder(); private: }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_IBINDER_H libs/binder/include/binder/IInterface.h0100644 0000000 0000000 00000015522 13756501735 017072 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #ifndef ANDROID_IINTERFACE_H #define ANDROID_IINTERFACE_H #include namespace android { // ---------------------------------------------------------------------- class IInterface : public virtual RefBase { public: IInterface(); static sp asBinder(const IInterface*); static sp asBinder(const sp&); protected: virtual ~IInterface(); virtual IBinder* onAsBinder() = 0; }; // ---------------------------------------------------------------------- template inline sp interface_cast(const sp& obj) { return INTERFACE::asInterface(obj); } // ---------------------------------------------------------------------- template class BnInterface : public INTERFACE, public BBinder { public: virtual sp queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; protected: typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; // ---------------------------------------------------------------------- template class BpInterface : public INTERFACE, public BpRefBase { public: explicit BpInterface(const sp& remote); protected: typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; // ---------------------------------------------------------------------- #define DECLARE_META_INTERFACE(INTERFACE) \ public: \ static const ::android::String16 descriptor; \ static ::android::sp asInterface( \ const ::android::sp<::android::IBinder>& obj); \ virtual const ::android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \ static bool setDefaultImpl(std::unique_ptr impl); \ static const std::unique_ptr& getDefaultImpl(); \ private: \ static std::unique_ptr default_impl; \ public: \ #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ const ::android::String16 I##INTERFACE::descriptor(NAME); \ const ::android::String16& \ I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ ::android::sp I##INTERFACE::asInterface( \ const ::android::sp<::android::IBinder>& obj) \ { \ ::android::sp intr; \ if (obj != nullptr) { \ intr = static_cast( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ if (intr == nullptr) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \ std::unique_ptr I##INTERFACE::default_impl; \ bool I##INTERFACE::setDefaultImpl(std::unique_ptr impl)\ { \ if (!I##INTERFACE::default_impl && impl) { \ I##INTERFACE::default_impl = std::move(impl); \ return true; \ } \ return false; \ } \ const std::unique_ptr& I##INTERFACE::getDefaultImpl() \ { \ return I##INTERFACE::default_impl; \ } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { } \ #define CHECK_INTERFACE(interface, data, reply) \ do { \ if (!(data).checkInterface(this)) { return PERMISSION_DENIED; } \ } while (false) \ // ---------------------------------------------------------------------- // No user-serviceable parts after this... template inline sp BnInterface::queryLocalInterface( const String16& _descriptor) { if (_descriptor == INTERFACE::descriptor) return this; return nullptr; } template inline const String16& BnInterface::getInterfaceDescriptor() const { return INTERFACE::getInterfaceDescriptor(); } template IBinder* BnInterface::onAsBinder() { return this; } template inline BpInterface::BpInterface(const sp& remote) : BpRefBase(remote) { } template inline IBinder* BpInterface::onAsBinder() { return remote(); } // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IINTERFACE_H libs/binder/include/binder/IMediaResourceMonitor.h0100644 0000000 0000000 00000003440 13756501735 021265 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_I_MEDIA_RESOURCE_MONITOR_H #define ANDROID_I_MEDIA_RESOURCE_MONITOR_H #ifndef __ANDROID_VNDK__ #include namespace android { // ---------------------------------------------------------------------- class IMediaResourceMonitor : public IInterface { public: DECLARE_META_INTERFACE(MediaResourceMonitor) // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX. enum { TYPE_VIDEO_CODEC = 0, TYPE_AUDIO_CODEC = 1, }; virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0; enum { NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION, }; }; // ---------------------------------------------------------------------- class BnMediaResourceMonitor : public BnInterface { public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H libs/binder/include/binder/IMemory.h0100644 0000000 0000000 00000005366 13756501735 016447 0ustar000000000 0000000 /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_IMEMORY_H #define ANDROID_IMEMORY_H #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class IMemoryHeap : public IInterface { public: DECLARE_META_INTERFACE(MemoryHeap) // flags returned by getFlags() enum { READ_ONLY = 0x00000001 }; virtual int getHeapID() const = 0; virtual void* getBase() const = 0; virtual size_t getSize() const = 0; virtual uint32_t getFlags() const = 0; virtual off_t getOffset() const = 0; // these are there just for backward source compatibility int32_t heapID() const { return getHeapID(); } void* base() const { return getBase(); } size_t virtualSize() const { return getSize(); } }; class BnMemoryHeap : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); BnMemoryHeap(); protected: virtual ~BnMemoryHeap(); }; // ---------------------------------------------------------------------------- class IMemory : public IInterface { public: DECLARE_META_INTERFACE(Memory) // NOLINTNEXTLINE(google-default-arguments) virtual sp getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0; // helpers void* fastPointer(const sp& heap, ssize_t offset) const; void* pointer() const; size_t size() const; ssize_t offset() const; }; class BnMemory : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); BnMemory(); protected: virtual ~BnMemory(); }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IMEMORY_H libs/binder/include/binder/IPCThreadState.h0100644 0000000 0000000 00000022355 13756501735 017627 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_IPC_THREAD_STATE_H #define ANDROID_IPC_THREAD_STATE_H #include #include #include #include #if defined(_WIN32) typedef int uid_t; #endif // --------------------------------------------------------------------------- namespace android { class IPCThreadStateBase; class IPCThreadState { public: static IPCThreadState* self(); static IPCThreadState* selfOrNull(); // self(), but won't instantiate sp process(); status_t clearLastError(); pid_t getCallingPid() const; // nullptr if unavailable // // this can't be restored once it's cleared, and it does not return the // context of the current process when not in a binder call. const char* getCallingSid() const; uid_t getCallingUid() const; void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; // See Binder#setCallingWorkSourceUid in Binder.java. int64_t setCallingWorkSourceUid(uid_t uid); // Internal only. Use setCallingWorkSourceUid(uid) instead. int64_t setCallingWorkSourceUidWithoutPropagation(uid_t uid); // See Binder#getCallingWorkSourceUid in Binder.java. uid_t getCallingWorkSourceUid() const; // See Binder#clearCallingWorkSource in Binder.java. int64_t clearCallingWorkSource(); // See Binder#restoreCallingWorkSource in Binder.java. void restoreCallingWorkSource(int64_t token); void clearPropagateWorkSource(); bool shouldPropagateWorkSource() const; void setLastTransactionBinderFlags(int32_t flags); int32_t getLastTransactionBinderFlags() const; int64_t clearCallingIdentity(); // Restores PID/UID (not SID) void restoreCallingIdentity(int64_t token); int setupPolling(int* fd); status_t handlePolledCommands(); void flushCommands(); void joinThreadPool(bool isMain = true); // Stop the local process. void stopProcess(bool immediate = true); status_t transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); void incStrongHandle(int32_t handle, BpBinder *proxy); void decStrongHandle(int32_t handle); void incWeakHandle(int32_t handle, BpBinder *proxy); void decWeakHandle(int32_t handle); status_t attemptIncStrongHandle(int32_t handle); static void expungeHandle(int32_t handle, IBinder* binder); status_t requestDeathNotification( int32_t handle, BpBinder* proxy); status_t clearDeathNotification( int32_t handle, BpBinder* proxy); static void shutdown(); // Call this to disable switching threads to background scheduling when // receiving incoming IPC calls. This is specifically here for the // Android system process, since it expects to have background apps calling // in to it but doesn't want to acquire locks in its services while in // the background. static void disableBackgroundScheduling(bool disable); bool backgroundSchedulingDisabled(); // Call blocks until the number of executing binder threads is less than // the maximum number of binder threads threads allowed for this process. void blockUntilThreadAvailable(); // Is this thread currently serving a binder call. This method // returns true if while traversing backwards from the function call // stack for this thread, we encounter a function serving a binder // call before encountering a hwbinder call / hitting the end of the // call stack. // Eg: If thread T1 went through the following call pattern // 1) T1 receives and executes hwbinder call H1. // 2) While handling H1, T1 makes binder call B1. // 3) The handler of B1, calls into T1 with a callback B2. // If isServingCall() is called during H1 before 3), this method // will return false, else true. // // ---- // | B2 | ---> While callback B2 is being handled, during 3). // ---- // | H1 | ---> While H1 is being handled. // ---- // Fig: Thread Call stack while handling B2 // // This is since after 3), while traversing the thread call stack, // we hit a binder call before a hwbinder call / end of stack. This // method may be typically used to determine whether to use // hardware::IPCThreadState methods or IPCThreadState methods to // infer information about thread state. bool isServingCall() const; // The work source represents the UID of the process we should attribute the transaction // to. We use -1 to specify that the work source was not set using #setWorkSource. // // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java // side. static const int32_t kUnsetWorkSource = -1; private: IPCThreadState(); ~IPCThreadState(); status_t sendReply(const Parcel& reply, uint32_t flags); status_t waitForResponse(Parcel *reply, status_t *acquireResult=nullptr); status_t talkWithDriver(bool doReceive=true); status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer); status_t getAndExecuteCommand(); status_t executeCommand(int32_t command); void processPendingDerefs(); void processPostWriteDerefs(); void clearCaller(); static void threadDestructor(void *st); static void freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsSize, void* cookie); const sp mProcess; Vector mPendingStrongDerefs; Vector mPendingWeakDerefs; Vector mPostWriteStrongDerefs; Vector mPostWriteWeakDerefs; Parcel mIn; Parcel mOut; status_t mLastError; pid_t mCallingPid; const char* mCallingSid; uid_t mCallingUid; // The UID of the process who is responsible for this transaction. // This is used for resource attribution. int32_t mWorkSource; // Whether the work source should be propagated. bool mPropagateWorkSource; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; IPCThreadStateBase *mIPCThreadStateBase; ProcessState::CallRestriction mCallRestriction; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_IPC_THREAD_STATE_H libs/binder/include/binder/IPermissionController.h0100644 0000000 0000000 00000004650 13756501735 021366 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #ifndef ANDROID_IPERMISSION_CONTROLLER_H #define ANDROID_IPERMISSION_CONTROLLER_H #ifndef __ANDROID_VNDK__ #include #include namespace android { // ---------------------------------------------------------------------- class IPermissionController : public IInterface { public: DECLARE_META_INTERFACE(PermissionController) virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) = 0; virtual int32_t noteOp(const String16& op, int32_t uid, const String16& packageName) = 0; virtual void getPackagesForUid(const uid_t uid, Vector &packages) = 0; virtual bool isRuntimePermission(const String16& permission) = 0; virtual int getPackageUid(const String16& package, int flags) = 0; enum { CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, NOTE_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1, GET_PACKAGES_FOR_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 2, IS_RUNTIME_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 3, GET_PACKAGE_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 4 }; }; // ---------------------------------------------------------------------- class BnPermissionController : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IPERMISSION_CONTROLLER_H libs/binder/include/binder/IProcessInfoService.h0100644 0000000 0000000 00000003520 13756501735 020740 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_I_PROCESS_INFO_SERVICE_H #define ANDROID_I_PROCESS_INFO_SERVICE_H #ifndef __ANDROID_VNDK__ #include namespace android { // ---------------------------------------------------------------------- class IProcessInfoService : public IInterface { public: DECLARE_META_INTERFACE(ProcessInfoService) virtual status_t getProcessStatesFromPids( size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states) = 0; virtual status_t getProcessStatesAndOomScoresFromPids( size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states, /*out*/ int32_t* scores) = 0; enum { GET_PROCESS_STATES_FROM_PIDS = IBinder::FIRST_CALL_TRANSACTION, GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS, }; }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_I_PROCESS_INFO_SERVICE_H libs/binder/include/binder/IResultReceiver.h0100644 0000000 0000000 00000003066 13756501735 020135 0ustar000000000 0000000 /* * 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. */ // #ifndef ANDROID_IRESULT_RECEIVER_H #define ANDROID_IRESULT_RECEIVER_H #include namespace android { // ---------------------------------------------------------------------- class IResultReceiver : public IInterface { public: DECLARE_META_INTERFACE(ResultReceiver) virtual void send(int32_t resultCode) = 0; enum { OP_SEND = IBinder::FIRST_CALL_TRANSACTION }; }; // ---------------------------------------------------------------------- class BnResultReceiver : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IRESULT_RECEIVER_H libs/binder/include/binder/IServiceManager.h0100644 0000000 0000000 00000006706 13756501735 020071 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #ifndef ANDROID_ISERVICE_MANAGER_H #define ANDROID_ISERVICE_MANAGER_H #include #include #include namespace android { // ---------------------------------------------------------------------- class IServiceManager : public IInterface { public: DECLARE_META_INTERFACE(ServiceManager) /** * Must match values in IServiceManager.java */ /* Allows services to dump sections according to priorities. */ static const int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0; static const int DUMP_FLAG_PRIORITY_HIGH = 1 << 1; static const int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2; /** * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the * same priority as NORMAL priority but the services are not called with dump priority * arguments. */ static const int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3; static const int DUMP_FLAG_PRIORITY_ALL = DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT; static const int DUMP_FLAG_PROTO = 1 << 4; /** * Retrieve an existing service, blocking for a few seconds * if it doesn't yet exist. */ virtual sp getService( const String16& name) const = 0; /** * Retrieve an existing service, non-blocking. */ virtual sp checkService( const String16& name) const = 0; /** * Register a service. */ // NOLINTNEXTLINE(google-default-arguments) virtual status_t addService(const String16& name, const sp& service, bool allowIsolated = false, int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0; /** * Return list of all existing services. */ // NOLINTNEXTLINE(google-default-arguments) virtual Vector listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0; enum { GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION, }; }; sp defaultServiceManager(); template status_t getService(const String16& name, sp* outService) { const sp sm = defaultServiceManager(); if (sm != nullptr) { *outService = interface_cast(sm->getService(name)); if ((*outService) != nullptr) return NO_ERROR; } return NAME_NOT_FOUND; } bool checkCallingPermission(const String16& permission); bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid); bool checkPermission(const String16& permission, pid_t pid, uid_t uid); }; // namespace android #endif // ANDROID_ISERVICE_MANAGER_H libs/binder/include/binder/IShellCallback.h0100644 0000000 0000000 00000003203 13756501735 017647 0ustar000000000 0000000 /* * 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. */ // #ifndef ANDROID_ISHELL_CALLBACK_H #define ANDROID_ISHELL_CALLBACK_H #include namespace android { // ---------------------------------------------------------------------- class IShellCallback : public IInterface { public: DECLARE_META_INTERFACE(ShellCallback); virtual int openFile(const String16& path, const String16& seLinuxContext, const String16& mode) = 0; enum { OP_OPEN_OUTPUT_FILE = IBinder::FIRST_CALL_TRANSACTION }; }; // ---------------------------------------------------------------------- class BnShellCallback : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_ISHELL_CALLBACK_H libs/binder/include/binder/IUidObserver.h0100644 0000000 0000000 00000003737 13756501735 017430 0ustar000000000 0000000 /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #ifndef ANDROID_IUID_OBSERVER_H #define ANDROID_IUID_OBSERVER_H #ifndef __ANDROID_VNDK__ #include namespace android { // ---------------------------------------------------------------------- class IUidObserver : public IInterface { public: DECLARE_META_INTERFACE(UidObserver) virtual void onUidGone(uid_t uid, bool disabled) = 0; virtual void onUidActive(uid_t uid) = 0; virtual void onUidIdle(uid_t uid, bool disabled) = 0; virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0; enum { ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, ON_UID_ACTIVE_TRANSACTION, ON_UID_IDLE_TRANSACTION, ON_UID_STATE_CHANGED_TRANSACTION }; }; // ---------------------------------------------------------------------- class BnUidObserver : public BnInterface { public: // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IUID_OBSERVER_H libs/binder/include/binder/IpPrefix.h0100644 0000000 0000000 00000005120 13756501735 016600 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_IP_PREFIX_H #define ANDROID_IP_PREFIX_H #ifndef __ANDROID_VNDK__ #include #include #include #include namespace android { namespace net { /* * C++ implementation of the Java class android.net.IpPrefix */ class IpPrefix : public Parcelable { public: IpPrefix() = default; virtual ~IpPrefix() = default; IpPrefix(const IpPrefix& prefix) = default; IpPrefix(const struct in6_addr& addr, int32_t plen): mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { } IpPrefix(const struct in_addr& addr, int32_t plen): mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { } bool getAddressAsIn6Addr(struct in6_addr* addr) const; bool getAddressAsInAddr(struct in_addr* addr) const; const struct in6_addr& getAddressAsIn6Addr() const; const struct in_addr& getAddressAsInAddr() const; bool isIpv6() const; bool isIpv4() const; int32_t getPrefixLength() const; void setAddress(const struct in6_addr& addr); void setAddress(const struct in_addr& addr); void setPrefixLength(int32_t prefix); friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs); friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) { return !(lhs == rhs); } public: // Overrides status_t writeToParcel(Parcel* parcel) const override; status_t readFromParcel(const Parcel* parcel) override; private: union InternalUnion { InternalUnion() = default; explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }; explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }; struct in6_addr mIn6Addr; struct in_addr mInAddr; } mUnion; int32_t mPrefixLength; bool mIsIpv6; }; } // namespace net } // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_IP_PREFIX_H libs/binder/include/binder/Map.h0100644 0000000 0000000 00000002134 13756501735 015571 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_MAP_H #define ANDROID_MAP_H #include #include // --------------------------------------------------------------------------- namespace android { namespace binder { class Value; /** * Convenience typedef for ::std::map<::std::string,::android::binder::Value> */ typedef ::std::map<::std::string, Value> Map; } // namespace binder } // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_MAP_H libs/binder/include/binder/MemoryBase.h0100644 0000000 0000000 00000002706 13756501735 017124 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_MEMORY_BASE_H #define ANDROID_MEMORY_BASE_H #include #include #include namespace android { // --------------------------------------------------------------------------- class MemoryBase : public BnMemory { public: MemoryBase(const sp& heap, ssize_t offset, size_t size); virtual ~MemoryBase(); virtual sp getMemory(ssize_t* offset, size_t* size) const; protected: size_t getSize() const { return mSize; } ssize_t getOffset() const { return mOffset; } const sp& getHeap() const { return mHeap; } private: size_t mSize; ssize_t mOffset; sp mHeap; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_MEMORY_BASE_H libs/binder/include/binder/MemoryDealer.h0100644 0000000 0000000 00000003623 13756501735 017445 0ustar000000000 0000000 /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_MEMORY_DEALER_H #define ANDROID_MEMORY_DEALER_H #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class SimpleBestFitAllocator; // ---------------------------------------------------------------------------- class MemoryDealer : public RefBase { public: explicit MemoryDealer(size_t size, const char* name = nullptr, uint32_t flags = 0 /* or bits such as MemoryHeapBase::READ_ONLY */ ); virtual sp allocate(size_t size); virtual void deallocate(size_t offset); virtual void dump(const char* what) const; // allocations are aligned to some value. return that value so clients can account for it. static size_t getAllocationAlignment(); sp getMemoryHeap() const { return heap(); } protected: virtual ~MemoryDealer(); private: const sp& heap() const; SimpleBestFitAllocator* allocator() const; sp mHeap; SimpleBestFitAllocator* mAllocator; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_MEMORY_DEALER_H libs/binder/include/binder/MemoryHeapBase.h0100644 0000000 0000000 00000005675 13756501735 017732 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_MEMORY_HEAP_BASE_H #define ANDROID_MEMORY_HEAP_BASE_H #include #include #include namespace android { // --------------------------------------------------------------------------- class MemoryHeapBase : public virtual BnMemoryHeap { public: enum { READ_ONLY = IMemoryHeap::READ_ONLY, // memory won't be mapped locally, but will be mapped in the remote // process. DONT_MAP_LOCALLY = 0x00000100, NO_CACHING = 0x00000200 }; /* * maps the memory referenced by fd. but DOESN'T take ownership * of the filedescriptor (it makes a copy with dup() */ MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0); /* * maps memory from the given device */ explicit MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); /* * maps memory from ashmem, with the given name for debugging */ explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr); virtual ~MemoryHeapBase(); /* implement IMemoryHeap interface */ virtual int getHeapID() const; /* virtual address of the heap. returns MAP_FAILED in case of error */ virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; off_t getOffset() const override; const char* getDevice() const; /* this closes this heap -- use carefully */ void dispose(); /* this is only needed as a workaround, use only if you know * what you are doing */ status_t setDevice(const char* device) { if (mDevice == nullptr) mDevice = device; return mDevice ? NO_ERROR : ALREADY_EXISTS; } protected: MemoryHeapBase(); // init() takes ownership of fd status_t init(int fd, void *base, size_t size, int flags = 0, const char* device = nullptr); private: status_t mapfd(int fd, size_t size, off_t offset = 0); int mFD; size_t mSize; void* mBase; uint32_t mFlags; const char* mDevice; bool mNeedUnmap; off_t mOffset; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_MEMORY_HEAP_BASE_H libs/binder/include/binder/Parcel.h0100644 0000000 0000000 00000105707 13756501735 016274 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_PARCEL_H #define ANDROID_PARCEL_H #include #include #include #include #include #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { template class Flattenable; template class LightFlattenable; class IBinder; class IPCThreadState; class ProcessState; class String8; class TextOutput; namespace binder { class Value; }; class Parcel { friend class IPCThreadState; public: class ReadableBlob; class WritableBlob; Parcel(); ~Parcel(); const uint8_t* data() const; size_t dataSize() const; size_t dataAvail() const; size_t dataPosition() const; size_t dataCapacity() const; status_t setDataSize(size_t size); void setDataPosition(size_t pos) const; status_t setDataCapacity(size_t size); status_t setData(const uint8_t* buffer, size_t len); status_t appendFrom(const Parcel *parcel, size_t start, size_t len); int compareData(const Parcel& other); bool allowFds() const; bool pushAllowFds(bool allowFds); void restoreAllowFds(bool lastValue); bool hasFileDescriptors() const; // Writes the RPC header. status_t writeInterfaceToken(const String16& interface); // Parses the RPC header, returning true if the interface name // in the header matches the expected interface from the caller. // // Additionally, enforceInterface does part of the work of // propagating the StrictMode policy mask, populating the current // IPCThreadState, which as an optimization may optionally be // passed in. bool enforceInterface(const String16& interface, IPCThreadState* threadState = nullptr) const; bool checkInterface(IBinder*) const; void freeData(); private: const binder_size_t* objects() const; public: size_t objectsCount() const; status_t errorCheck() const; void setError(status_t err); status_t write(const void* data, size_t len); void* writeInplace(size_t len); status_t writeUnpadded(const void* data, size_t len); status_t writeInt32(int32_t val); status_t writeUint32(uint32_t val); status_t writeInt64(int64_t val); status_t writeUint64(uint64_t val); status_t writeFloat(float val); status_t writeDouble(double val); status_t writeCString(const char* str); status_t writeString8(const String8& str); status_t writeString16(const String16& str); status_t writeString16(const std::unique_ptr& str); status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp& val); status_t writeWeakBinder(const wp& val); status_t writeInt32Array(size_t len, const int32_t *val); status_t writeByteArray(size_t len, const uint8_t *val); status_t writeBool(bool val); status_t writeChar(char16_t val); status_t writeByte(int8_t val); // Take a UTF8 encoded string, convert to UTF16, write it to the parcel. status_t writeUtf8AsUtf16(const std::string& str); status_t writeUtf8AsUtf16(const std::unique_ptr& str); status_t writeByteVector(const std::unique_ptr>& val); status_t writeByteVector(const std::vector& val); status_t writeByteVector(const std::unique_ptr>& val); status_t writeByteVector(const std::vector& val); status_t writeInt32Vector(const std::unique_ptr>& val); status_t writeInt32Vector(const std::vector& val); status_t writeInt64Vector(const std::unique_ptr>& val); status_t writeInt64Vector(const std::vector& val); status_t writeUint64Vector(const std::unique_ptr>& val); status_t writeUint64Vector(const std::vector& val); status_t writeFloatVector(const std::unique_ptr>& val); status_t writeFloatVector(const std::vector& val); status_t writeDoubleVector(const std::unique_ptr>& val); status_t writeDoubleVector(const std::vector& val); status_t writeBoolVector(const std::unique_ptr>& val); status_t writeBoolVector(const std::vector& val); status_t writeCharVector(const std::unique_ptr>& val); status_t writeCharVector(const std::vector& val); status_t writeString16Vector( const std::unique_ptr>>& val); status_t writeString16Vector(const std::vector& val); status_t writeUtf8VectorAsUtf16Vector( const std::unique_ptr>>& val); status_t writeUtf8VectorAsUtf16Vector(const std::vector& val); status_t writeStrongBinderVector(const std::unique_ptr>>& val); status_t writeStrongBinderVector(const std::vector>& val); template status_t writeParcelableVector(const std::unique_ptr>>& val); template status_t writeParcelableVector(const std::shared_ptr>>& val); template status_t writeParcelableVector(const std::vector& val); template status_t writeNullableParcelable(const std::unique_ptr& parcelable); status_t writeParcelable(const Parcelable& parcelable); status_t writeValue(const binder::Value& value); template status_t write(const Flattenable& val); template status_t write(const LightFlattenable& val); template status_t writeVectorSize(const std::vector& val); template status_t writeVectorSize(const std::unique_ptr>& val); status_t writeMap(const binder::Map& map); status_t writeNullableMap(const std::unique_ptr& map); // Place a native_handle into the parcel (the native_handle's file- // descriptors are dup'ed, so it is safe to delete the native_handle // when this function returns). // Doesn't take ownership of the native_handle. status_t writeNativeHandle(const native_handle* handle); // Place a file descriptor into the parcel. The given fd must remain // valid for the lifetime of the parcel. // The Parcel does not take ownership of the given fd unless you ask it to. status_t writeFileDescriptor(int fd, bool takeOwnership = false); // Place a file descriptor into the parcel. A dup of the fd is made, which // will be closed once the parcel is destroyed. status_t writeDupFileDescriptor(int fd); // Place a Java "parcel file descriptor" into the parcel. The given fd must remain // valid for the lifetime of the parcel. // The Parcel does not take ownership of the given fd unless you ask it to. status_t writeParcelFileDescriptor(int fd, bool takeOwnership = false); // Place a Java "parcel file descriptor" into the parcel. A dup of the fd is made, which will // be closed once the parcel is destroyed. status_t writeDupParcelFileDescriptor(int fd); // Place a file descriptor into the parcel. This will not affect the // semantics of the smart file descriptor. A new descriptor will be // created, and will be closed when the parcel is destroyed. status_t writeUniqueFileDescriptor( const base::unique_fd& fd); // Place a vector of file desciptors into the parcel. Each descriptor is // dup'd as in writeDupFileDescriptor status_t writeUniqueFileDescriptorVector( const std::unique_ptr>& val); status_t writeUniqueFileDescriptorVector( const std::vector& val); // Writes a blob to the parcel. // If the blob is small, then it is stored in-place, otherwise it is // transferred by way of an anonymous shared memory region. Prefer sending // immutable blobs if possible since they may be subsequently transferred between // processes without further copying whereas mutable blobs always need to be copied. // The caller should call release() on the blob after writing its contents. status_t writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob); // Write an existing immutable blob file descriptor to the parcel. // This allows the client to send the same blob to multiple processes // as long as it keeps a dup of the blob file descriptor handy for later. status_t writeDupImmutableBlobFileDescriptor(int fd); status_t writeObject(const flat_binder_object& val, bool nullMetaData); // Like Parcel.java's writeNoException(). Just writes a zero int32. // Currently the native implementation doesn't do any of the StrictMode // stack gathering and serialization that the Java implementation does. status_t writeNoException(); void remove(size_t start, size_t amt); status_t read(void* outData, size_t len) const; const void* readInplace(size_t len) const; int32_t readInt32() const; status_t readInt32(int32_t *pArg) const; uint32_t readUint32() const; status_t readUint32(uint32_t *pArg) const; int64_t readInt64() const; status_t readInt64(int64_t *pArg) const; uint64_t readUint64() const; status_t readUint64(uint64_t *pArg) const; float readFloat() const; status_t readFloat(float *pArg) const; double readDouble() const; status_t readDouble(double *pArg) const; intptr_t readIntPtr() const; status_t readIntPtr(intptr_t *pArg) const; bool readBool() const; status_t readBool(bool *pArg) const; char16_t readChar() const; status_t readChar(char16_t *pArg) const; int8_t readByte() const; status_t readByte(int8_t *pArg) const; // Read a UTF16 encoded string, convert to UTF8 status_t readUtf8FromUtf16(std::string* str) const; status_t readUtf8FromUtf16(std::unique_ptr* str) const; const char* readCString() const; String8 readString8() const; status_t readString8(String8* pArg) const; String16 readString16() const; status_t readString16(String16* pArg) const; status_t readString16(std::unique_ptr* pArg) const; const char16_t* readString16Inplace(size_t* outLen) const; sp readStrongBinder() const; status_t readStrongBinder(sp* val) const; status_t readNullableStrongBinder(sp* val) const; wp readWeakBinder() const; template status_t readParcelableVector( std::unique_ptr>>* val) const; template status_t readParcelableVector(std::vector* val) const; status_t readParcelable(Parcelable* parcelable) const; template status_t readParcelable(std::unique_ptr* parcelable) const; status_t readValue(binder::Value* value) const; template status_t readStrongBinder(sp* val) const; template status_t readNullableStrongBinder(sp* val) const; status_t readStrongBinderVector(std::unique_ptr>>* val) const; status_t readStrongBinderVector(std::vector>* val) const; status_t readByteVector(std::unique_ptr>* val) const; status_t readByteVector(std::vector* val) const; status_t readByteVector(std::unique_ptr>* val) const; status_t readByteVector(std::vector* val) const; status_t readInt32Vector(std::unique_ptr>* val) const; status_t readInt32Vector(std::vector* val) const; status_t readInt64Vector(std::unique_ptr>* val) const; status_t readInt64Vector(std::vector* val) const; status_t readUint64Vector(std::unique_ptr>* val) const; status_t readUint64Vector(std::vector* val) const; status_t readFloatVector(std::unique_ptr>* val) const; status_t readFloatVector(std::vector* val) const; status_t readDoubleVector(std::unique_ptr>* val) const; status_t readDoubleVector(std::vector* val) const; status_t readBoolVector(std::unique_ptr>* val) const; status_t readBoolVector(std::vector* val) const; status_t readCharVector(std::unique_ptr>* val) const; status_t readCharVector(std::vector* val) const; status_t readString16Vector( std::unique_ptr>>* val) const; status_t readString16Vector(std::vector* val) const; status_t readUtf8VectorFromUtf16Vector( std::unique_ptr>>* val) const; status_t readUtf8VectorFromUtf16Vector(std::vector* val) const; template status_t read(Flattenable& val) const; template status_t read(LightFlattenable& val) const; template status_t resizeOutVector(std::vector* val) const; template status_t resizeOutVector(std::unique_ptr>* val) const; status_t readMap(binder::Map* map)const; status_t readNullableMap(std::unique_ptr* map) const; // Like Parcel.java's readExceptionCode(). Reads the first int32 // off of a Parcel's header, returning 0 or the negative error // code on exceptions, but also deals with skipping over rich // response headers. Callers should use this to read & parse the // response headers rather than doing it by hand. int32_t readExceptionCode() const; // Retrieve native_handle from the parcel. This returns a copy of the // parcel's native_handle (the caller takes ownership). The caller // must free the native_handle with native_handle_close() and // native_handle_delete(). native_handle* readNativeHandle() const; // Retrieve a file descriptor from the parcel. This returns the raw fd // in the parcel, which you do not own -- use dup() to get your own copy. int readFileDescriptor() const; // Retrieve a Java "parcel file descriptor" from the parcel. This returns the raw fd // in the parcel, which you do not own -- use dup() to get your own copy. int readParcelFileDescriptor() const; // Retrieve a smart file descriptor from the parcel. status_t readUniqueFileDescriptor( base::unique_fd* val) const; // Retrieve a Java "parcel file descriptor" from the parcel. status_t readUniqueParcelFileDescriptor(base::unique_fd* val) const; // Retrieve a vector of smart file descriptors from the parcel. status_t readUniqueFileDescriptorVector( std::unique_ptr>* val) const; status_t readUniqueFileDescriptorVector( std::vector* val) const; // Reads a blob from the parcel. // The caller should call release() on the blob after reading its contents. status_t readBlob(size_t len, ReadableBlob* outBlob) const; const flat_binder_object* readObject(bool nullMetaData) const; // Explicitly close all file descriptors in the parcel. void closeFileDescriptors(); // Debugging: get metrics on current allocations. static size_t getGlobalAllocSize(); static size_t getGlobalAllocCount(); bool replaceCallingWorkSourceUid(uid_t uid); // Returns the work source provided by the caller. This can only be trusted for trusted calling // uid. uid_t readCallingWorkSourceUid(); void readRequestHeaders() const; private: typedef void (*release_func)(Parcel* parcel, const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsSize, void* cookie); uintptr_t ipcData() const; size_t ipcDataSize() const; uintptr_t ipcObjects() const; size_t ipcObjectsCount() const; void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie); public: void print(TextOutput& to, uint32_t flags = 0) const; private: Parcel(const Parcel& o); Parcel& operator=(const Parcel& o); status_t finishWrite(size_t len); void releaseObjects(); void acquireObjects(); status_t growData(size_t len); status_t restartWrite(size_t desired); status_t continueWrite(size_t desired); status_t writePointer(uintptr_t val); status_t readPointer(uintptr_t *pArg) const; uintptr_t readPointer() const; void freeDataNoInit(); void initState(); void scanForFds() const; status_t validateReadData(size_t len) const; void updateWorkSourceRequestHeaderPosition() const; template status_t readAligned(T *pArg) const; template T readAligned() const; template status_t writeAligned(T val); status_t writeRawNullableParcelable(const Parcelable* parcelable); template status_t unsafeReadTypedVector(std::vector* val, status_t(Parcel::*read_func)(U*) const) const; template status_t readNullableTypedVector(std::unique_ptr>* val, status_t(Parcel::*read_func)(T*) const) const; template status_t readTypedVector(std::vector* val, status_t(Parcel::*read_func)(T*) const) const; template status_t unsafeWriteTypedVector(const std::vector& val, status_t(Parcel::*write_func)(U)); template status_t writeNullableTypedVector(const std::unique_ptr>& val, status_t(Parcel::*write_func)(const T&)); template status_t writeNullableTypedVector(const std::unique_ptr>& val, status_t(Parcel::*write_func)(T)); template status_t writeTypedVector(const std::vector& val, status_t(Parcel::*write_func)(const T&)); template status_t writeTypedVector(const std::vector& val, status_t(Parcel::*write_func)(T)); status_t mError; uint8_t* mData; size_t mDataSize; size_t mDataCapacity; mutable size_t mDataPos; binder_size_t* mObjects; size_t mObjectsSize; size_t mObjectsCapacity; mutable size_t mNextObjectHint; mutable bool mObjectsSorted; mutable bool mRequestHeaderPresent; mutable size_t mWorkSourceRequestHeaderPosition; mutable bool mFdsKnown; mutable bool mHasFds; bool mAllowFds; release_func mOwner; void* mOwnerCookie; class Blob { public: Blob(); ~Blob(); void clear(); void release(); inline size_t size() const { return mSize; } inline int fd() const { return mFd; } inline bool isMutable() const { return mMutable; } protected: void init(int fd, void* data, size_t size, bool isMutable); int mFd; // owned by parcel so not closed when released void* mData; size_t mSize; bool mMutable; }; #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif // FlattenableHelperInterface and FlattenableHelper avoid generating a vtable entry in objects // following Flattenable template/protocol. class FlattenableHelperInterface { protected: ~FlattenableHelperInterface() { } public: virtual size_t getFlattenedSize() const = 0; virtual size_t getFdCount() const = 0; virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const = 0; virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) = 0; }; #if defined(__clang__) #pragma clang diagnostic pop #endif // Concrete implementation of FlattenableHelperInterface that delegates virtual calls to the // specified class T implementing the Flattenable protocol. It "virtualizes" a compile-time // protocol. template class FlattenableHelper : public FlattenableHelperInterface { friend class Parcel; const Flattenable& val; explicit FlattenableHelper(const Flattenable& _val) : val(_val) { } protected: ~FlattenableHelper() = default; public: virtual size_t getFlattenedSize() const { return val.getFlattenedSize(); } virtual size_t getFdCount() const { return val.getFdCount(); } virtual status_t flatten(void* buffer, size_t size, int* fds, size_t count) const { return val.flatten(buffer, size, fds, count); } virtual status_t unflatten(void const* buffer, size_t size, int const* fds, size_t count) { return const_cast&>(val).unflatten(buffer, size, fds, count); } }; status_t write(const FlattenableHelperInterface& val); status_t read(FlattenableHelperInterface& val) const; public: class ReadableBlob : public Blob { friend class Parcel; public: inline const void* data() const { return mData; } inline void* mutableData() { return isMutable() ? mData : nullptr; } }; class WritableBlob : public Blob { friend class Parcel; public: inline void* data() { return mData; } }; private: size_t mOpenAshmemSize; public: // TODO: Remove once ABI can be changed. size_t getBlobAshmemSize() const; size_t getOpenAshmemSize() const; }; // --------------------------------------------------------------------------- template status_t Parcel::write(const Flattenable& val) { const FlattenableHelper helper(val); return write(helper); } template status_t Parcel::write(const LightFlattenable& val) { size_t size(val.getFlattenedSize()); if (!val.isFixedSize()) { if (size > INT32_MAX) { return BAD_VALUE; } status_t err = writeInt32(static_cast(size)); if (err != NO_ERROR) { return err; } } if (size) { void* buffer = writeInplace(size); if (buffer == nullptr) return NO_MEMORY; return val.flatten(buffer, size); } return NO_ERROR; } template status_t Parcel::read(Flattenable& val) const { FlattenableHelper helper(val); return read(helper); } template status_t Parcel::read(LightFlattenable& val) const { size_t size; if (val.isFixedSize()) { size = val.getFlattenedSize(); } else { int32_t s; status_t err = readInt32(&s); if (err != NO_ERROR) { return err; } size = static_cast(s); } if (size) { void const* buffer = readInplace(size); return buffer == nullptr ? NO_MEMORY : val.unflatten(buffer, size); } return NO_ERROR; } template status_t Parcel::writeVectorSize(const std::vector& val) { if (val.size() > INT32_MAX) { return BAD_VALUE; } return writeInt32(static_cast(val.size())); } template status_t Parcel::writeVectorSize(const std::unique_ptr>& val) { if (!val) { return writeInt32(-1); } return writeVectorSize(*val); } template status_t Parcel::resizeOutVector(std::vector* val) const { int32_t size; status_t err = readInt32(&size); if (err != NO_ERROR) { return err; } if (size < 0) { return UNEXPECTED_NULL; } val->resize(size_t(size)); return OK; } template status_t Parcel::resizeOutVector(std::unique_ptr>* val) const { int32_t size; status_t err = readInt32(&size); if (err != NO_ERROR) { return err; } val->reset(); if (size >= 0) { val->reset(new std::vector(size_t(size))); } return OK; } template status_t Parcel::readStrongBinder(sp* val) const { sp tmp; status_t ret = readStrongBinder(&tmp); if (ret == OK) { *val = interface_cast(tmp); if (val->get() == nullptr) { return UNKNOWN_ERROR; } } return ret; } template status_t Parcel::readNullableStrongBinder(sp* val) const { sp tmp; status_t ret = readNullableStrongBinder(&tmp); if (ret == OK) { *val = interface_cast(tmp); if (val->get() == nullptr && tmp.get() != nullptr) { ret = UNKNOWN_ERROR; } } return ret; } template status_t Parcel::unsafeReadTypedVector( std::vector* val, status_t(Parcel::*read_func)(U*) const) const { int32_t size; status_t status = this->readInt32(&size); if (status != OK) { return status; } if (size < 0) { return UNEXPECTED_NULL; } if (val->max_size() < static_cast(size)) { return NO_MEMORY; } val->resize(static_cast(size)); if (val->size() < static_cast(size)) { return NO_MEMORY; } for (auto& v: *val) { status = (this->*read_func)(&v); if (status != OK) { return status; } } return OK; } template status_t Parcel::readTypedVector(std::vector* val, status_t(Parcel::*read_func)(T*) const) const { return unsafeReadTypedVector(val, read_func); } template status_t Parcel::readNullableTypedVector(std::unique_ptr>* val, status_t(Parcel::*read_func)(T*) const) const { const size_t start = dataPosition(); int32_t size; status_t status = readInt32(&size); val->reset(); if (status != OK || size < 0) { return status; } setDataPosition(start); val->reset(new std::vector()); status = unsafeReadTypedVector(val->get(), read_func); if (status != OK) { val->reset(); } return status; } template status_t Parcel::unsafeWriteTypedVector(const std::vector& val, status_t(Parcel::*write_func)(U)) { if (val.size() > std::numeric_limits::max()) { return BAD_VALUE; } status_t status = this->writeInt32(static_cast(val.size())); if (status != OK) { return status; } for (const auto& item : val) { status = (this->*write_func)(item); if (status != OK) { return status; } } return OK; } template status_t Parcel::writeTypedVector(const std::vector& val, status_t(Parcel::*write_func)(const T&)) { return unsafeWriteTypedVector(val, write_func); } template status_t Parcel::writeTypedVector(const std::vector& val, status_t(Parcel::*write_func)(T)) { return unsafeWriteTypedVector(val, write_func); } template status_t Parcel::writeNullableTypedVector(const std::unique_ptr>& val, status_t(Parcel::*write_func)(const T&)) { if (val.get() == nullptr) { return this->writeInt32(-1); } return unsafeWriteTypedVector(*val, write_func); } template status_t Parcel::writeNullableTypedVector(const std::unique_ptr>& val, status_t(Parcel::*write_func)(T)) { if (val.get() == nullptr) { return this->writeInt32(-1); } return unsafeWriteTypedVector(*val, write_func); } template status_t Parcel::readParcelableVector(std::vector* val) const { return unsafeReadTypedVector(val, &Parcel::readParcelable); } template status_t Parcel::readParcelableVector(std::unique_ptr>>* val) const { const size_t start = dataPosition(); int32_t size; status_t status = readInt32(&size); val->reset(); if (status != OK || size < 0) { return status; } setDataPosition(start); val->reset(new std::vector>()); status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable); if (status != OK) { val->reset(); } return status; } template status_t Parcel::readParcelable(std::unique_ptr* parcelable) const { const size_t start = dataPosition(); int32_t present; status_t status = readInt32(&present); parcelable->reset(); if (status != OK || !present) { return status; } setDataPosition(start); parcelable->reset(new T()); status = readParcelable(parcelable->get()); if (status != OK) { parcelable->reset(); } return status; } template status_t Parcel::writeNullableParcelable(const std::unique_ptr& parcelable) { return writeRawNullableParcelable(parcelable.get()); } template status_t Parcel::writeParcelableVector(const std::vector& val) { return unsafeWriteTypedVector(val, &Parcel::writeParcelable); } template status_t Parcel::writeParcelableVector(const std::unique_ptr>>& val) { if (val.get() == nullptr) { return this->writeInt32(-1); } return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable); } template status_t Parcel::writeParcelableVector(const std::shared_ptr>>& val) { if (val.get() == nullptr) { return this->writeInt32(-1); } return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable); } // --------------------------------------------------------------------------- inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) { parcel.print(to); return to; } // --------------------------------------------------------------------------- // Generic acquire and release of objects. void acquire_object(const sp& proc, const flat_binder_object& obj, const void* who); void release_object(const sp& proc, const flat_binder_object& obj, const void* who); void flatten_binder(const sp& proc, const sp& binder, flat_binder_object* out); void flatten_binder(const sp& proc, const wp& binder, flat_binder_object* out); status_t unflatten_binder(const sp& proc, const flat_binder_object& flat, sp* out); status_t unflatten_binder(const sp& proc, const flat_binder_object& flat, wp* out); }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_PARCEL_H libs/binder/include/binder/ParcelFileDescriptor.h0100644 0000000 0000000 00000003310 13756501735 021116 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_PARCEL_FILE_DESCRIPTOR_H_ #define ANDROID_PARCEL_FILE_DESCRIPTOR_H_ #include #include #include namespace android { namespace os { /* * C++ implementation of the Java class android.os.ParcelFileDescriptor */ class ParcelFileDescriptor : public android::Parcelable { public: ParcelFileDescriptor(); explicit ParcelFileDescriptor(android::base::unique_fd fd); ParcelFileDescriptor(ParcelFileDescriptor&& other) : mFd(std::move(other.mFd)) { } ~ParcelFileDescriptor() override; int get() const { return mFd.get(); } android::base::unique_fd release() { return std::move(mFd); } void reset(android::base::unique_fd fd = android::base::unique_fd()) { mFd = std::move(fd); } // android::Parcelable override: android::status_t writeToParcel(android::Parcel* parcel) const override; android::status_t readFromParcel(const android::Parcel* parcel) override; private: android::base::unique_fd mFd; }; } // namespace os } // namespace android #endif // ANDROID_OS_PARCEL_FILE_DESCRIPTOR_H_ libs/binder/include/binder/Parcelable.h0100644 0000000 0000000 00000003574 13756501735 017117 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_PARCELABLE_H #define ANDROID_PARCELABLE_H #include #include #include namespace android { class Parcel; #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #endif // Abstract interface of all parcelables. class Parcelable { public: virtual ~Parcelable() = default; Parcelable() = default; Parcelable(const Parcelable&) = default; // Write |this| parcelable to the given |parcel|. Keep in mind that // implementations of writeToParcel must be manually kept in sync // with readFromParcel and the Java equivalent versions of these methods. // // Returns android::OK on success and an appropriate error otherwise. virtual status_t writeToParcel(Parcel* parcel) const = 0; // Read data from the given |parcel| into |this|. After readFromParcel // completes, |this| should have equivalent state to the object that // wrote itself to the parcel. // // Returns android::OK on success and an appropriate error otherwise. virtual status_t readFromParcel(const Parcel* parcel) = 0; }; // class Parcelable #if defined(__clang__) #pragma clang diagnostic pop #endif } // namespace android #endif // ANDROID_PARCELABLE_H libs/binder/include/binder/PermissionCache.h0100644 0000000 0000000 00000005150 13756501735 020131 0ustar000000000 0000000 /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BINDER_PERMISSION_H #define BINDER_PERMISSION_H #ifndef __ANDROID_VNDK__ #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- /* * PermissionCache caches permission checks for a given uid. * * Currently the cache is not updated when there is a permission change, * for instance when an application is uninstalled. * * IMPORTANT: for the reason stated above, only system permissions are safe * to cache. This restriction may be lifted at a later time. * */ class PermissionCache : Singleton { struct Entry { String16 name; uid_t uid; bool granted; inline bool operator < (const Entry& e) const { return (uid == e.uid) ? (name < e.name) : (uid < e.uid); } }; mutable Mutex mLock; // we pool all the permission names we see, as many permissions checks // will have identical names SortedVector< String16 > mPermissionNamesPool; // this is our cache per say. it stores pooled names. SortedVector< Entry > mCache; // free the whole cache, but keep the permission name pool void purge(); status_t check(bool* granted, const String16& permission, uid_t uid) const; void cache(const String16& permission, uid_t uid, bool granted); public: PermissionCache(); static bool checkCallingPermission(const String16& permission); static bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid); static bool checkPermission(const String16& permission, pid_t pid, uid_t uid); }; // --------------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif /* BINDER_PERMISSION_H */ libs/binder/include/binder/PermissionController.h0100644 0000000 0000000 00000003660 13756501735 021255 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_PERMISSION_CONTROLLER_H #define ANDROID_PERMISSION_CONTROLLER_H #ifndef __ANDROID_VNDK__ #include #include // --------------------------------------------------------------------------- namespace android { class PermissionController { public: enum { MATCH_SYSTEM_ONLY = 1<<16, MATCH_UNINSTALLED_PACKAGES = 1<<13, MATCH_FACTORY_ONLY = 1<<21, MATCH_INSTANT = 1<<23 }; enum { MODE_ALLOWED = 0, MODE_IGNORED = 1, MODE_ERRORED = 2, MODE_DEFAULT = 3, }; PermissionController(); bool checkPermission(const String16& permission, int32_t pid, int32_t uid); int32_t noteOp(const String16& op, int32_t uid, const String16& packageName); void getPackagesForUid(const uid_t uid, Vector& packages); bool isRuntimePermission(const String16& permission); int getPackageUid(const String16& package, int flags); private: Mutex mLock; sp mService; sp getService(); }; }; // namespace android // --------------------------------------------------------------------------- #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_PERMISSION_CONTROLLER_H libs/binder/include/binder/PersistableBundle.h0100644 0000000 0000000 00000013003 13756501735 020460 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_PERSISTABLE_BUNDLE_H #define ANDROID_PERSISTABLE_BUNDLE_H #include #include #include #include #include #include namespace android { namespace os { /* * C++ implementation of PersistableBundle, a mapping from String values to * various types that can be saved to persistent and later restored. */ class PersistableBundle : public Parcelable { public: PersistableBundle() = default; virtual ~PersistableBundle() = default; PersistableBundle(const PersistableBundle& bundle) = default; status_t writeToParcel(Parcel* parcel) const override; status_t readFromParcel(const Parcel* parcel) override; bool empty() const; size_t size() const; size_t erase(const String16& key); /* * Setters for PersistableBundle. Adds a a key-value pair instantiated with * |key| and |value| into the member map appropriate for the type of |value|. * If there is already an existing value for |key|, |value| will replace it. */ void putBoolean(const String16& key, bool value); void putInt(const String16& key, int32_t value); void putLong(const String16& key, int64_t value); void putDouble(const String16& key, double value); void putString(const String16& key, const String16& value); void putBooleanVector(const String16& key, const std::vector& value); void putIntVector(const String16& key, const std::vector& value); void putLongVector(const String16& key, const std::vector& value); void putDoubleVector(const String16& key, const std::vector& value); void putStringVector(const String16& key, const std::vector& value); void putPersistableBundle(const String16& key, const PersistableBundle& value); /* * Getters for PersistableBundle. If |key| exists, these methods write the * value associated with |key| into |out|, and return true. Otherwise, these * methods return false. */ bool getBoolean(const String16& key, bool* out) const; bool getInt(const String16& key, int32_t* out) const; bool getLong(const String16& key, int64_t* out) const; bool getDouble(const String16& key, double* out) const; bool getString(const String16& key, String16* out) const; bool getBooleanVector(const String16& key, std::vector* out) const; bool getIntVector(const String16& key, std::vector* out) const; bool getLongVector(const String16& key, std::vector* out) const; bool getDoubleVector(const String16& key, std::vector* out) const; bool getStringVector(const String16& key, std::vector* out) const; bool getPersistableBundle(const String16& key, PersistableBundle* out) const; /* Getters for all keys for each value type */ std::set getBooleanKeys() const; std::set getIntKeys() const; std::set getLongKeys() const; std::set getDoubleKeys() const; std::set getStringKeys() const; std::set getBooleanVectorKeys() const; std::set getIntVectorKeys() const; std::set getLongVectorKeys() const; std::set getDoubleVectorKeys() const; std::set getStringVectorKeys() const; std::set getPersistableBundleKeys() const; friend bool operator==(const PersistableBundle& lhs, const PersistableBundle& rhs) { return (lhs.mBoolMap == rhs.mBoolMap && lhs.mIntMap == rhs.mIntMap && lhs.mLongMap == rhs.mLongMap && lhs.mDoubleMap == rhs.mDoubleMap && lhs.mStringMap == rhs.mStringMap && lhs.mBoolVectorMap == rhs.mBoolVectorMap && lhs.mIntVectorMap == rhs.mIntVectorMap && lhs.mLongVectorMap == rhs.mLongVectorMap && lhs.mDoubleVectorMap == rhs.mDoubleVectorMap && lhs.mStringVectorMap == rhs.mStringVectorMap && lhs.mPersistableBundleMap == rhs.mPersistableBundleMap); } friend bool operator!=(const PersistableBundle& lhs, const PersistableBundle& rhs) { return !(lhs == rhs); } private: status_t writeToParcelInner(Parcel* parcel) const; status_t readFromParcelInner(const Parcel* parcel, size_t length); std::map mBoolMap; std::map mIntMap; std::map mLongMap; std::map mDoubleMap; std::map mStringMap; std::map> mBoolVectorMap; std::map> mIntVectorMap; std::map> mLongVectorMap; std::map> mDoubleVectorMap; std::map> mStringVectorMap; std::map mPersistableBundleMap; }; } // namespace os } // namespace android #endif // ANDROID_PERSISTABLE_BUNDLE_H libs/binder/include/binder/ProcessInfoService.h0100644 0000000 0000000 00000006224 13756501735 020633 0ustar000000000 0000000 /* * Copyright 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. */ #ifndef ANDROID_PROCESS_INFO_SERVICE_H #define ANDROID_PROCESS_INFO_SERVICE_H #ifndef __ANDROID_VNDK__ #include #include #include #include namespace android { // ---------------------------------------------------------------------- class ProcessInfoService : public Singleton { friend class Singleton; sp mProcessInfoService; Mutex mProcessInfoLock; ProcessInfoService(); status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states); status_t getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states, /*out*/ int32_t *scores); void updateBinderLocked(); static const int BINDER_ATTEMPT_LIMIT = 5; public: /** * For each PID in the given "pids" input array, write the current process state * for that process into the "states" output array, or * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID * exists. * * Returns NO_ERROR if this operation was successful, or a negative error code otherwise. */ static status_t getProcessStatesFromPids(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states) { return ProcessInfoService::getInstance().getProcessStatesImpl(length, /*in*/ pids, /*out*/ states); } /** * For each PID in the given "pids" input array, write the current process state * for that process into the "states" output array, or * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID * exists. OoM scores will also be written in the "scores" output array. * Please also note that clients calling this method need to have * "GET_PROCESS_STATE_AND_OOM_SCORE" permission. * * Returns NO_ERROR if this operation was successful, or a negative error code otherwise. */ static status_t getProcessStatesScoresFromPids(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states, /*out*/ int32_t *scores) { return ProcessInfoService::getInstance().getProcessStatesScoresImpl( length, /*in*/ pids, /*out*/ states, /*out*/ scores); } }; // ---------------------------------------------------------------------- }; // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif // __ANDROID_VNDK__ #endif // ANDROID_PROCESS_INFO_SERVICE_H libs/binder/include/binder/ProcessState.h0100644 0000000 0000000 00000012466 13756501735 017504 0ustar000000000 0000000 /* * Copyright (C) 2005 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_PROCESS_STATE_H #define ANDROID_PROCESS_STATE_H #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { class IPCThreadState; class ProcessState : public virtual RefBase { public: static sp self(); static sp selfOrNull(); /* initWithDriver() can be used to configure libbinder to use * a different binder driver dev node. It must be called *before* * any call to ProcessState::self(). The default is /dev/vndbinder * for processes built with the VNDK and /dev/binder for those * which are not. */ static sp initWithDriver(const char *driver); void setContextObject(const sp& object); sp getContextObject(const sp& caller); void setContextObject(const sp& object, const String16& name); sp getContextObject(const String16& name, const sp& caller); void startThreadPool(); typedef bool (*context_check_func)(const String16& name, const sp& caller, void* userData); bool isContextManager(void) const; bool becomeContextManager( context_check_func checkFunc, void* userData); sp getStrongProxyForHandle(int32_t handle); wp getWeakProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); void spawnPooledThread(bool isMain); status_t setThreadPoolMaxThreadCount(size_t maxThreads); void giveThreadPoolName(); String8 getDriverName(); ssize_t getKernelReferences(size_t count, uintptr_t* buf); enum class CallRestriction { // all calls okay NONE, // log when calls are blocking ERROR_IF_NOT_ONEWAY, // abort process on blocking calls FATAL_IF_NOT_ONEWAY, }; // Sets calling restrictions for all transactions in this process. This must be called // before any threads are spawned. void setCallRestriction(CallRestriction restriction); private: friend class IPCThreadState; explicit ProcessState(const char* driver); ~ProcessState(); ProcessState(const ProcessState& o); ProcessState& operator=(const ProcessState& o); String8 makeBinderThreadName(); struct handle_entry { IBinder* binder; RefBase::weakref_type* refs; }; handle_entry* lookupHandleLocked(int32_t handle); String8 mDriverName; int mDriverFD; void* mVMStart; // Protects thread count variable below. pthread_mutex_t mThreadCountLock; pthread_cond_t mThreadCountDecrement; // Number of binder threads current executing a command. size_t mExecutingThreadsCount; // Maximum number for binder threads allowed for this process. size_t mMaxThreads; // Time when thread pool was emptied int64_t mStarvationStartTimeMs; mutable Mutex mLock; // protects everything below. VectormHandleToObject; bool mManagesContexts; context_check_func mBinderContextCheckFunc; void* mBinderContextUserData; KeyedVector > mContexts; String8 mRootDir; bool mThreadPoolStarted; volatile int32_t mThreadPoolSeq; CallRestriction mCallRestriction; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_PROCESS_STATE_H libs/binder/include/binder/SafeInterface.h0100644 0000000 0000000 00000076055 13756501735 017570 0ustar000000000 0000000 /* * Copyright 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. */ #pragma once #include #include #include // Set to 1 to enable CallStacks when logging errors #define SI_DUMP_CALLSTACKS 0 #if SI_DUMP_CALLSTACKS #include #endif #include #include #include namespace android { namespace SafeInterface { // ParcelHandler is responsible for writing/reading various types to/from a Parcel in a generic way class ParcelHandler { public: explicit ParcelHandler(const char* logTag) : mLogTag(logTag) {} // Specializations for types with dedicated handling in Parcel status_t read(const Parcel& parcel, bool* b) const { return callParcel("readBool", [&]() { return parcel.readBool(b); }); } status_t write(Parcel* parcel, bool b) const { return callParcel("writeBool", [&]() { return parcel->writeBool(b); }); } template typename std::enable_if::value, status_t>::type read(const Parcel& parcel, E* e) const { typename std::underlying_type::type u{}; status_t result = read(parcel, &u); *e = static_cast(u); return result; } template typename std::enable_if::value, status_t>::type write(Parcel* parcel, E e) const { return write(parcel, static_cast::type>(e)); } template typename std::enable_if, T>::value, status_t>::type read( const Parcel& parcel, T* t) const { return callParcel("read(Flattenable)", [&]() { return parcel.read(*t); }); } template typename std::enable_if, T>::value, status_t>::type write( Parcel* parcel, const T& t) const { return callParcel("write(Flattenable)", [&]() { return parcel->write(t); }); } template typename std::enable_if, T>::value, status_t>::type read( const Parcel& parcel, sp* t) const { *t = new T{}; return callParcel("read(sp)", [&]() { return parcel.read(*(t->get())); }); } template typename std::enable_if, T>::value, status_t>::type write( Parcel* parcel, const sp& t) const { return callParcel("write(sp)", [&]() { return parcel->write(*(t.get())); }); } template typename std::enable_if, T>::value, status_t>::type read( const Parcel& parcel, T* t) const { return callParcel("read(LightFlattenable)", [&]() { return parcel.read(*t); }); } template typename std::enable_if, T>::value, status_t>::type write( Parcel* parcel, const T& t) const { return callParcel("write(LightFlattenable)", [&]() { return parcel->write(t); }); } template typename std::enable_if>::value, status_t>::type read( const Parcel& parcel, NH* nh) { *nh = NativeHandle::create(parcel.readNativeHandle(), true); return NO_ERROR; } template typename std::enable_if>::value, status_t>::type write( Parcel* parcel, const NH& nh) { return callParcel("write(sp)", [&]() { return parcel->writeNativeHandle(nh->handle()); }); } template typename std::enable_if::value, status_t>::type read( const Parcel& parcel, T* t) const { return callParcel("readParcelable", [&]() { return parcel.readParcelable(t); }); } template typename std::enable_if::value, status_t>::type write( Parcel* parcel, const T& t) const { return callParcel("writeParcelable", [&]() { return parcel->writeParcelable(t); }); } status_t read(const Parcel& parcel, String8* str) const { return callParcel("readString8", [&]() { return parcel.readString8(str); }); } status_t write(Parcel* parcel, const String8& str) const { return callParcel("writeString8", [&]() { return parcel->writeString8(str); }); } template typename std::enable_if::value, status_t>::type read( const Parcel& parcel, sp* pointer) const { return callParcel("readNullableStrongBinder", [&]() { return parcel.readNullableStrongBinder(pointer); }); } template typename std::enable_if::value, status_t>::type write( Parcel* parcel, const sp& pointer) const { return callParcel("writeStrongBinder", [&]() { return parcel->writeStrongBinder(pointer); }); } template typename std::enable_if::value, status_t>::type read( const Parcel& parcel, sp* pointer) const { return callParcel("readNullableStrongBinder[IInterface]", [&]() { return parcel.readNullableStrongBinder(pointer); }); } template typename std::enable_if::value, status_t>::type write( Parcel* parcel, const sp& interface) const { return write(parcel, IInterface::asBinder(interface)); } template typename std::enable_if::value, status_t>::type read( const Parcel& parcel, std::vector* v) const { return callParcel("readParcelableVector", [&]() { return parcel.readParcelableVector(v); }); } template typename std::enable_if::value, status_t>::type write( Parcel* parcel, const std::vector& v) const { return callParcel("writeParcelableVector", [&]() { return parcel->writeParcelableVector(v); }); } status_t read(const Parcel& parcel, float* f) const { return callParcel("readFloat", [&]() { return parcel.readFloat(f); }); } status_t write(Parcel* parcel, float f) const { return callParcel("writeFloat", [&]() { return parcel->writeFloat(f); }); } // Templates to handle integral types. We use a struct template to require that the called // function exactly matches the signedness and size of the argument (e.g., the argument isn't // silently widened). template struct HandleInt; template struct HandleInt { static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { return handler.callParcel("readInt32", [&]() { return parcel.readInt32(i); }); } static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { return handler.callParcel("writeInt32", [&]() { return parcel->writeInt32(i); }); } }; template struct HandleInt { static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { return handler.callParcel("readUint32", [&]() { return parcel.readUint32(i); }); } static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { return handler.callParcel("writeUint32", [&]() { return parcel->writeUint32(i); }); } }; template struct HandleInt { static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { return handler.callParcel("readInt64", [&]() { return parcel.readInt64(i); }); } static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { return handler.callParcel("writeInt64", [&]() { return parcel->writeInt64(i); }); } }; template struct HandleInt { static status_t read(const ParcelHandler& handler, const Parcel& parcel, I* i) { return handler.callParcel("readUint64", [&]() { return parcel.readUint64(i); }); } static status_t write(const ParcelHandler& handler, Parcel* parcel, I i) { return handler.callParcel("writeUint64", [&]() { return parcel->writeUint64(i); }); } }; template typename std::enable_if::value, status_t>::type read(const Parcel& parcel, I* i) const { return HandleInt::value, sizeof(I), I>::read(*this, parcel, i); } template typename std::enable_if::value, status_t>::type write(Parcel* parcel, I i) const { return HandleInt::value, sizeof(I), I>::write(*this, parcel, i); } private: const char* const mLogTag; // Helper to encapsulate error handling while calling the various Parcel methods template status_t callParcel(const char* name, Function f) const { status_t error = f(); if (CC_UNLIKELY(error != NO_ERROR)) { ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error)); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); #endif } return error; } }; // Utility struct template which allows us to retrieve the types of the parameters of a member // function pointer template struct ParamExtractor; template struct ParamExtractor { using ParamTuple = std::tuple; }; template struct ParamExtractor { using ParamTuple = std::tuple; }; } // namespace SafeInterface template class SafeBpInterface : public BpInterface { protected: SafeBpInterface(const sp& impl, const char* logTag) : BpInterface(impl), mLogTag(logTag) {} ~SafeBpInterface() override = default; // callRemote is used to invoke a synchronous procedure call over Binder template status_t callRemote(TagType tag, Args&&... args) const { static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t"); // Verify that the arguments are compatible with the parameters using ParamTuple = typename SafeInterface::ParamExtractor::ParamTuple; static_assert(ArgsMatchParams, ParamTuple>::value, "Invalid argument type"); // Write the input arguments to the data Parcel Parcel data; data.writeInterfaceToken(this->getInterfaceDescriptor()); status_t error = writeInputs(&data, std::forward(args)...); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by writeInputs return error; } // Send the data Parcel to the remote and retrieve the reply parcel Parcel reply; error = this->remote()->transact(static_cast(tag), data, &reply); if (CC_UNLIKELY(error != NO_ERROR)) { ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); #endif return error; } // Read the outputs from the reply Parcel into the output arguments error = readOutputs(reply, std::forward(args)...); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by readOutputs return error; } // Retrieve the result code from the reply Parcel status_t result = NO_ERROR; error = reply.readInt32(&result); if (CC_UNLIKELY(error != NO_ERROR)) { ALOG(LOG_ERROR, mLogTag, "Failed to obtain result"); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); #endif return error; } return result; } // callRemoteAsync is used to invoke an asynchronous procedure call over Binder template void callRemoteAsync(TagType tag, Args&&... args) const { static_assert(sizeof(TagType) <= sizeof(uint32_t), "Tag must fit inside uint32_t"); // Verify that the arguments are compatible with the parameters using ParamTuple = typename SafeInterface::ParamExtractor::ParamTuple; static_assert(ArgsMatchParams, ParamTuple>::value, "Invalid argument type"); // Write the input arguments to the data Parcel Parcel data; data.writeInterfaceToken(this->getInterfaceDescriptor()); status_t error = writeInputs(&data, std::forward(args)...); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by writeInputs return; } // There will be no data in the reply Parcel since the call is one-way Parcel reply; error = this->remote()->transact(static_cast(tag), data, &reply, IBinder::FLAG_ONEWAY); if (CC_UNLIKELY(error != NO_ERROR)) { ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); #endif } } private: const char* const mLogTag; // This struct provides information on whether the decayed types of the elements at Index in the // tuple types T and U (that is, the types after stripping cv-qualifiers, removing references, // and a few other less common operations) are the same template struct DecayedElementsMatch { private: using FirstT = typename std::tuple_element::type; using DecayedT = typename std::decay::type; using FirstU = typename std::tuple_element::type; using DecayedU = typename std::decay::type; public: static constexpr bool value = std::is_same::value; }; // When comparing whether the argument types match the parameter types, we first decay them (see // DecayedElementsMatch) to avoid falsely flagging, say, T&& against T even though they are // equivalent enough for our purposes template struct ArgsMatchParams {}; template struct ArgsMatchParams, std::tuple> { static_assert(sizeof...(Args) <= sizeof...(Params), "Too many arguments"); static_assert(sizeof...(Args) >= sizeof...(Params), "Not enough arguments"); private: template static constexpr typename std::enable_if<(Index < sizeof...(Args)), bool>::type elementsMatch() { if (!DecayedElementsMatch, std::tuple>::value) { return false; } return elementsMatch(); } template static constexpr typename std::enable_if<(Index >= sizeof...(Args)), bool>::type elementsMatch() { return true; } public: static constexpr bool value = elementsMatch<0>(); }; // Since we assume that pointer arguments are outputs, we can use this template struct to // determine whether or not a given argument is fundamentally a pointer type and thus an output template struct IsPointerIfDecayed { private: using Decayed = typename std::decay::type; public: static constexpr bool value = std::is_pointer::value; }; template typename std::enable_if::value, status_t>::type writeIfInput( Parcel* data, T&& t) const { return SafeInterface::ParcelHandler{mLogTag}.write(data, std::forward(t)); } template typename std::enable_if::value, status_t>::type writeIfInput( Parcel* /*data*/, T&& /*t*/) const { return NO_ERROR; } // This method iterates through all of the arguments, writing them to the data Parcel if they // are an input (i.e., if they are not a pointer type) template status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const { status_t error = writeIfInput(data, std::forward(t)); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by writeIfInput return error; } return writeInputs(data, std::forward(remaining)...); } static status_t writeInputs(Parcel* /*data*/) { return NO_ERROR; } template typename std::enable_if::value, status_t>::type readIfOutput( const Parcel& reply, T&& t) const { return SafeInterface::ParcelHandler{mLogTag}.read(reply, std::forward(t)); } template static typename std::enable_if::value, status_t>::type readIfOutput( const Parcel& /*reply*/, T&& /*t*/) { return NO_ERROR; } // Similar to writeInputs except that it reads output arguments from the reply Parcel template status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const { status_t error = readIfOutput(reply, std::forward(t)); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by readIfOutput return error; } return readOutputs(reply, std::forward(remaining)...); } static status_t readOutputs(const Parcel& /*data*/) { return NO_ERROR; } }; template class SafeBnInterface : public BnInterface { public: explicit SafeBnInterface(const char* logTag) : mLogTag(logTag) {} protected: template status_t callLocal(const Parcel& data, Parcel* reply, Method method) { CHECK_INTERFACE(this, data, reply); // Since we need to both pass inputs into the call as well as retrieve outputs, we create a // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the // outputs. When we ultimately call into the method, we will pass the addresses of the // output arguments instead of their tuple members directly, but the storage will live in // the tuple. using ParamTuple = typename SafeInterface::ParamExtractor::ParamTuple; typename RawConverter, ParamTuple>::type rawArgs{}; // Read the inputs from the data Parcel into the argument tuple status_t error = InputReader{mLogTag}.readInputs(data, &rawArgs); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by read return error; } // Call the local method status_t result = MethodCaller::call(this, method, &rawArgs); // Extract the outputs from the argument tuple and write them into the reply Parcel error = OutputWriter{mLogTag}.writeOutputs(reply, &rawArgs); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by write return error; } // Return the result code in the reply Parcel error = reply->writeInt32(result); if (CC_UNLIKELY(error != NO_ERROR)) { ALOG(LOG_ERROR, mLogTag, "Failed to write result"); #if SI_DUMP_CALLSTACKS CallStack callStack(mLogTag); #endif return error; } return NO_ERROR; } template status_t callLocalAsync(const Parcel& data, Parcel* /*reply*/, Method method) { // reply is not actually used by CHECK_INTERFACE CHECK_INTERFACE(this, data, reply); // Since we need to both pass inputs into the call as well as retrieve outputs, we create a // "raw" tuple, where the inputs are interleaved with actual, non-pointer versions of the // outputs. When we ultimately call into the method, we will pass the addresses of the // output arguments instead of their tuple members directly, but the storage will live in // the tuple. using ParamTuple = typename SafeInterface::ParamExtractor::ParamTuple; typename RawConverter, ParamTuple>::type rawArgs{}; // Read the inputs from the data Parcel into the argument tuple status_t error = InputReader{mLogTag}.readInputs(data, &rawArgs); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged by read return error; } // Call the local method MethodCaller::callVoid(this, method, &rawArgs); // After calling, there is nothing more to do since asynchronous calls do not return a value // to the caller return NO_ERROR; } private: const char* const mLogTag; // RemoveFirst strips the first element from a tuple. // For example, given T = std::tuple, RemoveFirst::type = std::tuple template struct RemoveFirst; template struct RemoveFirst> { using type = std::tuple; }; // RawConverter strips a tuple down to its fundamental types, discarding both pointers and // references. This allows us to allocate storage for both input (non-pointer) arguments and // output (pointer) arguments in one tuple. // For example, given T = std::tuple, RawConverter::type = std::tuple template struct RawConverter; template struct RawConverter, Unconverted> { private: using ElementType = typename std::tuple_element<0, Unconverted>::type; using Decayed = typename std::decay::type; using WithoutPointer = typename std::remove_pointer::type; public: using type = typename RawConverter, typename RemoveFirst::type>::type; }; template struct RawConverter, std::tuple<>> { using type = std::tuple; }; // This provides a simple way to determine whether the indexed element of Args... is a pointer template struct ElementIsPointer { private: using ElementType = typename std::tuple_element>::type; public: static constexpr bool value = std::is_pointer::value; }; // This class iterates over the parameter types, and if a given parameter is an input // (i.e., is not a pointer), reads the corresponding argument tuple element from the data Parcel template class InputReader; template class InputReader> { public: explicit InputReader(const char* logTag) : mLogTag(logTag) {} // Note that in this case (as opposed to in SafeBpInterface), we iterate using an explicit // index (starting with 0 here) instead of using recursion and stripping the first element. // This is because in SafeBpInterface we aren't actually operating on a real tuple, but are // instead just using a tuple as a convenient container for variadic types, whereas here we // can't modify the argument tuple without causing unnecessary copies or moves of the data // contained therein. template status_t readInputs(const Parcel& data, RawTuple* args) { return dispatchArg<0>(data, args); } private: const char* const mLogTag; template typename std::enable_if::value, status_t>::type readIfInput( const Parcel& data, RawTuple* args) { return SafeInterface::ParcelHandler{mLogTag}.read(data, &std::get(*args)); } template typename std::enable_if::value, status_t>::type readIfInput( const Parcel& /*data*/, RawTuple* /*args*/) { return NO_ERROR; } // Recursively iterate through the arguments template typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg( const Parcel& data, RawTuple* args) { status_t error = readIfInput(data, args); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged in read return error; } return dispatchArg(data, args); } template typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg( const Parcel& /*data*/, RawTuple* /*args*/) { return NO_ERROR; } }; // getForCall uses the types of the parameters to determine whether a given element of the // argument tuple is an input, which should be passed directly into the call, or an output, for // which its address should be passed into the call template static typename std::enable_if< ElementIsPointer::value, typename std::tuple_element>::type>::type getForCall(RawTuple* args) { return &std::get(*args); } template static typename std::enable_if< !ElementIsPointer::value, typename std::tuple_element>::type>::type& getForCall(RawTuple* args) { return std::get(*args); } // This template class uses std::index_sequence and parameter pack expansion to call the given // method using the elements of the argument tuple (after those arguments are passed through // getForCall to get addresses instead of values for output arguments) template struct MethodCaller; template struct MethodCaller> { public: // The calls through these to the helper methods are necessary to generate the // std::index_sequences used to unpack the argument tuple into the method call template static status_t call(Class* instance, MemberFunction function, RawTuple* args) { return callHelper(instance, function, args, std::index_sequence_for{}); } template static void callVoid(Class* instance, MemberFunction function, RawTuple* args) { callVoidHelper(instance, function, args, std::index_sequence_for{}); } private: template static status_t callHelper(Class* instance, MemberFunction function, RawTuple* args, std::index_sequence /*unused*/) { return (instance->*function)(getForCall(args)...); } template static void callVoidHelper(Class* instance, MemberFunction function, RawTuple* args, std::index_sequence /*unused*/) { (instance->*function)(getForCall(args)...); } }; // This class iterates over the parameter types, and if a given parameter is an output // (i.e., is a pointer), writes the corresponding argument tuple element into the reply Parcel template struct OutputWriter; template struct OutputWriter> { public: explicit OutputWriter(const char* logTag) : mLogTag(logTag) {} // See the note on InputReader::readInputs for why this differs from the arguably simpler // RemoveFirst approach in SafeBpInterface template status_t writeOutputs(Parcel* reply, RawTuple* args) { return dispatchArg<0>(reply, args); } private: const char* const mLogTag; template typename std::enable_if::value, status_t>::type writeIfOutput(Parcel* reply, RawTuple* args) { return SafeInterface::ParcelHandler{mLogTag}.write(reply, std::get(*args)); } template typename std::enable_if::value, status_t>::type writeIfOutput(Parcel* /*reply*/, RawTuple* /*args*/) { return NO_ERROR; } // Recursively iterate through the arguments template typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg( Parcel* reply, RawTuple* args) { status_t error = writeIfOutput(reply, args); if (CC_UNLIKELY(error != NO_ERROR)) { // A message will have been logged in read return error; } return dispatchArg(reply, args); } template typename std::enable_if<(I >= sizeof...(Params)), status_t>::type dispatchArg( Parcel* /*reply*/, RawTuple* /*args*/) { return NO_ERROR; } }; }; } // namespace android libs/binder/include/binder/Status.h0100644 0000000 0000000 00000014041 13756501735 016337 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_BINDER_STATUS_H #define ANDROID_BINDER_STATUS_H #include #include #include #include #include namespace android { namespace binder { // An object similar in function to a status_t except that it understands // how exceptions are encoded in the prefix of a Parcel. Used like: // // Parcel data; // Parcel reply; // status_t status; // binder::Status remote_exception; // if ((status = data.writeInterfaceToken(interface_descriptor)) != OK || // (status = data.writeInt32(function_input)) != OK) { // // We failed to write into the memory of our local parcel? // } // if ((status = remote()->transact(transaction, data, &reply)) != OK) { // // Something has gone wrong in the binder driver or libbinder. // } // if ((status = remote_exception.readFromParcel(reply)) != OK) { // // The remote didn't correctly write the exception header to the // // reply. // } // if (!remote_exception.isOk()) { // // The transaction went through correctly, but the remote reported an // // exception during handling. // } // class Status final { public: // Keep the exception codes in sync with android/os/Parcel.java. enum Exception { EX_NONE = 0, EX_SECURITY = -1, EX_BAD_PARCELABLE = -2, EX_ILLEGAL_ARGUMENT = -3, EX_NULL_POINTER = -4, EX_ILLEGAL_STATE = -5, EX_NETWORK_MAIN_THREAD = -6, EX_UNSUPPORTED_OPERATION = -7, EX_SERVICE_SPECIFIC = -8, EX_PARCELABLE = -9, // This is special and Java specific; see Parcel.java. EX_HAS_REPLY_HEADER = -128, // This is special, and indicates to C++ binder proxies that the // transaction has failed at a low level. EX_TRANSACTION_FAILED = -129, }; // A more readable alias for the default constructor. static Status ok(); // Authors should explicitly pick whether their integer is: // - an exception code (EX_* above) // - service specific error code // - status_t // // Prefer a generic exception code when possible, then a service specific // code, and finally a status_t for low level failures or legacy support. // Exception codes and service specific errors map to nicer exceptions for // Java clients. static Status fromExceptionCode(int32_t exceptionCode); static Status fromExceptionCode(int32_t exceptionCode, const String8& message); static Status fromExceptionCode(int32_t exceptionCode, const char* message); static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode); static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode, const String8& message); static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode, const char* message); static Status fromStatusT(status_t status); static std::string exceptionToString(status_t exceptionCode); Status() = default; ~Status() = default; // Status objects are copyable and contain just simple data. Status(const Status& status) = default; Status(Status&& status) = default; Status& operator=(const Status& status) = default; // Bear in mind that if the client or service is a Java endpoint, this // is not the logic which will provide/interpret the data here. status_t readFromParcel(const Parcel& parcel); status_t writeToParcel(Parcel* parcel) const; // Set one of the pre-defined exception types defined above. void setException(int32_t ex, const String8& message); // Set a service specific exception with error code. void setServiceSpecificError(int32_t errorCode, const String8& message); // Setting a |status| != OK causes generated code to return |status| // from Binder transactions, rather than writing an exception into the // reply Parcel. This is the least preferable way of reporting errors. void setFromStatusT(status_t status); // Get information about an exception. int32_t exceptionCode() const { return mException; } const String8& exceptionMessage() const { return mMessage; } status_t transactionError() const { return mException == EX_TRANSACTION_FAILED ? mErrorCode : OK; } int32_t serviceSpecificErrorCode() const { return mException == EX_SERVICE_SPECIFIC ? mErrorCode : 0; } bool isOk() const { return mException == EX_NONE; } // For logging. String8 toString8() const; private: Status(int32_t exceptionCode, int32_t errorCode); Status(int32_t exceptionCode, int32_t errorCode, const String8& message); // If |mException| == EX_TRANSACTION_FAILED, generated code will return // |mErrorCode| as the result of the transaction rather than write an // exception to the reply parcel. // // Otherwise, we always write |mException| to the parcel. // If |mException| != EX_NONE, we write |mMessage| as well. // If |mException| == EX_SERVICE_SPECIFIC we write |mErrorCode| as well. int32_t mException = EX_NONE; int32_t mErrorCode = 0; String8 mMessage; }; // class Status // For gtest output logging std::stringstream& operator<< (std::stringstream& stream, const Status& s); } // namespace binder } // namespace android #endif // ANDROID_BINDER_STATUS_H libs/binder/include/binder/TextOutput.h0100644 0000000 0000000 00000012457 13756501735 017232 0ustar000000000 0000000 /* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_TEXTOUTPUT_H #define ANDROID_TEXTOUTPUT_H #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { class TextOutput { public: TextOutput(); virtual ~TextOutput(); virtual status_t print(const char* txt, size_t len) = 0; virtual void moveIndent(int delta) = 0; class Bundle { public: inline explicit Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } inline ~Bundle() { mTO.popBundle(); } private: TextOutput& mTO; }; virtual void pushBundle() = 0; virtual void popBundle() = 0; }; // --------------------------------------------------------------------------- // Text output stream for printing to the log (via utils/Log.h). extern TextOutput& alog; // Text output stream for printing to stdout. extern TextOutput& aout; // Text output stream for printing to stderr. extern TextOutput& aerr; typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); TextOutput& endl(TextOutput& to); TextOutput& indent(TextOutput& to); TextOutput& dedent(TextOutput& to); template TextOutput& operator<<(TextOutput& to, const T& val) { std::stringstream strbuf; strbuf << val; std::string str = strbuf.str(); to.print(str.c_str(), str.size()); return to; } TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); class TypeCode { public: inline explicit TypeCode(uint32_t code); inline ~TypeCode(); inline uint32_t typeCode() const; private: uint32_t mCode; }; TextOutput& operator<<(TextOutput& to, const TypeCode& val); class HexDump { public: HexDump(const void *buf, size_t size, size_t bytesPerLine=16); inline ~HexDump(); inline HexDump& setBytesPerLine(size_t bytesPerLine); inline HexDump& setSingleLineCutoff(int32_t bytes); inline HexDump& setAlignment(size_t alignment); inline HexDump& setCArrayStyle(bool enabled); inline const void* buffer() const; inline size_t size() const; inline size_t bytesPerLine() const; inline int32_t singleLineCutoff() const; inline size_t alignment() const; inline bool carrayStyle() const; private: const void* mBuffer; size_t mSize; size_t mBytesPerLine; int32_t mSingleLineCutoff; size_t mAlignment; bool mCArrayStyle; }; TextOutput& operator<<(TextOutput& to, const HexDump& val); inline TextOutput& operator<<(TextOutput& to, decltype(std::endl>) /*val*/) { endl(to); return to; } inline TextOutput& operator<<(TextOutput& to, const char &c) { to.print(&c, 1); return to; } inline TextOutput& operator<<(TextOutput& to, const bool &val) { if (val) to.print("true", 4); else to.print("false", 5); return to; } inline TextOutput& operator<<(TextOutput& to, const String16& val) { to << String8(val).string(); return to; } // --------------------------------------------------------------------------- // No user servicable parts below. inline TextOutput& endl(TextOutput& to) { to.print("\n", 1); return to; } inline TextOutput& indent(TextOutput& to) { to.moveIndent(1); return to; } inline TextOutput& dedent(TextOutput& to) { to.moveIndent(-1); return to; } inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) { return (*func)(to); } inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } inline TypeCode::~TypeCode() { } inline uint32_t TypeCode::typeCode() const { return mCode; } inline HexDump::~HexDump() { } inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { mBytesPerLine = bytesPerLine; return *this; } inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { mSingleLineCutoff = bytes; return *this; } inline HexDump& HexDump::setAlignment(size_t alignment) { mAlignment = alignment; return *this; } inline HexDump& HexDump::setCArrayStyle(bool enabled) { mCArrayStyle = enabled; return *this; } inline const void* HexDump::buffer() const { return mBuffer; } inline size_t HexDump::size() const { return mSize; } inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } inline size_t HexDump::alignment() const { return mAlignment; } inline bool HexDump::carrayStyle() const { return mCArrayStyle; } // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_TEXTOUTPUT_H libs/binder/include/binder/Value.h0100644 0000000 0000000 00000015411 13756501735 016132 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_VALUE_H #define ANDROID_VALUE_H #include #include #include #include #include #include #include #include #include #include #include namespace android { class Parcel; namespace binder { /** * A limited C++ generic type. The purpose of this class is to allow C++ * programs to make use of (or implement) Binder interfaces which make use * the Java "Object" generic type (either via the use of the Map type or * some other mechanism). * * This class only supports a limited set of types, but additional types * may be easily added to this class in the future as needed---without * breaking binary compatability. * * This class was written in such a way as to help avoid type errors by * giving each type their own explicity-named accessor methods (rather than * overloaded methods). * * When reading or writing this class to a Parcel, use the `writeValue()` * and `readValue()` methods. */ class Value { public: Value(); virtual ~Value(); Value& swap(Value &); bool empty() const; void clear(); #ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO const std::type_info& type() const; #endif int32_t parcelType() const; bool operator==(const Value& rhs) const; bool operator!=(const Value& rhs) const { return !this->operator==(rhs); } Value(const Value& value); Value(const bool& value); // NOLINT(google-explicit-constructor) Value(const int8_t& value); // NOLINT(google-explicit-constructor) Value(const int32_t& value); // NOLINT(google-explicit-constructor) Value(const int64_t& value); // NOLINT(google-explicit-constructor) Value(const double& value); // NOLINT(google-explicit-constructor) Value(const String16& value); // NOLINT(google-explicit-constructor) Value(const std::vector& value); // NOLINT(google-explicit-constructor) Value(const std::vector& value); // NOLINT(google-explicit-constructor) Value(const std::vector& value); // NOLINT(google-explicit-constructor) Value(const std::vector& value); // NOLINT(google-explicit-constructor) Value(const std::vector& value); // NOLINT(google-explicit-constructor) Value(const std::vector& value); // NOLINT(google-explicit-constructor) Value(const os::PersistableBundle& value); // NOLINT(google-explicit-constructor) Value(const binder::Map& value); // NOLINT(google-explicit-constructor) Value& operator=(const Value& rhs); Value& operator=(const int8_t& rhs); Value& operator=(const bool& rhs); Value& operator=(const int32_t& rhs); Value& operator=(const int64_t& rhs); Value& operator=(const double& rhs); Value& operator=(const String16& rhs); Value& operator=(const std::vector& rhs); Value& operator=(const std::vector& rhs); Value& operator=(const std::vector& rhs); Value& operator=(const std::vector& rhs); Value& operator=(const std::vector& rhs); Value& operator=(const std::vector& rhs); Value& operator=(const os::PersistableBundle& rhs); Value& operator=(const binder::Map& rhs); void putBoolean(const bool& value); void putByte(const int8_t& value); void putInt(const int32_t& value); void putLong(const int64_t& value); void putDouble(const double& value); void putString(const String16& value); void putBooleanVector(const std::vector& value); void putByteVector(const std::vector& value); void putIntVector(const std::vector& value); void putLongVector(const std::vector& value); void putDoubleVector(const std::vector& value); void putStringVector(const std::vector& value); void putPersistableBundle(const os::PersistableBundle& value); void putMap(const binder::Map& value); bool getBoolean(bool* out) const; bool getByte(int8_t* out) const; bool getInt(int32_t* out) const; bool getLong(int64_t* out) const; bool getDouble(double* out) const; bool getString(String16* out) const; bool getBooleanVector(std::vector* out) const; bool getByteVector(std::vector* out) const; bool getIntVector(std::vector* out) const; bool getLongVector(std::vector* out) const; bool getDoubleVector(std::vector* out) const; bool getStringVector(std::vector* out) const; bool getPersistableBundle(os::PersistableBundle* out) const; bool getMap(binder::Map* out) const; bool isBoolean() const; bool isByte() const; bool isInt() const; bool isLong() const; bool isDouble() const; bool isString() const; bool isBooleanVector() const; bool isByteVector() const; bool isIntVector() const; bool isLongVector() const; bool isDoubleVector() const; bool isStringVector() const; bool isPersistableBundle() const; bool isMap() const; // String Convenience Adapters // --------------------------- explicit Value(const String8& value): Value(String16(value)) { } explicit Value(const ::std::string& value): Value(String8(value.c_str())) { } void putString(const String8& value) { return putString(String16(value)); } void putString(const ::std::string& value) { return putString(String8(value.c_str())); } Value& operator=(const String8& rhs) { return *this = String16(rhs); } Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); } bool getString(String8* out) const; bool getString(::std::string* out) const; private: // This allows ::android::Parcel to call the two methods below. friend class ::android::Parcel; // This is called by ::android::Parcel::writeValue() status_t writeToParcel(Parcel* parcel) const; // This is called by ::android::Parcel::readValue() status_t readFromParcel(const Parcel* parcel); template class Content; class ContentBase; ContentBase* mContent; }; } // namespace binder } // namespace android #endif // ANDROID_VALUE_H libs/binder/include/private/0040755 0000000 0000000 00000000000 13756501735 015115 5ustar000000000 0000000 libs/binder/include/private/binder/0040755 0000000 0000000 00000000000 13756501735 016360 5ustar000000000 0000000 libs/binder/include/private/binder/ParcelValTypes.h0100644 0000000 0000000 00000002325 13756501735 021426 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ namespace android { namespace binder { // Keep in sync with frameworks/base/core/java/android/os/Parcel.java. enum { VAL_NULL = -1, VAL_STRING = 0, VAL_INTEGER = 1, VAL_MAP = 2, VAL_BUNDLE = 3, VAL_PARCELABLE = 4, VAL_SHORT = 5, VAL_LONG = 6, VAL_DOUBLE = 8, VAL_BOOLEAN = 9, VAL_BYTEARRAY = 13, VAL_STRINGARRAY = 14, VAL_IBINDER = 15, VAL_INTARRAY = 18, VAL_LONGARRAY = 19, VAL_BYTE = 20, VAL_SERIALIZABLE = 21, VAL_BOOLEANARRAY = 23, VAL_PERSISTABLEBUNDLE = 25, VAL_DOUBLEARRAY = 28, }; } // namespace binder } // namespace android libs/binder/include/private/binder/Static.h0100644 0000000 0000000 00000002516 13756501735 017761 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ // All static variables go here, to control initialization and // destruction order in the library. #include #include #include #ifndef __ANDROID_VNDK__ #include #endif #include namespace android { // For TextStream.cpp extern Vector gTextBuffers; // For ProcessState.cpp extern Mutex& gProcessMutex; extern sp gProcess; // For IServiceManager.cpp extern Mutex gDefaultServiceManagerLock; extern sp gDefaultServiceManager; #ifndef __ANDROID_VNDK__ extern sp gPermissionController; #endif extern bool gSystemBootCompleted; } // namespace android libs/binder/include/private/binder/binder_module.h0100644 0000000 0000000 00000001631 13756501735 021337 0ustar000000000 0000000 /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _BINDER_MODULE_H_ #define _BINDER_MODULE_H_ #ifdef __cplusplus namespace android { #endif /* obtain structures and constants from the kernel header */ #include #include #ifdef __cplusplus } // namespace android #endif #endif // _BINDER_MODULE_H_ libs/binder/ndk/0040755 0000000 0000000 00000000000 13756501735 012574 5ustar000000000 0000000 libs/binder/ndk/.clang-format0100644 0000000 0000000 00000000315 13756501735 015143 0ustar000000000 0000000 BasedOnStyle: Google ColumnLimit: 100 IndentWidth: 4 ContinuationIndentWidth: 8 PointerAlignment: Left TabWidth: 4 AllowShortFunctionsOnASingleLine: Inline PointerAlignment: Left TabWidth: 4 UseTab: Never libs/binder/ndk/Android.bp0100644 0000000 0000000 00000003223 13756501735 014474 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ cc_library { name: "libbinder_ndk", vendor_available: true, export_include_dirs: [ "include_ndk", "include_apex", ], cflags: [ "-Wall", "-Wextra", "-Werror", ], srcs: [ "ibinder.cpp", "ibinder_jni.cpp", "parcel.cpp", "process.cpp", "status.cpp", "service_manager.cpp", ], shared_libs: [ "libandroid_runtime_lazy", "libbase", "libbinder", "libutils", ], header_libs: [ "jni_headers", ], export_header_lib_headers: [ "jni_headers", ], version_script: "libbinder_ndk.map.txt", stubs: { symbol_file: "libbinder_ndk.map.txt", versions: ["29"], }, } ndk_headers { name: "libbinder_ndk_headers", from: "include_ndk/android", to: "android", srcs: [ "include_ndk/android/*.h", ], license: "NOTICE", } ndk_library { name: "libbinder_ndk", symbol_file: "libbinder_ndk.map.txt", first_version: "29", } libs/binder/ndk/NOTICE0100644 0000000 0000000 00000024701 13756501735 013501 0ustar000000000 0000000 Copyright (c) 2018, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS libs/binder/ndk/ibinder.cpp0100644 0000000 0000000 00000044443 13756501735 014722 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include "ibinder_internal.h" #include #include "parcel_internal.h" #include "status_internal.h" #include #include using DeathRecipient = ::android::IBinder::DeathRecipient; using ::android::IBinder; using ::android::Parcel; using ::android::sp; using ::android::status_t; using ::android::String16; using ::android::String8; using ::android::wp; namespace ABBinderTag { static const void* kId = "ABBinder"; static void* kValue = static_cast(new bool{true}); void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */}; static void attach(const sp& binder) { binder->attachObject(kId, kValue, nullptr /*cookie*/, clean); } static bool has(const sp& binder) { return binder != nullptr && binder->findObject(kId) == kValue; } } // namespace ABBinderTag namespace ABpBinderTag { static std::mutex gLock; static const void* kId = "ABpBinder"; struct Value { wp binder; }; void clean(const void* id, void* obj, void* cookie) { CHECK(id == kId) << id << " " << obj << " " << cookie; delete static_cast(obj); }; } // namespace ABpBinderTag AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {} AIBinder::~AIBinder() {} bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; if (mClazz == clazz) return true; String8 newDescriptor(clazz->getInterfaceDescriptor()); if (mClazz != nullptr) { String8 currentDescriptor(mClazz->getInterfaceDescriptor()); if (newDescriptor == currentDescriptor) { LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor << "' match during associateClass, but they are different class objects. " "Class descriptor collision?"; } else { LOG(ERROR) << __func__ << ": Class cannot be associated on object which already has a class. " "Trying to associate to '" << newDescriptor.c_str() << "' but already set to '" << currentDescriptor.c_str() << "'."; } // always a failure because we know mClazz != clazz return false; } CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor String8 descriptor(getBinder()->getInterfaceDescriptor()); if (descriptor != newDescriptor) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str() << "' but descriptor is actually '" << descriptor.c_str() << "'."; return false; } // if this is a local object, it's not one known to libbinder_ndk mClazz = clazz; return true; } ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) : AIBinder(clazz), BBinder(), mUserData(userData) { CHECK(clazz != nullptr); } ABBinder::~ABBinder() { getClass()->onDestroy(mUserData); } const String16& ABBinder::getInterfaceDescriptor() const { return getClass()->getInterfaceDescriptor(); } status_t ABBinder::dump(int fd, const ::android::Vector& args) { AIBinder_onDump onDump = getClass()->onDump; if (onDump == nullptr) { return STATUS_OK; } // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be // null in Java if (args.size() > INT32_MAX) { LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size(); return STATUS_BAD_VALUE; } std::vector utf8Args; // owns memory of utf8s utf8Args.reserve(args.size()); std::vector utf8Pointers; // what can be passed over NDK API utf8Pointers.reserve(args.size()); for (size_t i = 0; i < args.size(); i++) { utf8Args.push_back(String8(args[i])); utf8Pointers.push_back(utf8Args[i].c_str()); } return onDump(this, fd, utf8Pointers.data(), utf8Pointers.size()); } status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply, binder_flags_t flags) { if (isUserCommand(code)) { if (!data.checkInterface(this)) { return STATUS_BAD_TYPE; } const AParcel in = AParcel::readOnly(this, &data); AParcel out = AParcel(this, reply, false /*owns*/); binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); } else { return BBinder::onTransact(code, data, reply, flags); } } ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) : AIBinder(nullptr /*clazz*/), BpRefBase(binder) { CHECK(binder != nullptr); } ABpBinder::~ABpBinder() {} void ABpBinder::onLastStrongRef(const void* id) { { std::lock_guard lock(ABpBinderTag::gLock); // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for // the ABpBinder to be deleted. Since a strong reference to this ABpBinder object should no // longer be able to exist at the time of this method call, there is no longer a need to // recover it. ABpBinderTag::Value* value = static_cast(remote()->findObject(ABpBinderTag::kId)); if (value != nullptr) { value->binder = nullptr; } } BpRefBase::onLastStrongRef(id); } sp ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) { if (binder == nullptr) { return nullptr; } if (ABBinderTag::has(binder)) { return static_cast(binder.get()); } // The following code ensures that for a given binder object (remote or local), if it is not an // ABBinder then at most one ABpBinder object exists in a given process representing it. std::lock_guard lock(ABpBinderTag::gLock); ABpBinderTag::Value* value = static_cast(binder->findObject(ABpBinderTag::kId)); if (value == nullptr) { value = new ABpBinderTag::Value; binder->attachObject(ABpBinderTag::kId, static_cast(value), nullptr /*cookie*/, ABpBinderTag::clean); } sp ret = value->binder.promote(); if (ret == nullptr) { ret = new ABpBinder(binder); value->binder = ret; } return ret; } struct AIBinder_Weak { wp binder; }; AIBinder_Weak* AIBinder_Weak_new(AIBinder* binder) { if (binder == nullptr) { return nullptr; } return new AIBinder_Weak{wp(binder)}; } void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) { delete weakBinder; } AIBinder* AIBinder_Weak_promote(AIBinder_Weak* weakBinder) { if (weakBinder == nullptr) { return nullptr; } sp binder = weakBinder->binder.promote(); AIBinder_incStrong(binder.get()); return binder.get(); } AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) : onCreate(onCreate), onDestroy(onDestroy), onTransact(onTransact), mInterfaceDescriptor(interfaceDescriptor) {} AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) { if (interfaceDescriptor == nullptr || onCreate == nullptr || onDestroy == nullptr || onTransact == nullptr) { return nullptr; } return new AIBinder_Class(interfaceDescriptor, onCreate, onDestroy, onTransact); } void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { CHECK(clazz != nullptr) << "setOnDump requires non-null clazz"; // this is required to be called before instances are instantiated clazz->onDump = onDump; } void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp& who) { CHECK(who == mWho); mOnDied(mCookie); sp recipient = mParentRecipient.promote(); sp strongWho = who.promote(); // otherwise this will be cleaned up later with pruneDeadTransferEntriesLocked if (recipient != nullptr && strongWho != nullptr) { status_t result = recipient->unlinkToDeath(strongWho, mCookie); if (result != ::android::DEAD_OBJECT) { LOG(WARNING) << "Unlinking to dead binder resulted in: " << result; } } mWho = nullptr; } AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied) : mOnDied(onDied) { CHECK(onDied != nullptr); } void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(), [](const sp& tdr) { return tdr->getWho() == nullptr; }), mDeathRecipients.end()); } binder_status_t AIBinder_DeathRecipient::linkToDeath(sp binder, void* cookie) { CHECK(binder != nullptr); std::lock_guard l(mDeathRecipientsMutex); sp recipient = new TransferDeathRecipient(binder, cookie, this, mOnDied); status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/); if (status != STATUS_OK) { return PruneStatusT(status); } mDeathRecipients.push_back(recipient); pruneDeadTransferEntriesLocked(); return STATUS_OK; } binder_status_t AIBinder_DeathRecipient::unlinkToDeath(sp binder, void* cookie) { CHECK(binder != nullptr); std::lock_guard l(mDeathRecipientsMutex); for (auto it = mDeathRecipients.rbegin(); it != mDeathRecipients.rend(); ++it) { sp recipient = *it; if (recipient->getCookie() == cookie && recipient->getWho() == binder) { mDeathRecipients.erase(it.base() - 1); status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { LOG(ERROR) << __func__ << ": removed reference to death recipient but unlink failed."; } return PruneStatusT(status); } } return STATUS_NAME_NOT_FOUND; } // start of C-API methods AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) { if (clazz == nullptr) { LOG(ERROR) << __func__ << ": Must provide class to construct local binder."; return nullptr; } void* userData = clazz->onCreate(args); sp ret = new ABBinder(clazz, userData); ABBinderTag::attach(ret->getBinder()); AIBinder_incStrong(ret.get()); return ret.get(); } bool AIBinder_isRemote(const AIBinder* binder) { if (binder == nullptr) { return false; } return binder->isRemote(); } bool AIBinder_isAlive(const AIBinder* binder) { if (binder == nullptr) { return false; } return const_cast(binder)->getBinder()->isBinderAlive(); } binder_status_t AIBinder_ping(AIBinder* binder) { if (binder == nullptr) { return STATUS_UNEXPECTED_NULL; } return PruneStatusT(binder->getBinder()->pingBinder()); } binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) { if (binder == nullptr) { return STATUS_UNEXPECTED_NULL; } ABBinder* bBinder = binder->asABBinder(); if (bBinder != nullptr) { AIBinder_onDump onDump = binder->getClass()->onDump; if (onDump == nullptr) { return STATUS_OK; } return PruneStatusT(onDump(bBinder, fd, args, numArgs)); } ::android::Vector utf16Args; utf16Args.setCapacity(numArgs); for (uint32_t i = 0; i < numArgs; i++) { utf16Args.push(String16(String8(args[i]))); } status_t status = binder->getBinder()->dump(fd, utf16Args); return PruneStatusT(status); } binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { LOG(ERROR) << __func__ << ": Must provide binder and recipient."; return STATUS_UNEXPECTED_NULL; } // returns binder_status_t return recipient->linkToDeath(binder->getBinder(), cookie); } binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { LOG(ERROR) << __func__ << ": Must provide binder and recipient."; return STATUS_UNEXPECTED_NULL; } // returns binder_status_t return recipient->unlinkToDeath(binder->getBinder(), cookie); } uid_t AIBinder_getCallingUid() { return ::android::IPCThreadState::self()->getCallingUid(); } pid_t AIBinder_getCallingPid() { return ::android::IPCThreadState::self()->getCallingPid(); } void AIBinder_incStrong(AIBinder* binder) { if (binder == nullptr) { LOG(ERROR) << __func__ << ": on null binder"; return; } binder->incStrong(nullptr); } void AIBinder_decStrong(AIBinder* binder) { if (binder == nullptr) { LOG(ERROR) << __func__ << ": on null binder"; return; } binder->decStrong(nullptr); } int32_t AIBinder_debugGetRefCount(AIBinder* binder) { if (binder == nullptr) { LOG(ERROR) << __func__ << ": on null binder"; return -1; } return binder->getStrongCount(); } bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) { if (binder == nullptr) { return false; } return binder->associateClass(clazz); } const AIBinder_Class* AIBinder_getClass(AIBinder* binder) { if (binder == nullptr) { return nullptr; } return binder->getClass(); } void* AIBinder_getUserData(AIBinder* binder) { if (binder == nullptr) { return nullptr; } ABBinder* bBinder = binder->asABBinder(); if (bBinder == nullptr) { return nullptr; } return bBinder->getUserData(); } binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { if (binder == nullptr || in == nullptr) { LOG(ERROR) << __func__ << ": requires non-null parameters."; return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); if (clazz == nullptr) { LOG(ERROR) << __func__ << ": Class must be defined for a remote binder transaction. See " "AIBinder_associateClass."; return STATUS_INVALID_OPERATION; } if (!binder->isRemote()) { LOG(WARNING) << "A binder object at " << binder << " is being transacted on, however, this object is in the same process as " "its proxy. Transacting with this binder is expensive compared to just " "calling the corresponding functionality in the same process."; } *in = new AParcel(binder); status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor()); binder_status_t ret = PruneStatusT(status); if (ret != STATUS_OK) { delete *in; *in = nullptr; } return ret; } static void DestroyParcel(AParcel** parcel) { delete *parcel; *parcel = nullptr; } binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in, AParcel** out, binder_flags_t flags) { if (in == nullptr) { LOG(ERROR) << __func__ << ": requires non-null in parameter"; return STATUS_UNEXPECTED_NULL; } using AutoParcelDestroyer = std::unique_ptr; // This object is the input to the transaction. This function takes ownership of it and deletes // it. AutoParcelDestroyer forIn(in, DestroyParcel); if (!isUserCommand(code)) { LOG(ERROR) << __func__ << ": Only user-defined transactions can be made from the NDK."; return STATUS_UNKNOWN_TRANSACTION; } if ((flags & ~FLAG_ONEWAY) != 0) { LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags; return STATUS_BAD_VALUE; } if (binder == nullptr || *in == nullptr || out == nullptr) { LOG(ERROR) << __func__ << ": requires non-null parameters."; return STATUS_UNEXPECTED_NULL; } if ((*in)->getBinder() != binder) { LOG(ERROR) << __func__ << ": parcel is associated with binder object " << binder << " but called with " << (*in)->getBinder(); return STATUS_BAD_VALUE; } *out = new AParcel(binder); status_t status = binder->getBinder()->transact(code, *(*in)->get(), (*out)->get(), flags); binder_status_t ret = PruneStatusT(status); if (ret != STATUS_OK) { delete *out; *out = nullptr; } return ret; } AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( AIBinder_DeathRecipient_onBinderDied onBinderDied) { if (onBinderDied == nullptr) { LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter."; return nullptr; } auto ret = new AIBinder_DeathRecipient(onBinderDied); ret->incStrong(nullptr); return ret; } void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) { if (recipient == nullptr) { return; } recipient->decStrong(nullptr); } libs/binder/ndk/ibinder_internal.h0100644 0000000 0000000 00000015337 13756501735 016263 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include "ibinder_internal.h" #include #include #include #include #include #include inline bool isUserCommand(transaction_code_t code) { return code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION; } struct ABBinder; struct ABpBinder; struct AIBinder : public virtual ::android::RefBase { explicit AIBinder(const AIBinder_Class* clazz); virtual ~AIBinder(); bool associateClass(const AIBinder_Class* clazz); const AIBinder_Class* getClass() const { return mClazz; } virtual ::android::sp<::android::IBinder> getBinder() = 0; virtual ABBinder* asABBinder() { return nullptr; } virtual ABpBinder* asABpBinder() { return nullptr; } bool isRemote() const { ::android::sp<::android::IBinder> binder = const_cast(this)->getBinder(); return binder->remoteBinder() != nullptr; } private: // AIBinder instance is instance of this class for a local object. In order to transact on a // remote object, this also must be set for simplicity (although right now, only the // interfaceDescriptor from it is used). const AIBinder_Class* mClazz; }; // This is a local AIBinder object with a known class. struct ABBinder : public AIBinder, public ::android::BBinder { virtual ~ABBinder(); void* getUserData() { return mUserData; } ::android::sp<::android::IBinder> getBinder() override { return this; } ABBinder* asABBinder() override { return this; } const ::android::String16& getInterfaceDescriptor() const override; ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override; ::android::status_t onTransact(uint32_t code, const ::android::Parcel& data, ::android::Parcel* reply, binder_flags_t flags) override; private: ABBinder(const AIBinder_Class* clazz, void* userData); // only thing that should create an ABBinder friend AIBinder* AIBinder_new(const AIBinder_Class*, void*); // Can contain implementation if this is a local binder. This can still be nullptr for a local // binder. If it is nullptr, the implication is the implementation state is entirely external to // this object and the functionality provided in the AIBinder_Class is sufficient. void* mUserData; }; // This binder object may be remote or local (even though it is 'Bp'). The implication if it is // local is that it is an IBinder object created outside of the domain of libbinder_ndk. struct ABpBinder : public AIBinder, public ::android::BpRefBase { // Looks up to see if this object has or is an existing ABBinder or ABpBinder object, otherwise // it creates an ABpBinder object. static ::android::sp lookupOrCreateFromBinder( const ::android::sp<::android::IBinder>& binder); virtual ~ABpBinder(); void onLastStrongRef(const void* id) override; ::android::sp<::android::IBinder> getBinder() override { return remote(); } ABpBinder* asABpBinder() override { return this; } private: explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); }; struct AIBinder_Class { AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact); const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; } // required to be non-null, implemented for every class const AIBinder_Class_onCreate onCreate; const AIBinder_Class_onDestroy onDestroy; const AIBinder_Class_onTransact onTransact; // optional methods for a class AIBinder_onDump onDump; private: // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to // one. const ::android::String16 mInterfaceDescriptor; }; // Ownership is like this (when linked to death): // // AIBinder_DeathRecipient -sp-> TransferDeathRecipient <-wp-> IBinder // // When the AIBinder_DeathRecipient is dropped, so are the actual underlying death recipients. When // the IBinder dies, only a wp to it is kept. struct AIBinder_DeathRecipient : ::android::RefBase { // One of these is created for every linkToDeath. This is to be able to recover data when a // binderDied receipt only gives us information about the IBinder. struct TransferDeathRecipient : ::android::IBinder::DeathRecipient { TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie, const ::android::wp& parentRecipient, const AIBinder_DeathRecipient_onBinderDied onDied) : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {} void binderDied(const ::android::wp<::android::IBinder>& who) override; const ::android::wp<::android::IBinder>& getWho() { return mWho; } void* getCookie() { return mCookie; } private: ::android::wp<::android::IBinder> mWho; void* mCookie; ::android::wp mParentRecipient; // This is kept separately from AIBinder_DeathRecipient in case the death recipient is // deleted while the death notification is fired const AIBinder_DeathRecipient_onBinderDied mOnDied; }; explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied); binder_status_t linkToDeath(::android::sp<::android::IBinder>, void* cookie); binder_status_t unlinkToDeath(::android::sp<::android::IBinder> binder, void* cookie); private: // When the user of this API deletes a Bp object but not the death recipient, the // TransferDeathRecipient object can't be cleaned up. This is called whenever a new // TransferDeathRecipient is linked, and it ensures that mDeathRecipients can't grow unbounded. void pruneDeadTransferEntriesLocked(); std::mutex mDeathRecipientsMutex; std::vector<::android::sp> mDeathRecipients; AIBinder_DeathRecipient_onBinderDied mOnDied; }; libs/binder/ndk/ibinder_jni.cpp0100644 0000000 0000000 00000002524 13756501735 015554 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include "ibinder_internal.h" #include using ::android::IBinder; using ::android::ibinderForJavaObject; using ::android::javaObjectForIBinder; using ::android::sp; AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) { if (binder == nullptr) { return nullptr; } sp ibinder = ibinderForJavaObject(env, binder); sp cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder); AIBinder_incStrong(cbinder.get()); return cbinder.get(); } jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) { if (binder == nullptr) { return nullptr; } return javaObjectForIBinder(env, binder->getBinder()); } libs/binder/ndk/include_apex/0040755 0000000 0000000 00000000000 13756501735 015234 5ustar000000000 0000000 libs/binder/ndk/include_apex/android/0040755 0000000 0000000 00000000000 13756501735 016654 5ustar000000000 0000000 libs/binder/ndk/include_apex/android/binder_manager.h0100644 0000000 0000000 00000003732 13756501735 021764 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include __BEGIN_DECLS /** * This registers the service with the default service manager under this instance name. This does * not take ownership of binder. * * \param binder object to register globally with the service manager. * \param instance identifier of the service. This will be used to lookup the service. * * \return STATUS_OK on success. */ binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance); /** * Gets a binder object with this specific instance name. Will return nullptr immediately if the * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this * function is responsible for calling AIBinder_decStrong). * * \param instance identifier of the service used to lookup the service. */ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance); /** * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible * for calling AIBinder_decStrong). * * \param instance identifier of the service used to lookup the service. */ __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance); __END_DECLS libs/binder/ndk/include_apex/android/binder_process.h0100644 0000000 0000000 00000002534 13756501735 022027 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include __BEGIN_DECLS /** * This creates a threadpool for incoming binder transactions if it has not already been created. */ void ABinderProcess_startThreadPool(); /** * This sets the maximum number of threads that can be started in the threadpool. By default, after * startThreadPool is called, this is one. If it is called additional times, it will only prevent * the kernel from starting new threads and will not delete already existing threads. */ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); /** * This adds the current thread to the threadpool. This may cause the threadpool to exceed the * maximum size. */ void ABinderProcess_joinThreadPool(); __END_DECLS libs/binder/ndk/include_ndk/0040755 0000000 0000000 00000000000 13756501735 015053 5ustar000000000 0000000 libs/binder/ndk/include_ndk/android/0040755 0000000 0000000 00000000000 13756501735 016473 5ustar000000000 0000000 libs/binder/ndk/include_ndk/android/binder_auto_utils.h0100644 0000000 0000000 00000016267 13756501735 022370 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_auto_utils.h * @brief These objects provide a more C++-like thin interface to the . */ #pragma once #include #include #include #include #include #include namespace ndk { /** * Represents one strong pointer to an AIBinder object. */ class SpAIBinder { public: /** * Takes ownership of one strong refcount of binder. */ explicit SpAIBinder(AIBinder* binder = nullptr) : mBinder(binder) {} /** * Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not * explicit because it is not taking ownership of anything. */ SpAIBinder(std::nullptr_t) : SpAIBinder() {} // NOLINT(google-explicit-constructor) /** * This will delete the underlying object if it exists. See operator=. */ SpAIBinder(const SpAIBinder& other) { *this = other; } /** * This deletes the underlying object if it exists. See set. */ ~SpAIBinder() { set(nullptr); } /** * This takes ownership of a binder from another AIBinder object but it does not affect the * ownership of that other object. */ SpAIBinder& operator=(const SpAIBinder& other) { AIBinder_incStrong(other.mBinder); set(other.mBinder); return *this; } /** * Takes ownership of one strong refcount of binder */ void set(AIBinder* binder) { AIBinder* old = *const_cast(&mBinder); if (old != nullptr) AIBinder_decStrong(old); if (old != *const_cast(&mBinder)) { __assert(__FILE__, __LINE__, "Race detected."); } mBinder = binder; } /** * This returns the underlying binder object for transactions. If it is used to create another * SpAIBinder object, it should first be incremented. */ AIBinder* get() const { return mBinder; } /** * This allows the value in this class to be set from beneath it. If you call this method and * then change the value of T*, you must take ownership of the value you are replacing and add * ownership to the object that is put in here. * * Recommended use is like this: * SpAIBinder a; // will be nullptr * SomeInitFunction(a.getR()); // value is initialized with refcount * * Other usecases are discouraged. * */ AIBinder** getR() { return &mBinder; } private: AIBinder* mBinder = nullptr; }; namespace impl { /** * This baseclass owns a single object, used to make various classes RAII. */ template class ScopedAResource { public: /** * Takes ownership of t. */ explicit ScopedAResource(T t = DEFAULT) : mT(t) {} /** * This deletes the underlying object if it exists. See set. */ ~ScopedAResource() { set(DEFAULT); } /** * Takes ownership of t. */ void set(T t) { Destroy(mT); mT = t; } /** * This returns the underlying object to be modified but does not affect ownership. */ T get() { return mT; } /** * This returns the const underlying object but does not affect ownership. */ const T get() const { return mT; } /** * This allows the value in this class to be set from beneath it. If you call this method and * then change the value of T*, you must take ownership of the value you are replacing and add * ownership to the object that is put in here. * * Recommended use is like this: * ScopedAResource a; // will be nullptr * SomeInitFunction(a.getR()); // value is initialized with refcount * * Other usecases are discouraged. * */ T* getR() { return &mT; } // copy-constructing, or move/copy assignment is disallowed ScopedAResource(const ScopedAResource&) = delete; ScopedAResource& operator=(const ScopedAResource&) = delete; ScopedAResource& operator=(ScopedAResource&&) = delete; // move-constructing is okay ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; } private: T mT; }; } // namespace impl /** * Convenience wrapper. See AParcel. */ class ScopedAParcel : public impl::ScopedAResource { public: /** * Takes ownership of a. */ explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {} ~ScopedAParcel() {} ScopedAParcel(ScopedAParcel&&) = default; }; /** * Convenience wrapper. See AStatus. */ class ScopedAStatus : public impl::ScopedAResource { public: /** * Takes ownership of a. */ explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {} ~ScopedAStatus() {} ScopedAStatus(ScopedAStatus&&) = default; /** * See AStatus_isOk. */ bool isOk() { return get() != nullptr && AStatus_isOk(get()); } /** * Convenience method for okay status. */ static ScopedAStatus ok() { return ScopedAStatus(AStatus_newOk()); } }; /** * Convenience wrapper. See AIBinder_DeathRecipient. */ class ScopedAIBinder_DeathRecipient : public impl::ScopedAResource { public: /** * Takes ownership of a. */ explicit ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient* a = nullptr) : ScopedAResource(a) {} ~ScopedAIBinder_DeathRecipient() {} ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default; }; /** * Convenience wrapper. See AIBinder_Weak. */ class ScopedAIBinder_Weak : public impl::ScopedAResource { public: /** * Takes ownership of a. */ explicit ScopedAIBinder_Weak(AIBinder_Weak* a = nullptr) : ScopedAResource(a) {} ~ScopedAIBinder_Weak() {} ScopedAIBinder_Weak(ScopedAIBinder_Weak&&) = default; /** * See AIBinder_Weak_promote. */ SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); } }; /** * Convenience wrapper for a file descriptor. */ class ScopedFileDescriptor : public impl::ScopedAResource { public: /** * Takes ownership of a. */ explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {} ~ScopedFileDescriptor() {} ScopedFileDescriptor(ScopedFileDescriptor&&) = default; }; } // namespace ndk /** @} */ libs/binder/ndk/include_ndk/android/binder_ibinder.h0100644 0000000 0000000 00000054444 13756501735 021613 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_ibinder.h * @brief Object which can receive transactions and be sent across processes. */ #pragma once #include #include #include #include #include __BEGIN_DECLS #if __ANDROID_API__ >= __ANDROID_API_Q__ // Also see TF_* in kernel's binder.h typedef uint32_t binder_flags_t; enum { /** * The transaction will be dispatched and then returned to the caller. The outgoing process * cannot block a call made by this, and execution of the call will not be waited on. An error * can still be returned if the call is unable to be processed by the binder driver. All oneway * calls are guaranteed to be ordered if they are sent on the same AIBinder object. */ FLAG_ONEWAY = 0x01, }; // Also see IBinder.h in libbinder typedef uint32_t transaction_code_t; enum { /** * The first transaction code available for user commands (inclusive). */ FIRST_CALL_TRANSACTION = 0x00000001, /** * The last transaction code available for user commands (inclusive). */ LAST_CALL_TRANSACTION = 0x00ffffff, }; /** * Represents a type of AIBinder object which can be sent out. */ struct AIBinder_Class; typedef struct AIBinder_Class AIBinder_Class; /** * Represents a local or remote object which can be used for IPC or which can itself be sent. * * This object has a refcount associated with it and will be deleted when its refcount reaches zero. * How methods interactive with this refcount is described below. When using this API, it is * intended for a client of a service to hold a strong reference to that service. This also means * that user data typically should hold a strong reference to a local AIBinder object. A remote * AIBinder object automatically holds a strong reference to the AIBinder object in the server's * process. A typically memory layout looks like this: * * Key: * ---> Ownership/a strong reference * ...> A weak reference * * (process boundary) * | * MyInterface ---> AIBinder_Weak | ProxyForMyInterface * ^ . | | * | . | | * | v | v * UserData <--- AIBinder <-|- AIBinder * | * * In this way, you'll notice that a proxy for the interface holds a strong reference to the * implementation and that in the server process, the AIBinder object which was sent can be resent * so that the same AIBinder object always represents the same object. This allows, for instance, an * implementation (usually a callback) to transfer all ownership to a remote process and * automatically be deleted when the remote process is done with it or dies. Other memory models are * possible, but this is the standard one. * * If the process containing an AIBinder dies, it is possible to be holding a strong reference to * an object which does not exist. In this case, transactions to this binder will return * STATUS_DEAD_OBJECT. See also AIBinder_linkToDeath, AIBinder_unlinkToDeath, and AIBinder_isAlive. * * Once an AIBinder is created, anywhere it is passed (remotely or locally), there is a 1-1 * correspondence between the address of an AIBinder and the object it represents. This means that * when two AIBinder pointers point to the same address, they represent the same object (whether * that object is local or remote). This correspondance can be broken accidentally if AIBinder_new * is erronesouly called to create the same object multiple times. */ struct AIBinder; typedef struct AIBinder AIBinder; /** * The AIBinder object associated with this can be retrieved if it is still alive so that it can be * re-used. The intention of this is to enable the same AIBinder object to always represent the same * object. */ struct AIBinder_Weak; typedef struct AIBinder_Weak AIBinder_Weak; /** * Represents a handle on a death notification. See AIBinder_linkToDeath/AIBinder_unlinkToDeath. */ struct AIBinder_DeathRecipient; typedef struct AIBinder_DeathRecipient AIBinder_DeathRecipient; /** * This is called whenever a new AIBinder object is needed of a specific class. * * \param args these can be used to construct a new class. These are passed from AIBinder_new. * \return this is the userdata representing the class. It can be retrieved using * AIBinder_getUserData. */ typedef void* (*AIBinder_Class_onCreate)(void* args); /** * This is called whenever an AIBinder object is no longer referenced and needs destroyed. * * Typically, this just deletes whatever the implementation is. * * \param userData this is the same object returned by AIBinder_Class_onCreate */ typedef void (*AIBinder_Class_onDestroy)(void* userData); /** * This is called whenever a transaction needs to be processed by a local implementation. * * \param binder the object being transacted on. * \param code implementation-specific code representing which transaction should be taken. * \param in the implementation-specific input data to this transaction. * \param out the implementation-specific output data to this transaction. * * \return the implementation-specific output code. This may be forwarded from another service, the * result of a parcel read or write, or another error as is applicable to the specific * implementation. Usually, implementation-specific error codes are written to the output parcel, * and the transaction code is reserved for kernel errors or error codes that have been repeated * from subsequent transactions. */ typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder, transaction_code_t code, const AParcel* in, AParcel* out); /** * This creates a new instance of a class of binders which can be instantiated. This is called one * time during library initialization and cleaned up when the process exits or execs. * * None of these parameters can be null. * * \param interfaceDescriptor this is a unique identifier for the class. This is used internally for * sanity checks on transactions. * \param onCreate see AIBinder_Class_onCreate. * \param onDestroy see AIBinder_Class_onDestroy. * \param onTransact see AIBinder_Class_onTransact. * * \return the class object representing these parameters or null on error. */ __attribute__((warn_unused_result)) AIBinder_Class* AIBinder_Class_define( const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) __INTRODUCED_IN(29); /** * Dump information about an AIBinder (usually for debugging). * * When no arguments are provided, a brief overview of the interview should be given. * * \param binder interface being dumped * \param fd file descriptor to be dumped to, should be flushed, ownership is not passed. * \param args array of null-terminated strings for dump (may be null if numArgs is 0) * \param numArgs number of args to be sent * * \return binder_status_t result of transaction (if remote, for instance) */ typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char** args, uint32_t numArgs); /** * This sets the implementation of the dump method for a class. * * If this isn't set, nothing will be dumped when dump is called (for instance with * android.os.Binder#dump). Must be called before any instance of the class is created. * * \param dump function to call when an instance of this binder class is being dumped. */ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29); /** * Creates a new binder object of the appropriate class. * * Ownership of args is passed to this object. The lifecycle is implemented with AIBinder_incStrong * and AIBinder_decStrong. When the reference count reaches zero, onDestroy is called. * * When this is called, the refcount is implicitly 1. So, calling decStrong exactly one time is * required to delete this object. * * Once an AIBinder object is created using this API, re-creating that AIBinder for the same * instance of the same class will break pointer equality for that specific AIBinder object. For * instance, if someone erroneously created two AIBinder instances representing the same callback * object and passed one to a hypothetical addCallback function and then later another one to a * hypothetical removeCallback function, the remote process would have no way to determine that * these two objects are actually equal using the AIBinder pointer alone (which they should be able * to do). Also see the suggested memory ownership model suggested above. * * \param clazz the type of the object to be created. * \param args the args to pass to AIBinder_onCreate for that class. * * \return a binder object representing the newly instantiated object. */ __attribute__((warn_unused_result)) AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) __INTRODUCED_IN(29); /** * If this is hosted in a process other than the current one. * * \param binder the binder being queried. * * \return true if the AIBinder represents an object in another process. */ bool AIBinder_isRemote(const AIBinder* binder) __INTRODUCED_IN(29); /** * If this binder is known to be alive. This will not send a transaction to a remote process and * returns a result based on the last known information. That is, whenever a transaction is made, * this is automatically updated to reflect the current alive status of this binder. This will be * updated as the result of a transaction made using AIBinder_transact, but it will also be updated * based on the results of bookkeeping or other transactions made internally. * * \param binder the binder being queried. * * \return true if the binder is alive. */ bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29); /** * Built-in transaction for all binder objects. This sends a transaction that will immediately * return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a * sanity check. * * \param binder the binder being queried. * * \return STATUS_OK if the ping succeeds. */ binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29); /** * Built-in transaction for all binder objects. This dumps information about a given binder. * * See also AIBinder_Class_setOnDump, AIBinder_onDump * * \param binder the binder to dump information about * \param fd where information should be dumped to * \param args null-terminated arguments to pass (may be null if numArgs is 0) * \param numArgs number of args to send * * \return STATUS_OK if dump succeeds (or if there is nothing to dump) */ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) __INTRODUCED_IN(29); /** * Registers for notifications that the associated binder is dead. The same death recipient may be * associated with multiple different binders. If the binder is local, then no death recipient will * be given (since if the local process dies, then no recipient will exist to recieve a * transaction). The cookie is passed to recipient in the case that this binder dies and can be * null. The exact cookie must also be used to unlink this transaction (see AIBinder_linkToDeath). * This function may return a binder transaction failure. The cookie can be used both for * identification and holding user data. * * If binder is local, this will return STATUS_INVALID_OPERATION. * * \param binder the binder object you want to receive death notifications from. * \param recipient the callback that will receive notifications when/if the binder dies. * \param cookie the value that will be passed to the death recipient on death. * * \return STATUS_OK on success. */ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) __INTRODUCED_IN(29); /** * Stops registration for the associated binder dying. Does not delete the recipient. This function * may return a binder transaction failure and in case the death recipient cannot be found, it * returns STATUS_NAME_NOT_FOUND. * * This only ever needs to be called when the AIBinder_DeathRecipient remains for use with other * AIBinder objects. If the death recipient is deleted, all binders will automatically be unlinked. * If the binder dies, it will automatically unlink. If the binder is deleted, it will be * automatically unlinked. * * \param binder the binder object to remove a previously linked death recipient from. * \param recipient the callback to remove. * \param cookie the cookie used to link to death. * * \return STATUS_OK on success. STATUS_NAME_NOT_FOUND if the binder cannot be found to be unlinked. */ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) __INTRODUCED_IN(29); /** * This returns the calling UID assuming that this thread is called from a thread that is processing * a binder transaction (for instance, in the implementation of AIBinder_Class_onTransact). * * This can be used with higher-level system services to determine the caller's identity and check * permissions. * * \return calling uid or the current process's UID if this thread isn't processing a transaction. */ uid_t AIBinder_getCallingUid(); /** * This returns the calling PID assuming that this thread is called from a thread that is processing * a binder transaction (for instance, in the implementation of AIBinder_Class_onTransact). * * This can be used with higher-level system services to determine the caller's identity and check * permissions. However, when doing this, one should be aware of possible TOCTOU problems when the * calling process dies and is replaced with another process with elevated permissions and the same * PID. * * \return calling pid or the current process's PID if this thread isn't processing a transaction. * If the transaction being processed is a oneway transaction, then this method will return 0. */ pid_t AIBinder_getCallingPid(); /** * This can only be called if a strong reference to this object already exists in process. * * \param binder the binder object to add a refcount to. */ void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29); /** * This will delete the object and call onDestroy once the refcount reaches zero. * * \param binder the binder object to remove a refcount from. */ void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29); /** * For debugging only! * * \param binder the binder object to retrieve the refcount of. * * \return the number of strong-refs on this binder in this process. If binder is null, this will be * -1. */ int32_t AIBinder_debugGetRefCount(AIBinder* binder) __INTRODUCED_IN(29); /** * This sets the class of an AIBinder object. This checks to make sure the remote object is of * the expected class. A class must be set in order to use transactions on an AIBinder object. * However, if an object is just intended to be passed through to another process or used as a * handle this need not be called. * * This returns true if the class association succeeds. If it fails, no change is made to the * binder object. * * \param binder the object to attach the class to. * \param clazz the clazz to attach to binder. * * \return true if the binder has the class clazz and if the association was successful. */ bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) __INTRODUCED_IN(29); /** * Returns the class that this binder was constructed with or associated with. * * \param binder the object that is being queried. * * \return the class that this binder is associated with. If this binder wasn't created with * AIBinder_new, and AIBinder_associateClass hasn't been called, then this will return null. */ const AIBinder_Class* AIBinder_getClass(AIBinder* binder) __INTRODUCED_IN(29); /** * Value returned by onCreate for a local binder. For stateless classes (if onCreate returns * null), this also returns null. For a remote binder, this will always return null. * * \param binder the object that is being queried. * * \return the userdata returned from AIBinder_onCreate when this object was created. This may be * null for stateless objects. For remote objects, this is always null. */ void* AIBinder_getUserData(AIBinder* binder) __INTRODUCED_IN(29); /** * A transaction is a series of calls to these functions which looks this * - call AIBinder_prepareTransaction * - fill out the in parcel with parameters (lifetime of the 'in' variable) * - call AIBinder_transact * - read results from the out parcel (lifetime of the 'out' variable) */ /** * Creates a parcel to start filling out for a transaction. This may add data to the parcel for * security, debugging, or other purposes. This parcel is to be sent via AIBinder_transact and it * represents the input data to the transaction. It is recommended to check if the object is local * and call directly into its user data before calling this as the parceling and unparceling cost * can be avoided. This AIBinder must be either built with a class or associated with a class before * using this API. * * This does not affect the ownership of binder. When this function succeeds, the in parcel's * ownership is passed to the caller. At this point, the parcel can be filled out and passed to * AIBinder_transact. Alternatively, if there is an error while filling out the parcel, it can be * deleted with AParcel_delete. * * \param binder the binder object to start a transaction on. * \param in out parameter for input data to the transaction. * * \return STATUS_OK on success. This will return STATUS_INVALID_OPERATION if the binder has not yet * been associated with a class (see AIBinder_new and AIBinder_associateClass). */ binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) __INTRODUCED_IN(29); /** * Transact using a parcel created from AIBinder_prepareTransaction. This actually communicates with * the object representing this binder object. This also passes out a parcel to be used for the * return transaction. This takes ownership of the in parcel and automatically deletes it after it * is sent to the remote process. The output parcel is the result of the transaction. If the * transaction has FLAG_ONEWAY, the out parcel will be empty. Otherwise, this will block until the * remote process has processed the transaction, and the out parcel will contain the output data * from transaction. * * This does not affect the ownership of binder. The out parcel's ownership is passed to the caller * and must be released with AParcel_delete when finished reading. * * \param binder the binder object to transact on. * \param code the implementation-specific code representing which transaction should be taken. * \param in the implementation-specific input data to this transaction. * \param out the implementation-specific output data to this transaction. * \param flags possible flags to alter the way in which the transaction is conducted or 0. * * \return the result from the kernel or from the remote process. Usually, implementation-specific * error codes are written to the output parcel, and the transaction code is reserved for kernel * errors or error codes that have been repeated from subsequent transactions. */ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in, AParcel** out, binder_flags_t flags) __INTRODUCED_IN(29); /** * This does not take any ownership of the input binder, but it can be used to retrieve it if * something else in some process still holds a reference to it. * * \param binder object to create a weak pointer to. * * \return object representing a weak pointer to binder (or null if binder is null). */ __attribute__((warn_unused_result)) AIBinder_Weak* AIBinder_Weak_new(AIBinder* binder) __INTRODUCED_IN(29); /** * Deletes the weak reference. This will have no impact on the lifetime of the binder. * * \param weakBinder object created with AIBinder_Weak_new. */ void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); /** * If promotion succeeds, result will have one strong refcount added to it. Otherwise, this returns * null. * * \param weakBinder weak pointer to attempt retrieving the original object from. * * \return an AIBinder object with one refcount given to the caller or null. */ __attribute__((warn_unused_result)) AIBinder* AIBinder_Weak_promote(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); /** * This function is executed on death receipt. See AIBinder_linkToDeath/AIBinder_unlinkToDeath. * * \param cookie the cookie passed to AIBinder_linkToDeath. */ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29); /** * Creates a new binder death recipient. This can be attached to multiple different binder objects. * * \param onBinderDied the callback to call when this death recipient is invoked. * * \return the newly constructed object (or null if onBinderDied is null). */ __attribute__((warn_unused_result)) AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( AIBinder_DeathRecipient_onBinderDied onBinderDied) __INTRODUCED_IN(29); /** * Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before * calling this as these will all be automatically unlinked. * * \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new). */ void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) __INTRODUCED_IN(29); #endif //__ANDROID_API__ >= __ANDROID_API_Q__ __END_DECLS /** @} */ libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h0100644 0000000 0000000 00000004244 13756501735 022444 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_ibinder_jni.h * @brief Conversions between AIBinder and android.os.IBinder */ #pragma once #include #include __BEGIN_DECLS #if __ANDROID_API__ >= __ANDROID_API_Q__ /** * Converts an android.os.IBinder object into an AIBinder* object. * * If either env or the binder is null, null is returned. If this binder object was originally an * AIBinder object, the original object is returned. The returned object has one refcount * associated with it, and so this should be accompanied with an AIBinder_decStrong call. * * \param env Java environment. * \param binder android.os.IBinder java object. * * \return an AIBinder object representing the Java binder object. If either parameter is null, or * the Java object is of the wrong type, this will return null. */ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) __INTRODUCED_IN(29); /** * Converts an AIBinder* object into an android.os.IBinder object. * * If either env or the binder is null, null is returned. If this binder object was originally an * IBinder object, the original java object will be returned. * * \param env Java environment. * \param binder the object to convert. * * \return an android.os.IBinder object or null if the parameters were null. */ __attribute__((warn_unused_result)) jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) __INTRODUCED_IN(29); #endif //__ANDROID_API__ >= __ANDROID_API_Q__ __END_DECLS /** @} */ libs/binder/ndk/include_ndk/android/binder_interface_utils.h0100644 0000000 0000000 00000017165 13756501735 023356 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_interface_utils.h * @brief This provides common C++ classes for common operations and as base classes for C++ * interfaces. */ #pragma once #include #include #include #include #include namespace ndk { /** * analog using std::shared_ptr for internally held refcount * * ref must be called at least one time during the lifetime of this object. The recommended way to * construct this object is with SharedRefBase::make. */ class SharedRefBase { public: SharedRefBase() {} virtual ~SharedRefBase() { std::call_once(mFlagThis, [&]() { __assert(__FILE__, __LINE__, "SharedRefBase: no ref created during lifetime"); }); } /** * A shared_ptr must be held to this object when this is called. This must be called once during * the lifetime of this object. */ std::shared_ptr ref() { std::shared_ptr thiz = mThis.lock(); std::call_once(mFlagThis, [&]() { mThis = thiz = std::shared_ptr(this); }); return thiz; } /** * Convenience method for a ref (see above) which automatically casts to the desired child type. */ template std::shared_ptr ref() { return std::static_pointer_cast(ref()); } /** * Convenience method for making an object directly with a reference. */ template static std::shared_ptr make(Args&&... args) { T* t = new T(std::forward(args)...); return t->template ref(); } private: std::once_flag mFlagThis; std::weak_ptr mThis; }; /** * wrapper analog to IInterface */ class ICInterface : public SharedRefBase { public: ICInterface() {} virtual ~ICInterface() {} /** * This either returns the single existing implementation or creates a new implementation. */ virtual SpAIBinder asBinder() = 0; /** * Returns whether this interface is in a remote process. If it cannot be determined locally, * this will be checked using AIBinder_isRemote. */ virtual bool isRemote() = 0; /** * Dumps information about the interface. By default, dumps nothing. */ virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/); /** * Interprets this binder as this underlying interface if this has stored an ICInterface in the * binder's user data. * * This does not do type checking and should only be used when the binder is known to originate * from ICInterface. Most likely, you want to use I*::fromBinder. */ static inline std::shared_ptr asInterface(AIBinder* binder); /** * Helper method to create a class */ static inline AIBinder_Class* defineClass(const char* interfaceDescriptor, AIBinder_Class_onTransact onTransact); private: class ICInterfaceData { public: std::shared_ptr interface; static inline std::shared_ptr getInterface(AIBinder* binder); static inline void* onCreate(void* args); static inline void onDestroy(void* userData); static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args, uint32_t numArgs); }; }; /** * implementation of IInterface for server (n = native) */ template class BnCInterface : public INTERFACE { public: BnCInterface() {} virtual ~BnCInterface() {} SpAIBinder asBinder() override; bool isRemote() override { return false; } protected: /** * This function should only be called by asBinder. Otherwise, there is a possibility of * multiple AIBinder* objects being created for the same instance of an object. */ virtual SpAIBinder createBinder() = 0; private: std::mutex mMutex; // for asBinder ScopedAIBinder_Weak mWeakBinder; }; /** * implementation of IInterface for client (p = proxy) */ template class BpCInterface : public INTERFACE { public: explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {} virtual ~BpCInterface() {} SpAIBinder asBinder() override; bool isRemote() override { return AIBinder_isRemote(mBinder.get()); } binder_status_t dump(int fd, const char** args, uint32_t numArgs) override { return AIBinder_dump(asBinder().get(), fd, args, numArgs); } private: SpAIBinder mBinder; }; // END OF CLASS DECLARATIONS binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) { return STATUS_OK; } std::shared_ptr ICInterface::asInterface(AIBinder* binder) { return ICInterfaceData::getInterface(binder); } AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, AIBinder_Class_onTransact onTransact) { AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate, ICInterfaceData::onDestroy, onTransact); if (clazz == nullptr) { return nullptr; } // We can't know if this method is overriden by a subclass interface, so we must register // ourselves. The default (nothing to dump) is harmless. AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump); return clazz; } std::shared_ptr ICInterface::ICInterfaceData::getInterface(AIBinder* binder) { if (binder == nullptr) return nullptr; void* userData = AIBinder_getUserData(binder); if (userData == nullptr) return nullptr; return static_cast(userData)->interface; } void* ICInterface::ICInterfaceData::onCreate(void* args) { std::shared_ptr interface = static_cast(args)->ref(); ICInterfaceData* data = new ICInterfaceData{interface}; return static_cast(data); } void ICInterface::ICInterfaceData::onDestroy(void* userData) { delete static_cast(userData); } binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) { std::shared_ptr interface = getInterface(binder); return interface->dump(fd, args, numArgs); } template SpAIBinder BnCInterface::asBinder() { std::lock_guard l(mMutex); SpAIBinder binder; if (mWeakBinder.get() != nullptr) { binder.set(AIBinder_Weak_promote(mWeakBinder.get())); } if (binder.get() == nullptr) { binder = createBinder(); mWeakBinder.set(AIBinder_Weak_new(binder.get())); } return binder; } template SpAIBinder BpCInterface::asBinder() { return mBinder; } } // namespace ndk /** @} */ libs/binder/ndk/include_ndk/android/binder_parcel.h0100644 0000000 0000000 00000121055 13756501735 021436 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_parcel.h * @brief A collection of data that can be sent as a single packet. */ #pragma once #include #include struct AIBinder; typedef struct AIBinder AIBinder; __BEGIN_DECLS #if __ANDROID_API__ >= __ANDROID_API_Q__ /** * This object represents a package of data that can be sent between processes. When transacting, an * instance of it is automatically created to be used for the transaction. When two processes use * binder to communicate, they must agree on a format of this parcel to be used in order to transfer * data. This is usually done in an IDL (see AIDL, specificially). */ struct AParcel; typedef struct AParcel AParcel; /** * Cleans up a parcel. * * \param parcel A parcel returned by AIBinder_prepareTransaction or AIBinder_transact when a * transaction is being aborted. */ void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29); /** * Sets the position within the parcel. * * \param parcel The parcel of which to set the position. * \param position Position of the parcel to set. This must be a value returned by * AParcel_getDataPosition. Positions are constant for a given parcel between processes. * * \return STATUS_OK on success. If position is negative, then STATUS_BAD_VALUE will be returned. */ binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) __INTRODUCED_IN(29); /** * Gets the current position within the parcel. * * \param parcel The parcel of which to get the position. * * \return The size of the parcel. This will always be greater than 0. The values returned by this * function before and after calling various reads and writes are not defined. Only the delta * between two positions between a specific sequence of calls is defined. For instance, if position * is X, writeBool is called, and then position is Y, readBool can be called from position X will * return the same value, and then position will be Y. */ int32_t AParcel_getDataPosition(const AParcel* parcel) __INTRODUCED_IN(29); /** * This is called to allocate a buffer for a C-style string (null-terminated). The returned buffer * should be at least length bytes. This includes space for a null terminator. For a string, length * will always be strictly less than or equal to the maximum size that can be held in a size_t and * will always be greater than 0. However, if a 'null' string is being read, length will be -1. * * See also AParcel_readString. * * If allocation fails, null should be returned. * * \param stringData some external representation of a string * \param length the length of the buffer needed to fill (including the null-terminator) * \param buffer a buffer of size 'length' or null if allocation failed. * * \return true if the allocation succeeded, false otherwise. If length is -1, a true return here * means that a 'null' value (or equivalent) was successfully stored. */ typedef bool (*AParcel_stringAllocator)(void* stringData, int32_t length, char** buffer); /** * This is called to allocate an array of size 'length'. If length is -1, then a 'null' array (or * equivalent) should be created. * * See also AParcel_readStringArray * * \param arrayData some external representation of an array * \param length the length to allocate this array to * * \return true if allocation succeeded. If length is -1, a true return here means that a 'null' * value (or equivalent) was successfully stored. */ typedef bool (*AParcel_stringArrayAllocator)(void* arrayData, int32_t length); /** * This is called to allocate a string inside of an array that was allocated by an * AParcel_stringArrayAllocator. * * The index returned will always be within the range [0, length of arrayData). The returned buffer * should be at least length bytes. This includes space for a null-terminator. For a string, length * will always be strictly less than or equal to the maximum size that can be held in a size_t and * will always be greater than 0. However, if a 'null' string is being read, length will be -1. * * See also AParcel_readStringArray * * \param arrayData some external representation of an array. * \param index the index at which a string should be allocated. * \param length the length of the string to be allocated at this index. See also * AParcel_stringAllocator. This includes the length required for a null-terminator. * \param buffer a buffer of size 'length' or null if allocation failed. * * \return true if the allocation succeeded, false otherwise. If length is -1, a true return here * means that a 'null' value (or equivalent) was successfully stored. */ typedef bool (*AParcel_stringArrayElementAllocator)(void* arrayData, size_t index, int32_t length, char** buffer); /** * This returns the length and buffer of an array at a specific index in an arrayData object. * * See also AParcel_writeStringArray * * \param arrayData some external representation of an array. * \param index the index at which a string should be allocated. * \param outLength an out parameter for the length of the string at the specified index. This * should not include the length for a null-terminator if there is one. If the object at this index * is 'null', then this should be set to -1. * * \param a buffer of size outLength or more representing the string at the provided index. This is * not required to be null-terminated. If the object at index is null, then this should be null. */ typedef const char* (*AParcel_stringArrayElementGetter)(const void* arrayData, size_t index, int32_t* outLength); /** * This is called to allocate an array of size 'length'. If length is -1, then a 'null' array (or * equivalent) should be created. * * See also AParcel_readParcelableArray * * \param arrayData some external representation of an array * \param length the length to allocate this array to * * \return true if allocation succeeded. If length is -1, a true return here means that a 'null' * value (or equivalent) was successfully stored. */ typedef bool (*AParcel_parcelableArrayAllocator)(void* arrayData, int32_t length); /** * This is called to parcel the underlying data from an arrayData object at index. * * See also AParcel_writeParcelableArray * * \param parcel parcel to write the parcelable to * \param arrayData some external representation of an array of parcelables (a user-defined type). * \param index the index of the value to be retrieved. * * \return status (usually returned from other parceling functions). STATUS_OK for success. */ typedef binder_status_t (*AParcel_writeParcelableElement)(AParcel* parcel, const void* arrayData, size_t index); /** * This is called to set an underlying value in an arrayData object at index. * * See also AParcel_readParcelableArray * * \param parcel parcel to read the parcelable from * \param arrayData some external representation of an array of parcelables (a user-defined type). * \param index the index of the value to be set. * * \return status (usually returned from other parceling functions). STATUS_OK for success. */ typedef binder_status_t (*AParcel_readParcelableElement)(const AParcel* parcel, void* arrayData, size_t index); // @START-PRIMITIVE-VECTOR-GETTERS /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readInt32Array * * \param arrayData some external representation of an array of int32_t. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of int32_t of size 'length' (if length is >= 0, if length is 0, this * may be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_int32ArrayAllocator)(void* arrayData, int32_t length, int32_t** outBuffer); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readUint32Array * * \param arrayData some external representation of an array of uint32_t. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of uint32_t of size 'length' (if length is >= 0, if length is 0, this * may be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_uint32ArrayAllocator)(void* arrayData, int32_t length, uint32_t** outBuffer); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readInt64Array * * \param arrayData some external representation of an array of int64_t. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of int64_t of size 'length' (if length is >= 0, if length is 0, this * may be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_int64ArrayAllocator)(void* arrayData, int32_t length, int64_t** outBuffer); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readUint64Array * * \param arrayData some external representation of an array of uint64_t. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of uint64_t of size 'length' (if length is >= 0, if length is 0, this * may be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_uint64ArrayAllocator)(void* arrayData, int32_t length, uint64_t** outBuffer); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readFloatArray * * \param arrayData some external representation of an array of float. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of float of size 'length' (if length is >= 0, if length is 0, this may * be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_floatArrayAllocator)(void* arrayData, int32_t length, float** outBuffer); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readDoubleArray * * \param arrayData some external representation of an array of double. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of double of size 'length' (if length is >= 0, if length is 0, this may * be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_doubleArrayAllocator)(void* arrayData, int32_t length, double** outBuffer); /** * This allocates an array of size 'length' inside of arrayData and returns whether or not there was * a success. If length is -1, then this should allocate some representation of a null array. * * See also AParcel_readBoolArray * * \param arrayData some external representation of an array of bool. * \param length the length to allocate arrayData to (or -1 if this represents a null array). * * \return whether the allocation succeeded. */ typedef bool (*AParcel_boolArrayAllocator)(void* arrayData, int32_t length); /** * This is called to get the underlying data from an arrayData object at index. * * See also AParcel_writeBoolArray * * \param arrayData some external representation of an array of bool. * \param index the index of the value to be retrieved. * * \return the value of the array at index index. */ typedef bool (*AParcel_boolArrayGetter)(const void* arrayData, size_t index); /** * This is called to set an underlying value in an arrayData object at index. * * See also AParcel_readBoolArray * * \param arrayData some external representation of an array of bool. * \param index the index of the value to be set. * \param value the value to set at index index. */ typedef void (*AParcel_boolArraySetter)(void* arrayData, size_t index, bool value); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readCharArray * * \param arrayData some external representation of an array of char16_t. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of char16_t of size 'length' (if length is >= 0, if length is 0, this * may be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_charArrayAllocator)(void* arrayData, int32_t length, char16_t** outBuffer); /** * This is called to get the underlying data from an arrayData object. * * The implementation of this function should allocate a contiguous array of size 'length' and * return that underlying buffer to be filled out. If there is an error or length is 0, null may be * returned. If length is -1, this should allocate some representation of a null array. * * See also AParcel_readByteArray * * \param arrayData some external representation of an array of int8_t. * \param length the length to allocate arrayData to. * \param outBuffer a buffer of int8_t of size 'length' (if length is >= 0, if length is 0, this may * be nullptr). * * \return whether or not the allocation was successful (or whether a null array is represented when * length is -1). */ typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length, int8_t** outBuffer); // @END-PRIMITIVE-VECTOR-GETTERS /** * Writes an AIBinder to the next location in a non-null parcel. Can be null. This does not take any * refcounts of ownership of the binder from the client. * * \param parcel the parcel to write to. * \param binder the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __INTRODUCED_IN(29); /** * Reads an AIBinder from the next location in a non-null parcel. One strong ref-count of ownership * is passed to the caller of this function. * * \param parcel the parcel to read from. * \param binder the out parameter for what is read from the parcel. This may be null. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binder) __INTRODUCED_IN(29); /** * Writes a file descriptor to the next location in a non-null parcel. This does not take ownership * of fd. * * This corresponds to the SDK's android.os.ParcelFileDescriptor. * * \param parcel the parcel to write to. * \param fd the value to write to the parcel (-1 to represent a null ParcelFileDescriptor). * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd); /** * Reads an int from the next location in a non-null parcel. * * The returned fd must be closed. * * This corresponds to the SDK's android.os.ParcelFileDescriptor. * * \param parcel the parcel to read from. * \param fd the out parameter for what is read from the parcel (or -1 to represent a null * ParcelFileDescriptor) * * \return STATUS_OK on successful write. */ binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd); /** * Writes an AStatus object to the next location in a non-null parcel. * * If the status is considered to be a low-level status and has no additional information other * than a binder_status_t (for instance, if it is created with AStatus_fromStatus), then that * status will be returned from this method and nothing will be written to the parcel. If either * this happens or if writing the status object itself fails, the return value from this function * should be propagated to the client, and AParcel_readStatusHeader shouldn't be called. * * \param parcel the parcel to write to. * \param status the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) __INTRODUCED_IN(29); /** * Reads an AStatus from the next location in a non-null parcel. Ownership is passed to the caller * of this function. * * \param parcel the parcel to read from. * \param status the out parameter for what is read from the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) __INTRODUCED_IN(29); /** * Writes utf-8 string value to the next location in a non-null parcel. * * If length is -1, and string is nullptr, this will write a 'null' string to the parcel. * * \param parcel the parcel to write to. * \param string the null-terminated string to write to the parcel, at least of size 'length'. * \param length the length of the string to be written. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) __INTRODUCED_IN(29); /** * Reads and allocates utf-8 string value from the next location in a non-null parcel. * * Data is passed to the string allocator once the string size is known. This size includes the * space for the null-terminator of this string. This allocator returns a buffer which is used as * the output buffer from this read. If there is a 'null' string on the binder buffer, the allocator * will be called with length -1. * * \param parcel the parcel to read from. * \param stringData some external representation of a string. * \param allocator allocator that will be called once the size of the string is known. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, AParcel_stringAllocator allocator) __INTRODUCED_IN(29); /** * Writes utf-8 string array data to the next location in a non-null parcel. * * length is the length of the array. AParcel_stringArrayElementGetter will be called for all * indices in range [0, length) with the arrayData provided here. The string length and buffer * returned from this function will be used to fill out the data from the parcel. If length is -1, * this will write a 'null' string array to the binder buffer. * * \param parcel the parcel to write to. * \param arrayData some external representation of an array. * \param length the length of the array to be written. * \param getter the callback that will be called for every index of the array to retrieve the * corresponding string buffer. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, int32_t length, AParcel_stringArrayElementGetter getter) __INTRODUCED_IN(29); /** * Reads and allocates utf-8 string array value from the next location in a non-null parcel. * * First, AParcel_stringArrayAllocator will be called with the size of the array to be read where * length is the length of the array to be read from the parcel. Then, for each index i in [0, * length), AParcel_stringArrayElementAllocator will be called with the length of the string to be * read from the parcel. The resultant buffer from each of these calls will be filled according to * the contents of the string that is read. If the string array being read is 'null', this will * instead just pass -1 to AParcel_stringArrayAllocator. * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called with arrayData once the size of the output * array is known. * \param elementAllocator the callback that will be called on every index of arrayData to allocate * the string at that location. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, AParcel_stringArrayAllocator allocator, AParcel_stringArrayElementAllocator elementAllocator) __INTRODUCED_IN(29); /** * Writes an array of parcelables (user-defined types) to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * \param elementWriter function to be called for every array index to write the user-defined type * at that location. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayData, int32_t length, AParcel_writeParcelableElement elementWriter) __INTRODUCED_IN(29); /** * Reads an array of parcelables (user-defined types) from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, elementReader will be called for every index to read the * corresponding parcelable. * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * \param elementReader the callback that will be called to fill out individual elements. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData, AParcel_parcelableArrayAllocator allocator, AParcel_readParcelableElement elementReader) __INTRODUCED_IN(29); // @START-PRIMITIVE-READ-WRITE /** * Writes int32_t value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) __INTRODUCED_IN(29); /** * Writes uint32_t value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) __INTRODUCED_IN(29); /** * Writes int64_t value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) __INTRODUCED_IN(29); /** * Writes uint64_t value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) __INTRODUCED_IN(29); /** * Writes float value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeFloat(AParcel* parcel, float value) __INTRODUCED_IN(29); /** * Writes double value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeDouble(AParcel* parcel, double value) __INTRODUCED_IN(29); /** * Writes bool value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeBool(AParcel* parcel, bool value) __INTRODUCED_IN(29); /** * Writes char16_t value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) __INTRODUCED_IN(29); /** * Writes int8_t value to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) __INTRODUCED_IN(29); /** * Reads into int32_t value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) __INTRODUCED_IN(29); /** * Reads into uint32_t value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) __INTRODUCED_IN(29); /** * Reads into int64_t value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) __INTRODUCED_IN(29); /** * Reads into uint64_t value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) __INTRODUCED_IN(29); /** * Reads into float value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) __INTRODUCED_IN(29); /** * Reads into double value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) __INTRODUCED_IN(29); /** * Reads into bool value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) __INTRODUCED_IN(29); /** * Reads into char16_t value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) __INTRODUCED_IN(29); /** * Reads into int8_t value from the next location in a non-null parcel. * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODUCED_IN(29); /** * Writes an array of int32_t to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of uint32_t to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of int64_t to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of uint64_t to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of float to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of double to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of bool to the next location in a non-null parcel. * * getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying * values to write to the parcel. * * \param parcel the parcel to write to. * \param arrayData some external representation of an array. * \param length the length of arrayData (or -1 if this represents a null array). * \param getter the callback to retrieve data at specific locations in the array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, int32_t length, AParcel_boolArrayGetter getter) __INTRODUCED_IN(29); /** * Writes an array of char16_t to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Writes an array of int8_t to the next location in a non-null parcel. * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. * * \return STATUS_OK on successful write. */ binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, int32_t length) __INTRODUCED_IN(29); /** * Reads an array of int32_t from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, AParcel_int32ArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of uint32_t from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, AParcel_uint32ArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of int64_t from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, AParcel_int64ArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of uint64_t from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, AParcel_uint64ArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of float from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, AParcel_floatArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of double from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, AParcel_doubleArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of bool from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. Then, for every i in [0, length), * setter(arrayData, i, x) will be called where x is the value at the associated index. * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * \param setter the callback that will be called to set a value at a specific location in the * array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, AParcel_boolArrayAllocator allocator, AParcel_boolArraySetter setter) __INTRODUCED_IN(29); /** * Reads an array of char16_t from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, AParcel_charArrayAllocator allocator) __INTRODUCED_IN(29); /** * Reads an array of int8_t from the next location in a non-null parcel. * * First, allocator will be called with the length of the array. If the allocation succeeds and the * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. * * \return STATUS_OK on successful read. */ binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, AParcel_byteArrayAllocator allocator) __INTRODUCED_IN(29); // @END-PRIMITIVE-READ-WRITE #endif //__ANDROID_API__ >= __ANDROID_API_Q__ __END_DECLS /** @} */ libs/binder/ndk/include_ndk/android/binder_parcel_utils.h0100644 0000000 0000000 00000074430 13756501735 022662 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_parcel_utils.h * @brief A collection of helper wrappers for AParcel. */ #pragma once #include #include #include #include #include namespace ndk { /** * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. */ template static inline bool AParcel_stdVectorAllocator(void* vectorData, int32_t length, T** outBuffer) { if (length < 0) return false; std::vector* vec = static_cast*>(vectorData); if (static_cast(length) > vec->max_size()) return false; vec->resize(length); *outBuffer = vec->data(); return true; } /** * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. */ template static inline bool AParcel_nullableStdVectorAllocator(void* vectorData, int32_t length, T** outBuffer) { std::optional>* vec = static_cast>*>(vectorData); if (length < 0) { *vec = std::nullopt; return true; } *vec = std::optional>(std::vector{}); if (static_cast(length) > (*vec)->max_size()) return false; (*vec)->resize(length); *outBuffer = (*vec)->data(); return true; } /** * This allocates a vector to size 'length' and returns whether the allocation is successful. * * See also AParcel_stdVectorAllocator. Types used with this allocator have their sizes defined * externally with respect to the NDK, and that size information is not passed into the NDK. * Instead, it is used in cases where callbacks are used. Note that when this allocator is used, * null arrays are not supported. * * See AParcel_readVector(const AParcel* parcel, std::vector) * See AParcel_readVector(const AParcel* parcel, std::vector) */ template static inline bool AParcel_stdVectorExternalAllocator(void* vectorData, int32_t length) { if (length < 0) return false; std::vector* vec = static_cast*>(vectorData); if (static_cast(length) > vec->max_size()) return false; vec->resize(length); return true; } /** * This allocates a vector to size 'length' and returns whether the allocation is successful. * * See also AParcel_stdVectorAllocator. Types used with this allocator have their sizes defined * externally with respect to the NDK, and that size information is not passed into the NDK. * Instead, it is used in cases where callbacks are used. Note, when this allocator is used, * the vector itself can be nullable. * * See AParcel_readVector(const AParcel* parcel, * std::optional>>) */ template static inline bool AParcel_nullableStdVectorExternalAllocator(void* vectorData, int32_t length) { std::optional>* vec = static_cast>*>(vectorData); if (length < 0) { *vec = std::nullopt; return true; } *vec = std::optional>(std::vector{}); if (static_cast(length) > (*vec)->max_size()) return false; (*vec)->resize(length); return true; } /** * This retrieves the underlying value in a vector which may not be contiguous at index from a * corresponding vectorData. */ template static inline T AParcel_stdVectorGetter(const void* vectorData, size_t index) { const std::vector* vec = static_cast*>(vectorData); return (*vec)[index]; } /** * This sets the underlying value in a corresponding vectorData which may not be contiguous at * index. */ template static inline void AParcel_stdVectorSetter(void* vectorData, size_t index, T value) { std::vector* vec = static_cast*>(vectorData); (*vec)[index] = value; } /** * This sets the underlying value in a corresponding vectorData which may not be contiguous at * index. */ template static inline void AParcel_nullableStdVectorSetter(void* vectorData, size_t index, T value) { std::optional>* vec = static_cast>*>(vectorData); vec->value()[index] = value; } /** * Convenience method to write a nullable strong binder. */ static inline binder_status_t AParcel_writeNullableStrongBinder(AParcel* parcel, const SpAIBinder& binder) { return AParcel_writeStrongBinder(parcel, binder.get()); } /** * Convenience method to read a nullable strong binder. */ static inline binder_status_t AParcel_readNullableStrongBinder(const AParcel* parcel, SpAIBinder* binder) { AIBinder* readBinder; binder_status_t status = AParcel_readStrongBinder(parcel, &readBinder); if (status == STATUS_OK) { binder->set(readBinder); } return status; } /** * Convenience method to write a strong binder but return an error if it is null. */ static inline binder_status_t AParcel_writeRequiredStrongBinder(AParcel* parcel, const SpAIBinder& binder) { if (binder.get() == nullptr) { return STATUS_UNEXPECTED_NULL; } return AParcel_writeStrongBinder(parcel, binder.get()); } /** * Convenience method to read a strong binder but return an error if it is null. */ static inline binder_status_t AParcel_readRequiredStrongBinder(const AParcel* parcel, SpAIBinder* binder) { AIBinder* readBinder; binder_status_t ret = AParcel_readStrongBinder(parcel, &readBinder); if (ret == STATUS_OK) { if (readBinder == nullptr) { return STATUS_UNEXPECTED_NULL; } binder->set(readBinder); } return ret; } /** * Convenience method to write a ParcelFileDescriptor where -1 represents a null value. */ static inline binder_status_t AParcel_writeNullableParcelFileDescriptor( AParcel* parcel, const ScopedFileDescriptor& fd) { return AParcel_writeParcelFileDescriptor(parcel, fd.get()); } /** * Convenience method to read a ParcelFileDescriptor where -1 represents a null value. */ static inline binder_status_t AParcel_readNullableParcelFileDescriptor(const AParcel* parcel, ScopedFileDescriptor* fd) { int readFd; binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd); if (status == STATUS_OK) { fd->set(readFd); } return status; } /** * Convenience method to write a valid ParcelFileDescriptor. */ static inline binder_status_t AParcel_writeRequiredParcelFileDescriptor( AParcel* parcel, const ScopedFileDescriptor& fd) { if (fd.get() < 0) { return STATUS_UNEXPECTED_NULL; } return AParcel_writeParcelFileDescriptor(parcel, fd.get()); } /** * Convenience method to read a valid ParcelFileDescriptor. */ static inline binder_status_t AParcel_readRequiredParcelFileDescriptor(const AParcel* parcel, ScopedFileDescriptor* fd) { int readFd; binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd); if (status == STATUS_OK) { if (readFd < 0) { return STATUS_UNEXPECTED_NULL; } fd->set(readFd); } return status; } /** * Allocates a std::string to length and returns the underlying buffer. For use with * AParcel_readString. See use below in AParcel_readString(const AParcel*, std::string*). */ static inline bool AParcel_stdStringAllocator(void* stringData, int32_t length, char** buffer) { if (length <= 0) return false; std::string* str = static_cast(stringData); str->resize(length - 1); *buffer = &(*str)[0]; return true; } /** * Allocates a string in a std::optional to size 'length' (or to std::nullopt when * length is -1) and returns the underlying buffer. For use with AParcel_readString. See use below * in AParcel_readString(const AParcel*, std::optional*). */ static inline bool AParcel_nullableStdStringAllocator(void* stringData, int32_t length, char** buffer) { if (length == 0) return false; std::optional* str = static_cast*>(stringData); if (length < 0) { *str = std::nullopt; return true; } *str = std::optional(std::string{}); (*str)->resize(length - 1); *buffer = &(**str)[0]; return true; } /** * Allocates a std::string inside of a std::vector at index 'index' to size 'length'. */ static inline bool AParcel_stdVectorStringElementAllocator(void* vectorData, size_t index, int32_t length, char** buffer) { std::vector* vec = static_cast*>(vectorData); std::string& element = vec->at(index); return AParcel_stdStringAllocator(static_cast(&element), length, buffer); } /** * This gets the length and buffer of a std::string inside of a std::vector at index * index. */ static inline const char* AParcel_stdVectorStringElementGetter(const void* vectorData, size_t index, int32_t* outLength) { const std::vector* vec = static_cast*>(vectorData); const std::string& element = vec->at(index); *outLength = element.size(); return element.c_str(); } /** * Allocates a string in a std::optional inside of a * std::optional>> at index 'index' to size 'length' (or to * std::nullopt when length is -1). */ static inline bool AParcel_nullableStdVectorStringElementAllocator(void* vectorData, size_t index, int32_t length, char** buffer) { std::optional>>* vec = static_cast>>*>(vectorData); std::optional& element = vec->value().at(index); return AParcel_nullableStdStringAllocator(static_cast(&element), length, buffer); } /** * This gets the length and buffer of a std::optional inside of a * std::vector at index index. If the string is null, then it returns null and a length * of -1. */ static inline const char* AParcel_nullableStdVectorStringElementGetter(const void* vectorData, size_t index, int32_t* outLength) { const std::optional>>* vec = static_cast>>*>(vectorData); const std::optional& element = vec->value().at(index); if (!element) { *outLength = -1; return nullptr; } *outLength = element->size(); return element->c_str(); } /** * Convenience API for writing a std::string. */ static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) { return AParcel_writeString(parcel, str.c_str(), str.size()); } /** * Convenience API for reading a std::string. */ static inline binder_status_t AParcel_readString(const AParcel* parcel, std::string* str) { void* stringData = static_cast(str); return AParcel_readString(parcel, stringData, AParcel_stdStringAllocator); } /** * Convenience API for writing a std::optional. */ static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::optional& str) { if (!str) { return AParcel_writeString(parcel, nullptr, -1); } return AParcel_writeString(parcel, str->c_str(), str->size()); } /** * Convenience API for reading a std::optional. */ static inline binder_status_t AParcel_readString(const AParcel* parcel, std::optional* str) { void* stringData = static_cast(str); return AParcel_readString(parcel, stringData, AParcel_nullableStdStringAllocator); } /** * Convenience API for writing a std::vector */ static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { const void* vectorData = static_cast(&vec); return AParcel_writeStringArray(parcel, vectorData, vec.size(), AParcel_stdVectorStringElementGetter); } /** * Convenience API for reading a std::vector */ static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readStringArray(parcel, vectorData, AParcel_stdVectorExternalAllocator, AParcel_stdVectorStringElementAllocator); } /** * Convenience API for writing a std::optional>> */ static inline binder_status_t AParcel_writeVector( AParcel* parcel, const std::optional>>& vec) { const void* vectorData = static_cast(&vec); return AParcel_writeStringArray(parcel, vectorData, (vec ? vec->size() : -1), AParcel_nullableStdVectorStringElementGetter); } /** * Convenience API for reading a std::optional>> */ static inline binder_status_t AParcel_readVector( const AParcel* parcel, std::optional>>* vec) { void* vectorData = static_cast(vec); return AParcel_readStringArray( parcel, vectorData, AParcel_nullableStdVectorExternalAllocator>, AParcel_nullableStdVectorStringElementAllocator); } /** * Writes a parcelable object of type P inside a std::vector

at index 'index' to 'parcel'. */ template binder_status_t AParcel_writeStdVectorParcelableElement(AParcel* parcel, const void* vectorData, size_t index) { const std::vector

* vector = static_cast*>(vectorData); return vector->at(index).writeToParcel(parcel); } /** * Reads a parcelable object of type P inside a std::vector

at index 'index' from 'parcel'. */ template binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, void* vectorData, size_t index) { std::vector

* vector = static_cast*>(vectorData); return vector->at(index).readFromParcel(parcel); } /** * Convenience API for writing a std::vector

*/ template static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector

& vec) { const void* vectorData = static_cast(&vec); return AParcel_writeParcelableArray(parcel, vectorData, vec.size(), AParcel_writeStdVectorParcelableElement

); } /** * Convenience API for reading a std::vector

*/ template static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector

* vec) { void* vectorData = static_cast(vec); return AParcel_readParcelableArray(parcel, vectorData, AParcel_stdVectorExternalAllocator

, AParcel_readStdVectorParcelableElement

); } // @START /** * Writes a vector of int32_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeInt32Array(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of int32_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeInt32Array(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of int32_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readInt32Array(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of int32_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readInt32Array(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of uint32_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeUint32Array(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of uint32_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeUint32Array(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of uint32_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readUint32Array(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of uint32_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readUint32Array(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of int64_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeInt64Array(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of int64_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeInt64Array(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of int64_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readInt64Array(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of int64_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readInt64Array(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of uint64_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeUint64Array(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of uint64_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeUint64Array(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of uint64_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readUint64Array(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of uint64_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readUint64Array(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of float to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeFloatArray(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of float to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeFloatArray(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of float from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readFloatArray(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of float from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readFloatArray(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of double to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeDoubleArray(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of double to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeDoubleArray(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of double from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readDoubleArray(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of double from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readDoubleArray(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of bool to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeBoolArray(parcel, static_cast(&vec), vec.size(), AParcel_stdVectorGetter); } /** * Writes an optional vector of bool to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeBoolArray(parcel, nullptr, -1, AParcel_stdVectorGetter); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of bool from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readBoolArray(parcel, vectorData, AParcel_stdVectorExternalAllocator, AParcel_stdVectorSetter); } /** * Reads an optional vector of bool from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readBoolArray(parcel, vectorData, AParcel_nullableStdVectorExternalAllocator, AParcel_nullableStdVectorSetter); } /** * Writes a vector of char16_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeCharArray(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of char16_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeCharArray(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of char16_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readCharArray(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of char16_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readCharArray(parcel, vectorData, AParcel_nullableStdVectorAllocator); } /** * Writes a vector of int8_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector& vec) { return AParcel_writeByteArray(parcel, vec.data(), vec.size()); } /** * Writes an optional vector of int8_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) { if (!vec) return AParcel_writeByteArray(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** * Reads a vector of int8_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector* vec) { void* vectorData = static_cast(vec); return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator); } /** * Reads an optional vector of int8_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) { void* vectorData = static_cast(vec); return AParcel_readByteArray(parcel, vectorData, AParcel_nullableStdVectorAllocator); } // @END /** * Convenience API for writing the size of a vector. */ template static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, const std::vector& vec) { if (vec.size() > INT32_MAX) { return STATUS_BAD_VALUE; } return AParcel_writeInt32(parcel, static_cast(vec.size())); } /** * Convenience API for writing the size of a vector. */ template static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, const std::optional>& vec) { if (!vec) { return AParcel_writeInt32(parcel, -1); } if (vec->size() > INT32_MAX) { return STATUS_BAD_VALUE; } return AParcel_writeInt32(parcel, static_cast(vec->size())); } /** * Convenience API for resizing a vector. */ template static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, std::vector* vec) { int32_t size; binder_status_t err = AParcel_readInt32(parcel, &size); if (err != STATUS_OK) return err; if (size < 0) return STATUS_UNEXPECTED_NULL; vec->resize(static_cast(size)); return STATUS_OK; } /** * Convenience API for resizing a vector. */ template static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, std::optional>* vec) { int32_t size; binder_status_t err = AParcel_readInt32(parcel, &size); if (err != STATUS_OK) return err; if (size < -1) return STATUS_UNEXPECTED_NULL; if (size == -1) { *vec = std::nullopt; return STATUS_OK; } *vec = std::optional>(std::vector{}); (*vec)->resize(static_cast(size)); return STATUS_OK; } } // namespace ndk /** @} */ libs/binder/ndk/include_ndk/android/binder_status.h0100644 0000000 0000000 00000017437 13756501735 021523 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /** * @addtogroup NdkBinder * @{ */ /** * @file binder_status.h */ #pragma once #include #include #include __BEGIN_DECLS #if __ANDROID_API__ >= __ANDROID_API_Q__ enum { STATUS_OK = 0, STATUS_UNKNOWN_ERROR = (-2147483647 - 1), // INT32_MIN value STATUS_NO_MEMORY = -ENOMEM, STATUS_INVALID_OPERATION = -ENOSYS, STATUS_BAD_VALUE = -EINVAL, STATUS_BAD_TYPE = (STATUS_UNKNOWN_ERROR + 1), STATUS_NAME_NOT_FOUND = -ENOENT, STATUS_PERMISSION_DENIED = -EPERM, STATUS_NO_INIT = -ENODEV, STATUS_ALREADY_EXISTS = -EEXIST, STATUS_DEAD_OBJECT = -EPIPE, STATUS_FAILED_TRANSACTION = (STATUS_UNKNOWN_ERROR + 2), STATUS_BAD_INDEX = -EOVERFLOW, STATUS_NOT_ENOUGH_DATA = -ENODATA, STATUS_WOULD_BLOCK = -EWOULDBLOCK, STATUS_TIMED_OUT = -ETIMEDOUT, STATUS_UNKNOWN_TRANSACTION = -EBADMSG, STATUS_FDS_NOT_ALLOWED = (STATUS_UNKNOWN_ERROR + 7), STATUS_UNEXPECTED_NULL = (STATUS_UNKNOWN_ERROR + 8), }; /** * One of the STATUS_* values. * * All unrecognized values are coerced into STATUS_UNKNOWN_ERROR. */ typedef int32_t binder_status_t; enum { EX_NONE = 0, EX_SECURITY = -1, EX_BAD_PARCELABLE = -2, EX_ILLEGAL_ARGUMENT = -3, EX_NULL_POINTER = -4, EX_ILLEGAL_STATE = -5, EX_NETWORK_MAIN_THREAD = -6, EX_UNSUPPORTED_OPERATION = -7, EX_SERVICE_SPECIFIC = -8, EX_PARCELABLE = -9, /** * This is special, and indicates to native binder proxies that the * transaction has failed at a low level. */ EX_TRANSACTION_FAILED = -129, }; /** * One of the EXCEPTION_* types. * * All unrecognized values are coerced into EXCEPTION_TRANSACTION_FAILED. * * These exceptions values are used by the SDK for parcelables. Also see Parcel.java. */ typedef int32_t binder_exception_t; /** * This is a helper class that encapsulates a standard way to keep track of and chain binder errors * along with service specific errors. * * It is not required to be used in order to parcel/receive transactions, but it is required in * order to be compatible with standard AIDL transactions since it is written as the header to the * out parcel for transactions which get executed (don't fail during unparceling of input arguments * or sooner). */ struct AStatus; typedef struct AStatus AStatus; /** * New status which is considered a success. * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29); /** * New status with exception code. * * \param exception the code that this status should represent. If this is EX_NONE, then this * constructs an non-error status object. * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCode(binder_exception_t exception) __INTRODUCED_IN(29); /** * New status with exception code and message. * * \param exception the code that this status should represent. If this is EX_NONE, then this * constructs an non-error status object. * \param message the error message to associate with this status object. * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCodeWithMessage( binder_exception_t exception, const char* message) __INTRODUCED_IN(29); /** * New status with a service speciic error. * * This is considered to be EX_TRANSACTION_FAILED with extra information. * * \param serviceSpecific an implementation defined error code. * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificError( int32_t serviceSpecific) __INTRODUCED_IN(29); /** * New status with a service specific error and message. * * This is considered to be EX_TRANSACTION_FAILED with extra information. * * \param serviceSpecific an implementation defined error code. * \param message the error message to associate with this status object. * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificErrorWithMessage( int32_t serviceSpecific, const char* message) __INTRODUCED_IN(29); /** * New status with binder_status_t. This is typically for low level failures when a binder_status_t * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning * an AStatus instance. * * \param a low-level error to associate with this status object. * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_fromStatus(binder_status_t status) __INTRODUCED_IN(29); /** * Whether this object represents a successful transaction. If this function returns true, then * AStatus_getExceptionCode will return EX_NONE. * * \param status the status being queried. * * \return whether the status represents a successful transaction. For more details, see below. */ bool AStatus_isOk(const AStatus* status) __INTRODUCED_IN(29); /** * The exception that this status object represents. * * \param status the status being queried. * * \return the exception code that this object represents. */ binder_exception_t AStatus_getExceptionCode(const AStatus* status) __INTRODUCED_IN(29); /** * The service specific error if this object represents one. This function will only ever return a * non-zero result if AStatus_getExceptionCode returns EX_SERVICE_SPECIFIC. If this function returns * 0, the status object may still represent a different exception or status. To find out if this * transaction as a whole is okay, use AStatus_isOk instead. * * \param status the status being queried. * * \return the service-specific error code if the exception code is EX_SERVICE_SPECIFIC or 0. */ int32_t AStatus_getServiceSpecificError(const AStatus* status) __INTRODUCED_IN(29); /** * The status if this object represents one. This function will only ever return a non-zero result * if AStatus_getExceptionCode returns EX_TRANSACTION_FAILED. If this function return 0, the status * object may represent a different exception or a service specific error. To find out if this * transaction as a whole is okay, use AStatus_isOk instead. * * \param status the status being queried. * * \return the status code if the exception code is EX_TRANSACTION_FAILED or 0. */ binder_status_t AStatus_getStatus(const AStatus* status) __INTRODUCED_IN(29); /** * If there is a message associated with this status, this will return that message. If there is no * message, this will return an empty string. * * The returned string has the lifetime of the status object passed into this function. * * \param status the status being queried. * * \return the message associated with this error. */ const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29); /** * Deletes memory associated with the status instance. * * \param status the status to delete, returned from AStatus_newOk or one of the AStatus_from* APIs. */ void AStatus_delete(AStatus* status) __INTRODUCED_IN(29); #endif //__ANDROID_API__ >= __ANDROID_API_Q__ __END_DECLS /** @} */ libs/binder/ndk/libbinder_ndk.map.txt0100644 0000000 0000000 00000005271 13756501735 016701 0ustar000000000 0000000 LIBBINDER_NDK { # introduced=29 global: AIBinder_associateClass; AIBinder_Class_define; AIBinder_Class_setOnDump; AIBinder_DeathRecipient_delete; AIBinder_DeathRecipient_new; AIBinder_debugGetRefCount; AIBinder_decStrong; AIBinder_dump; AIBinder_fromJavaBinder; AIBinder_getCallingPid; AIBinder_getCallingUid; AIBinder_getClass; AIBinder_getUserData; AIBinder_incStrong; AIBinder_isAlive; AIBinder_isRemote; AIBinder_linkToDeath; AIBinder_new; AIBinder_ping; AIBinder_prepareTransaction; AIBinder_toJavaBinder; AIBinder_transact; AIBinder_unlinkToDeath; AIBinder_Weak_delete; AIBinder_Weak_new; AIBinder_Weak_promote; AParcel_delete; AParcel_getDataPosition; AParcel_readBool; AParcel_readBoolArray; AParcel_readByte; AParcel_readByteArray; AParcel_readChar; AParcel_readCharArray; AParcel_readDouble; AParcel_readDoubleArray; AParcel_readFloat; AParcel_readFloatArray; AParcel_readInt32; AParcel_readInt32Array; AParcel_readInt64; AParcel_readInt64Array; AParcel_readParcelableArray; AParcel_readParcelFileDescriptor; AParcel_readStatusHeader; AParcel_readString; AParcel_readStringArray; AParcel_readStrongBinder; AParcel_readUint32; AParcel_readUint32Array; AParcel_readUint64; AParcel_readUint64Array; AParcel_setDataPosition; AParcel_writeBool; AParcel_writeBoolArray; AParcel_writeByte; AParcel_writeByteArray; AParcel_writeChar; AParcel_writeCharArray; AParcel_writeDouble; AParcel_writeDoubleArray; AParcel_writeFloat; AParcel_writeFloatArray; AParcel_writeInt32; AParcel_writeInt32Array; AParcel_writeInt64; AParcel_writeInt64Array; AParcel_writeParcelableArray; AParcel_writeParcelFileDescriptor; AParcel_writeStatusHeader; AParcel_writeString; AParcel_writeStringArray; AParcel_writeStrongBinder; AParcel_writeUint32; AParcel_writeUint32Array; AParcel_writeUint64; AParcel_writeUint64Array; AStatus_delete; AStatus_fromExceptionCode; AStatus_fromExceptionCodeWithMessage; AStatus_fromServiceSpecificError; AStatus_fromServiceSpecificErrorWithMessage; AStatus_fromStatus; AStatus_getExceptionCode; AStatus_getMessage; AStatus_getServiceSpecificError; AStatus_getStatus; AStatus_isOk; AStatus_newOk; ABinderProcess_joinThreadPool; # apex ABinderProcess_setThreadPoolMaxThreadCount; # apex ABinderProcess_startThreadPool; # apex AServiceManager_addService; # apex AServiceManager_checkService; # apex AServiceManager_getService; # apex local: *; }; libs/binder/ndk/parcel.cpp0100644 0000000 0000000 00000053504 13756501735 014552 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include "parcel_internal.h" #include "ibinder_internal.h" #include "status_internal.h" #include #include #include #include #include #include using ::android::IBinder; using ::android::Parcel; using ::android::sp; using ::android::status_t; using ::android::base::unique_fd; using ::android::os::ParcelFileDescriptor; template using ContiguousArrayAllocator = bool (*)(void* arrayData, int32_t length, T** outBuffer); template using ArrayAllocator = bool (*)(void* arrayData, int32_t length); template using ArrayGetter = T (*)(const void* arrayData, size_t index); template using ArraySetter = void (*)(void* arrayData, size_t index, T value); binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int32_t length) { // only -1 can be used to represent a null array if (length < -1) return STATUS_BAD_VALUE; if (!isNullArray && length < 0) { LOG(ERROR) << __func__ << ": null array must be used with length == -1."; return STATUS_BAD_VALUE; } if (isNullArray && length > 0) { LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array."; return STATUS_BAD_VALUE; } Parcel* rawParcel = parcel->get(); status_t status = rawParcel->writeInt32(static_cast(length)); if (status != STATUS_OK) return PruneStatusT(status); return STATUS_OK; } template binder_status_t WriteArray(AParcel* parcel, const T* array, int32_t length) { binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length); if (status != STATUS_OK) return status; if (length <= 0) return STATUS_OK; int32_t size = 0; if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY; void* const data = parcel->get()->writeInplace(size); if (data == nullptr) return STATUS_NO_MEMORY; memcpy(data, array, size); return STATUS_OK; } // Each element in a char16_t array is converted to an int32_t (not packed). template <> binder_status_t WriteArray(AParcel* parcel, const char16_t* array, int32_t length) { binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length); if (status != STATUS_OK) return status; if (length <= 0) return STATUS_OK; int32_t size = 0; if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY; Parcel* rawParcel = parcel->get(); for (int32_t i = 0; i < length; i++) { status = rawParcel->writeChar(array[i]); if (status != STATUS_OK) return PruneStatusT(status); } return STATUS_OK; } template binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ContiguousArrayAllocator allocator) { const Parcel* rawParcel = parcel->get(); int32_t length; status_t status = rawParcel->readInt32(&length); if (status != STATUS_OK) return PruneStatusT(status); if (length < -1) return STATUS_BAD_VALUE; T* array; if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; if (length <= 0) return STATUS_OK; if (array == nullptr) return STATUS_NO_MEMORY; int32_t size = 0; if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY; const void* data = rawParcel->readInplace(size); if (data == nullptr) return STATUS_NO_MEMORY; memcpy(array, data, size); return STATUS_OK; } // Each element in a char16_t array is converted to an int32_t (not packed) template <> binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ContiguousArrayAllocator allocator) { const Parcel* rawParcel = parcel->get(); int32_t length; status_t status = rawParcel->readInt32(&length); if (status != STATUS_OK) return PruneStatusT(status); if (length < -1) return STATUS_BAD_VALUE; char16_t* array; if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; if (length <= 0) return STATUS_OK; if (array == nullptr) return STATUS_NO_MEMORY; int32_t size = 0; if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY; for (int32_t i = 0; i < length; i++) { status = rawParcel->readChar(array + i); if (status != STATUS_OK) return PruneStatusT(status); } return STATUS_OK; } template binder_status_t WriteArray(AParcel* parcel, const void* arrayData, int32_t length, ArrayGetter getter, status_t (Parcel::*write)(T)) { // we have no clue if arrayData represents a null object or not, we can only infer from length bool arrayIsNull = length < 0; binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); if (status != STATUS_OK) return status; if (length <= 0) return STATUS_OK; Parcel* rawParcel = parcel->get(); for (int32_t i = 0; i < length; i++) { status = (rawParcel->*write)(getter(arrayData, i)); if (status != STATUS_OK) return PruneStatusT(status); } return STATUS_OK; } template binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ArrayAllocator allocator, ArraySetter setter, status_t (Parcel::*read)(T*) const) { const Parcel* rawParcel = parcel->get(); int32_t length; status_t status = rawParcel->readInt32(&length); if (status != STATUS_OK) return PruneStatusT(status); if (length < -1) return STATUS_BAD_VALUE; if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; if (length <= 0) return STATUS_OK; for (int32_t i = 0; i < length; i++) { T readTarget; status = (rawParcel->*read)(&readTarget); if (status != STATUS_OK) return PruneStatusT(status); setter(arrayData, i, readTarget); } return STATUS_OK; } void AParcel_delete(AParcel* parcel) { delete parcel; } binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) { if (position < 0) { return STATUS_BAD_VALUE; } parcel->get()->setDataPosition(position); return STATUS_OK; } int32_t AParcel_getDataPosition(const AParcel* parcel) { return parcel->get()->dataPosition(); } binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) { sp writeBinder = binder != nullptr ? binder->getBinder() : nullptr; return parcel->get()->writeStrongBinder(writeBinder); } binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binder) { sp readBinder = nullptr; status_t status = parcel->get()->readNullableStrongBinder(&readBinder); if (status != STATUS_OK) { return PruneStatusT(status); } sp ret = ABpBinder::lookupOrCreateFromBinder(readBinder); AIBinder_incStrong(ret.get()); *binder = ret.get(); return PruneStatusT(status); } binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) { std::unique_ptr parcelFd; if (fd < 0) { if (fd != -1) { return STATUS_UNKNOWN_ERROR; } // parcelFd = nullptr } else { // fd >= 0 parcelFd = std::make_unique(unique_fd(fd)); } status_t status = parcel->get()->writeNullableParcelable(parcelFd); // ownership is retained by caller if (parcelFd != nullptr) { (void)parcelFd->release().release(); } return PruneStatusT(status); } binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) { std::unique_ptr parcelFd; status_t status = parcel->get()->readParcelable(&parcelFd); if (status != STATUS_OK) return PruneStatusT(status); if (parcelFd) { *fd = parcelFd->release().release(); } else { *fd = -1; } return STATUS_OK; } binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) { return PruneStatusT(status->get()->writeToParcel(parcel->get())); } binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) { ::android::binder::Status bstatus; binder_status_t ret = PruneStatusT(bstatus.readFromParcel(*parcel->get())); if (ret == STATUS_OK) { *status = new AStatus(std::move(bstatus)); } return PruneStatusT(ret); } binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) { if (string == nullptr) { if (length != -1) { LOG(WARNING) << __func__ << ": null string must be used with length == -1."; return STATUS_BAD_VALUE; } status_t err = parcel->get()->writeInt32(-1); return PruneStatusT(err); } if (length < 0) { LOG(WARNING) << __func__ << ": Negative string length: " << length; return STATUS_BAD_VALUE; } const uint8_t* str8 = (uint8_t*)string; const ssize_t len16 = utf8_to_utf16_length(str8, length); if (len16 < 0 || len16 >= std::numeric_limits::max()) { LOG(WARNING) << __func__ << ": Invalid string length: " << len16; return STATUS_BAD_VALUE; } status_t err = parcel->get()->writeInt32(len16); if (err) { return PruneStatusT(err); } void* str16 = parcel->get()->writeInplace((len16 + 1) * sizeof(char16_t)); if (str16 == nullptr) { return STATUS_NO_MEMORY; } utf8_to_utf16(str8, length, (char16_t*)str16, (size_t)len16 + 1); return STATUS_OK; } binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, AParcel_stringAllocator allocator) { size_t len16; const char16_t* str16 = parcel->get()->readString16Inplace(&len16); if (str16 == nullptr) { if (allocator(stringData, -1, nullptr)) { return STATUS_OK; } return STATUS_UNEXPECTED_NULL; } ssize_t len8; if (len16 == 0) { len8 = 1; } else { len8 = utf16_to_utf8_length(str16, len16) + 1; } if (len8 <= 0 || len8 > std::numeric_limits::max()) { LOG(WARNING) << __func__ << ": Invalid string length: " << len8; return STATUS_BAD_VALUE; } char* str8; bool success = allocator(stringData, len8, &str8); if (!success || str8 == nullptr) { LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate."; return STATUS_NO_MEMORY; } utf16_to_utf8(str16, len16, str8, len8); return STATUS_OK; } binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, int32_t length, AParcel_stringArrayElementGetter getter) { // we have no clue if arrayData represents a null object or not, we can only infer from length bool arrayIsNull = length < 0; binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); if (status != STATUS_OK) return status; if (length <= 0) return STATUS_OK; for (int32_t i = 0; i < length; i++) { int32_t elementLength = 0; const char* str = getter(arrayData, i, &elementLength); if (str == nullptr && elementLength != -1) return STATUS_BAD_VALUE; binder_status_t status = AParcel_writeString(parcel, str, elementLength); if (status != STATUS_OK) return status; } return STATUS_OK; } // This implements AParcel_stringAllocator for a string using an array, index, and element // allocator. struct StringArrayElementAllocationAdapter { void* arrayData; // stringData from the NDK int32_t index; // index into the string array AParcel_stringArrayElementAllocator elementAllocator; static bool Allocator(void* stringData, int32_t length, char** buffer) { StringArrayElementAllocationAdapter* adapter = static_cast(stringData); return adapter->elementAllocator(adapter->arrayData, adapter->index, length, buffer); } }; binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, AParcel_stringArrayAllocator allocator, AParcel_stringArrayElementAllocator elementAllocator) { const Parcel* rawParcel = parcel->get(); int32_t length; status_t status = rawParcel->readInt32(&length); if (status != STATUS_OK) return PruneStatusT(status); if (length < -1) return STATUS_BAD_VALUE; if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; if (length == -1) return STATUS_OK; // null string array StringArrayElementAllocationAdapter adapter{ .arrayData = arrayData, .index = 0, .elementAllocator = elementAllocator, }; for (; adapter.index < length; adapter.index++) { binder_status_t status = AParcel_readString(parcel, static_cast(&adapter), StringArrayElementAllocationAdapter::Allocator); if (status != STATUS_OK) return status; } return STATUS_OK; } binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayData, int32_t length, AParcel_writeParcelableElement elementWriter) { // we have no clue if arrayData represents a null object or not, we can only infer from length bool arrayIsNull = length < 0; binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); if (status != STATUS_OK) return status; if (length <= 0) return STATUS_OK; for (int32_t i = 0; i < length; i++) { binder_status_t status = elementWriter(parcel, arrayData, i); if (status != STATUS_OK) return status; } return STATUS_OK; } binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData, AParcel_parcelableArrayAllocator allocator, AParcel_readParcelableElement elementReader) { const Parcel* rawParcel = parcel->get(); int32_t length; status_t status = rawParcel->readInt32(&length); if (status != STATUS_OK) return PruneStatusT(status); if (length < -1) return STATUS_BAD_VALUE; if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; if (length == -1) return STATUS_OK; // null array for (int32_t i = 0; i < length; i++) { binder_status_t status = elementReader(parcel, arrayData, i); if (status != STATUS_OK) return status; } return STATUS_OK; } // See gen_parcel_helper.py. These auto-generated read/write methods use the same types for // libbinder and this library. // @START binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) { status_t status = parcel->get()->writeInt32(value); return PruneStatusT(status); } binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) { status_t status = parcel->get()->writeUint32(value); return PruneStatusT(status); } binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) { status_t status = parcel->get()->writeInt64(value); return PruneStatusT(status); } binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) { status_t status = parcel->get()->writeUint64(value); return PruneStatusT(status); } binder_status_t AParcel_writeFloat(AParcel* parcel, float value) { status_t status = parcel->get()->writeFloat(value); return PruneStatusT(status); } binder_status_t AParcel_writeDouble(AParcel* parcel, double value) { status_t status = parcel->get()->writeDouble(value); return PruneStatusT(status); } binder_status_t AParcel_writeBool(AParcel* parcel, bool value) { status_t status = parcel->get()->writeBool(value); return PruneStatusT(status); } binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) { status_t status = parcel->get()->writeChar(value); return PruneStatusT(status); } binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) { status_t status = parcel->get()->writeByte(value); return PruneStatusT(status); } binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) { status_t status = parcel->get()->readInt32(value); return PruneStatusT(status); } binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) { status_t status = parcel->get()->readUint32(value); return PruneStatusT(status); } binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) { status_t status = parcel->get()->readInt64(value); return PruneStatusT(status); } binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) { status_t status = parcel->get()->readUint64(value); return PruneStatusT(status); } binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) { status_t status = parcel->get()->readFloat(value); return PruneStatusT(status); } binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) { status_t status = parcel->get()->readDouble(value); return PruneStatusT(status); } binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) { status_t status = parcel->get()->readBool(value); return PruneStatusT(status); } binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) { status_t status = parcel->get()->readChar(value); return PruneStatusT(status); } binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) { status_t status = parcel->get()->readByte(value); return PruneStatusT(status); } binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, int32_t length, AParcel_boolArrayGetter getter) { return WriteArray(parcel, arrayData, length, getter, &Parcel::writeBool); } binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, int32_t length) { return WriteArray(parcel, arrayData, length); } binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, AParcel_int32ArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, AParcel_uint32ArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, AParcel_int64ArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, AParcel_uint64ArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, AParcel_floatArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, AParcel_doubleArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, AParcel_boolArrayAllocator allocator, AParcel_boolArraySetter setter) { return ReadArray(parcel, arrayData, allocator, setter, &Parcel::readBool); } binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, AParcel_charArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, AParcel_byteArrayAllocator allocator) { return ReadArray(parcel, arrayData, allocator); } // @END libs/binder/ndk/parcel_internal.h0100644 0000000 0000000 00000003250 13756501735 016104 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include #include #include "ibinder_internal.h" struct AParcel { const ::android::Parcel* get() const { return mParcel; } ::android::Parcel* get() { return mParcel; } explicit AParcel(const AIBinder* binder) : AParcel(binder, new ::android::Parcel, true /*owns*/) {} AParcel(const AIBinder* binder, ::android::Parcel* parcel, bool owns) : mBinder(binder), mParcel(parcel), mOwns(owns) {} ~AParcel() { if (mOwns) { delete mParcel; } } static const AParcel readOnly(const AIBinder* binder, const ::android::Parcel* parcel) { return AParcel(binder, const_cast<::android::Parcel*>(parcel), false); } const AIBinder* getBinder() { return mBinder; } private: // This object is associated with a calls to a specific AIBinder object. This is used for sanity // checking to make sure that a parcel is one that is expected. const AIBinder* mBinder; ::android::Parcel* mParcel; bool mOwns; }; libs/binder/ndk/process.cpp0100644 0000000 0000000 00000002241 13756501735 014752 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include #include using ::android::IPCThreadState; using ::android::ProcessState; void ABinderProcess_startThreadPool() { ProcessState::self()->startThreadPool(); ProcessState::self()->giveThreadPoolName(); } bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) { return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0; } void ABinderProcess_joinThreadPool() { IPCThreadState::self()->joinThreadPool(); } libs/binder/ndk/runtests.sh0100755 0000000 0000000 00000002664 13756501735 015027 0ustar000000000 0000000 #!/usr/bin/env bash # Copyright (C) 2018 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. if [ -z $ANDROID_BUILD_TOP ]; then echo "You need to source and lunch before you can use this script" exit 1 fi set -ex function run_libbinder_ndk_test() { adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server & # avoid getService 1s delay for most runs, non-critical sleep 0.1 adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \ adb shell killall libbinder_ndk_test_server } [ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \ MODULES-IN-frameworks-native-libs-binder-ndk adb root adb wait-for-device adb sync data # very simple unit tests, tests things outside of the NDK as well run_libbinder_ndk_test # CTS tests (much more comprehensive, new tests should ideally go here) atest android.binder.cts libs/binder/ndk/scripts/0040755 0000000 0000000 00000000000 13756501735 014263 5ustar000000000 0000000 libs/binder/ndk/scripts/format.sh0100755 0000000 0000000 00000001533 13756501735 016111 0ustar000000000 0000000 #!/usr/bin/env bash # Copyright (C) 2018 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 echo "Formatting code" bpfmt -w $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -name "Android.bp") clang-format -i $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -\( -name "*.cpp" -o -name "*.h" -\)) libs/binder/ndk/scripts/gen_parcel_helper.py0100755 0000000 0000000 00000035115 13756501735 020300 0ustar000000000 0000000 #!/usr/bin/env python3 # Copyright (C) 2018 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. import os import sys # list (pretty, cpp) data_types = [ ("Int32", "int32_t"), ("Uint32", "uint32_t"), ("Int64", "int64_t"), ("Uint64", "uint64_t"), ("Float", "float"), ("Double", "double"), ("Bool", "bool"), ("Char", "char16_t"), ("Byte", "int8_t"), ] non_contiguously_addressable = {"Bool"} def replaceFileTags(path, content, start_tag, end_tag): print("Updating", path) with open(path, "r+") as f: lines = f.readlines() start = lines.index("// @" + start_tag + "\n") end = lines.index("// @" + end_tag + "\n") if end <= start or start < 0 or end < 0: print("Failed to find tags in", path) exit(1) f.seek(0) f.write("".join(lines[:start+1]) + content + "".join(lines[end:])) f.truncate() def main(): if len(sys.argv) != 1: print("No arguments.") exit(1) ABT = os.environ.get('ANDROID_BUILD_TOP', None) if ABT is None: print("Can't get ANDROID_BUILD_TOP. Lunch?") exit(1) ROOT = ABT + "/frameworks/native/libs/binder/ndk/" print("Updating auto-generated code") pre_header = "" header = "" source = "" cpp_helper = "" for pretty, cpp in data_types: header += "/**\n" header += " * Writes " + cpp + " value to the next location in a non-null parcel.\n" header += " *\n" header += " * \\param parcel the parcel to write to.\n" header += " * \\param value the value to write to the parcel.\n" header += " *\n" header += " * \\return STATUS_OK on successful write.\n" header += " */\n" header += "binder_status_t AParcel_write" + pretty + "(AParcel* parcel, " + cpp + " value) __INTRODUCED_IN(29);\n\n" source += "binder_status_t AParcel_write" + pretty + "(AParcel* parcel, " + cpp + " value) {\n" source += " status_t status = parcel->get()->write" + pretty + "(value);\n" source += " return PruneStatusT(status);\n" source += "}\n\n" for pretty, cpp in data_types: header += "/**\n" header += " * Reads into " + cpp + " value from the next location in a non-null parcel.\n" header += " *\n" header += " * \\param parcel the parcel to read from.\n" header += " * \\param value the value to read from the parcel.\n" header += " *\n" header += " * \\return STATUS_OK on successful read.\n" header += " */\n" header += "binder_status_t AParcel_read" + pretty + "(const AParcel* parcel, " + cpp + "* value) __INTRODUCED_IN(29);\n\n" source += "binder_status_t AParcel_read" + pretty + "(const AParcel* parcel, " + cpp + "* value) {\n" source += " status_t status = parcel->get()->read" + pretty + "(value);\n" source += " return PruneStatusT(status);\n" source += "}\n\n" for pretty, cpp in data_types: nca = pretty in non_contiguously_addressable arg_types = "const " + cpp + "* arrayData, int32_t length" if nca: arg_types = "const void* arrayData, int32_t length, AParcel_" + pretty.lower() + "ArrayGetter getter" args = "arrayData, length" if nca: args = "arrayData, length, getter, &Parcel::write" + pretty header += "/**\n" header += " * Writes an array of " + cpp + " to the next location in a non-null parcel.\n" if nca: header += " *\n" header += " * getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying values to write " header += "to the parcel.\n" header += " *\n" header += " * \\param parcel the parcel to write to.\n" if nca: header += " * \\param arrayData some external representation of an array.\n" header += " * \\param length the length of arrayData (or -1 if this represents a null array).\n" header += " * \\param getter the callback to retrieve data at specific locations in the array.\n" else: header += " * \\param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).\n" header += " * \\param length the length of arrayData or -1 if this represents a null array.\n" header += " *\n" header += " * \\return STATUS_OK on successful write.\n" header += " */\n" header += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_types + ") __INTRODUCED_IN(29);\n\n" source += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_types + ") {\n" source += " return WriteArray<" + cpp + ">(parcel, " + args + ");\n"; source += "}\n\n" for pretty, cpp in data_types: nca = pretty in non_contiguously_addressable read_func = "AParcel_read" + pretty + "Array" write_func = "AParcel_write" + pretty + "Array" allocator_type = "AParcel_" + pretty.lower() + "ArrayAllocator" getter_type = "AParcel_" + pretty.lower() + "ArrayGetter" setter_type = "AParcel_" + pretty.lower() + "ArraySetter" if nca: pre_header += "/**\n" pre_header += " * This allocates an array of size 'length' inside of arrayData and returns whether or not there was " pre_header += "a success. If length is -1, then this should allocate some representation of a null array.\n" pre_header += " *\n" pre_header += " * See also " + read_func + "\n" pre_header += " *\n" pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" pre_header += " * \\param length the length to allocate arrayData to (or -1 if this represents a null array).\n" pre_header += " *\n" pre_header += " * \\return whether the allocation succeeded.\n" pre_header += " */\n" pre_header += "typedef bool (*" + allocator_type + ")(void* arrayData, int32_t length);\n\n" pre_header += "/**\n" pre_header += " * This is called to get the underlying data from an arrayData object at index.\n" pre_header += " *\n" pre_header += " * See also " + write_func + "\n" pre_header += " *\n" pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" pre_header += " * \\param index the index of the value to be retrieved.\n" pre_header += " *\n" pre_header += " * \\return the value of the array at index index.\n" pre_header += " */\n" pre_header += "typedef " + cpp + " (*" + getter_type + ")(const void* arrayData, size_t index);\n\n" pre_header += "/**\n" pre_header += " * This is called to set an underlying value in an arrayData object at index.\n" pre_header += " *\n" pre_header += " * See also " + read_func + "\n" pre_header += " *\n" pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" pre_header += " * \\param index the index of the value to be set.\n" pre_header += " * \\param value the value to set at index index.\n" pre_header += " */\n" pre_header += "typedef void (*" + setter_type + ")(void* arrayData, size_t index, " + cpp + " value);\n\n" else: pre_header += "/**\n" pre_header += " * This is called to get the underlying data from an arrayData object.\n" pre_header += " *\n" pre_header += " * The implementation of this function should allocate a contiguous array of size 'length' and " pre_header += "return that underlying buffer to be filled out. If there is an error or length is 0, null may be " pre_header += "returned. If length is -1, this should allocate some representation of a null array.\n" pre_header += " *\n" pre_header += " * See also " + read_func + "\n" pre_header += " *\n" pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" pre_header += " * \\param length the length to allocate arrayData to.\n" pre_header += " * \\param outBuffer a buffer of " + cpp + " of size 'length' (if length is >= 0, if length is 0, " pre_header += "this may be nullptr).\n" pre_header += " *\n" pre_header += " * \\return whether or not the allocation was successful (or whether a null array is represented when length is -1).\n" pre_header += " */\n" pre_header += "typedef bool (*" + allocator_type + ")(void* arrayData, int32_t length, " + cpp + "** outBuffer);\n\n" read_array_args = [("const AParcel*", "parcel")] read_array_args += [("void*", "arrayData")] read_array_args += [(allocator_type, "allocator")] if nca: read_array_args += [(setter_type, "setter")] read_type_args = ", ".join((varType + " " + name for varType, name in read_array_args)) read_call_args = ", ".join((name for varType, name in read_array_args)) header += "/**\n" header += " * Reads an array of " + cpp + " from the next location in a non-null parcel.\n" header += " *\n" if nca: header += " * First, allocator will be called with the length of the array. Then, for every i in [0, length), " header += "setter(arrayData, i, x) will be called where x is the value at the associated index.\n" else: header += " * First, allocator will be called with the length of the array. If the allocation succeeds and the " header += "length is greater than zero, the buffer returned by the allocator will be filled with the corresponding data\n" header += " *\n" header += " * \\param parcel the parcel to read from.\n" header += " * \\param arrayData some external representation of an array.\n" header += " * \\param allocator the callback that will be called to allocate the array.\n" if nca: header += " * \\param setter the callback that will be called to set a value at a specific location in the array.\n" header += " *\n" header += " * \\return STATUS_OK on successful read.\n" header += " */\n" header += "binder_status_t " + read_func + "(" + read_type_args + ") __INTRODUCED_IN(29);\n\n" source += "binder_status_t " + read_func + "(" + read_type_args + ") {\n" additional_args = "" if nca: additional_args = ", &Parcel::read" + pretty source += " return ReadArray<" + cpp + ">(" + read_call_args + additional_args + ");\n"; source += "}\n\n" cpp_helper += "/**\n" cpp_helper += " * Writes a vector of " + cpp + " to the next location in a non-null parcel.\n" cpp_helper += " */\n" cpp_helper += "inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<" + cpp + ">& vec) {\n" write_args = "vec.data(), vec.size()" if nca: write_args = "static_cast(&vec), vec.size(), AParcel_stdVectorGetter<" + cpp + ">" cpp_helper += " return AParcel_write" + pretty + "Array(parcel, " + write_args + ");\n" cpp_helper += "}\n\n" cpp_helper += "/**\n" cpp_helper += " * Writes an optional vector of " + cpp + " to the next location in a non-null parcel.\n" cpp_helper += " */\n" cpp_helper += "inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional>& vec) {\n" extra_args = "" if nca: extra_args = ", AParcel_stdVectorGetter<" + cpp + ">" cpp_helper += " if (!vec) return AParcel_write" + pretty + "Array(parcel, nullptr, -1" + extra_args + ");\n" cpp_helper += " return AParcel_writeVector(parcel, *vec);\n" cpp_helper += "}\n\n" cpp_helper += "/**\n" cpp_helper += " * Reads a vector of " + cpp + " from the next location in a non-null parcel.\n" cpp_helper += " */\n" cpp_helper += "inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<" + cpp + ">* vec) {\n" cpp_helper += " void* vectorData = static_cast(vec);\n" read_args = [] read_args += ["parcel"] read_args += ["vectorData"] if nca: read_args += ["AParcel_stdVectorExternalAllocator"] read_args += ["AParcel_stdVectorSetter<" + cpp + ">"] else: read_args += ["AParcel_stdVectorAllocator<" + cpp + ">"] cpp_helper += " return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n" cpp_helper += "}\n\n" cpp_helper += "/**\n" cpp_helper += " * Reads an optional vector of " + cpp + " from the next location in a non-null parcel.\n" cpp_helper += " */\n" cpp_helper += "inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional>* vec) {\n" cpp_helper += " void* vectorData = static_cast(vec);\n" read_args = [] read_args += ["parcel"] read_args += ["vectorData"] if nca: read_args += ["AParcel_nullableStdVectorExternalAllocator"] read_args += ["AParcel_nullableStdVectorSetter<" + cpp + ">"] else: read_args += ["AParcel_nullableStdVectorAllocator<" + cpp + ">"] cpp_helper += " return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n" cpp_helper += "}\n\n" replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", pre_header, "START-PRIMITIVE-VECTOR-GETTERS", "END-PRIMITIVE-VECTOR-GETTERS") replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header, "START-PRIMITIVE-READ-WRITE", "END-PRIMITIVE-READ-WRITE") replaceFileTags(ROOT + "parcel.cpp", source, "START", "END") replaceFileTags(ROOT + "include_ndk/android/binder_parcel_utils.h", cpp_helper, "START", "END") print("Updating DONE.") if __name__ == "__main__": main() libs/binder/ndk/scripts/init_map.sh0100755 0000000 0000000 00000001440 13756501735 016416 0ustar000000000 0000000 #!/usr/bin/env bash # Simple helper for ease of development until this API is frozen. echo "LIBBINDER_NDK { # introduced=29" echo " global:" { grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder.h; grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder_jni.h; grep -oP "AParcel_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_parcel.h; grep -oP "AStatus_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_status.h; } | sort | uniq | awk '{ print " " $0 ";"; }' { grep -oP "AServiceManager_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_manager.h; grep -oP "ABinderProcess_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_process.h; } | sort | uniq | awk '{ print " " $0 "; # apex"; }' echo " local:" echo " *;" echo "};" libs/binder/ndk/service_manager.cpp0100644 0000000 0000000 00000003742 13756501735 016435 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include "ibinder_internal.h" #include "status_internal.h" #include using ::android::defaultServiceManager; using ::android::IBinder; using ::android::IServiceManager; using ::android::sp; using ::android::status_t; using ::android::String16; binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) { if (binder == nullptr || instance == nullptr) { return STATUS_UNEXPECTED_NULL; } sp sm = defaultServiceManager(); status_t status = sm->addService(String16(instance), binder->getBinder()); return PruneStatusT(status); } AIBinder* AServiceManager_checkService(const char* instance) { if (instance == nullptr) { return nullptr; } sp sm = defaultServiceManager(); sp binder = sm->checkService(String16(instance)); sp ret = ABpBinder::lookupOrCreateFromBinder(binder); AIBinder_incStrong(ret.get()); return ret.get(); } AIBinder* AServiceManager_getService(const char* instance) { if (instance == nullptr) { return nullptr; } sp sm = defaultServiceManager(); sp binder = sm->getService(String16(instance)); sp ret = ABpBinder::lookupOrCreateFromBinder(binder); AIBinder_incStrong(ret.get()); return ret.get(); } libs/binder/ndk/status.cpp0100644 0000000 0000000 00000012047 13756501735 014624 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include "status_internal.h" #include using ::android::status_t; using ::android::binder::Status; AStatus* AStatus_newOk() { return new AStatus(); } AStatus* AStatus_fromExceptionCode(binder_exception_t exception) { return new AStatus(Status::fromExceptionCode(PruneException(exception))); } AStatus* AStatus_fromExceptionCodeWithMessage(binder_exception_t exception, const char* message) { return new AStatus(Status::fromExceptionCode(PruneException(exception), message)); } AStatus* AStatus_fromServiceSpecificError(int32_t serviceSpecific) { return new AStatus(Status::fromServiceSpecificError(serviceSpecific)); } AStatus* AStatus_fromServiceSpecificErrorWithMessage(int32_t serviceSpecific, const char* message) { return new AStatus(Status::fromServiceSpecificError(serviceSpecific, message)); } AStatus* AStatus_fromStatus(binder_status_t status) { return new AStatus(Status::fromStatusT(PruneStatusT(status))); } bool AStatus_isOk(const AStatus* status) { return status->get()->isOk(); } binder_exception_t AStatus_getExceptionCode(const AStatus* status) { return PruneException(status->get()->exceptionCode()); } int32_t AStatus_getServiceSpecificError(const AStatus* status) { return status->get()->serviceSpecificErrorCode(); } binder_status_t AStatus_getStatus(const AStatus* status) { return PruneStatusT(status->get()->transactionError()); } const char* AStatus_getMessage(const AStatus* status) { return status->get()->exceptionMessage().c_str(); } void AStatus_delete(AStatus* status) { delete status; } binder_status_t PruneStatusT(status_t status) { switch (status) { case ::android::OK: return STATUS_OK; case ::android::NO_MEMORY: return STATUS_NO_MEMORY; case ::android::INVALID_OPERATION: return STATUS_INVALID_OPERATION; case ::android::BAD_VALUE: return STATUS_BAD_VALUE; case ::android::BAD_TYPE: return STATUS_BAD_TYPE; case ::android::NAME_NOT_FOUND: return STATUS_NAME_NOT_FOUND; case ::android::PERMISSION_DENIED: return STATUS_PERMISSION_DENIED; case ::android::NO_INIT: return STATUS_NO_INIT; case ::android::ALREADY_EXISTS: return STATUS_ALREADY_EXISTS; case ::android::DEAD_OBJECT: return STATUS_DEAD_OBJECT; case ::android::FAILED_TRANSACTION: return STATUS_FAILED_TRANSACTION; case ::android::BAD_INDEX: return STATUS_BAD_INDEX; case ::android::NOT_ENOUGH_DATA: return STATUS_NOT_ENOUGH_DATA; case ::android::WOULD_BLOCK: return STATUS_WOULD_BLOCK; case ::android::TIMED_OUT: return STATUS_TIMED_OUT; case ::android::UNKNOWN_TRANSACTION: return STATUS_UNKNOWN_TRANSACTION; case ::android::FDS_NOT_ALLOWED: return STATUS_FDS_NOT_ALLOWED; case ::android::UNEXPECTED_NULL: return STATUS_UNEXPECTED_NULL; case ::android::UNKNOWN_ERROR: return STATUS_UNKNOWN_ERROR; default: LOG(WARNING) << __func__ << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status; return STATUS_UNKNOWN_ERROR; } } binder_exception_t PruneException(int32_t exception) { switch (exception) { case Status::EX_NONE: return EX_NONE; case Status::EX_SECURITY: return EX_SECURITY; case Status::EX_BAD_PARCELABLE: return EX_BAD_PARCELABLE; case Status::EX_ILLEGAL_ARGUMENT: return EX_ILLEGAL_ARGUMENT; case Status::EX_NULL_POINTER: return EX_NULL_POINTER; case Status::EX_ILLEGAL_STATE: return EX_ILLEGAL_STATE; case Status::EX_NETWORK_MAIN_THREAD: return EX_NETWORK_MAIN_THREAD; case Status::EX_UNSUPPORTED_OPERATION: return EX_UNSUPPORTED_OPERATION; case Status::EX_SERVICE_SPECIFIC: return EX_SERVICE_SPECIFIC; case Status::EX_PARCELABLE: return EX_PARCELABLE; case Status::EX_TRANSACTION_FAILED: return EX_TRANSACTION_FAILED; default: LOG(WARNING) << __func__ << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception; return EX_TRANSACTION_FAILED; } } libs/binder/ndk/status_internal.h0100644 0000000 0000000 00000002360 13756501735 016162 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include #include struct AStatus { AStatus() {} // ok explicit AStatus(::android::binder::Status&& status) : mStatus(std::move(status)) {} ::android::binder::Status* get() { return &mStatus; } const ::android::binder::Status* get() const { return &mStatus; } private: ::android::binder::Status mStatus; }; // This collapses the statuses into the declared range. binder_status_t PruneStatusT(android::status_t status); // This collapses the exception into the declared range. binder_exception_t PruneException(int32_t exception); libs/binder/ndk/test/0040755 0000000 0000000 00000000000 13756501735 013553 5ustar000000000 0000000 libs/binder/ndk/test/Android.bp0100644 0000000 0000000 00000003557 13756501735 015465 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ cc_defaults { name: "test_libbinder_ndk_defaults", shared_libs: [ "libbase", ], strip: { none: true, }, cflags: [ "-O0", "-g", ], } cc_library_static { name: "test_libbinder_ndk_library", defaults: ["test_libbinder_ndk_defaults"], export_include_dirs: ["include"], shared_libs: ["libbinder_ndk"], export_shared_lib_headers: ["libbinder_ndk"], srcs: ["iface.cpp"], } cc_defaults { name: "test_libbinder_ndk_test_defaults", defaults: ["test_libbinder_ndk_defaults"], shared_libs: [ "libandroid_runtime_lazy", "libbase", "libbinder", "libutils", ], static_libs: [ "libbinder_ndk", "test_libbinder_ndk_library", ], } // This test is a unit test of the low-level API that is presented here, // specifically the parts which are outside of the NDK. Actual users should // also instead use AIDL to generate these stubs. See android.binder.cts. cc_test { name: "libbinder_ndk_test_client", defaults: ["test_libbinder_ndk_test_defaults"], srcs: ["main_client.cpp"], } cc_test { name: "libbinder_ndk_test_server", defaults: ["test_libbinder_ndk_test_defaults"], srcs: ["main_server.cpp"], gtest: false, } libs/binder/ndk/test/iface.cpp0100644 0000000 0000000 00000012430 13756501735 015323 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include #include using ::android::sp; using ::android::wp; const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo"; const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die"; const char* kIFooDescriptor = "my-special-IFoo-class"; struct IFoo_Class_Data { sp foo; }; void* IFoo_Class_onCreate(void* args) { IFoo_Class_Data* foo = static_cast(args); // This is a foo, but we're currently not verifying that. So, the method newLocalBinder is // coupled with this. return static_cast(foo); } void IFoo_Class_onDestroy(void* userData) { delete static_cast(userData); } binder_status_t IFoo_Class_onTransact(AIBinder* binder, transaction_code_t code, const AParcel* in, AParcel* out) { binder_status_t stat = STATUS_FAILED_TRANSACTION; sp foo = static_cast(AIBinder_getUserData(binder))->foo; CHECK(foo != nullptr) << "Transaction made on already deleted object"; switch (code) { case IFoo::DOFOO: { int32_t valueIn; int32_t valueOut; stat = AParcel_readInt32(in, &valueIn); if (stat != STATUS_OK) break; stat = foo->doubleNumber(valueIn, &valueOut); if (stat != STATUS_OK) break; stat = AParcel_writeInt32(out, valueOut); break; } case IFoo::DIE: { stat = foo->die(); break; } } return stat; } AIBinder_Class* IFoo::kClass = AIBinder_Class_define(kIFooDescriptor, IFoo_Class_onCreate, IFoo_Class_onDestroy, IFoo_Class_onTransact); class BpFoo : public IFoo { public: explicit BpFoo(AIBinder* binder) : mBinder(binder) {} virtual ~BpFoo() { AIBinder_decStrong(mBinder); } virtual binder_status_t doubleNumber(int32_t in, int32_t* out) { binder_status_t stat = STATUS_OK; AParcel* parcelIn; stat = AIBinder_prepareTransaction(mBinder, &parcelIn); if (stat != STATUS_OK) return stat; stat = AParcel_writeInt32(parcelIn, in); if (stat != STATUS_OK) return stat; ::ndk::ScopedAParcel parcelOut; stat = AIBinder_transact(mBinder, IFoo::DOFOO, &parcelIn, parcelOut.getR(), 0 /*flags*/); if (stat != STATUS_OK) return stat; stat = AParcel_readInt32(parcelOut.get(), out); if (stat != STATUS_OK) return stat; return stat; } virtual binder_status_t die() { binder_status_t stat = STATUS_OK; AParcel* parcelIn; stat = AIBinder_prepareTransaction(mBinder, &parcelIn); ::ndk::ScopedAParcel parcelOut; stat = AIBinder_transact(mBinder, IFoo::DIE, &parcelIn, parcelOut.getR(), 0 /*flags*/); return stat; } private: // Always assumes one refcount AIBinder* mBinder; }; IFoo::~IFoo() { AIBinder_Weak_delete(mWeakBinder); } binder_status_t IFoo::addService(const char* instance) { AIBinder* binder = nullptr; if (mWeakBinder != nullptr) { // one strong ref count of binder binder = AIBinder_Weak_promote(mWeakBinder); } if (binder == nullptr) { // or one strong refcount here binder = AIBinder_new(IFoo::kClass, static_cast(new IFoo_Class_Data{this})); if (mWeakBinder != nullptr) { AIBinder_Weak_delete(mWeakBinder); } mWeakBinder = AIBinder_Weak_new(binder); } binder_status_t status = AServiceManager_addService(binder, instance); // Strong references we care about kept by remote process AIBinder_decStrong(binder); return status; } sp IFoo::getService(const char* instance, AIBinder** outBinder) { AIBinder* binder = AServiceManager_getService(instance); // maybe nullptr if (binder == nullptr) { return nullptr; } if (!AIBinder_associateClass(binder, IFoo::kClass)) { AIBinder_decStrong(binder); return nullptr; } if (outBinder != nullptr) { AIBinder_incStrong(binder); *outBinder = binder; } if (AIBinder_isRemote(binder)) { sp ret = new BpFoo(binder); // takes ownership of binder return ret; } IFoo_Class_Data* data = static_cast(AIBinder_getUserData(binder)); CHECK(data != nullptr); // always created with non-null data sp ret = data->foo; AIBinder* held = AIBinder_Weak_promote(ret->mWeakBinder); CHECK(held == binder); AIBinder_decStrong(held); AIBinder_decStrong(binder); return ret; } libs/binder/ndk/test/include/0040755 0000000 0000000 00000000000 13756501735 015176 5ustar000000000 0000000 libs/binder/ndk/test/include/iface/0040755 0000000 0000000 00000000000 13756501735 016245 5ustar000000000 0000000 libs/binder/ndk/test/include/iface/iface.h0100644 0000000 0000000 00000003357 13756501735 017472 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include // warning: it is recommended to use AIDL output instead of this. binder_ibinder_utils.h and some of // the other niceties make sure that, for instance, binder proxies are always the same. They also // don't use internal Android APIs like refbase which are used here only for convenience. class IFoo : public virtual ::android::RefBase { public: static const char* kSomeInstanceName; static const char* kInstanceNameToDieFor; static AIBinder_Class* kClass; // Takes ownership of IFoo binder_status_t addService(const char* instance); static ::android::sp getService(const char* instance, AIBinder** outBinder = nullptr); enum Call { DOFOO = FIRST_CALL_TRANSACTION + 0, DIE = FIRST_CALL_TRANSACTION + 1, }; virtual ~IFoo(); virtual binder_status_t doubleNumber(int32_t in, int32_t* out) = 0; virtual binder_status_t die() = 0; private: // this variable is only when IFoo is local (since this test combines 'IFoo' and 'BnFoo'), not // for BpFoo. AIBinder_Weak* mWeakBinder = nullptr; }; libs/binder/ndk/test/main_client.cpp0100644 0000000 0000000 00000015025 13756501735 016541 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include #include #include #include #include #include #include using ::android::sp; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; // This is too slow // TEST(NdkBinder, GetServiceThatDoesntExist) { // sp foo = IFoo::getService("asdfghkl;"); // EXPECT_EQ(nullptr, foo.get()); // } TEST(NdkBinder, CheckServiceThatDoesntExist) { AIBinder* binder = AServiceManager_checkService("asdfghkl;"); ASSERT_EQ(nullptr, binder); } TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService); EXPECT_NE(nullptr, binder); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); AIBinder_decStrong(binder); } TEST(NdkBinder, DoubleNumber) { sp foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); int32_t out; EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out)); EXPECT_EQ(2, out); } void LambdaOnDeath(void* cookie) { auto onDeath = static_cast*>(cookie); (*onDeath)(); }; TEST(NdkBinder, DeathRecipient) { using namespace std::chrono_literals; AIBinder* binder; sp foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder); ASSERT_NE(nullptr, foo.get()); ASSERT_NE(nullptr, binder); std::mutex deathMutex; std::condition_variable deathCv; bool deathRecieved = false; std::function onDeath = [&] { std::cerr << "Binder died (as requested)." << std::endl; deathRecieved = true; deathCv.notify_one(); }; AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath); EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast(&onDeath))); // the binder driver should return this if the service dies during the transaction EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); foo = nullptr; AIBinder_decStrong(binder); binder = nullptr; std::unique_lock lock(deathMutex); EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; })); EXPECT_TRUE(deathRecieved); AIBinder_DeathRecipient_delete(recipient); } TEST(NdkBinder, RetrieveNonNdkService) { AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binder); EXPECT_TRUE(AIBinder_isRemote(binder)); EXPECT_TRUE(AIBinder_isAlive(binder)); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); AIBinder_decStrong(binder); } void OnBinderDeath(void* cookie) { LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie; } TEST(NdkBinder, LinkToDeath) { AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binder); AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath); ASSERT_NE(nullptr, recipient); EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr)); EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr)); AIBinder_DeathRecipient_delete(recipient); AIBinder_decStrong(binder); } class MyTestFoo : public IFoo { binder_status_t doubleNumber(int32_t in, int32_t* out) override { *out = 2 * in; LOG(INFO) << "doubleNumber (" << in << ") => " << *out; return STATUS_OK; } binder_status_t die() override { ADD_FAILURE() << "die called on local instance"; return STATUS_OK; } }; TEST(NdkBinder, GetServiceInProcess) { static const char* kInstanceName = "test-get-service-in-process"; sp foo = new MyTestFoo; EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName)); sp getFoo = IFoo::getService(kInstanceName); EXPECT_EQ(foo.get(), getFoo.get()); int32_t out; EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out)); EXPECT_EQ(2, out); } TEST(NdkBinder, EqualityOfRemoteBinderPointer) { AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binderA); AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService); ASSERT_NE(nullptr, binderB); EXPECT_EQ(binderA, binderB); AIBinder_decStrong(binderA); AIBinder_decStrong(binderB); } TEST(NdkBinder, ToFromJavaNullptr) { EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr)); EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr)); } TEST(NdkBinder, ABpBinderRefCount) { AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); AIBinder_Weak* wBinder = AIBinder_Weak_new(binder); ASSERT_NE(nullptr, binder); EXPECT_EQ(1, AIBinder_debugGetRefCount(binder)); AIBinder_decStrong(binder); // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder)); AIBinder_Weak_delete(wBinder); } TEST(NdkBinder, AddServiceMultipleTimes) { static const char* kInstanceName1 = "test-multi-1"; static const char* kInstanceName2 = "test-multi-2"; sp foo = new MyTestFoo; EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1)); EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2)); EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2)); } int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); } #include #include #include libs/binder/ndk/test/main_server.cpp0100644 0000000 0000000 00000003134 13756501735 016567 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include using ::android::sp; class MyFoo : public IFoo { binder_status_t doubleNumber(int32_t in, int32_t* out) override { *out = 2 * in; LOG(INFO) << "doubleNumber (" << in << ") => " << *out; return STATUS_OK; } binder_status_t die() override { LOG(FATAL) << "IFoo::die called!"; return STATUS_UNKNOWN_ERROR; } }; int service(const char* instance) { ABinderProcess_setThreadPoolMaxThreadCount(0); // Strong reference to MyFoo kept by service manager. binder_status_t status = (new MyFoo)->addService(instance); if (status != STATUS_OK) { LOG(FATAL) << "Could not register: " << status << " " << instance; } ABinderProcess_joinThreadPool(); return 1; // should not return } int main() { if (fork() == 0) { return service(IFoo::kInstanceNameToDieFor); } return service(IFoo::kSomeInstanceName); } libs/binder/ndk/update.sh0100755 0000000 0000000 00000001453 13756501735 014415 0ustar000000000 0000000 #!/usr/bin/env bash # Copyright (C) 2018 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 -ex # This script makes sure that the source code is in sync with the various scripts ./scripts/gen_parcel_helper.py ./scripts/format.sh ./scripts/init_map.sh > libbinder_ndk.map.txt libs/binder/tests/0040755 0000000 0000000 00000000000 13756501735 013162 5ustar000000000 0000000 libs/binder/tests/Android.bp0100644 0000000 0000000 00000006235 13756501735 015070 0ustar000000000 0000000 // // 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. // cc_defaults { name: "binder_test_defaults", cflags: [ "-Wall", "-Werror", "-Wno-unused-private-field", "-Wno-unused-variable", ], } cc_test { name: "binderDriverInterfaceTest_IPC_32", srcs: ["binderDriverInterfaceTest.cpp"], defaults: ["binder_test_defaults"], compile_multilib: "32", cflags: ["-DBINDER_IPC_32BIT=1"], } cc_test { product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, name: "binderDriverInterfaceTest", srcs: ["binderDriverInterfaceTest.cpp"], defaults: ["binder_test_defaults"], } cc_test { name: "binderValueTypeTest", srcs: ["binderValueTypeTest.cpp"], defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", ], } cc_test { name: "binderLibTest_IPC_32", srcs: ["binderLibTest.cpp"], defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", ], compile_multilib: "32", cflags: ["-DBINDER_IPC_32BIT=1"], } cc_test { product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, defaults: ["binder_test_defaults"], name: "binderLibTest", srcs: ["binderLibTest.cpp"], shared_libs: [ "libbinder", "libutils", ], } cc_test { name: "binderThroughputTest", srcs: ["binderThroughputTest.cpp"], defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", ], clang: true, cflags: [ "-g", "-Wno-missing-field-initializers", "-Wno-sign-compare", "-O3", ], } cc_test { name: "binderTextOutputTest", srcs: ["binderTextOutputTest.cpp"], defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", "libbase", ], } cc_test { name: "schd-dbg", srcs: ["schd-dbg.cpp"], defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", "libbase", ], } cc_test { name: "binderSafeInterfaceTest", srcs: ["binderSafeInterfaceTest.cpp"], defaults: ["binder_test_defaults"], cppflags: [ "-Weverything", "-Wno-c++98-compat", "-Wno-c++98-compat-pedantic", "-Wno-global-constructors", "-Wno-padded", "-Wno-weak-vtables", ], cpp_std: "experimental", gnu_extensions: false, shared_libs: [ "libbinder", "libcutils", "liblog", "libutils", ], } libs/binder/tests/binderDriverInterfaceTest.cpp0100644 0000000 0000000 00000026564 13756501735 021000 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include #include #include #include #define BINDER_DEV_NAME "/dev/binder" testing::Environment* binder_env; class BinderDriverInterfaceTestEnv : public ::testing::Environment { virtual void SetUp() { int ret; uint32_t max_threads = 0; m_binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); ASSERT_GE(m_binderFd, 0); m_buffer = mmap(nullptr, 64*1024, PROT_READ, MAP_SHARED, m_binderFd, 0); ASSERT_NE(m_buffer, (void *)nullptr); ret = ioctl(m_binderFd, BINDER_SET_MAX_THREADS, &max_threads); EXPECT_EQ(0, ret); EnterLooper(); } virtual void TearDown() { close(m_binderFd); } private: int m_binderFd; void *m_buffer; public: int getBinderFd(void) { return m_binderFd; } void EnterLooper(void) { int ret; const uint32_t bc[] = { BC_ENTER_LOOPER, }; struct binder_write_read bwr = binder_write_read(); bwr.write_buffer = (uintptr_t)bc; bwr.write_size = sizeof(bc); ret = ioctl(m_binderFd, BINDER_WRITE_READ, &bwr); EXPECT_EQ(0, ret); if (ret < 0) { EXPECT_EQ(0, errno); } EXPECT_EQ(sizeof(bc), bwr.write_consumed); } }; class BinderDriverInterfaceTest : public ::testing::Test { public: virtual void SetUp() { m_binderFd = static_cast(binder_env)->getBinderFd(); } virtual void TearDown() { } protected: /* The ioctl must either return 0, or if it doesn't errno should be accepted_errno */ void binderTestIoctlSuccessOrError(int cmd, void *arg, int accepted_errno) { int ret; ret = ioctl(m_binderFd, cmd, arg); if (ret != 0) { EXPECT_EQ(errno, accepted_errno); } } void binderTestIoctlRetErr2(int cmd, void *arg, int expect_ret, int expect_errno, int accept_errno) { int ret; ret = ioctl(m_binderFd, cmd, arg); EXPECT_EQ(expect_ret, ret); if (ret < 0) { if (errno != accept_errno) EXPECT_EQ(expect_errno, errno); } } void binderTestIoctlErr2(int cmd, void *arg, int expect_errno, int accept_errno) { binderTestIoctlRetErr2(cmd, arg, -1, expect_errno, accept_errno); } void binderTestIoctlErr1(int cmd, void *arg, int expect_errno) { binderTestIoctlErr2(cmd, arg, expect_errno, expect_errno); } void binderTestIoctl(int cmd, void *arg) { binderTestIoctlRetErr2(cmd, arg, 0, 0, 0); } void binderTestIoctlUnimplemented(int cmd, void *arg) { int ret; ret = ioctl(m_binderFd, cmd, arg); if (ret < 0) { /* Not currently implmented. Allow ret == -1, errno == EINVAL */ EXPECT_EQ(-1, ret); EXPECT_EQ(EINVAL, errno); } } void binderTestReadEmpty(void) { size_t i; uint32_t br[32]; struct binder_write_read bwr = binder_write_read(); SCOPED_TRACE("TestReadEmpty"); bwr.read_buffer = (uintptr_t)br; bwr.read_size = sizeof(br); binderTestIoctlErr1(BINDER_WRITE_READ, &bwr, EAGAIN); EXPECT_EQ(0u, bwr.read_consumed); for (i = 0; i * sizeof(uint32_t) < bwr.read_consumed; i++) { SCOPED_TRACE(testing::Message() << "i = " << i); EXPECT_EQ(BR_NOOP, br[i]); } } void binderWaitForReadData(int timeout_ms) { int ret; pollfd pfd = pollfd(); pfd.fd = m_binderFd; pfd.events = POLLIN; ret = poll(&pfd, 1, timeout_ms); EXPECT_EQ(1, ret); } private: int m_binderFd; }; TEST_F(BinderDriverInterfaceTest, Version) { struct binder_version version; binderTestIoctl(BINDER_VERSION, &version); ASSERT_EQ(BINDER_CURRENT_PROTOCOL_VERSION, version.protocol_version); } TEST_F(BinderDriverInterfaceTest, OpenNoMmap) { int binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); ASSERT_GE(binderFd, 0); close(binderFd); } TEST_F(BinderDriverInterfaceTest, WriteReadNull) { binderTestIoctlErr1(BINDER_WRITE_READ, nullptr, EFAULT); } TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNull) { binderTestIoctlErr2(BINDER_SET_IDLE_TIMEOUT, nullptr, EFAULT, EINVAL); } TEST_F(BinderDriverInterfaceTest, SetMaxThreadsNull) { binderTestIoctlErr2(BINDER_SET_MAX_THREADS, nullptr, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ } TEST_F(BinderDriverInterfaceTest, SetIdlePriorityNull) { binderTestIoctlErr2(BINDER_SET_IDLE_PRIORITY, nullptr, EFAULT, EINVAL); } TEST_F(BinderDriverInterfaceTest, VersionNull) { binderTestIoctlErr2(BINDER_VERSION, nullptr, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ } TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNoTest) { int64_t idle_timeout = 100000; binderTestIoctlUnimplemented(BINDER_SET_IDLE_TIMEOUT, &idle_timeout); } TEST_F(BinderDriverInterfaceTest, SetMaxThreads) { uint32_t max_threads = 0; binderTestIoctl(BINDER_SET_MAX_THREADS, &max_threads); } TEST_F(BinderDriverInterfaceTest, SetIdlePriorityNoTest) { int idle_priority = 0; binderTestIoctlUnimplemented(BINDER_SET_IDLE_PRIORITY, &idle_priority); } TEST_F(BinderDriverInterfaceTest, SetContextMgrBusy) { int32_t dummy = 0; binderTestIoctlErr1(BINDER_SET_CONTEXT_MGR, &dummy, EBUSY); } TEST_F(BinderDriverInterfaceTest, ThreadExit) { int32_t dummy = 0; binderTestIoctl(BINDER_THREAD_EXIT, &dummy); static_cast(binder_env)->EnterLooper(); } TEST_F(BinderDriverInterfaceTest, WriteReadEmpty) { struct binder_write_read bwr = binder_write_read(); binderTestIoctl(BINDER_WRITE_READ, &bwr); } TEST_F(BinderDriverInterfaceTest, Read) { binderTestReadEmpty(); } TEST_F(BinderDriverInterfaceTest, IncRefsAcquireReleaseDecRefs) { const uint32_t bc[] = { BC_INCREFS, 0, BC_ACQUIRE, 0, BC_RELEASE, 0, BC_DECREFS, 0, }; struct binder_write_read bwr = binder_write_read(); bwr.write_buffer = (uintptr_t)bc; bwr.write_size = sizeof(bc); binderTestIoctl(BINDER_WRITE_READ, &bwr); EXPECT_EQ(sizeof(bc), bwr.write_consumed); binderTestReadEmpty(); } TEST_F(BinderDriverInterfaceTest, Transaction) { binder_uintptr_t cookie = 1234; struct { uint32_t cmd1; struct binder_transaction_data arg1; } __attribute__((packed)) bc1 = { .cmd1 = BC_TRANSACTION, .arg1 = { .target = { 0 }, .cookie = 0, .code = android::IBinder::PING_TRANSACTION, .flags = 0, .sender_pid = 0, .sender_euid = 0, .data_size = 0, .offsets_size = 0, .data = { .ptr = {0, 0}, }, }, }; struct { uint32_t cmd0; uint32_t cmd1; uint32_t cmd2; binder_transaction_data arg2; uint32_t pad[16]; } __attribute__((packed)) br; struct binder_write_read bwr = binder_write_read(); bwr.write_buffer = (uintptr_t)&bc1; bwr.write_size = sizeof(bc1); bwr.read_buffer = (uintptr_t)&br; bwr.read_size = sizeof(br); { SCOPED_TRACE("1st WriteRead"); binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr, EAGAIN); } EXPECT_EQ(sizeof(bc1), bwr.write_consumed); if (bwr.read_consumed < offsetof(typeof(br), pad)) { SCOPED_TRACE("2nd WriteRead"); binderWaitForReadData(10000); binderTestIoctl(BINDER_WRITE_READ, &bwr); } EXPECT_EQ(offsetof(typeof(br), pad), bwr.read_consumed); if (bwr.read_consumed > offsetof(typeof(br), cmd0)) EXPECT_EQ(BR_NOOP, br.cmd0); if (bwr.read_consumed > offsetof(typeof(br), cmd1)) EXPECT_EQ(BR_TRANSACTION_COMPLETE, br.cmd1); if (bwr.read_consumed > offsetof(typeof(br), cmd2)) EXPECT_EQ(BR_REPLY, br.cmd2); if (bwr.read_consumed >= offsetof(typeof(br), pad)) { EXPECT_EQ(0u, br.arg2.target.ptr); EXPECT_EQ(0u, br.arg2.cookie); EXPECT_EQ(0u, br.arg2.code); EXPECT_EQ(0u, br.arg2.flags); EXPECT_EQ(0u, br.arg2.data_size); EXPECT_EQ(0u, br.arg2.offsets_size); SCOPED_TRACE("3rd WriteRead"); binderTestReadEmpty(); struct { uint32_t cmd1; binder_uintptr_t arg1; } __attribute__((packed)) bc2 = { .cmd1 = BC_FREE_BUFFER, .arg1 = br.arg2.data.ptr.buffer, }; bwr.write_buffer = (uintptr_t)&bc2; bwr.write_size = sizeof(bc2); bwr.write_consumed = 0; bwr.read_size = 0; binderTestIoctl(BINDER_WRITE_READ, &bwr); EXPECT_EQ(sizeof(bc2), bwr.write_consumed); } binderTestReadEmpty(); } TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) { binder_uintptr_t cookie = 1234; struct { uint32_t cmd0; uint32_t arg0; uint32_t cmd1; struct binder_handle_cookie arg1; uint32_t cmd2; struct binder_handle_cookie arg2; uint32_t cmd3; uint32_t arg3; } __attribute__((packed)) bc = { .cmd0 = BC_INCREFS, .arg0 = 0, .cmd1 = BC_REQUEST_DEATH_NOTIFICATION, .arg1 = { .handle = 0, .cookie = cookie, }, .cmd2 = BC_CLEAR_DEATH_NOTIFICATION, .arg2 = { .handle = 0, .cookie = cookie, }, .cmd3 = BC_DECREFS, .arg3 = 0, }; struct { uint32_t cmd0; uint32_t cmd1; binder_uintptr_t arg1; uint32_t pad[16]; } __attribute__((packed)) br; struct binder_write_read bwr = binder_write_read(); bwr.write_buffer = (uintptr_t)&bc; bwr.write_size = sizeof(bc); bwr.read_buffer = (uintptr_t)&br; bwr.read_size = sizeof(br); binderTestIoctl(BINDER_WRITE_READ, &bwr); EXPECT_EQ(sizeof(bc), bwr.write_consumed); EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed); EXPECT_EQ(BR_NOOP, br.cmd0); EXPECT_EQ(BR_CLEAR_DEATH_NOTIFICATION_DONE, br.cmd1); EXPECT_EQ(cookie, br.arg1); binderTestReadEmpty(); } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv()); return RUN_ALL_TESTS(); } libs/binder/tests/binderLibTest.cpp0100644 0000000 0000000 00000141723 13756501735 016425 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; static ::testing::AssertionResult IsPageAligned(void *buf) { if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0) return ::testing::AssertionSuccess(); else return ::testing::AssertionFailure() << buf << " is not page aligned"; } static testing::Environment* binder_env; static char *binderservername; static char *binderserversuffix; static char binderserverarg[] = "--binderserver"; static String16 binderLibTestServiceName = String16("test.binderLib"); enum BinderLibTestTranscationCode { BINDER_LIB_TEST_NOP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, BINDER_LIB_TEST_REGISTER_SERVER, BINDER_LIB_TEST_ADD_SERVER, BINDER_LIB_TEST_ADD_POLL_SERVER, BINDER_LIB_TEST_CALL_BACK, BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF, BINDER_LIB_TEST_DELAYED_CALL_BACK, BINDER_LIB_TEST_NOP_CALL_BACK, BINDER_LIB_TEST_GET_SELF_TRANSACTION, BINDER_LIB_TEST_GET_ID_TRANSACTION, BINDER_LIB_TEST_INDIRECT_TRANSACTION, BINDER_LIB_TEST_SET_ERROR_TRANSACTION, BINDER_LIB_TEST_GET_STATUS_TRANSACTION, BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, BINDER_LIB_TEST_EXIT_TRANSACTION, BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, BINDER_LIB_TEST_ECHO_VECTOR, }; pid_t start_server_process(int arg2, bool usePoll = false) { int ret; pid_t pid; status_t status; int pipefd[2]; char stri[16]; char strpipefd1[16]; char usepoll[2]; char *childargv[] = { binderservername, binderserverarg, stri, strpipefd1, usepoll, binderserversuffix, nullptr }; ret = pipe(pipefd); if (ret < 0) return ret; snprintf(stri, sizeof(stri), "%d", arg2); snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]); snprintf(usepoll, sizeof(usepoll), "%d", usePoll ? 1 : 0); pid = fork(); if (pid == -1) return pid; if (pid == 0) { close(pipefd[0]); execv(binderservername, childargv); status = -errno; write(pipefd[1], &status, sizeof(status)); fprintf(stderr, "execv failed, %s\n", strerror(errno)); _exit(EXIT_FAILURE); } close(pipefd[1]); ret = read(pipefd[0], &status, sizeof(status)); //printf("pipe read returned %d, status %d\n", ret, status); close(pipefd[0]); if (ret == sizeof(status)) { ret = status; } else { kill(pid, SIGKILL); if (ret >= 0) { ret = NO_INIT; } } if (ret < 0) { wait(nullptr); return ret; } return pid; } class BinderLibTestEnv : public ::testing::Environment { public: BinderLibTestEnv() {} sp getServer(void) { return m_server; } private: virtual void SetUp() { m_serverpid = start_server_process(0); //printf("m_serverpid %d\n", m_serverpid); ASSERT_GT(m_serverpid, 0); sp sm = defaultServiceManager(); //printf("%s: pid %d, get service\n", __func__, m_pid); m_server = sm->getService(binderLibTestServiceName); ASSERT_TRUE(m_server != nullptr); //printf("%s: pid %d, get service done\n", __func__, m_pid); } virtual void TearDown() { status_t ret; Parcel data, reply; int exitStatus; pid_t pid; //printf("%s: pid %d\n", __func__, m_pid); if (m_server != nullptr) { ret = m_server->transact(BINDER_LIB_TEST_GET_STATUS_TRANSACTION, data, &reply); EXPECT_EQ(0, ret); ret = m_server->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(0, ret); } if (m_serverpid > 0) { //printf("wait for %d\n", m_pids[i]); pid = wait(&exitStatus); EXPECT_EQ(m_serverpid, pid); EXPECT_TRUE(WIFEXITED(exitStatus)); EXPECT_EQ(0, WEXITSTATUS(exitStatus)); } } pid_t m_serverpid; sp m_server; }; class BinderLibTest : public ::testing::Test { public: virtual void SetUp() { m_server = static_cast(binder_env)->getServer(); IPCThreadState::self()->restoreCallingWorkSource(0); } virtual void TearDown() { } protected: sp addServerEtc(int32_t *idPtr, int code) { int ret; int32_t id; Parcel data, reply; sp binder; ret = m_server->transact(code, data, &reply); EXPECT_EQ(NO_ERROR, ret); EXPECT_FALSE(binder != nullptr); binder = reply.readStrongBinder(); EXPECT_TRUE(binder != nullptr); ret = reply.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); if (idPtr) *idPtr = id; return binder; } sp addServer(int32_t *idPtr = nullptr) { return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_SERVER); } sp addPollServer(int32_t *idPtr = nullptr) { return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_POLL_SERVER); } void waitForReadData(int fd, int timeout_ms) { int ret; pollfd pfd = pollfd(); pfd.fd = fd; pfd.events = POLLIN; ret = poll(&pfd, 1, timeout_ms); EXPECT_EQ(1, ret); } sp m_server; }; class BinderLibTestBundle : public Parcel { public: BinderLibTestBundle(void) {} explicit BinderLibTestBundle(const Parcel *source) : m_isValid(false) { int32_t mark; int32_t bundleLen; size_t pos; if (source->readInt32(&mark)) return; if (mark != MARK_START) return; if (source->readInt32(&bundleLen)) return; pos = source->dataPosition(); if (Parcel::appendFrom(source, pos, bundleLen)) return; source->setDataPosition(pos + bundleLen); if (source->readInt32(&mark)) return; if (mark != MARK_END) return; m_isValid = true; setDataPosition(0); } void appendTo(Parcel *dest) { dest->writeInt32(MARK_START); dest->writeInt32(dataSize()); dest->appendFrom(this, 0, dataSize()); dest->writeInt32(MARK_END); }; bool isValid(void) { return m_isValid; } private: enum { MARK_START = B_PACK_CHARS('B','T','B','S'), MARK_END = B_PACK_CHARS('B','T','B','E'), }; bool m_isValid; }; class BinderLibTestEvent { public: BinderLibTestEvent(void) : m_eventTriggered(false) { pthread_mutex_init(&m_waitMutex, nullptr); pthread_cond_init(&m_waitCond, nullptr); } int waitEvent(int timeout_s) { int ret; pthread_mutex_lock(&m_waitMutex); if (!m_eventTriggered) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout_s; pthread_cond_timedwait(&m_waitCond, &m_waitMutex, &ts); } ret = m_eventTriggered ? NO_ERROR : TIMED_OUT; pthread_mutex_unlock(&m_waitMutex); return ret; } pthread_t getTriggeringThread() { return m_triggeringThread; } protected: void triggerEvent(void) { pthread_mutex_lock(&m_waitMutex); pthread_cond_signal(&m_waitCond); m_eventTriggered = true; m_triggeringThread = pthread_self(); pthread_mutex_unlock(&m_waitMutex); }; private: pthread_mutex_t m_waitMutex; pthread_cond_t m_waitCond; bool m_eventTriggered; pthread_t m_triggeringThread; }; class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent { public: BinderLibTestCallBack() : m_result(NOT_ENOUGH_DATA) , m_prev_end(nullptr) { } status_t getResult(void) { return m_result; } private: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { (void)reply; (void)flags; switch(code) { case BINDER_LIB_TEST_CALL_BACK: { status_t status = data.readInt32(&m_result); if (status != NO_ERROR) { m_result = status; } triggerEvent(); return NO_ERROR; } case BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF: { sp server; int ret; const uint8_t *buf = data.data(); size_t size = data.dataSize(); if (m_prev_end) { /* 64-bit kernel needs at most 8 bytes to align buffer end */ EXPECT_LE((size_t)(buf - m_prev_end), (size_t)8); } else { EXPECT_TRUE(IsPageAligned((void *)buf)); } m_prev_end = buf + size + data.objectsCount() * sizeof(binder_size_t); if (size > 0) { server = static_cast(binder_env)->getServer(); ret = server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, reply); EXPECT_EQ(NO_ERROR, ret); } return NO_ERROR; } default: return UNKNOWN_TRANSACTION; } } status_t m_result; const uint8_t *m_prev_end; }; class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent { private: virtual void binderDied(const wp& who) { (void)who; triggerEvent(); }; }; TEST_F(BinderLibTest, NopTransaction) { status_t ret; Parcel data, reply; ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, SetError) { int32_t testValue[] = { 0, -123, 123 }; for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) { status_t ret; Parcel data, reply; data.writeInt32(testValue[i]); ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply); EXPECT_EQ(testValue[i], ret); } } TEST_F(BinderLibTest, GetId) { status_t ret; int32_t id; Parcel data, reply; ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); ret = reply.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); EXPECT_EQ(0, id); } TEST_F(BinderLibTest, PtrSize) { status_t ret; int32_t ptrsize; Parcel data, reply; sp server = addServer(); ASSERT_TRUE(server != nullptr); ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); ret = reply.readInt32(&ptrsize); EXPECT_EQ(NO_ERROR, ret); RecordProperty("TestPtrSize", sizeof(void *)); RecordProperty("ServerPtrSize", sizeof(void *)); } TEST_F(BinderLibTest, IndirectGetId2) { status_t ret; int32_t id; int32_t count; Parcel data, reply; int32_t serverId[3]; data.writeInt32(ARRAY_SIZE(serverId)); for (size_t i = 0; i < ARRAY_SIZE(serverId); i++) { sp server; BinderLibTestBundle datai; server = addServer(&serverId[i]); ASSERT_TRUE(server != nullptr); data.writeStrongBinder(server); data.writeInt32(BINDER_LIB_TEST_GET_ID_TRANSACTION); datai.appendTo(&data); } ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); ASSERT_EQ(NO_ERROR, ret); ret = reply.readInt32(&id); ASSERT_EQ(NO_ERROR, ret); EXPECT_EQ(0, id); ret = reply.readInt32(&count); ASSERT_EQ(NO_ERROR, ret); EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); for (size_t i = 0; i < (size_t)count; i++) { BinderLibTestBundle replyi(&reply); EXPECT_TRUE(replyi.isValid()); ret = replyi.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); EXPECT_EQ(serverId[i], id); EXPECT_EQ(replyi.dataSize(), replyi.dataPosition()); } EXPECT_EQ(reply.dataSize(), reply.dataPosition()); } TEST_F(BinderLibTest, IndirectGetId3) { status_t ret; int32_t id; int32_t count; Parcel data, reply; int32_t serverId[3]; data.writeInt32(ARRAY_SIZE(serverId)); for (size_t i = 0; i < ARRAY_SIZE(serverId); i++) { sp server; BinderLibTestBundle datai; BinderLibTestBundle datai2; server = addServer(&serverId[i]); ASSERT_TRUE(server != nullptr); data.writeStrongBinder(server); data.writeInt32(BINDER_LIB_TEST_INDIRECT_TRANSACTION); datai.writeInt32(1); datai.writeStrongBinder(m_server); datai.writeInt32(BINDER_LIB_TEST_GET_ID_TRANSACTION); datai2.appendTo(&datai); datai.appendTo(&data); } ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); ASSERT_EQ(NO_ERROR, ret); ret = reply.readInt32(&id); ASSERT_EQ(NO_ERROR, ret); EXPECT_EQ(0, id); ret = reply.readInt32(&count); ASSERT_EQ(NO_ERROR, ret); EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); for (size_t i = 0; i < (size_t)count; i++) { int32_t counti; BinderLibTestBundle replyi(&reply); EXPECT_TRUE(replyi.isValid()); ret = replyi.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); EXPECT_EQ(serverId[i], id); ret = replyi.readInt32(&counti); ASSERT_EQ(NO_ERROR, ret); EXPECT_EQ(1, counti); BinderLibTestBundle replyi2(&replyi); EXPECT_TRUE(replyi2.isValid()); ret = replyi2.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); EXPECT_EQ(0, id); EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition()); EXPECT_EQ(replyi.dataSize(), replyi.dataPosition()); } EXPECT_EQ(reply.dataSize(), reply.dataPosition()); } TEST_F(BinderLibTest, CallBack) { status_t ret; Parcel data, reply; sp callBack = new BinderLibTestCallBack(); data.writeStrongBinder(callBack); ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); ret = callBack->waitEvent(5); EXPECT_EQ(NO_ERROR, ret); ret = callBack->getResult(); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, AddServer) { sp server = addServer(); ASSERT_TRUE(server != nullptr); } TEST_F(BinderLibTest, DeathNotificationNoRefs) { status_t ret; sp testDeathRecipient = new TestDeathRecipient(); { sp binder = addServer(); ASSERT_TRUE(binder != nullptr); ret = binder->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); } IPCThreadState::self()->flushCommands(); ret = testDeathRecipient->waitEvent(5); EXPECT_EQ(NO_ERROR, ret); #if 0 /* Is there an unlink api that does not require a strong reference? */ ret = binder->unlinkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); #endif } TEST_F(BinderLibTest, DeathNotificationWeakRef) { status_t ret; wp wbinder; sp testDeathRecipient = new TestDeathRecipient(); { sp binder = addServer(); ASSERT_TRUE(binder != nullptr); ret = binder->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); wbinder = binder; } IPCThreadState::self()->flushCommands(); ret = testDeathRecipient->waitEvent(5); EXPECT_EQ(NO_ERROR, ret); #if 0 /* Is there an unlink api that does not require a strong reference? */ ret = binder->unlinkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); #endif } TEST_F(BinderLibTest, DeathNotificationStrongRef) { status_t ret; sp sbinder; sp testDeathRecipient = new TestDeathRecipient(); { sp binder = addServer(); ASSERT_TRUE(binder != nullptr); ret = binder->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); sbinder = binder; } { Parcel data, reply; ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(0, ret); } IPCThreadState::self()->flushCommands(); ret = testDeathRecipient->waitEvent(5); EXPECT_EQ(NO_ERROR, ret); ret = sbinder->unlinkToDeath(testDeathRecipient); EXPECT_EQ(DEAD_OBJECT, ret); } TEST_F(BinderLibTest, DeathNotificationMultiple) { status_t ret; const int clientcount = 2; sp target; sp linkedclient[clientcount]; sp callBack[clientcount]; sp passiveclient[clientcount]; target = addServer(); ASSERT_TRUE(target != nullptr); for (int i = 0; i < clientcount; i++) { { Parcel data, reply; linkedclient[i] = addServer(); ASSERT_TRUE(linkedclient[i] != nullptr); callBack[i] = new BinderLibTestCallBack(); data.writeStrongBinder(target); data.writeStrongBinder(callBack[i]); ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); } { Parcel data, reply; passiveclient[i] = addServer(); ASSERT_TRUE(passiveclient[i] != nullptr); data.writeStrongBinder(target); ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); } } { Parcel data, reply; ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(0, ret); } for (int i = 0; i < clientcount; i++) { ret = callBack[i]->waitEvent(5); EXPECT_EQ(NO_ERROR, ret); ret = callBack[i]->getResult(); EXPECT_EQ(NO_ERROR, ret); } } TEST_F(BinderLibTest, DeathNotificationThread) { status_t ret; sp callback; sp target = addServer(); ASSERT_TRUE(target != nullptr); sp client = addServer(); ASSERT_TRUE(client != nullptr); sp testDeathRecipient = new TestDeathRecipient(); ret = target->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); { Parcel data, reply; ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(0, ret); } /* Make sure it's dead */ testDeathRecipient->waitEvent(5); /* Now, pass the ref to another process and ask that process to * call linkToDeath() on it, and wait for a response. This tests * two things: * 1) You still get death notifications when calling linkToDeath() * on a ref that is already dead when it was passed to you. * 2) That death notifications are not directly pushed to the thread * registering them, but to the threadpool (proc workqueue) instead. * * 2) is tested because the thread handling BINDER_LIB_TEST_DEATH_TRANSACTION * is blocked on a condition variable waiting for the death notification to be * called; therefore, that thread is not available for handling proc work. * So, if the death notification was pushed to the thread workqueue, the callback * would never be called, and the test would timeout and fail. * * Note that we can't do this part of the test from this thread itself, because * the binder driver would only push death notifications to the thread if * it is a looper thread, which this thread is not. * * See b/23525545 for details. */ { Parcel data, reply; callback = new BinderLibTestCallBack(); data.writeStrongBinder(target); data.writeStrongBinder(callback); ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); } ret = callback->waitEvent(5); EXPECT_EQ(NO_ERROR, ret); ret = callback->getResult(); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, PassFile) { int ret; int pipefd[2]; uint8_t buf[1] = { 0 }; uint8_t write_value = 123; ret = pipe2(pipefd, O_NONBLOCK); ASSERT_EQ(0, ret); { Parcel data, reply; uint8_t writebuf[1] = { write_value }; ret = data.writeFileDescriptor(pipefd[1], true); EXPECT_EQ(NO_ERROR, ret); ret = data.writeInt32(sizeof(writebuf)); EXPECT_EQ(NO_ERROR, ret); ret = data.write(writebuf, sizeof(writebuf)); EXPECT_EQ(NO_ERROR, ret); ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); } ret = read(pipefd[0], buf, sizeof(buf)); EXPECT_EQ(sizeof(buf), (size_t)ret); EXPECT_EQ(write_value, buf[0]); waitForReadData(pipefd[0], 5000); /* wait for other proccess to close pipe */ ret = read(pipefd[0], buf, sizeof(buf)); EXPECT_EQ(0, ret); close(pipefd[0]); } TEST_F(BinderLibTest, PassParcelFileDescriptor) { const int datasize = 123; std::vector writebuf(datasize); for (size_t i = 0; i < writebuf.size(); ++i) { writebuf[i] = i; } android::base::unique_fd read_end, write_end; { int pipefd[2]; ASSERT_EQ(0, pipe2(pipefd, O_NONBLOCK)); read_end.reset(pipefd[0]); write_end.reset(pipefd[1]); } { Parcel data; EXPECT_EQ(NO_ERROR, data.writeDupParcelFileDescriptor(write_end.get())); write_end.reset(); EXPECT_EQ(NO_ERROR, data.writeInt32(datasize)); EXPECT_EQ(NO_ERROR, data.write(writebuf.data(), datasize)); Parcel reply; EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, data, &reply)); } std::vector readbuf(datasize); EXPECT_EQ(datasize, read(read_end.get(), readbuf.data(), datasize)); EXPECT_EQ(writebuf, readbuf); waitForReadData(read_end.get(), 5000); /* wait for other proccess to close pipe */ EXPECT_EQ(0, read(read_end.get(), readbuf.data(), datasize)); } TEST_F(BinderLibTest, PromoteLocal) { sp strong = new BBinder(); wp weak = strong; sp strong_from_weak = weak.promote(); EXPECT_TRUE(strong != nullptr); EXPECT_EQ(strong, strong_from_weak); strong = nullptr; strong_from_weak = nullptr; strong_from_weak = weak.promote(); EXPECT_TRUE(strong_from_weak == nullptr); } TEST_F(BinderLibTest, PromoteRemote) { int ret; Parcel data, reply; sp strong = new BBinder(); sp server = addServer(); ASSERT_TRUE(server != nullptr); ASSERT_TRUE(strong != nullptr); ret = data.writeWeakBinder(strong); EXPECT_EQ(NO_ERROR, ret); ret = server->transact(BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, data, &reply); EXPECT_GE(ret, 0); } TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { status_t ret; Parcel data, reply; ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); const flat_binder_object *fb = reply.readObject(false); ASSERT_TRUE(fb != nullptr); EXPECT_EQ(BINDER_TYPE_HANDLE, fb->hdr.type); EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle)); EXPECT_EQ((binder_uintptr_t)0, fb->cookie); EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32); } TEST_F(BinderLibTest, FreedBinder) { status_t ret; sp server = addServer(); ASSERT_TRUE(server != nullptr); __u32 freedHandle; wp keepFreedBinder; { Parcel data, reply; data.writeBool(false); /* request weak reference */ ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply); ASSERT_EQ(NO_ERROR, ret); struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data()); freedHandle = freed->handle; /* Add a weak ref to the freed binder so the driver does not * delete its reference to it - otherwise the transaction * fails regardless of whether the driver is fixed. */ keepFreedBinder = reply.readWeakBinder(); } { Parcel data, reply; data.writeStrongBinder(server); /* Replace original handle with handle to the freed binder */ struct flat_binder_object *strong = (struct flat_binder_object *)(data.data()); __u32 oldHandle = strong->handle; strong->handle = freedHandle; ret = server->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply); /* Returns DEAD_OBJECT (-32) if target crashes and * FAILED_TRANSACTION if the driver rejects the invalid * object. */ EXPECT_EQ((status_t)FAILED_TRANSACTION, ret); /* Restore original handle so parcel destructor does not use * the wrong handle. */ strong->handle = oldHandle; } } TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) { status_t ret; Parcel data, reply; sp callBack = new BinderLibTestCallBack(); for (int i = 0; i < 2; i++) { BinderLibTestBundle datai; datai.appendFrom(&data, 0, data.dataSize()); data.freeData(); data.writeInt32(1); data.writeStrongBinder(callBack); data.writeInt32(BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF); datai.appendTo(&data); } ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, OnewayQueueing) { status_t ret; Parcel data, data2; sp pollServer = addPollServer(); sp callBack = new BinderLibTestCallBack(); data.writeStrongBinder(callBack); data.writeInt32(500000); // delay in us before calling back sp callBack2 = new BinderLibTestCallBack(); data2.writeStrongBinder(callBack2); data2.writeInt32(0); // delay in us ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); // The delay ensures that this second transaction will end up on the async_todo list // (for a single-threaded server) ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); // The server will ensure that the two transactions are handled in the expected order; // If the ordering is not as expected, an error will be returned through the callbacks. ret = callBack->waitEvent(2); EXPECT_EQ(NO_ERROR, ret); ret = callBack->getResult(); EXPECT_EQ(NO_ERROR, ret); ret = callBack2->waitEvent(2); EXPECT_EQ(NO_ERROR, ret); ret = callBack2->getResult(); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, WorkSourceUnsetByDefault) { status_t ret; Parcel data, reply; data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); EXPECT_EQ(-1, reply.readInt32()); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, WorkSourceSet) { status_t ret; Parcel data, reply; IPCThreadState::self()->clearCallingWorkSource(); int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100); data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); EXPECT_EQ(100, reply.readInt32()); EXPECT_EQ(-1, previousWorkSource); EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation) { status_t ret; Parcel data, reply; IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100); EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); EXPECT_EQ(-1, reply.readInt32()); EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, WorkSourceCleared) { status_t ret; Parcel data, reply; IPCThreadState::self()->setCallingWorkSourceUid(100); int64_t token = IPCThreadState::self()->clearCallingWorkSource(); int32_t previousWorkSource = (int32_t)token; data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); EXPECT_EQ(-1, reply.readInt32()); EXPECT_EQ(100, previousWorkSource); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, WorkSourceRestored) { status_t ret; Parcel data, reply; IPCThreadState::self()->setCallingWorkSourceUid(100); int64_t token = IPCThreadState::self()->clearCallingWorkSource(); IPCThreadState::self()->restoreCallingWorkSource(token); data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); EXPECT_EQ(100, reply.readInt32()); EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); EXPECT_EQ(NO_ERROR, ret); } TEST_F(BinderLibTest, PropagateFlagSet) { status_t ret; Parcel data, reply; IPCThreadState::self()->clearPropagateWorkSource(); IPCThreadState::self()->setCallingWorkSourceUid(100); EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); } TEST_F(BinderLibTest, PropagateFlagCleared) { status_t ret; Parcel data, reply; IPCThreadState::self()->setCallingWorkSourceUid(100); IPCThreadState::self()->clearPropagateWorkSource(); EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); } TEST_F(BinderLibTest, PropagateFlagRestored) { status_t ret; Parcel data, reply; int token = IPCThreadState::self()->setCallingWorkSourceUid(100); IPCThreadState::self()->restoreCallingWorkSource(token); EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); } TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) { IPCThreadState::self()->setCallingWorkSourceUid(100); Parcel data, reply; status_t ret; data.writeInterfaceToken(binderLibTestServiceName); ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); Parcel data2, reply2; status_t ret2; data2.writeInterfaceToken(binderLibTestServiceName); ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2); EXPECT_EQ(100, reply2.readInt32()); EXPECT_EQ(NO_ERROR, ret2); } TEST_F(BinderLibTest, VectorSent) { Parcel data, reply; sp server = addServer(); ASSERT_TRUE(server != nullptr); std::vector const testValue = { std::numeric_limits::max(), 0, 200 }; data.writeUint64Vector(testValue); status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply); EXPECT_EQ(NO_ERROR, ret); std::vector readValue; ret = reply.readUint64Vector(&readValue); EXPECT_EQ(readValue, testValue); } class BinderLibTestService : public BBinder { public: explicit BinderLibTestService(int32_t id) : m_id(id) , m_nextServerId(id + 1) , m_serverStartRequested(false) , m_callback(nullptr) { pthread_mutex_init(&m_serverWaitMutex, nullptr); pthread_cond_init(&m_serverWaitCond, nullptr); } ~BinderLibTestService() { exit(EXIT_SUCCESS); } void processPendingCall() { if (m_callback != nullptr) { Parcel data; data.writeInt32(NO_ERROR); m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY); m_callback = nullptr; } } virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { //printf("%s: code %d\n", __func__, code); (void)flags; if (getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) { return PERMISSION_DENIED; } switch (code) { case BINDER_LIB_TEST_REGISTER_SERVER: { int32_t id; sp binder; id = data.readInt32(); binder = data.readStrongBinder(); if (binder == nullptr) { return BAD_VALUE; } if (m_id != 0) return INVALID_OPERATION; pthread_mutex_lock(&m_serverWaitMutex); if (m_serverStartRequested) { m_serverStartRequested = false; m_serverStarted = binder; pthread_cond_signal(&m_serverWaitCond); } pthread_mutex_unlock(&m_serverWaitMutex); return NO_ERROR; } case BINDER_LIB_TEST_ADD_POLL_SERVER: case BINDER_LIB_TEST_ADD_SERVER: { int ret; uint8_t buf[1] = { 0 }; int serverid; if (m_id != 0) { return INVALID_OPERATION; } pthread_mutex_lock(&m_serverWaitMutex); if (m_serverStartRequested) { ret = -EBUSY; } else { serverid = m_nextServerId++; m_serverStartRequested = true; bool usePoll = code == BINDER_LIB_TEST_ADD_POLL_SERVER; pthread_mutex_unlock(&m_serverWaitMutex); ret = start_server_process(serverid, usePoll); pthread_mutex_lock(&m_serverWaitMutex); } if (ret > 0) { if (m_serverStartRequested) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 5; ret = pthread_cond_timedwait(&m_serverWaitCond, &m_serverWaitMutex, &ts); } if (m_serverStartRequested) { m_serverStartRequested = false; ret = -ETIMEDOUT; } else { reply->writeStrongBinder(m_serverStarted); reply->writeInt32(serverid); m_serverStarted = nullptr; ret = NO_ERROR; } } else if (ret >= 0) { m_serverStartRequested = false; ret = UNKNOWN_ERROR; } pthread_mutex_unlock(&m_serverWaitMutex); return ret; } case BINDER_LIB_TEST_NOP_TRANSACTION: return NO_ERROR; case BINDER_LIB_TEST_DELAYED_CALL_BACK: { // Note: this transaction is only designed for use with a // poll() server. See comments around epoll_wait(). if (m_callback != nullptr) { // A callback was already pending; this means that // we received a second call while still processing // the first one. Fail the test. sp callback = data.readStrongBinder(); Parcel data2; data2.writeInt32(UNKNOWN_ERROR); callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, nullptr, TF_ONE_WAY); } else { m_callback = data.readStrongBinder(); int32_t delayUs = data.readInt32(); /* * It's necessary that we sleep here, so the next * transaction the caller makes will be queued to * the async queue. */ usleep(delayUs); /* * Now when we return, libbinder will tell the kernel * we are done with this transaction, and the kernel * can move the queued transaction to either the * thread todo worklist (for kernels without the fix), * or the proc todo worklist. In case of the former, * the next outbound call will pick up the pending * transaction, which leads to undesired reentrant * behavior. This is caught in the if() branch above. */ } return NO_ERROR; } case BINDER_LIB_TEST_NOP_CALL_BACK: { Parcel data2, reply2; sp binder; binder = data.readStrongBinder(); if (binder == nullptr) { return BAD_VALUE; } data2.writeInt32(NO_ERROR); binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2); return NO_ERROR; } case BINDER_LIB_TEST_GET_SELF_TRANSACTION: reply->writeStrongBinder(this); return NO_ERROR; case BINDER_LIB_TEST_GET_ID_TRANSACTION: reply->writeInt32(m_id); return NO_ERROR; case BINDER_LIB_TEST_INDIRECT_TRANSACTION: { int32_t count; uint32_t indirect_code; sp binder; count = data.readInt32(); reply->writeInt32(m_id); reply->writeInt32(count); for (int i = 0; i < count; i++) { binder = data.readStrongBinder(); if (binder == nullptr) { return BAD_VALUE; } indirect_code = data.readInt32(); BinderLibTestBundle data2(&data); if (!data2.isValid()) { return BAD_VALUE; } BinderLibTestBundle reply2; binder->transact(indirect_code, data2, &reply2); reply2.appendTo(reply); } return NO_ERROR; } case BINDER_LIB_TEST_SET_ERROR_TRANSACTION: reply->setError(data.readInt32()); return NO_ERROR; case BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION: reply->writeInt32(sizeof(void *)); return NO_ERROR; case BINDER_LIB_TEST_GET_STATUS_TRANSACTION: return NO_ERROR; case BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION: m_strongRef = data.readStrongBinder(); return NO_ERROR; case BINDER_LIB_TEST_LINK_DEATH_TRANSACTION: { int ret; Parcel data2, reply2; sp testDeathRecipient = new TestDeathRecipient(); sp target; sp callback; target = data.readStrongBinder(); if (target == nullptr) { return BAD_VALUE; } callback = data.readStrongBinder(); if (callback == nullptr) { return BAD_VALUE; } ret = target->linkToDeath(testDeathRecipient); if (ret == NO_ERROR) ret = testDeathRecipient->waitEvent(5); data2.writeInt32(ret); callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2); return NO_ERROR; } case BINDER_LIB_TEST_WRITE_FILE_TRANSACTION: { int ret; int32_t size; const void *buf; int fd; fd = data.readFileDescriptor(); if (fd < 0) { return BAD_VALUE; } ret = data.readInt32(&size); if (ret != NO_ERROR) { return ret; } buf = data.readInplace(size); if (buf == nullptr) { return BAD_VALUE; } ret = write(fd, buf, size); if (ret != size) return UNKNOWN_ERROR; return NO_ERROR; } case BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION: { int ret; int32_t size; const void *buf; android::base::unique_fd fd; ret = data.readUniqueParcelFileDescriptor(&fd); if (ret != NO_ERROR) { return ret; } ret = data.readInt32(&size); if (ret != NO_ERROR) { return ret; } buf = data.readInplace(size); if (buf == nullptr) { return BAD_VALUE; } ret = write(fd.get(), buf, size); if (ret != size) return UNKNOWN_ERROR; return NO_ERROR; } case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: { int ret; wp weak; sp strong; Parcel data2, reply2; sp sm = defaultServiceManager(); sp server = sm->getService(binderLibTestServiceName); weak = data.readWeakBinder(); if (weak == nullptr) { return BAD_VALUE; } strong = weak.promote(); ret = server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data2, &reply2); if (ret != NO_ERROR) exit(EXIT_FAILURE); if (strong == nullptr) { reply->setError(1); } return NO_ERROR; } case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION: alarm(10); return NO_ERROR; case BINDER_LIB_TEST_EXIT_TRANSACTION: while (wait(nullptr) != -1 || errno != ECHILD) ; exit(EXIT_SUCCESS); case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: { bool strongRef = data.readBool(); sp binder = new BBinder(); if (strongRef) { reply->writeStrongBinder(binder); } else { reply->writeWeakBinder(binder); } return NO_ERROR; } case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: { data.enforceInterface(binderLibTestServiceName); reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid()); return NO_ERROR; } case BINDER_LIB_TEST_ECHO_VECTOR: { std::vector vector; auto err = data.readUint64Vector(&vector); if (err != NO_ERROR) return err; reply->writeUint64Vector(vector); return NO_ERROR; } default: return UNKNOWN_TRANSACTION; }; } private: int32_t m_id; int32_t m_nextServerId; pthread_mutex_t m_serverWaitMutex; pthread_cond_t m_serverWaitCond; bool m_serverStartRequested; sp m_serverStarted; sp m_strongRef; bool m_callbackPending; sp m_callback; }; int run_server(int index, int readypipefd, bool usePoll) { binderLibTestServiceName += String16(binderserversuffix); status_t ret; sp sm = defaultServiceManager(); BinderLibTestService* testServicePtr; { sp testService = new BinderLibTestService(index); /* * We need this below, but can't hold a sp<> because it prevents the * node from being cleaned up automatically. It's safe in this case * because of how the tests are written. */ testServicePtr = testService.get(); if (index == 0) { ret = sm->addService(binderLibTestServiceName, testService); } else { sp server = sm->getService(binderLibTestServiceName); Parcel data, reply; data.writeInt32(index); data.writeStrongBinder(testService); ret = server->transact(BINDER_LIB_TEST_REGISTER_SERVER, data, &reply); } } write(readypipefd, &ret, sizeof(ret)); close(readypipefd); //printf("%s: ret %d\n", __func__, ret); if (ret) return 1; //printf("%s: joinThreadPool\n", __func__); if (usePoll) { int fd; struct epoll_event ev; int epoll_fd; IPCThreadState::self()->setupPolling(&fd); if (fd < 0) { return 1; } IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) { return 1; } ev.events = EPOLLIN; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { return 1; } while (1) { /* * We simulate a single-threaded process using the binder poll * interface; besides handling binder commands, it can also * issue outgoing transactions, by storing a callback in * m_callback and setting m_callbackPending. * * processPendingCall() will then issue that transaction. */ struct epoll_event events[1]; int numEvents = epoll_wait(epoll_fd, events, 1, 1000); if (numEvents < 0) { if (errno == EINTR) { continue; } return 1; } if (numEvents > 0) { IPCThreadState::self()->handlePolledCommands(); IPCThreadState::self()->flushCommands(); // flush BC_FREE_BUFFER testServicePtr->processPendingCall(); } } } else { ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } //printf("%s: joinThreadPool returned\n", __func__); return 1; /* joinThreadPool should not return */ } int main(int argc, char **argv) { int ret; if (argc == 4 && !strcmp(argv[1], "--servername")) { binderservername = argv[2]; } else { binderservername = argv[0]; } if (argc == 6 && !strcmp(argv[1], binderserverarg)) { binderserversuffix = argv[5]; return run_server(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) == 1); } binderserversuffix = new char[16]; snprintf(binderserversuffix, 16, "%d", getpid()); binderLibTestServiceName += String16(binderserversuffix); ::testing::InitGoogleTest(&argc, argv); binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv()); ProcessState::self()->startThreadPool(); return RUN_ALL_TESTS(); } libs/binder/tests/binderSafeInterfaceTest.cpp0100644 0000000 0000000 00000104663 13756501735 020420 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Weverything" #include #pragma clang diagnostic pop #include #include #include #include #include using namespace std::chrono_literals; // NOLINT - google-build-using-namespace namespace android { namespace tests { enum class TestEnum : uint32_t { INVALID = 0, INITIAL = 1, FINAL = 2, }; // This class serves two purposes: // 1) It ensures that the implementation doesn't require copying or moving the data (for // efficiency purposes) // 2) It tests that Parcelables can be passed correctly class NoCopyNoMove : public Parcelable { public: NoCopyNoMove() = default; explicit NoCopyNoMove(int32_t value) : mValue(value) {} ~NoCopyNoMove() override = default; // Not copyable NoCopyNoMove(const NoCopyNoMove&) = delete; NoCopyNoMove& operator=(const NoCopyNoMove&) = delete; // Not movable NoCopyNoMove(NoCopyNoMove&&) = delete; NoCopyNoMove& operator=(NoCopyNoMove&&) = delete; // Parcelable interface status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); } status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); } int32_t getValue() const { return mValue; } void setValue(int32_t value) { mValue = value; } private: int32_t mValue = 0; uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded }; struct TestFlattenable : Flattenable { TestFlattenable() = default; explicit TestFlattenable(int32_t v) : value(v) {} // Flattenable protocol size_t getFlattenedSize() const { return sizeof(value); } size_t getFdCount() const { return 0; } status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const { FlattenableUtils::write(buffer, size, value); return NO_ERROR; } status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) { FlattenableUtils::read(buffer, size, value); return NO_ERROR; } int32_t value = 0; }; struct TestLightFlattenable : LightFlattenablePod { TestLightFlattenable() = default; explicit TestLightFlattenable(int32_t v) : value(v) {} int32_t value = 0; }; // It seems like this should be able to inherit from TestFlattenable (to avoid duplicating code), // but the SafeInterface logic can't easily be extended to find an indirect Flattenable // base class class TestLightRefBaseFlattenable : public Flattenable, public LightRefBase { public: TestLightRefBaseFlattenable() = default; explicit TestLightRefBaseFlattenable(int32_t v) : value(v) {} // Flattenable protocol size_t getFlattenedSize() const { return sizeof(value); } size_t getFdCount() const { return 0; } status_t flatten(void*& buffer, size_t& size, int*& /*fds*/, size_t& /*count*/) const { FlattenableUtils::write(buffer, size, value); return NO_ERROR; } status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) { FlattenableUtils::read(buffer, size, value); return NO_ERROR; } int32_t value = 0; }; class TestParcelable : public Parcelable { public: TestParcelable() = default; explicit TestParcelable(int32_t value) : mValue(value) {} TestParcelable(const TestParcelable& other) : TestParcelable(other.mValue) {} TestParcelable(TestParcelable&& other) : TestParcelable(other.mValue) {} // Parcelable interface status_t writeToParcel(Parcel* parcel) const override { return parcel->writeInt32(mValue); } status_t readFromParcel(const Parcel* parcel) override { return parcel->readInt32(&mValue); } int32_t getValue() const { return mValue; } void setValue(int32_t value) { mValue = value; } private: int32_t mValue = 0; }; class ExitOnDeath : public IBinder::DeathRecipient { public: ~ExitOnDeath() override = default; void binderDied(const wp& /*who*/) override { ALOG(LOG_INFO, "ExitOnDeath", "Exiting"); exit(0); } }; // This callback class is used to test both one-way transactions and that sp can be // passed correctly class ICallback : public IInterface { public: DECLARE_META_INTERFACE(Callback) enum class Tag : uint32_t { OnCallback = IBinder::FIRST_CALL_TRANSACTION, Last, }; virtual void onCallback(int32_t aPlusOne) = 0; }; class BpCallback : public SafeBpInterface { public: explicit BpCallback(const sp& impl) : SafeBpInterface(impl, getLogTag()) {} void onCallback(int32_t aPlusOne) override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemoteAsync(Tag::OnCallback, aPlusOne); } private: static constexpr const char* getLogTag() { return "BpCallback"; } }; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wexit-time-destructors" IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback"); #pragma clang diagnostic pop class BnCallback : public SafeBnInterface { public: BnCallback() : SafeBnInterface("BnCallback") {} status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/) override { EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION); EXPECT_LT(code, static_cast(ICallback::Tag::Last)); ICallback::Tag tag = static_cast(code); switch (tag) { case ICallback::Tag::OnCallback: { return callLocalAsync(data, reply, &ICallback::onCallback); } case ICallback::Tag::Last: // Should not be possible because of the asserts at the beginning of the method [&]() { FAIL(); }(); return UNKNOWN_ERROR; } } }; class ISafeInterfaceTest : public IInterface { public: DECLARE_META_INTERFACE(SafeInterfaceTest) enum class Tag : uint32_t { SetDeathToken = IBinder::FIRST_CALL_TRANSACTION, ReturnsNoMemory, LogicalNot, ModifyEnum, IncrementFlattenable, IncrementLightFlattenable, IncrementLightRefBaseFlattenable, IncrementNativeHandle, IncrementNoCopyNoMove, IncrementParcelableVector, ToUpper, CallMeBack, IncrementInt32, IncrementUint32, IncrementInt64, IncrementUint64, IncrementFloat, IncrementTwo, Last, }; // This is primarily so that the remote service dies when the test does, but it also serves to // test the handling of sp and non-const methods virtual status_t setDeathToken(const sp& token) = 0; // This is the most basic test since it doesn't require parceling any arguments virtual status_t returnsNoMemory() const = 0; // These are ordered according to their corresponding methods in SafeInterface::ParcelHandler virtual status_t logicalNot(bool a, bool* notA) const = 0; virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0; virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0; virtual status_t increment(const TestLightFlattenable& a, TestLightFlattenable* aPlusOne) const = 0; virtual status_t increment(const sp& a, sp* aPlusOne) const = 0; virtual status_t increment(const sp& a, sp* aPlusOne) const = 0; virtual status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const = 0; virtual status_t increment(const std::vector& a, std::vector* aPlusOne) const = 0; virtual status_t toUpper(const String8& str, String8* upperStr) const = 0; // As mentioned above, sp is already tested by setDeathToken virtual void callMeBack(const sp& callback, int32_t a) const = 0; virtual status_t increment(int32_t a, int32_t* aPlusOne) const = 0; virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0; virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0; virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0; virtual status_t increment(float a, float* aPlusOne) const = 0; // This tests that input/output parameter interleaving works correctly virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const = 0; }; class BpSafeInterfaceTest : public SafeBpInterface { public: explicit BpSafeInterfaceTest(const sp& impl) : SafeBpInterface(impl, getLogTag()) {} status_t setDeathToken(const sp& token) override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::SetDeathToken, token); } status_t returnsNoMemory() const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::ReturnsNoMemory); } status_t logicalNot(bool a, bool* notA) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::LogicalNot, a, notA); } status_t modifyEnum(TestEnum a, TestEnum* b) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::ModifyEnum, a, b); } status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override { using Signature = status_t (ISafeInterfaceTest::*)(const TestFlattenable&, TestFlattenable*) const; ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::IncrementFlattenable, a, aPlusOne); } status_t increment(const TestLightFlattenable& a, TestLightFlattenable* aPlusOne) const override { using Signature = status_t (ISafeInterfaceTest::*)(const TestLightFlattenable&, TestLightFlattenable*) const; ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::IncrementLightFlattenable, a, aPlusOne); } status_t increment(const sp& a, sp* aPlusOne) const override { using Signature = status_t (ISafeInterfaceTest::*)(const sp&, sp*) const; return callRemote(Tag::IncrementLightRefBaseFlattenable, a, aPlusOne); } status_t increment(const sp& a, sp* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(const sp&, sp*) const; return callRemote(Tag::IncrementNativeHandle, a, aPlusOne); } status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const; return callRemote(Tag::IncrementNoCopyNoMove, a, aPlusOne); } status_t increment(const std::vector& a, std::vector* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(const std::vector&, std::vector*); return callRemote(Tag::IncrementParcelableVector, a, aPlusOne); } status_t toUpper(const String8& str, String8* upperStr) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemote(Tag::ToUpper, str, upperStr); } void callMeBack(const sp& callback, int32_t a) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return callRemoteAsync(Tag::CallMeBack, callback, a); } status_t increment(int32_t a, int32_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const; return callRemote(Tag::IncrementInt32, a, aPlusOne); } status_t increment(uint32_t a, uint32_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const; return callRemote(Tag::IncrementUint32, a, aPlusOne); } status_t increment(int64_t a, int64_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const; return callRemote(Tag::IncrementInt64, a, aPlusOne); } status_t increment(uint64_t a, uint64_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const; return callRemote(Tag::IncrementUint64, a, aPlusOne); } status_t increment(float a, float* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const; return callRemote(Tag::IncrementFloat, a, aPlusOne); } status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const; return callRemote(Tag::IncrementTwo, a, aPlusOne, b, bPlusOne); } private: static constexpr const char* getLogTag() { return "BpSafeInterfaceTest"; } }; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wexit-time-destructors" IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest"); static sp getDeathRecipient() { static sp recipient = new ExitOnDeath; return recipient; } #pragma clang diagnostic pop class BnSafeInterfaceTest : public SafeBnInterface { public: BnSafeInterfaceTest() : SafeBnInterface(getLogTag()) {} status_t setDeathToken(const sp& token) override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); token->linkToDeath(getDeathRecipient()); return NO_ERROR; } status_t returnsNoMemory() const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); return NO_MEMORY; } status_t logicalNot(bool a, bool* notA) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *notA = !a; return NO_ERROR; } status_t modifyEnum(TestEnum a, TestEnum* b) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID; return NO_ERROR; } status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); aPlusOne->value = a.value + 1; return NO_ERROR; } status_t increment(const TestLightFlattenable& a, TestLightFlattenable* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); aPlusOne->value = a.value + 1; return NO_ERROR; } status_t increment(const sp& a, sp* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = new TestLightRefBaseFlattenable(a->value + 1); return NO_ERROR; } status_t increment(const sp& a, sp* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); native_handle* rawHandle = native_handle_create(1 /*numFds*/, 1 /*numInts*/); if (rawHandle == nullptr) return NO_MEMORY; // Copy the fd over directly rawHandle->data[0] = dup(a->handle()->data[0]); // Increment the int rawHandle->data[1] = a->handle()->data[1] + 1; // This cannot fail, as it is just the sp taking responsibility for closing // the native_handle when it goes out of scope *aPlusOne = NativeHandle::create(rawHandle, true); return NO_ERROR; } status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); aPlusOne->setValue(a.getValue() + 1); return NO_ERROR; } status_t increment(const std::vector& a, std::vector* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); aPlusOne->resize(a.size()); for (size_t i = 0; i < a.size(); ++i) { (*aPlusOne)[i].setValue(a[i].getValue() + 1); } return NO_ERROR; } status_t toUpper(const String8& str, String8* upperStr) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *upperStr = str; upperStr->toUpper(); return NO_ERROR; } void callMeBack(const sp& callback, int32_t a) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); callback->onCallback(a + 1); } status_t increment(int32_t a, int32_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1; return NO_ERROR; } status_t increment(uint32_t a, uint32_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1; return NO_ERROR; } status_t increment(int64_t a, int64_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1; return NO_ERROR; } status_t increment(uint64_t a, uint64_t* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1; return NO_ERROR; } status_t increment(float a, float* aPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1.0f; return NO_ERROR; } status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1; *bPlusOne = b + 1; return NO_ERROR; } // BnInterface status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/) override { EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION); EXPECT_LT(code, static_cast(Tag::Last)); ISafeInterfaceTest::Tag tag = static_cast(code); switch (tag) { case ISafeInterfaceTest::Tag::SetDeathToken: { return callLocal(data, reply, &ISafeInterfaceTest::setDeathToken); } case ISafeInterfaceTest::Tag::ReturnsNoMemory: { return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory); } case ISafeInterfaceTest::Tag::LogicalNot: { return callLocal(data, reply, &ISafeInterfaceTest::logicalNot); } case ISafeInterfaceTest::Tag::ModifyEnum: { return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum); } case ISafeInterfaceTest::Tag::IncrementFlattenable: { using Signature = status_t (ISafeInterfaceTest::*)(const TestFlattenable& a, TestFlattenable* aPlusOne) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementLightFlattenable: { using Signature = status_t (ISafeInterfaceTest::*)(const TestLightFlattenable& a, TestLightFlattenable* aPlusOne) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementLightRefBaseFlattenable: { using Signature = status_t (ISafeInterfaceTest::*)(const sp&, sp*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementNativeHandle: { using Signature = status_t (ISafeInterfaceTest::*)(const sp&, sp*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementNoCopyNoMove: { using Signature = status_t (ISafeInterfaceTest::*)(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementParcelableVector: { using Signature = status_t (ISafeInterfaceTest::*)(const std::vector&, std::vector*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::ToUpper: { return callLocal(data, reply, &ISafeInterfaceTest::toUpper); } case ISafeInterfaceTest::Tag::CallMeBack: { return callLocalAsync(data, reply, &ISafeInterfaceTest::callMeBack); } case ISafeInterfaceTest::Tag::IncrementInt32: { using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementUint32: { using Signature = status_t (ISafeInterfaceTest::*)(uint32_t, uint32_t*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementInt64: { using Signature = status_t (ISafeInterfaceTest::*)(int64_t, int64_t*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementUint64: { using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementFloat: { using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::IncrementTwo: { using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const; return callLocal(data, reply, &ISafeInterfaceTest::increment); } case ISafeInterfaceTest::Tag::Last: // Should not be possible because of the asserts at the beginning of the method [&]() { FAIL(); }(); return UNKNOWN_ERROR; } } private: static constexpr const char* getLogTag() { return "BnSafeInterfaceTest"; } }; class SafeInterfaceTest : public ::testing::Test { public: SafeInterfaceTest() : mSafeInterfaceTest(getRemoteService()) { ProcessState::self()->startThreadPool(); } ~SafeInterfaceTest() override = default; protected: sp mSafeInterfaceTest; private: static constexpr const char* getLogTag() { return "SafeInterfaceTest"; } sp getRemoteService() { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wexit-time-destructors" static std::mutex sMutex; static sp sService; static sp sDeathToken = new BBinder; #pragma clang diagnostic pop std::unique_lock lock; if (sService == nullptr) { ALOG(LOG_INFO, getLogTag(), "Forking remote process"); pid_t forkPid = fork(); EXPECT_NE(forkPid, -1); const String16 serviceName("SafeInterfaceTest"); if (forkPid == 0) { ALOG(LOG_INFO, getLogTag(), "Remote process checking in"); sp nativeService = new BnSafeInterfaceTest; defaultServiceManager()->addService(serviceName, IInterface::asBinder(nativeService)); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); // We shouldn't get to this point [&]() { FAIL(); }(); } sp binder = defaultServiceManager()->getService(serviceName); sService = interface_cast(binder); EXPECT_TRUE(sService != nullptr); sService->setDeathToken(sDeathToken); } return sService; } }; TEST_F(SafeInterfaceTest, TestReturnsNoMemory) { status_t result = mSafeInterfaceTest->returnsNoMemory(); ASSERT_EQ(NO_MEMORY, result); } TEST_F(SafeInterfaceTest, TestLogicalNot) { const bool a = true; bool notA = true; status_t result = mSafeInterfaceTest->logicalNot(a, ¬A); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(!a, notA); // Test both since we don't want to accidentally catch a default false somewhere const bool b = false; bool notB = false; result = mSafeInterfaceTest->logicalNot(b, ¬B); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(!b, notB); } TEST_F(SafeInterfaceTest, TestModifyEnum) { const TestEnum a = TestEnum::INITIAL; TestEnum b = TestEnum::INVALID; status_t result = mSafeInterfaceTest->modifyEnum(a, &b); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(TestEnum::FINAL, b); } TEST_F(SafeInterfaceTest, TestIncrementFlattenable) { const TestFlattenable a{1}; TestFlattenable aPlusOne{0}; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a.value + 1, aPlusOne.value); } TEST_F(SafeInterfaceTest, TestIncrementLightFlattenable) { const TestLightFlattenable a{1}; TestLightFlattenable aPlusOne{0}; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a.value + 1, aPlusOne.value); } TEST_F(SafeInterfaceTest, TestIncrementLightRefBaseFlattenable) { sp a = new TestLightRefBaseFlattenable{1}; sp aPlusOne; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_NE(nullptr, aPlusOne.get()); ASSERT_EQ(a->value + 1, aPlusOne->value); } namespace { // Anonymous namespace bool fdsAreEquivalent(int a, int b) { struct stat statA {}; struct stat statB {}; if (fstat(a, &statA) != 0) return false; if (fstat(b, &statB) != 0) return false; return (statA.st_dev == statB.st_dev) && (statA.st_ino == statB.st_ino); } } // Anonymous namespace TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) { // Create an fd we can use to send and receive from the remote process base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)}; ASSERT_NE(-1, eventFd); // Determine the maximum number of fds this process can have open struct rlimit limit {}; ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit)); uint32_t maxFds = static_cast(limit.rlim_cur); // Perform this test enough times to rule out fd leaks for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) { native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/); ASSERT_NE(nullptr, handle); handle->data[0] = dup(eventFd.get()); handle->data[1] = 1; // This cannot fail, as it is just the sp taking responsibility for closing // the native_handle when it goes out of scope sp a = NativeHandle::create(handle, true); sp aPlusOne; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_TRUE(fdsAreEquivalent(a->handle()->data[0], aPlusOne->handle()->data[0])); ASSERT_EQ(a->handle()->data[1] + 1, aPlusOne->handle()->data[1]); } } TEST_F(SafeInterfaceTest, TestIncrementNoCopyNoMove) { const NoCopyNoMove a{1}; NoCopyNoMove aPlusOne{0}; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a.getValue() + 1, aPlusOne.getValue()); } TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) { const std::vector a{TestParcelable{1}, TestParcelable{2}}; std::vector aPlusOne; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(a.size(), aPlusOne.size()); for (size_t i = 0; i < a.size(); ++i) { ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue()); } } TEST_F(SafeInterfaceTest, TestToUpper) { const String8 str{"Hello, world!"}; String8 upperStr; status_t result = mSafeInterfaceTest->toUpper(str, &upperStr); ASSERT_EQ(NO_ERROR, result); ASSERT_TRUE(upperStr == String8{"HELLO, WORLD!"}); } TEST_F(SafeInterfaceTest, TestCallMeBack) { class CallbackReceiver : public BnCallback { public: void onCallback(int32_t aPlusOne) override { ALOG(LOG_INFO, "CallbackReceiver", "%s", __PRETTY_FUNCTION__); std::unique_lock lock(mMutex); mValue = aPlusOne; mCondition.notify_one(); } std::optional waitForCallback() { std::unique_lock lock(mMutex); bool success = mCondition.wait_for(lock, 100ms, [&]() { return static_cast(mValue); }); return success ? mValue : std::nullopt; } private: std::mutex mMutex; std::condition_variable mCondition; std::optional mValue; }; sp receiver = new CallbackReceiver; const int32_t a = 1; mSafeInterfaceTest->callMeBack(receiver, a); auto result = receiver->waitForCallback(); ASSERT_TRUE(result); ASSERT_EQ(a + 1, *result); } TEST_F(SafeInterfaceTest, TestIncrementInt32) { const int32_t a = 1; int32_t aPlusOne = 0; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a + 1, aPlusOne); } TEST_F(SafeInterfaceTest, TestIncrementUint32) { const uint32_t a = 1; uint32_t aPlusOne = 0; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a + 1, aPlusOne); } TEST_F(SafeInterfaceTest, TestIncrementInt64) { const int64_t a = 1; int64_t aPlusOne = 0; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a + 1, aPlusOne); } TEST_F(SafeInterfaceTest, TestIncrementUint64) { const uint64_t a = 1; uint64_t aPlusOne = 0; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a + 1, aPlusOne); } TEST_F(SafeInterfaceTest, TestIncrementFloat) { const float a = 1.0f; float aPlusOne = 0.0f; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a + 1.0f, aPlusOne); } TEST_F(SafeInterfaceTest, TestIncrementTwo) { const int32_t a = 1; int32_t aPlusOne = 0; const int32_t b = 2; int32_t bPlusOne = 0; status_t result = mSafeInterfaceTest->increment(1, &aPlusOne, 2, &bPlusOne); ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a + 1, aPlusOne); ASSERT_EQ(b + 1, bPlusOne); } } // namespace tests } // namespace android libs/binder/tests/binderTextOutputTest.cpp0100644 0000000 0000000 00000010676 13756501735 020066 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include "android-base/file.h" #include "android-base/test_utils.h" #include #include #include #include static void CheckMessage(CapturedStderr& cap, const char* expected, bool singleline) { cap.Stop(); std::string output = cap.str(); if (singleline) output.erase(std::remove(output.begin(), output.end(), '\n')); ASSERT_EQ(output, expected); } #define CHECK_LOG_(input, expect, singleline) \ { \ CapturedStderr cap; \ android::aerr << input << android::endl; \ CheckMessage(cap, expect, singleline); \ } \ #define CHECK_VAL_(val, singleline) \ { \ std::stringstream ss; \ ss << val; \ std::string s = ss.str(); \ CHECK_LOG_(val, s.c_str(), singleline); \ } \ #define CHECK_LOG(input, expect) CHECK_LOG_(input, expect, true) #define CHECK_VAL(val) CHECK_VAL_(val, true) TEST(TextOutput, HandlesStdEndl) { CapturedStderr cap; android::aerr << "foobar" << std::endl; cap.Stop(); ASSERT_EQ(cap.str(), "foobar\n"); } TEST(TextOutput, HandlesCEndl) { CapturedStderr cap; android::aerr << "foobar" << "\n"; cap.Stop(); ASSERT_EQ(cap.str(), "foobar\n"); } TEST(TextOutput, HandlesAndroidEndl) { CapturedStderr cap; android::aerr << "foobar" << android::endl; cap.Stop(); ASSERT_EQ(cap.str(), "foobar\n"); } TEST(TextOutput, HandleEmptyString) { CHECK_LOG("", ""); } TEST(TextOutput, HandleString) { CHECK_LOG("foobar", "foobar"); } TEST(TextOutput, HandleNum) { CHECK_LOG(12345, "12345"); } TEST(TextOutput, HandleBool) { CHECK_LOG(false, "false"); } TEST(TextOutput, HandleChar) { CHECK_LOG('T', "T"); } TEST(TextOutput, HandleParcel) { android::Parcel val; CHECK_LOG(val, "Parcel(NULL)"); } TEST(TextOutput, HandleHexDump) { const char buf[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; android::HexDump val(buf, sizeof(buf)); CHECK_LOG(val, "03020100 07060504 0b0a0908 0f0e0d0c '................'"); } TEST(TextOutput, HandleHexDumpCustom) { const char buf[4] = {0x11,0x22,0x33,0x44}; android::HexDump val(buf, sizeof(buf), 4); CHECK_LOG(val, "11 22 33 44 '.\"3D'"); } TEST(TextOutput, HandleTypeCode) { android::TypeCode val(1234); CHECK_LOG(val, "'\\x04\\xd2'"); } TEST(TextOutput, HandleCookie) { int32_t val = 321; //0x141 CHECK_LOG((void*)(long)val, "0x141"); } TEST(TextOutput, HandleString8) { android::String8 val("foobar"); CHECK_LOG(val, "foobar"); } TEST(TextOutput, HandleString16) { android::String16 val("foobar"); CHECK_LOG(val, "foobar"); } template class TextTest : public testing::Test {}; typedef testing::Types TestTypes; TYPED_TEST_CASE(TextTest, TestTypes); TYPED_TEST(TextTest, TextMax) { TypeParam max = std::numeric_limits::max(); CHECK_VAL(max); } TYPED_TEST(TextTest, TestMin) { TypeParam min = std::numeric_limits::min(); CHECK_VAL(min); } TYPED_TEST(TextTest, TestDenom) { TypeParam min = std::numeric_limits::denorm_min(); CHECK_VAL(min); } TYPED_TEST(TextTest, TestEpsilon) { TypeParam eps = std::numeric_limits::epsilon(); CHECK_VAL(eps); } libs/binder/tests/binderThroughputTest.cpp0100644 0000000 0000000 00000031073 13756501735 020064 0ustar000000000 0000000 #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace android; enum BinderWorkerServiceCode { BINDER_NOP = IBinder::FIRST_CALL_TRANSACTION, }; #define ASSERT_TRUE(cond) \ do { \ if (!(cond)) {\ cerr << __func__ << ":" << __LINE__ << " condition:" << #cond << " failed\n" << endl; \ exit(EXIT_FAILURE); \ } \ } while (0) class BinderWorkerService : public BBinder { public: BinderWorkerService() {} ~BinderWorkerService() {} virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { (void)flags; (void)data; (void)reply; switch (code) { case BINDER_NOP: return NO_ERROR; default: return UNKNOWN_TRANSACTION; }; } }; class Pipe { int m_readFd; int m_writeFd; Pipe(int readFd, int writeFd) : m_readFd{readFd}, m_writeFd{writeFd} {} Pipe(const Pipe &) = delete; Pipe& operator=(const Pipe &) = delete; Pipe& operator=(const Pipe &&) = delete; public: Pipe(Pipe&& rval) noexcept { m_readFd = rval.m_readFd; m_writeFd = rval.m_writeFd; rval.m_readFd = 0; rval.m_writeFd = 0; } ~Pipe() { if (m_readFd) close(m_readFd); if (m_writeFd) close(m_writeFd); } void signal() { bool val = true; int error = write(m_writeFd, &val, sizeof(val)); ASSERT_TRUE(error >= 0); }; void wait() { bool val = false; int error = read(m_readFd, &val, sizeof(val)); ASSERT_TRUE(error >= 0); } template void send(const T& v) { int error = write(m_writeFd, &v, sizeof(T)); ASSERT_TRUE(error >= 0); } template void recv(T& v) { int error = read(m_readFd, &v, sizeof(T)); ASSERT_TRUE(error >= 0); } static tuple createPipePair() { int a[2]; int b[2]; int error1 = pipe(a); int error2 = pipe(b); ASSERT_TRUE(error1 >= 0); ASSERT_TRUE(error2 >= 0); return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1])); } }; static const uint32_t num_buckets = 128; static uint64_t max_time_bucket = 50ull * 1000000; static uint64_t time_per_bucket = max_time_bucket / num_buckets; struct ProcResults { uint64_t m_worst = 0; uint32_t m_buckets[num_buckets] = {0}; uint64_t m_transactions = 0; uint64_t m_long_transactions = 0; uint64_t m_total_time = 0; uint64_t m_best = max_time_bucket; void add_time(uint64_t time) { if (time > max_time_bucket) { m_long_transactions++; } m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1; m_best = min(time, m_best); m_worst = max(time, m_worst); m_transactions += 1; m_total_time += time; } static ProcResults combine(const ProcResults& a, const ProcResults& b) { ProcResults ret; for (int i = 0; i < num_buckets; i++) { ret.m_buckets[i] = a.m_buckets[i] + b.m_buckets[i]; } ret.m_worst = max(a.m_worst, b.m_worst); ret.m_best = min(a.m_best, b.m_best); ret.m_transactions = a.m_transactions + b.m_transactions; ret.m_long_transactions = a.m_long_transactions + b.m_long_transactions; ret.m_total_time = a.m_total_time + b.m_total_time; return ret; } void dump() { if (m_long_transactions > 0) { cout << (double)m_long_transactions / m_transactions << "% of transactions took longer " "than estimated max latency. Consider setting -m to be higher than " << m_worst / 1000 << " microseconds" << endl; } double best = (double)m_best / 1.0E6; double worst = (double)m_worst / 1.0E6; double average = (double)m_total_time / m_transactions / 1.0E6; cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl; uint64_t cur_total = 0; float time_per_bucket_ms = time_per_bucket / 1.0E6; for (int i = 0; i < num_buckets; i++) { float cur_time = time_per_bucket_ms * i + 0.5f * time_per_bucket_ms; if ((cur_total < 0.5f * m_transactions) && (cur_total + m_buckets[i] >= 0.5f * m_transactions)) { cout << "50%: " << cur_time << " "; } if ((cur_total < 0.9f * m_transactions) && (cur_total + m_buckets[i] >= 0.9f * m_transactions)) { cout << "90%: " << cur_time << " "; } if ((cur_total < 0.95f * m_transactions) && (cur_total + m_buckets[i] >= 0.95f * m_transactions)) { cout << "95%: " << cur_time << " "; } if ((cur_total < 0.99f * m_transactions) && (cur_total + m_buckets[i] >= 0.99f * m_transactions)) { cout << "99%: " << cur_time << " "; } cur_total += m_buckets[i]; } cout << endl; } }; String16 generateServiceName(int num) { char num_str[32]; snprintf(num_str, sizeof(num_str), "%d", num); String16 serviceName = String16("binderWorker") + String16(num_str); return serviceName; } void worker_fx(int num, int worker_count, int iterations, int payload_size, bool cs_pair, Pipe p) { // Create BinderWorkerService and for go. ProcessState::self()->startThreadPool(); sp serviceMgr = defaultServiceManager(); sp service = new BinderWorkerService; serviceMgr->addService(generateServiceName(num), service); srand(num); p.signal(); p.wait(); // If client/server pairs, then half the workers are // servers and half are clients int server_count = cs_pair ? worker_count / 2 : worker_count; // Get references to other binder services. cout << "Created BinderWorker" << num << endl; (void)worker_count; vector > workers; for (int i = 0; i < server_count; i++) { if (num == i) continue; workers.push_back(serviceMgr->getService(generateServiceName(i))); } // Run the benchmark if client ProcResults results; chrono::time_point start, end; for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) { Parcel data, reply; int target = cs_pair ? num % server_count : rand() % workers.size(); int sz = payload_size; while (sz >= sizeof(uint32_t)) { data.writeInt32(0); sz -= sizeof(uint32_t); } start = chrono::high_resolution_clock::now(); status_t ret = workers[target]->transact(BINDER_NOP, data, &reply); end = chrono::high_resolution_clock::now(); uint64_t cur_time = uint64_t(chrono::duration_cast(end - start).count()); results.add_time(cur_time); if (ret != NO_ERROR) { cout << "thread " << num << " failed " << ret << "i : " << i << endl; exit(EXIT_FAILURE); } } // Signal completion to master and wait. p.signal(); p.wait(); // Send results to master and wait for go to exit. p.send(results); p.wait(); exit(EXIT_SUCCESS); } Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bool cs_pair) { auto pipe_pair = Pipe::createPipePair(); pid_t pid = fork(); if (pid) { /* parent */ return move(get<0>(pipe_pair)); } else { /* child */ worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair))); /* never get here */ return move(get<0>(pipe_pair)); } } void wait_all(vector& v) { for (int i = 0; i < v.size(); i++) { v[i].wait(); } } void signal_all(vector& v) { for (int i = 0; i < v.size(); i++) { v[i].signal(); } } void run_main(int iterations, int workers, int payload_size, int cs_pair, bool training_round=false) { vector pipes; // Create all the workers and wait for them to spawn. for (int i = 0; i < workers; i++) { pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair)); } wait_all(pipes); // Run the workers and wait for completion. chrono::time_point start, end; cout << "waiting for workers to complete" << endl; start = chrono::high_resolution_clock::now(); signal_all(pipes); wait_all(pipes); end = chrono::high_resolution_clock::now(); // Calculate overall throughput. double iterations_per_sec = double(iterations * workers) / (chrono::duration_cast(end - start).count() / 1.0E9); cout << "iterations per sec: " << iterations_per_sec << endl; // Collect all results from the workers. cout << "collecting results" << endl; signal_all(pipes); ProcResults tot_results; for (int i = 0; i < workers; i++) { ProcResults tmp_results; pipes[i].recv(tmp_results); tot_results = ProcResults::combine(tot_results, tmp_results); } // Kill all the workers. cout << "killing workers" << endl; signal_all(pipes); for (int i = 0; i < workers; i++) { int status; wait(&status); if (status != 0) { cout << "nonzero child status" << status << endl; } } if (training_round) { // sets max_time_bucket to 2 * m_worst from the training round. // Also needs to adjust time_per_bucket accordingly. max_time_bucket = 2 * tot_results.m_worst; time_per_bucket = max_time_bucket / num_buckets; cout << "Max latency during training: " << tot_results.m_worst / 1.0E6 << "ms" << endl; } else { tot_results.dump(); } } int main(int argc, char *argv[]) { int workers = 2; int iterations = 10000; int payload_size = 0; bool cs_pair = false; bool training_round = false; (void)argc; (void)argv; // Parse arguments. for (int i = 1; i < argc; i++) { if (string(argv[i]) == "--help") { cout << "Usage: binderThroughputTest [OPTIONS]" << endl; cout << "\t-i N : Specify number of iterations." << endl; cout << "\t-m N : Specify expected max latency in microseconds." << endl; cout << "\t-p : Split workers into client/server pairs." << endl; cout << "\t-s N : Specify payload size." << endl; cout << "\t-t N : Run training round." << endl; cout << "\t-w N : Specify total number of workers." << endl; return 0; } if (string(argv[i]) == "-w") { workers = atoi(argv[i+1]); i++; continue; } if (string(argv[i]) == "-i") { iterations = atoi(argv[i+1]); i++; continue; } if (string(argv[i]) == "-s") { payload_size = atoi(argv[i+1]); i++; } if (string(argv[i]) == "-p") { // client/server pairs instead of spreading // requests to all workers. If true, half // the workers become clients and half servers cs_pair = true; } if (string(argv[i]) == "-t") { // Run one training round before actually collecting data // to get an approximation of max latency. training_round = true; } if (string(argv[i]) == "-m") { // Caller specified the max latency in microseconds. // No need to run training round in this case. if (atoi(argv[i+1]) > 0) { max_time_bucket = strtoull(argv[i+1], (char **)nullptr, 10) * 1000; time_per_bucket = max_time_bucket / num_buckets; i++; } else { cout << "Max latency -m must be positive." << endl; exit(EXIT_FAILURE); } } } if (training_round) { cout << "Start training round" << endl; run_main(iterations, workers, payload_size, cs_pair, training_round=true); cout << "Completed training round" << endl << endl; } run_main(iterations, workers, payload_size, cs_pair); return 0; } libs/binder/tests/binderValueTypeTest.cpp0100644 0000000 0000000 00000007753 13756501735 017641 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include #include "android-base/file.h" #include #include #include #include using ::android::binder::Value; using ::android::os::PersistableBundle; using ::android::String16; using ::std::vector; #define VALUE_TYPE_TEST(T, TYPENAME, VAL) \ TEST(ValueType, Handles ## TYPENAME) { \ T x = VAL; \ T y = T(); \ Value value = VAL; \ ASSERT_FALSE(value.empty()); \ ASSERT_TRUE(value.is ## TYPENAME ()); \ ASSERT_TRUE(value.get ## TYPENAME (&y)); \ ASSERT_EQ(x, y); \ ASSERT_EQ(value, Value(y)); \ value.put ## TYPENAME (x); \ ASSERT_EQ(value, Value(y)); \ value = Value(); \ ASSERT_TRUE(value.empty()); \ ASSERT_NE(value, Value(y)); \ value = y; \ ASSERT_EQ(value, Value(x)); \ } #define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL) \ TEST(ValueType, Handles ## TYPENAME ## Vector) { \ vector x; \ vector y; \ x.push_back(VAL); \ x.push_back(T()); \ Value value(x); \ ASSERT_FALSE(value.empty()); \ ASSERT_TRUE(value.is ## TYPENAME ## Vector()); \ ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \ ASSERT_EQ(x, y); \ ASSERT_EQ(value, Value(y)); \ value.put ## TYPENAME ## Vector(x); \ ASSERT_EQ(value, Value(y)); \ value = Value(); \ ASSERT_TRUE(value.empty()); \ ASSERT_NE(value, Value(y)); \ value = y; \ ASSERT_EQ(value, Value(x)); \ } VALUE_TYPE_TEST(bool, Boolean, true) VALUE_TYPE_TEST(int32_t, Int, 31337) VALUE_TYPE_TEST(int64_t, Long, 13370133701337L) VALUE_TYPE_TEST(double, Double, 3.14159265358979323846) VALUE_TYPE_TEST(String16, String, String16("Lovely")) VALUE_TYPE_VECTOR_TEST(bool, Boolean, true) VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337) VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337L) VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846) VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely")) VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle()) TEST(ValueType, HandlesClear) { Value value; ASSERT_TRUE(value.empty()); value.putInt(31337); ASSERT_FALSE(value.empty()); value.clear(); ASSERT_TRUE(value.empty()); } TEST(ValueType, HandlesSwap) { Value value_a, value_b; int32_t int_x; value_a.putInt(31337); ASSERT_FALSE(value_a.empty()); ASSERT_TRUE(value_b.empty()); value_a.swap(value_b); ASSERT_FALSE(value_b.empty()); ASSERT_TRUE(value_a.empty()); ASSERT_TRUE(value_b.getInt(&int_x)); ASSERT_EQ(31337, int_x); } libs/binder/tests/schd-dbg.cpp0100644 0000000 0000000 00000034172 13756501735 015345 0ustar000000000 0000000 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace android; enum BinderWorkerServiceCode { BINDER_NOP = IBinder::FIRST_CALL_TRANSACTION, }; #define ASSERT(cond) \ do { \ if (!(cond)) { \ cerr << __func__ << ":" << __LINE__ << " condition:" << #cond \ << " failed\n" \ << endl; \ exit(EXIT_FAILURE); \ } \ } while (0) vector > workers; // the ratio that the service is synced on the same cpu beyond // GOOD_SYNC_MIN is considered as good #define GOOD_SYNC_MIN (0.6) #define DUMP_PRESICION 2 string trace_path = "/sys/kernel/debug/tracing"; // the default value int no_process = 2; int iterations = 100; int payload_size = 16; int no_inherent = 0; int no_sync = 0; int verbose = 0; int trace; bool traceIsOn() { fstream file; file.open(trace_path + "/tracing_on", ios::in); char on; file >> on; file.close(); return on == '1'; } void traceStop() { ofstream file; file.open(trace_path + "/tracing_on", ios::out | ios::trunc); file << '0' << endl; file.close(); } // the deadline latency that we are interested in uint64_t deadline_us = 2500; int thread_pri() { struct sched_param param; int policy; ASSERT(!pthread_getschedparam(pthread_self(), &policy, ¶m)); return param.sched_priority; } void thread_dump(const char* prefix) { struct sched_param param; int policy; if (!verbose) return; cout << "--------------------------------------------------" << endl; cout << setw(12) << left << prefix << " pid: " << getpid() << " tid: " << gettid() << " cpu: " << sched_getcpu() << endl; ASSERT(!pthread_getschedparam(pthread_self(), &policy, ¶m)); string s = (policy == SCHED_OTHER) ? "SCHED_OTHER" : (policy == SCHED_FIFO) ? "SCHED_FIFO" : (policy == SCHED_RR) ? "SCHED_RR" : "???"; cout << setw(12) << left << s << param.sched_priority << endl; return; } class BinderWorkerService : public BBinder { public: BinderWorkerService() { } ~BinderWorkerService() { } virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { (void)flags; (void)data; (void)reply; switch (code) { // The transaction format is like // // data[in]: int32: caller priority // int32: caller cpu // // reply[out]: int32: 1 if caller's priority != callee's priority // int32: 1 if caller's cpu != callee's cpu // // note the caller cpu read here is not always correct // there're still chances that the caller got switched out // right after it read the cpu number and still before the transaction. case BINDER_NOP: { thread_dump("binder"); int priority = thread_pri(); int priority_caller = data.readInt32(); int h = 0, s = 0; if (priority_caller != priority) { h++; if (verbose) { cout << "err priority_caller:" << priority_caller << ", priority:" << priority << endl; } } if (priority == sched_get_priority_max(SCHED_FIFO)) { int cpu = sched_getcpu(); int cpu_caller = data.readInt32(); if (cpu != cpu_caller) { s++; } } reply->writeInt32(h); reply->writeInt32(s); return NO_ERROR; } default: return UNKNOWN_TRANSACTION; }; } }; class Pipe { int m_readFd; int m_writeFd; Pipe(int readFd, int writeFd) : m_readFd{readFd}, m_writeFd{writeFd} { } Pipe(const Pipe&) = delete; Pipe& operator=(const Pipe&) = delete; Pipe& operator=(const Pipe&&) = delete; public: Pipe(Pipe&& rval) noexcept { m_readFd = rval.m_readFd; m_writeFd = rval.m_writeFd; rval.m_readFd = 0; rval.m_writeFd = 0; } ~Pipe() { if (m_readFd) close(m_readFd); if (m_writeFd) close(m_writeFd); } void signal() { bool val = true; int error = write(m_writeFd, &val, sizeof(val)); ASSERT(error >= 0); }; void wait() { bool val = false; int error = read(m_readFd, &val, sizeof(val)); ASSERT(error >= 0); } template void send(const T& v) { int error = write(m_writeFd, &v, sizeof(T)); ASSERT(error >= 0); } template void recv(T& v) { int error = read(m_readFd, &v, sizeof(T)); ASSERT(error >= 0); } static tuple createPipePair() { int a[2]; int b[2]; int error1 = pipe(a); int error2 = pipe(b); ASSERT(error1 >= 0); ASSERT(error2 >= 0); return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1])); } }; typedef chrono::time_point Tick; static inline Tick tickNow() { return chrono::high_resolution_clock::now(); } static inline uint64_t tickNano(Tick& sta, Tick& end) { return uint64_t(chrono::duration_cast(end - sta).count()); } struct Results { uint64_t m_best = 0xffffffffffffffffULL; uint64_t m_worst = 0; uint64_t m_transactions = 0; uint64_t m_total_time = 0; uint64_t m_miss = 0; bool tracing; explicit Results(bool _tracing) : tracing(_tracing) { } inline bool miss_deadline(uint64_t nano) { return nano > deadline_us * 1000; } void add_time(uint64_t nano) { m_best = min(nano, m_best); m_worst = max(nano, m_worst); m_transactions += 1; m_total_time += nano; if (miss_deadline(nano)) m_miss++; if (miss_deadline(nano) && tracing) { // There might be multiple process pair running the test concurrently // each may execute following statements and only the first one actually // stop the trace and any traceStop() afterthen has no effect. traceStop(); cout << endl; cout << "deadline triggered: halt & stop trace" << endl; cout << "log:" + trace_path + "/trace" << endl; cout << endl; exit(1); } } void dump() { double best = (double)m_best / 1.0E6; double worst = (double)m_worst / 1.0E6; double average = (double)m_total_time / m_transactions / 1.0E6; // FIXME: libjson? int W = DUMP_PRESICION + 2; cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left << average << ",\"wst\":" << setw(W) << left << worst << ",\"bst\":" << setw(W) << left << best << ",\"miss\":" << left << m_miss << ",\"meetR\":" << left << setprecision(DUMP_PRESICION + 3) << (1.0 - (double)m_miss / m_transactions) << "}"; } }; String16 generateServiceName(int num) { char num_str[32]; snprintf(num_str, sizeof(num_str), "%d", num); String16 serviceName = String16("binderWorker") + String16(num_str); return serviceName; } static void parcel_fill(Parcel& data, int sz, int priority, int cpu) { ASSERT(sz >= (int)sizeof(uint32_t) * 2); data.writeInt32(priority); data.writeInt32(cpu); sz -= sizeof(uint32_t); while (sz > (int)sizeof(uint32_t)) { data.writeInt32(0); sz -= sizeof(uint32_t); } } typedef struct { void* result; int target; } thread_priv_t; static void* thread_start(void* p) { thread_priv_t* priv = (thread_priv_t*)p; int target = priv->target; Results* results_fifo = (Results*)priv->result; Parcel data, reply; Tick sta, end; parcel_fill(data, payload_size, thread_pri(), sched_getcpu()); thread_dump("fifo-caller"); sta = tickNow(); status_t ret = workers[target]->transact(BINDER_NOP, data, &reply); end = tickNow(); results_fifo->add_time(tickNano(sta, end)); no_inherent += reply.readInt32(); no_sync += reply.readInt32(); return nullptr; } // create a fifo thread to transact and wait it to finished static void thread_transaction(int target, Results* results_fifo) { thread_priv_t thread_priv; void* dummy; pthread_t thread; pthread_attr_t attr; struct sched_param param; thread_priv.target = target; thread_priv.result = results_fifo; ASSERT(!pthread_attr_init(&attr)); ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO)); param.sched_priority = sched_get_priority_max(SCHED_FIFO); ASSERT(!pthread_attr_setschedparam(&attr, ¶m)); ASSERT(!pthread_create(&thread, &attr, &thread_start, &thread_priv)); ASSERT(!pthread_join(thread, &dummy)); } #define is_client(_num) ((_num) >= (no_process / 2)) void worker_fx(int num, int no_process, int iterations, int payload_size, Pipe p) { int dummy; Results results_other(false), results_fifo(trace); // Create BinderWorkerService and for go. ProcessState::self()->startThreadPool(); sp serviceMgr = defaultServiceManager(); sp service = new BinderWorkerService; serviceMgr->addService(generateServiceName(num), service); // init done p.signal(); // wait for kick-off p.wait(); // If client/server pairs, then half the workers are // servers and half are clients int server_count = no_process / 2; for (int i = 0; i < server_count; i++) { // self service is in-process so just skip if (num == i) continue; workers.push_back(serviceMgr->getService(generateServiceName(i))); } // Client for each pair iterates here // each iterations contains exatcly 2 transactions for (int i = 0; is_client(num) && i < iterations; i++) { Parcel data, reply; Tick sta, end; // the target is paired to make it easier to diagnose int target = num % server_count; // 1. transaction by fifo thread thread_transaction(target, &results_fifo); parcel_fill(data, payload_size, thread_pri(), sched_getcpu()); thread_dump("other-caller"); // 2. transaction by other thread sta = tickNow(); ASSERT(NO_ERROR == workers[target]->transact(BINDER_NOP, data, &reply)); end = tickNow(); results_other.add_time(tickNano(sta, end)); no_inherent += reply.readInt32(); no_sync += reply.readInt32(); } // Signal completion to master and wait. p.signal(); p.wait(); p.send(&dummy); // wait for kill p.wait(); // Client for each pair dump here if (is_client(num)) { int no_trans = iterations * 2; double sync_ratio = (1.0 - (double)no_sync / no_trans); // FIXME: libjson? cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\"" << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\"," << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << "," << "\"R\":" << sync_ratio << "," << endl; cout << " \"other_ms\":"; results_other.dump(); cout << "," << endl; cout << " \"fifo_ms\": "; results_fifo.dump(); cout << endl; cout << "}," << endl; } exit(no_inherent); } Pipe make_process(int num, int iterations, int no_process, int payload_size) { auto pipe_pair = Pipe::createPipePair(); pid_t pid = fork(); if (pid) { // parent return move(get<0>(pipe_pair)); } else { // child thread_dump(is_client(num) ? "client" : "server"); worker_fx(num, no_process, iterations, payload_size, move(get<1>(pipe_pair))); // never get here return move(get<0>(pipe_pair)); } } void wait_all(vector& v) { for (size_t i = 0; i < v.size(); i++) { v[i].wait(); } } void signal_all(vector& v) { for (size_t i = 0; i < v.size(); i++) { v[i].signal(); } } // This test is modified from binderThroughputTest.cpp int main(int argc, char** argv) { for (int i = 1; i < argc; i++) { if (string(argv[i]) == "-i") { iterations = atoi(argv[i + 1]); i++; continue; } if (string(argv[i]) == "-pair") { no_process = 2 * atoi(argv[i + 1]); i++; continue; } if (string(argv[i]) == "-deadline_us") { deadline_us = atoi(argv[i + 1]); i++; continue; } if (string(argv[i]) == "-v") { verbose = 1; } // The -trace argument is used like that: // // First start trace with atrace command as usual // >atrace --async_start sched freq // // then use schd-dbg with -trace arguments //./schd-dbg -trace -deadline_us 2500 // // This makes schd-dbg to stop trace once it detects a transaction // duration over the deadline. By writing '0' to // /sys/kernel/debug/tracing and halt the process. The tracelog is // then available on /sys/kernel/debug/trace if (string(argv[i]) == "-trace") { trace = 1; } } if (trace && !traceIsOn()) { cout << "trace is not running" << endl; cout << "check " << trace_path + "/tracing_on" << endl; cout << "use atrace --async_start first" << endl; exit(-1); } vector pipes; thread_dump("main"); // FIXME: libjson? cout << "{" << endl; cout << "\"cfg\":{\"pair\":" << (no_process / 2) << ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us << "}," << endl; // the main process fork 2 processes for each pairs // 1 server + 1 client // each has a pipe to communicate with for (int i = 0; i < no_process; i++) { pipes.push_back(make_process(i, iterations, no_process, payload_size)); } // wait for init done wait_all(pipes); // kick-off iterations signal_all(pipes); // wait for completion wait_all(pipes); // start to send result signal_all(pipes); for (int i = 0; i < no_process; i++) { int status; // kill pipes[i].signal(); wait(&status); // the exit status is number of transactions without priority inheritance // detected in the child process no_inherent += status; } // FIXME: libjson? cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"") << endl; cout << "}" << endl; return -no_inherent; } libs/binderthreadstate/0040755 0000000 0000000 00000000000 13756501735 014251 5ustar000000000 0000000 libs/binderthreadstate/Android.bp0100644 0000000 0000000 00000002164 13756501735 016154 0ustar000000000 0000000 // Copyright (C) 2018 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. cc_library { name: "libbinderthreadstate", recovery_available: true, vendor_available: false, vndk: { enabled: true, support_system_process: true, }, srcs: [ "IPCThreadStateBase.cpp", ], header_libs: [ "libbase_headers", "libutils_headers", ], shared_libs: [ "liblog", ], export_include_dirs: ["include"], sanitize: { misc_undefined: ["integer"], }, cflags: [ "-Wall", "-Werror", ], } libs/binderthreadstate/IPCThreadStateBase.cpp0100644 0000000 0000000 00000004723 13756501735 020317 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #define LOG_TAG "IPCThreadStateBase" #include #include #include #include #include #include namespace android { static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; static bool gHaveTLS = false; static pthread_key_t gTLS = 0; IPCThreadStateBase::IPCThreadStateBase() { pthread_setspecific(gTLS, this); } IPCThreadStateBase* IPCThreadStateBase::self() { if (gHaveTLS) { restart: const pthread_key_t k = gTLS; IPCThreadStateBase* st = (IPCThreadStateBase*)pthread_getspecific(k); if (st) return st; return new IPCThreadStateBase; } pthread_mutex_lock(&gTLSMutex); if (!gHaveTLS) { int key_create_value = pthread_key_create(&gTLS, threadDestructor); if (key_create_value != 0) { pthread_mutex_unlock(&gTLSMutex); ALOGW("IPCThreadStateBase::self() unable to create TLS key, expect a crash: %s\n", strerror(key_create_value)); return nullptr; } gHaveTLS = true; } pthread_mutex_unlock(&gTLSMutex); goto restart; } void IPCThreadStateBase::pushCurrentState(CallState callState) { mCallStateStack.emplace(callState); } IPCThreadStateBase::CallState IPCThreadStateBase::popCurrentState() { ALOG_ASSERT(mCallStateStack.size > 0); CallState val = mCallStateStack.top(); mCallStateStack.pop(); return val; } IPCThreadStateBase::CallState IPCThreadStateBase::getCurrentBinderCallState() { if (mCallStateStack.size() > 0) { return mCallStateStack.top(); } return CallState::NONE; } void IPCThreadStateBase::threadDestructor(void *st) { IPCThreadStateBase* const self = static_cast(st); if (self) { delete self; } } }; // namespace android libs/binderthreadstate/include/0040755 0000000 0000000 00000000000 13756501735 015674 5ustar000000000 0000000 libs/binderthreadstate/include/binderthreadstate/0040755 0000000 0000000 00000000000 13756501735 021370 5ustar000000000 0000000 libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h0100644 0000000 0000000 00000002327 13756501735 025101 0ustar000000000 0000000 /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H #define BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H #include namespace android { class IPCThreadStateBase { public: enum CallState { HWBINDER, BINDER, NONE, }; static IPCThreadStateBase* self(); void pushCurrentState(CallState callState); CallState popCurrentState(); CallState getCurrentBinderCallState(); private: IPCThreadStateBase(); static void threadDestructor(void *st); std::stack mCallStateStack; }; }; // namespace android #endif // BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H libs/cputimeinstate/0040755 0000000 0000000 00000000000 13756501735 013613 5ustar000000000 0000000 libs/cputimeinstate/Android.bp0100644 0000000 0000000 00000000755 13756501735 015522 0ustar000000000 0000000 cc_library { name: "libtimeinstate", srcs: ["cputimeinstate.cpp"], shared_libs: [ "libbase", "libbpf", "libbpf_android", "liblog", "libnetdutils" ], cflags: [ "-Werror", "-Wall", "-Wextra", ], } cc_test { name: "libtimeinstate_test", srcs: ["testtimeinstate.cpp"], shared_libs: [ "libtimeinstate", ], cflags: [ "-Werror", "-Wall", "-Wextra", ], } libs/cputimeinstate/cputimeinstate.cpp0100644 0000000 0000000 00000020773 13756501735 017363 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #define LOG_TAG "libtimeinstate" #include "cputimeinstate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BPF_FS_PATH "/sys/fs/bpf/" using android::base::StringPrintf; using android::base::unique_fd; namespace android { namespace bpf { struct time_key_t { uint32_t uid; uint32_t freq; }; struct val_t { uint64_t ar[100]; }; static std::mutex gInitializedMutex; static bool gInitialized = false; static uint32_t gNPolicies = 0; static std::vector> gPolicyFreqs; static std::vector> gPolicyCpus; static std::set gAllFreqs; static unique_fd gMapFd; static bool readNumbersFromFile(const std::string &path, std::vector *out) { std::string data; if (!android::base::ReadFileToString(path, &data)) return false; auto strings = android::base::Split(data, " \n"); for (const auto &s : strings) { if (s.empty()) continue; uint32_t n; if (!android::base::ParseUint(s, &n)) return false; out->emplace_back(n); } return true; } static int isPolicyFile(const struct dirent *d) { return android::base::StartsWith(d->d_name, "policy"); } static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) { uint32_t policyN1, policyN2; if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 || sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1) return 0; return policyN1 - policyN2; } static bool initGlobals() { std::lock_guard guard(gInitializedMutex); if (gInitialized) return true; struct dirent **dirlist; const char basepath[] = "/sys/devices/system/cpu/cpufreq"; int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); if (ret == -1) return false; gNPolicies = ret; std::vector policyFileNames; for (uint32_t i = 0; i < gNPolicies; ++i) { policyFileNames.emplace_back(dirlist[i]->d_name); free(dirlist[i]); } free(dirlist); for (const auto &policy : policyFileNames) { std::vector freqs; for (const auto &name : {"available", "boost"}) { std::string path = StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name); if (!readNumbersFromFile(path, &freqs)) return false; } std::sort(freqs.begin(), freqs.end()); gPolicyFreqs.emplace_back(freqs); for (auto freq : freqs) gAllFreqs.insert(freq); std::vector cpus; std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus"); if (!readNumbersFromFile(path, &cpus)) return false; gPolicyCpus.emplace_back(cpus); } gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")}; if (gMapFd < 0) return false; gInitialized = true; return true; } static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) { std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); int prog_fd = bpf_obj_get(path.c_str()); if (prog_fd < 0) return false; return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0; } // Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes. // Returns true on success, false otherwise. // Tracking is active only once a live process has successfully called this function; if the calling // process dies then it must be called again to resume tracking. // This function should *not* be called while tracking is already active; doing so is unnecessary // and can lead to accounting errors. bool startTrackingUidCpuFreqTimes() { return attachTracepointProgram("sched", "sched_switch") && attachTracepointProgram("power", "cpu_frequency"); } // Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes. // Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors // using the format: // [[t0_0, t0_1, ...], // [t1_0, t1_1, ...], ...] // where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. bool getUidCpuFreqTimes(uint32_t uid, std::vector> *freqTimes) { if (!gInitialized && !initGlobals()) return false; time_key_t key = {.uid = uid, .freq = 0}; freqTimes->clear(); freqTimes->resize(gNPolicies); std::vector idxs(gNPolicies, 0); val_t value; for (uint32_t freq : gAllFreqs) { key.freq = freq; int ret = findMapEntry(gMapFd, &key, &value); if (ret) { if (errno == ENOENT) memset(&value.ar, 0, sizeof(value.ar)); else return false; } for (uint32_t i = 0; i < gNPolicies; ++i) { if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue; uint64_t time = 0; for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; idxs[i] += 1; (*freqTimes)[i].emplace_back(time); } } return true; } // Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap. // Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to // vectors of vectors using the format: // { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], // uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. bool getUidsCpuFreqTimes( std::unordered_map>> *freqTimeMap) { if (!gInitialized && !initGlobals()) return false; int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times"); if (fd < 0) return false; BpfMap m(fd); std::vector> policyFreqIdxs; for (uint32_t i = 0; i < gNPolicies; ++i) { std::unordered_map freqIdxs; for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; policyFreqIdxs.emplace_back(freqIdxs); } auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, const BpfMap &) { if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { (*freqTimeMap)[key.uid].resize(gNPolicies); for (uint32_t i = 0; i < gNPolicies; ++i) { (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0); } } for (size_t policy = 0; policy < gNPolicies; ++policy) { for (const auto &cpu : gPolicyCpus[policy]) { auto freqIdx = policyFreqIdxs[policy][key.freq]; (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu]; } } return android::netdutils::status::ok; }; return isOk(m.iterateWithValue(fn)); } // Clear all time in state data for a given uid. Returns false on error, true otherwise. bool clearUidCpuFreqTimes(uint32_t uid) { if (!gInitialized && !initGlobals()) return false; time_key_t key = {.uid = uid, .freq = 0}; std::vector idxs(gNPolicies, 0); for (auto freq : gAllFreqs) { key.freq = freq; if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; } return true; } } // namespace bpf } // namespace android libs/cputimeinstate/cputimeinstate.h0100644 0000000 0000000 00000002007 13756501735 017016 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma once #include #include namespace android { namespace bpf { bool startTrackingUidCpuFreqTimes(); bool getUidCpuFreqTimes(unsigned int uid, std::vector> *freqTimes); bool getUidsCpuFreqTimes(std::unordered_map>> *tisMap); bool clearUidCpuFreqTimes(unsigned int uid); } // namespace bpf } // namespace android libs/cputimeinstate/testtimeinstate.cpp0100644 0000000 0000000 00000002762 13756501735 017551 0ustar000000000 0000000 #include #include #include #include namespace android { namespace bpf { using std::vector; TEST(TimeInStateTest, SingleUid) { vector> times; ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); EXPECT_FALSE(times.empty()); } TEST(TimeInStateTest, AllUid) { vector sizes; std::unordered_map>> map; ASSERT_TRUE(getUidsCpuFreqTimes(&map)); ASSERT_FALSE(map.empty()); auto firstEntry = map.begin()->second; for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); for (const auto &vec : map) { ASSERT_EQ(vec.second.size(), sizes.size()); for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); } } TEST(TimeInStateTest, RemoveUid) { vector> times, times2; ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); ASSERT_FALSE(times.empty()); uint64_t sum = 0; for (size_t i = 0; i < times.size(); ++i) { for (auto x : times[i]) sum += x; } ASSERT_GT(sum, (uint64_t)0); ASSERT_TRUE(clearUidCpuFreqTimes(0)); ASSERT_TRUE(getUidCpuFreqTimes(0, ×2)); ASSERT_EQ(times2.size(), times.size()); for (size_t i = 0; i < times.size(); ++i) { ASSERT_EQ(times2[i].size(), times[i].size()); for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]); } } } // namespace bpf } // namespace android libs/diskusage/0040755 0000000 0000000 00000000000 13756501735 012534 5ustar000000000 0000000 libs/diskusage/Android.bp0100644 0000000 0000000 00000001321 13756501735 014431 0ustar000000000 0000000 // Copyright (C) 2010 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. cc_library_static { name: "libdiskusage", srcs: ["dirsize.c"], cflags: ["-Wall", "-Werror"], } libs/diskusage/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501735 015654 0ustar000000000 0000000 libs/diskusage/dirsize.c0100644 0000000 0000000 00000003477 13756501735 014361 0ustar000000000 0000000 /* * * Copyright (C) 2008, 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. */ #include #include #include #include #include int64_t stat_size(struct stat *s) { return s->st_blocks * 512; } int64_t calculate_dir_size(int dfd) { int64_t size = 0; struct stat s; DIR *d; struct dirent *de; d = fdopendir(dfd); if (d == NULL) { close(dfd); return 0; } while ((de = readdir(d))) { const char *name = de->d_name; if (de->d_type == DT_DIR) { int subfd; /* always skip "." and ".." */ if (name[0] == '.') { if (name[1] == 0) continue; if ((name[1] == '.') && (name[2] == 0)) continue; } if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { size += stat_size(&s); } subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); if (subfd >= 0) { size += calculate_dir_size(subfd); } } else { if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { size += stat_size(&s); } } } closedir(d); return size; } libs/dumputils/0040755 0000000 0000000 00000000000 13756501735 012603 5ustar000000000 0000000 libs/dumputils/Android.bp0100644 0000000 0000000 00000001643 13756501735 014507 0ustar000000000 0000000 // Copyright (C) 2018 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. cc_library { name: "libdumputils", shared_libs: [ "libbase", "libbinder", "libhidlbase", "libhidltransport", "liblog", "libutils", ], srcs: ["dump_utils.cpp"], cflags: ["-Wall", "-Werror"], export_include_dirs: [ "include", ], } libs/dumputils/dump_utils.cpp0100644 0000000 0000000 00000013051 13756501735 015471 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #include #include #include #include #include #include #include #include /* list of native processes to include in the native dumps */ // This matches the /proc/pid/exe link instead of /proc/pid/cmdline. static const char* native_processes_to_dump[] = { "/system/bin/audioserver", "/system/bin/cameraserver", "/system/bin/drmserver", "/system/bin/mediadrmserver", "/system/bin/mediaextractor", // media.extractor "/system/bin/mediametrics", // media.metrics "/system/bin/mediaserver", "/system/bin/netd", "/system/bin/vold", "/system/bin/sdcard", "/system/bin/statsd", "/system/bin/surfaceflinger", "/system/bin/vehicle_network_service", "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec "/apex/com.android.media.swcodec/bin/mediaswcodec", // media.swcodec NULL, }; /* list of hal interface to dump containing process during native dumps */ static const char* hal_interfaces_to_dump[] { "android.hardware.audio@2.0::IDevicesFactory", "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.health@2.0::IHealth", "android.hardware.media.c2@1.0::IComponentStore", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", "android.hardware.power@1.3::IPower", "android.hardware.power.stats@1.0::IPowerStats", "android.hardware.sensors@1.0::ISensors", "android.hardware.thermal@2.0::IThermal", "android.hardware.vr@1.0::IVr", "android.hardware.automotive.audiocontrol@1.0::IAudioControl", "android.hardware.automotive.vehicle@2.0::IVehicle", "android.hardware.automotive.evs@1.0::IEvsCamera", NULL, }; /* list of extra hal interfaces to dump containing process during native dumps */ // This is filled when dumpstate is called. static std::set extra_hal_interfaces_to_dump; static void read_extra_hals_to_dump_from_property() { // extra hals to dump are already filled if (extra_hal_interfaces_to_dump.size() > 0) { return; } std::string value = android::base::GetProperty("ro.dump.hals.extra", ""); std::vector tokens = android::base::Split(value, ","); for (const auto &token : tokens) { std::string trimmed_token = android::base::Trim(token); if (trimmed_token.length() == 0) { continue; } extra_hal_interfaces_to_dump.insert(trimmed_token); } } // check if interface is included in either default hal list or extra hal list bool should_dump_hal_interface(const std::string& interface) { for (const char** i = hal_interfaces_to_dump; *i; i++) { if (interface == *i) { return true; } } return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end(); } bool should_dump_native_traces(const char* path) { for (const char** p = native_processes_to_dump; *p; p++) { if (!strcmp(*p, path)) { return true; } } return false; } std::set get_interesting_hal_pids() { using android::hidl::manager::V1_0::IServiceManager; using android::sp; using android::hardware::Return; sp manager = IServiceManager::getService(); std::set pids; read_extra_hals_to_dump_from_property(); Return ret = manager->debugDump([&](auto& hals) { for (const auto &info : hals) { if (info.pid == static_cast(IServiceManager::PidConstant::NO_PID)) { continue; } if (!should_dump_hal_interface(info.interfaceName)) { continue; } pids.insert(info.pid); } }); if (!ret.isOk()) { ALOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str()); } return pids; // whether it was okay or not } bool IsZygote(int pid) { std::string cmdline; if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &cmdline)) { return true; } // cmdline has embedded nulls; only consider argv[0]. cmdline = std::string(cmdline.c_str()); return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" || cmdline == "usap64"; } libs/dumputils/include/0040755 0000000 0000000 00000000000 13756501735 014226 5ustar000000000 0000000 libs/dumputils/include/dumputils/0040755 0000000 0000000 00000000000 13756501735 016254 5ustar000000000 0000000 libs/dumputils/include/dumputils/dump_utils.h0100644 0000000 0000000 00000001470 13756501735 020611 0ustar000000000 0000000 /** * 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. */ #ifndef DUMPUTILS_H_ #define DUMPUTILS_H_ #include bool should_dump_native_traces(const char* path); std::set get_interesting_hal_pids(); bool IsZygote(int pid); #endif // DUMPUTILS_H_ libs/graphicsenv/0040755 0000000 0000000 00000000000 13756501735 013066 5ustar000000000 0000000 libs/graphicsenv/Android.bp0100644 0000000 0000000 00000001736 13756501735 014775 0ustar000000000 0000000 // Copyright (C) 2017 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. cc_library_shared { name: "libgraphicsenv", srcs: [ "GpuStatsInfo.cpp", "GraphicsEnv.cpp", "IGpuService.cpp" ], cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", "libcutils", "libdl_android", "liblog", "libutils", ], export_include_dirs: ["include"], } libs/graphicsenv/GpuStatsInfo.cpp0100644 0000000 0000000 00000014215 13756501735 016160 0ustar000000000 0000000 /* * Copyright 2019 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. */ #include #include #include #include namespace android { using base::StringAppendF; status_t GpuStatsGlobalInfo::writeToParcel(Parcel* parcel) const { status_t status; if ((status = parcel->writeUtf8AsUtf16(driverPackageName)) != OK) return status; if ((status = parcel->writeUtf8AsUtf16(driverVersionName)) != OK) return status; if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status; if ((status = parcel->writeInt64(driverBuildTime)) != OK) return status; if ((status = parcel->writeInt32(glLoadingCount)) != OK) return status; if ((status = parcel->writeInt32(glLoadingFailureCount)) != OK) return status; if ((status = parcel->writeInt32(vkLoadingCount)) != OK) return status; if ((status = parcel->writeInt32(vkLoadingFailureCount)) != OK) return status; if ((status = parcel->writeInt32(vulkanVersion)) != OK) return status; if ((status = parcel->writeInt32(cpuVulkanVersion)) != OK) return status; if ((status = parcel->writeInt32(glesVersion)) != OK) return status; if ((status = parcel->writeInt32(angleLoadingCount)) != OK) return status; if ((status = parcel->writeInt32(angleLoadingFailureCount)) != OK) return status; return OK; } status_t GpuStatsGlobalInfo::readFromParcel(const Parcel* parcel) { status_t status; if ((status = parcel->readUtf8FromUtf16(&driverPackageName)) != OK) return status; if ((status = parcel->readUtf8FromUtf16(&driverVersionName)) != OK) return status; if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status; if ((status = parcel->readInt64(&driverBuildTime)) != OK) return status; if ((status = parcel->readInt32(&glLoadingCount)) != OK) return status; if ((status = parcel->readInt32(&glLoadingFailureCount)) != OK) return status; if ((status = parcel->readInt32(&vkLoadingCount)) != OK) return status; if ((status = parcel->readInt32(&vkLoadingFailureCount)) != OK) return status; if ((status = parcel->readInt32(&vulkanVersion)) != OK) return status; if ((status = parcel->readInt32(&cpuVulkanVersion)) != OK) return status; if ((status = parcel->readInt32(&glesVersion)) != OK) return status; if ((status = parcel->readInt32(&angleLoadingCount)) != OK) return status; if ((status = parcel->readInt32(&angleLoadingFailureCount)) != OK) return status; return OK; } std::string GpuStatsGlobalInfo::toString() const { std::string result; StringAppendF(&result, "driverPackageName = %s\n", driverPackageName.c_str()); StringAppendF(&result, "driverVersionName = %s\n", driverVersionName.c_str()); StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); StringAppendF(&result, "driverBuildTime = %" PRId64 "\n", driverBuildTime); StringAppendF(&result, "glLoadingCount = %d\n", glLoadingCount); StringAppendF(&result, "glLoadingFailureCount = %d\n", glLoadingFailureCount); StringAppendF(&result, "angleLoadingCount = %d\n", angleLoadingCount); StringAppendF(&result, "angleLoadingFailureCount = %d\n", angleLoadingFailureCount); StringAppendF(&result, "vkLoadingCount = %d\n", vkLoadingCount); StringAppendF(&result, "vkLoadingFailureCount = %d\n", vkLoadingFailureCount); StringAppendF(&result, "vulkanVersion = %d\n", vulkanVersion); StringAppendF(&result, "cpuVulkanVersion = %d\n", cpuVulkanVersion); StringAppendF(&result, "glesVersion = %d\n", glesVersion); return result; } status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { status_t status; if ((status = parcel->writeUtf8AsUtf16(appPackageName)) != OK) return status; if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status; if ((status = parcel->writeInt64Vector(glDriverLoadingTime)) != OK) return status; if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status; if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status; if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status; return OK; } status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { status_t status; if ((status = parcel->readUtf8FromUtf16(&appPackageName)) != OK) return status; if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status; if ((status = parcel->readInt64Vector(&glDriverLoadingTime)) != OK) return status; if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status; if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status; if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status; return OK; } std::string GpuStatsAppInfo::toString() const { std::string result; StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str()); StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse); result.append("glDriverLoadingTime:"); for (int32_t loadingTime : glDriverLoadingTime) { StringAppendF(&result, " %d", loadingTime); } result.append("\n"); result.append("angleDriverLoadingTime:"); for (int32_t loadingTime : angleDriverLoadingTime) { StringAppendF(&result, " %d", loadingTime); } result.append("\n"); result.append("vkDriverLoadingTime:"); for (int32_t loadingTime : vkDriverLoadingTime) { StringAppendF(&result, " %d", loadingTime); } result.append("\n"); return result; } } // namespace android libs/graphicsenv/GraphicsEnv.cpp0100644 0000000 0000000 00000056010 13756501735 016002 0ustar000000000 0000000 /* * Copyright 2017 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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 1 #define LOG_TAG "GraphicsEnv" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // TODO(b/37049319) Get this from a header once one exists extern "C" { android_namespace_t* android_get_exported_namespace(const char*); android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path, android_namespace_t* parent); bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to, const char* shared_libs_sonames); enum { ANDROID_NAMESPACE_TYPE_ISOLATED = 1, ANDROID_NAMESPACE_TYPE_SHARED = 2, }; } // TODO(ianelliott@): Get the following from an ANGLE header: #define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting // Version-2 API: typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse); typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle, int* rulesVersion); typedef bool (*fpANGLEGetSystemInfo)(void** handle); typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel, void* handle); typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion, void* systemInfoHandle, const char* appName); typedef bool (*fpANGLEFreeRulesHandle)(void* handle); typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); namespace android { enum NativeLibrary { LLNDK = 0, VNDKSP = 1, }; static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt", "/etc/vndksp.libraries.txt"}; static std::string vndkVersionStr() { #ifdef __BIONIC__ std::string version = android::base::GetProperty("ro.vndk.version", ""); if (version != "" && version != "current") { return "." + version; } #endif return ""; } static void insertVndkVersionStr(std::string* fileName) { LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr"); size_t insertPos = fileName->find_last_of("."); if (insertPos == std::string::npos) { insertPos = fileName->length(); } fileName->insert(insertPos, vndkVersionStr()); } static bool readConfig(const std::string& configFile, std::vector* soNames) { // Read list of public native libraries from the config file. std::string fileContent; if (!base::ReadFileToString(configFile, &fileContent)) { return false; } std::vector lines = base::Split(fileContent, "\n"); for (auto& line : lines) { auto trimmedLine = base::Trim(line); if (!trimmedLine.empty()) { soNames->push_back(trimmedLine); } } return true; } static const std::string getSystemNativeLibraries(NativeLibrary type) { static const char* androidRootEnv = getenv("ANDROID_ROOT"); static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system"; std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type]; insertVndkVersionStr(&nativeLibrariesSystemConfig); std::vector soNames; if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str()); return ""; } return base::Join(soNames, ':'); } /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { static GraphicsEnv env; return env; } int GraphicsEnv::getCanLoadSystemLibraries() { if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { // Return an integer value since this crosses library boundaries return 1; } return 0; } void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries) { if (!mDriverPath.empty() || !mSphalLibraries.empty()) { ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries " "from '%s' to '%s'", mDriverPath.c_str(), path.c_str(), mSphalLibraries.c_str(), sphalLibraries.c_str()); return; } ALOGV("setting driver path to '%s' and sphal libraries to '%s'", path.c_str(), sphalLibraries.c_str()); mDriverPath = path; mSphalLibraries = sphalLibraries; } void GraphicsEnv::hintActivityLaunch() { ATRACE_CALL(); std::thread trySendGpuStatsThread([this]() { // If there's already graphics driver preloaded in the process, just send // the stats info to GpuStats directly through async binder. std::lock_guard lock(mStatsLock); if (mGpuStats.glDriverToSend) { mGpuStats.glDriverToSend = false; sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime); } if (mGpuStats.vkDriverToSend) { mGpuStats.vkDriverToSend = false; sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime); } }); trySendGpuStatsThread.detach(); } void GraphicsEnv::setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int vulkanVersion) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); ALOGV("setGpuStats:\n" "\tdriverPackageName[%s]\n" "\tdriverVersionName[%s]\n" "\tdriverVersionCode[%" PRIu64 "]\n" "\tdriverBuildTime[%" PRId64 "]\n" "\tappPackageName[%s]\n" "\tvulkanVersion[%d]\n", driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime, appPackageName.c_str(), vulkanVersion); mGpuStats.driverPackageName = driverPackageName; mGpuStats.driverVersionName = driverVersionName; mGpuStats.driverVersionCode = driverVersionCode; mGpuStats.driverBuildTime = driverBuildTime; mGpuStats.appPackageName = appPackageName; mGpuStats.vulkanVersion = vulkanVersion; } void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); switch (driver) { case GraphicsEnv::Driver::GL: case GraphicsEnv::Driver::GL_UPDATED: case GraphicsEnv::Driver::ANGLE: { if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE || mGpuStats.glDriverToLoad == GraphicsEnv::Driver::GL) { mGpuStats.glDriverToLoad = driver; break; } if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) { mGpuStats.glDriverFallback = driver; } break; } case Driver::VULKAN: case Driver::VULKAN_UPDATED: { if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE || mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::VULKAN) { mGpuStats.vkDriverToLoad = driver; break; } if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) { mGpuStats.vkDriverFallback = driver; } break; } default: break; } } void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); const bool doNotSend = mGpuStats.appPackageName.empty(); if (api == GraphicsEnv::Api::API_GL) { if (doNotSend) mGpuStats.glDriverToSend = true; mGpuStats.glDriverLoadingTime = driverLoadingTime; } else { if (doNotSend) mGpuStats.vkDriverToSend = true; mGpuStats.vkDriverLoadingTime = driverLoadingTime; } sendGpuStatsLocked(api, isDriverLoaded, driverLoadingTime); } static sp getGpuService() { const sp binder = defaultServiceManager()->checkService(String16("gpu")); if (!binder) { ALOGE("Failed to get gpu service"); return nullptr; } return interface_cast(binder); } void GraphicsEnv::setTargetStats(const Stats stats, const uint64_t value) { ATRACE_CALL(); std::lock_guard lock(mStatsLock); const sp gpuService = getGpuService(); if (gpuService) { gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats, value); } } void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); // Do not sendGpuStats for those skipping the GraphicsEnvironment setup if (mGpuStats.appPackageName.empty()) return; ALOGV("sendGpuStats:\n" "\tdriverPackageName[%s]\n" "\tdriverVersionName[%s]\n" "\tdriverVersionCode[%" PRIu64 "]\n" "\tdriverBuildTime[%" PRId64 "]\n" "\tappPackageName[%s]\n" "\tvulkanVersion[%d]\n" "\tapi[%d]\n" "\tisDriverLoaded[%d]\n" "\tdriverLoadingTime[%" PRId64 "]", mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(), mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(), mGpuStats.vulkanVersion, static_cast(api), isDriverLoaded, driverLoadingTime); GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE; bool isIntendedDriverLoaded = false; if (api == GraphicsEnv::Api::API_GL) { driver = mGpuStats.glDriverToLoad; isIntendedDriverLoaded = isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE); } else { driver = mGpuStats.vkDriverToLoad; isIntendedDriverLoaded = isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE); } const sp gpuService = getGpuService(); if (gpuService) { gpuService->setGpuStats(mGpuStats.driverPackageName, mGpuStats.driverVersionName, mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName, mGpuStats.vulkanVersion, driver, isIntendedDriverLoaded, driverLoadingTime); } } void* GraphicsEnv::loadLibrary(std::string name) { const android_dlextinfo dlextinfo = { .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = getAngleNamespace(), }; std::string libName = std::string("lib") + name + "_angle.so"; void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); if (so) { ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so); return so; } else { ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror()); } return nullptr; } bool GraphicsEnv::checkAngleRules(void* so) { char manufacturer[PROPERTY_VALUE_MAX]; char model[PROPERTY_VALUE_MAX]; property_get("ro.product.manufacturer", manufacturer, "UNSET"); property_get("ro.product.model", model, "UNSET"); auto ANGLEGetFeatureSupportUtilAPIVersion = (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, "ANGLEGetFeatureSupportUtilAPIVersion"); if (!ANGLEGetFeatureSupportUtilAPIVersion) { ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); return false; } // Negotiate the interface version by requesting most recent known to the platform unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " "requested version %u", versionToUse); return false; } // Add and remove versions below as needed bool useAngle = false; switch (versionToUse) { case 2: { ALOGV("Using version %d of ANGLE feature-support library", versionToUse); void* rulesHandle = nullptr; int rulesVersion = 0; void* systemInfoHandle = nullptr; // Get the symbols for the feature-support-utility library: #define GET_SYMBOL(symbol) \ fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \ if (!symbol) { \ ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ break; \ } GET_SYMBOL(ANGLEAndroidParseRulesString); GET_SYMBOL(ANGLEGetSystemInfo); GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); GET_SYMBOL(ANGLEShouldBeUsedForApplication); GET_SYMBOL(ANGLEFreeRulesHandle); GET_SYMBOL(ANGLEFreeSystemInfoHandle); // Parse the rules, obtain the SystemInfo, and evaluate the // application against the rules: if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { ALOGW("ANGLE feature-support library cannot parse rules file"); break; } if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); break; } if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); break; } useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, systemInfoHandle, mAngleAppName.c_str()); (ANGLEFreeRulesHandle)(rulesHandle); (ANGLEFreeSystemInfoHandle)(systemInfoHandle); } break; default: ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); } ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); return useAngle; } bool GraphicsEnv::shouldUseAngle(std::string appName) { if (appName != mAngleAppName) { // Make sure we are checking the app we were init'ed for ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(), appName.c_str()); return false; } return shouldUseAngle(); } bool GraphicsEnv::shouldUseAngle() { // Make sure we are init'ed if (mAngleAppName.empty()) { ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE."); return false; } return (mUseAngle == YES) ? true : false; } void GraphicsEnv::updateUseAngle() { mUseAngle = NO; const char* ANGLE_PREFER_ANGLE = "angle"; const char* ANGLE_PREFER_NATIVE = "native"; if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { ALOGV("User set \"Developer Options\" to force the use of ANGLE"); mUseAngle = YES; } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { ALOGV("User set \"Developer Options\" to force the use of Native"); mUseAngle = NO; } else { // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily // load ANGLE and call the updatable opt-in/out logic: void* featureSo = loadLibrary("feature_support"); if (featureSo) { ALOGV("loaded ANGLE's opt-in/out logic from namespace"); mUseAngle = checkAngleRules(featureSo) ? YES : NO; dlclose(featureSo); featureSo = nullptr; } else { ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); } } } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, const std::string developerOptIn, const int rulesFd, const long rulesOffset, const long rulesLength) { if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), (mUseAngle == YES) ? "true" : "false"); return; } ALOGV("setting ANGLE path to '%s'", path.c_str()); mAnglePath = path; ALOGV("setting ANGLE app name to '%s'", appName.c_str()); mAngleAppName = appName; ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); mAngleDeveloperOptIn = developerOptIn; lseek(rulesFd, rulesOffset, SEEK_SET); mRulesBuffer = std::vector(rulesLength + 1); ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); if (numBytesRead < 0) { ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); numBytesRead = 0; } else if (numBytesRead == 0) { ALOGW("Empty rules file"); } if (numBytesRead != rulesLength) { ALOGW("Did not read all of the necessary bytes from the rules file." "expected: %ld, got: %zd", rulesLength, numBytesRead); } mRulesBuffer[numBytesRead] = '\0'; // Update the current status of whether we should use ANGLE or not updateUseAngle(); } void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) { if (mLayerPaths.empty()) { mLayerPaths = layerPaths; mAppNamespace = appNamespace; } else { ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'", layerPaths.c_str(), appNamespace); } } NativeLoaderNamespace* GraphicsEnv::getAppNamespace() { return mAppNamespace; } std::string& GraphicsEnv::getAngleAppName() { return mAngleAppName; } const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } const std::string& GraphicsEnv::getDebugLayers() { return mDebugLayers; } const std::string& GraphicsEnv::getDebugLayersGLES() { return mDebugLayersGLES; } void GraphicsEnv::setDebugLayers(const std::string layers) { mDebugLayers = layers; } void GraphicsEnv::setDebugLayersGLES(const std::string layers) { mDebugLayersGLES = layers; } // Return true if all the required libraries from vndk and sphal namespace are // linked to the Game Driver namespace correctly. bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) { const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK); if (llndkLibraries.empty()) { return false; } if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) { ALOGE("Failed to link default namespace[%s]", dlerror()); return false; } const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP); if (vndkspLibraries.empty()) { return false; } if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) { ALOGE("Failed to link vndk namespace[%s]", dlerror()); return false; } if (mSphalLibraries.empty()) { return true; } // Make additional libraries in sphal to be accessible auto sphalNamespace = android_get_exported_namespace("sphal"); if (!sphalNamespace) { ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace", mSphalLibraries.c_str()); return false; } if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) { ALOGE("Failed to link sphal namespace[%s]", dlerror()); return false; } return true; } android_namespace_t* GraphicsEnv::getDriverNamespace() { std::lock_guard lock(mNamespaceMutex); if (mDriverNamespace) { return mDriverNamespace; } if (mDriverPath.empty()) { return nullptr; } auto vndkNamespace = android_get_exported_namespace("vndk"); if (!vndkNamespace) { return nullptr; } mDriverNamespace = android_create_namespace("gfx driver", mDriverPath.c_str(), // ld_library_path mDriverPath.c_str(), // default_library_path ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); if (!linkDriverNamespaceLocked(vndkNamespace)) { mDriverNamespace = nullptr; } return mDriverNamespace; } android_namespace_t* GraphicsEnv::getAngleNamespace() { std::lock_guard lock(mNamespaceMutex); if (mAngleNamespace) { return mAngleNamespace; } if (mAnglePath.empty()) { ALOGV("mAnglePath is empty, not creating ANGLE namespace"); return nullptr; } mAngleNamespace = android_create_namespace("ANGLE", nullptr, // ld_library_path mAnglePath.c_str(), // default_library_path ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default"); return mAngleNamespace; } } // namespace android libs/graphicsenv/IGpuService.cpp0100644 0000000 0000000 00000017730 13756501735 015764 0ustar000000000 0000000 /* * Copyright 2019 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. */ #define LOG_TAG "GpuService" #include #include #include namespace android { class BpGpuService : public BpInterface { public: explicit BpGpuService(const sp& impl) : BpInterface(impl) {} virtual void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion, GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) { Parcel data, reply; data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); data.writeUtf8AsUtf16(driverPackageName); data.writeUtf8AsUtf16(driverVersionName); data.writeUint64(driverVersionCode); data.writeInt64(driverBuildTime); data.writeUtf8AsUtf16(appPackageName); data.writeInt32(vulkanVersion); data.writeInt32(static_cast(driver)); data.writeBool(isDriverLoaded); data.writeInt64(driverLoadingTime); remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY); } virtual status_t getGpuStatsGlobalInfo(std::vector* outStats) const { if (!outStats) return UNEXPECTED_NULL; Parcel data, reply; status_t status; if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) return status; if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) != OK) return status; int32_t result = 0; if ((status = reply.readInt32(&result)) != OK) return status; if (result != OK) return result; outStats->clear(); return reply.readParcelableVector(outStats); } virtual status_t getGpuStatsAppInfo(std::vector* outStats) const { if (!outStats) return UNEXPECTED_NULL; Parcel data, reply; status_t status; if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) { return status; } if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) != OK) { return status; } int32_t result = 0; if ((status = reply.readInt32(&result)) != OK) return status; if (result != OK) return result; outStats->clear(); return reply.readParcelableVector(outStats); } virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, const GraphicsEnv::Stats stats, const uint64_t value) { Parcel data, reply; data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); data.writeUtf8AsUtf16(appPackageName); data.writeUint64(driverVersionCode); data.writeInt32(static_cast(stats)); data.writeUint64(value); remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY); } }; IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService"); status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { ALOGV("onTransact code[0x%X]", code); status_t status; switch (code) { case SET_GPU_STATS: { CHECK_INTERFACE(IGpuService, data, reply); std::string driverPackageName; if ((status = data.readUtf8FromUtf16(&driverPackageName)) != OK) return status; std::string driverVersionName; if ((status = data.readUtf8FromUtf16(&driverVersionName)) != OK) return status; uint64_t driverVersionCode; if ((status = data.readUint64(&driverVersionCode)) != OK) return status; int64_t driverBuildTime; if ((status = data.readInt64(&driverBuildTime)) != OK) return status; std::string appPackageName; if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status; int32_t vulkanVersion; if ((status = data.readInt32(&vulkanVersion)) != OK) return status; int32_t driver; if ((status = data.readInt32(&driver)) != OK) return status; bool isDriverLoaded; if ((status = data.readBool(&isDriverLoaded)) != OK) return status; int64_t driverLoadingTime; if ((status = data.readInt64(&driverLoadingTime)) != OK) return status; setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, appPackageName, vulkanVersion, static_cast(driver), isDriverLoaded, driverLoadingTime); return OK; } case GET_GPU_STATS_GLOBAL_INFO: { CHECK_INTERFACE(IGpuService, data, reply); std::vector stats; const status_t result = getGpuStatsGlobalInfo(&stats); if ((status = reply->writeInt32(result)) != OK) return status; if (result != OK) return result; if ((status = reply->writeParcelableVector(stats)) != OK) return status; return OK; } case GET_GPU_STATS_APP_INFO: { CHECK_INTERFACE(IGpuService, data, reply); std::vector stats; const status_t result = getGpuStatsAppInfo(&stats); if ((status = reply->writeInt32(result)) != OK) return status; if (result != OK) return result; if ((status = reply->writeParcelableVector(stats)) != OK) return status; return OK; } case SET_TARGET_STATS: { CHECK_INTERFACE(IGpuService, data, reply); std::string appPackageName; if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status; uint64_t driverVersionCode; if ((status = data.readUint64(&driverVersionCode)) != OK) return status; int32_t stats; if ((status = data.readInt32(&stats)) != OK) return status; uint64_t value; if ((status = data.readUint64(&value)) != OK) return status; setTargetStats(appPackageName, driverVersionCode, static_cast(stats), value); return OK; } case SHELL_COMMAND_TRANSACTION: { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); std::vector args; data.readString16Vector(&args); sp unusedCallback; if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK) return status; sp resultReceiver; if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK) return status; status = shellCommand(in, out, err, args); if (resultReceiver != nullptr) resultReceiver->send(status); return OK; } default: return BBinder::onTransact(code, data, reply, flags); } } } // namespace android libs/graphicsenv/include/0040755 0000000 0000000 00000000000 13756501735 014511 5ustar000000000 0000000 libs/graphicsenv/include/graphicsenv/0040755 0000000 0000000 00000000000 13756501735 017022 5ustar000000000 0000000 libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h0100644 0000000 0000000 00000004606 13756501735 021564 0ustar000000000 0000000 /* * Copyright 2019 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. */ #pragma once #include #include #include namespace android { /* * class for transporting gpu global stats from GpuService to authorized * recipents. This class is intended to be a data container. */ class GpuStatsGlobalInfo : public Parcelable { public: GpuStatsGlobalInfo() = default; GpuStatsGlobalInfo(const GpuStatsGlobalInfo&) = default; virtual ~GpuStatsGlobalInfo() = default; virtual status_t writeToParcel(Parcel* parcel) const; virtual status_t readFromParcel(const Parcel* parcel); std::string toString() const; std::string driverPackageName = ""; std::string driverVersionName = ""; uint64_t driverVersionCode = 0; int64_t driverBuildTime = 0; int32_t glLoadingCount = 0; int32_t glLoadingFailureCount = 0; int32_t vkLoadingCount = 0; int32_t vkLoadingFailureCount = 0; int32_t vulkanVersion = 0; int32_t cpuVulkanVersion = 0; int32_t glesVersion = 0; int32_t angleLoadingCount = 0; int32_t angleLoadingFailureCount = 0; }; /* * class for transporting gpu app stats from GpuService to authorized recipents. * This class is intended to be a data container. */ class GpuStatsAppInfo : public Parcelable { public: GpuStatsAppInfo() = default; GpuStatsAppInfo(const GpuStatsAppInfo&) = default; virtual ~GpuStatsAppInfo() = default; virtual status_t writeToParcel(Parcel* parcel) const; virtual status_t readFromParcel(const Parcel* parcel); std::string toString() const; std::string appPackageName = ""; uint64_t driverVersionCode = 0; std::vector glDriverLoadingTime = {}; std::vector vkDriverLoadingTime = {}; std::vector angleDriverLoadingTime = {}; bool cpuVulkanInUse = false; }; } // namespace android libs/graphicsenv/include/graphicsenv/GraphicsEnv.h0100644 0000000 0000000 00000012677 13756501735 021416 0ustar000000000 0000000 /* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_GRAPHICS_ENV_H #define ANDROID_UI_GRAPHICS_ENV_H 1 #include #include #include struct android_namespace_t; namespace android { struct NativeLoaderNamespace; class GraphicsEnv { public: enum Api { API_GL = 0, API_VK = 1, }; enum Driver { NONE = 0, GL = 1, GL_UPDATED = 2, VULKAN = 3, VULKAN_UPDATED = 4, ANGLE = 5, }; enum Stats { CPU_VULKAN_IN_USE = 0, }; private: struct GpuStats { std::string driverPackageName; std::string driverVersionName; uint64_t driverVersionCode; int64_t driverBuildTime; std::string appPackageName; int32_t vulkanVersion; Driver glDriverToLoad; Driver glDriverFallback; Driver vkDriverToLoad; Driver vkDriverFallback; bool glDriverToSend; bool vkDriverToSend; int64_t glDriverLoadingTime; int64_t vkDriverLoadingTime; GpuStats() : driverPackageName(""), driverVersionName(""), driverVersionCode(0), driverBuildTime(0), appPackageName(""), vulkanVersion(0), glDriverToLoad(Driver::NONE), glDriverFallback(Driver::NONE), vkDriverToLoad(Driver::NONE), vkDriverFallback(Driver::NONE), glDriverToSend(false), vkDriverToSend(false), glDriverLoadingTime(0), vkDriverLoadingTime(0) {} }; public: static GraphicsEnv& getInstance(); int getCanLoadSystemLibraries(); // Set a search path for loading graphics drivers. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (drivers must be stored uncompressed and page aligned); such elements // in the search path must have a '!' after the zip filename, e.g. // /data/app/com.example.driver/base.apk!/lib/arm64-v8a // Also set additional required sphal libraries to the linker for loading // graphics drivers. The string is a list of libraries separated by ':', // which is required by android_link_namespaces. void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries); android_namespace_t* getDriverNamespace(); void hintActivityLaunch(); void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t versionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion); void setTargetStats(const Stats stats, const uint64_t value = 0); void setDriverToLoad(Driver driver); void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime); void sendGpuStatsLocked(Api api, bool isDriverLoaded, int64_t driverLoadingTime); bool shouldUseAngle(std::string appName); bool shouldUseAngle(); // Set a search path for loading ANGLE libraries. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (libraries must be stored uncompressed and page aligned); such elements // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, const int rulesFd, const long rulesOffset, const long rulesLength); android_namespace_t* getAngleNamespace(); std::string& getAngleAppName(); void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths); NativeLoaderNamespace* getAppNamespace(); const std::string& getLayerPaths(); void setDebugLayers(const std::string layers); void setDebugLayersGLES(const std::string layers); const std::string& getDebugLayers(); const std::string& getDebugLayersGLES(); private: enum UseAngle { UNKNOWN, YES, NO }; void* loadLibrary(std::string name); bool checkAngleRules(void* so); void updateUseAngle(); bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace); GraphicsEnv() = default; std::string mDriverPath; std::string mSphalLibraries; std::mutex mStatsLock; GpuStats mGpuStats; std::string mAnglePath; std::string mAngleAppName; std::string mAngleDeveloperOptIn; std::vector mRulesBuffer; UseAngle mUseAngle = UNKNOWN; std::string mDebugLayers; std::string mDebugLayersGLES; std::string mLayerPaths; std::mutex mNamespaceMutex; android_namespace_t* mDriverNamespace = nullptr; android_namespace_t* mAngleNamespace = nullptr; NativeLoaderNamespace* mAppNamespace = nullptr; }; } // namespace android #endif // ANDROID_UI_GRAPHICS_ENV_H libs/graphicsenv/include/graphicsenv/IGpuService.h0100644 0000000 0000000 00000004732 13756501735 021363 0ustar000000000 0000000 /* * Copyright 2019 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. */ #pragma once #include #include #include #include #include namespace android { /* * This class defines the Binder IPC interface for GPU-related queries and * control. */ class IGpuService : public IInterface { public: DECLARE_META_INTERFACE(GpuService) // set GPU stats from GraphicsEnvironment. virtual void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion, GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) = 0; // set target stats. virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, const GraphicsEnv::Stats stats, const uint64_t value = 0) = 0; // get GPU global stats from GpuStats module. virtual status_t getGpuStatsGlobalInfo(std::vector* outStats) const = 0; // get GPU app stats from GpuStats module. virtual status_t getGpuStatsAppInfo(std::vector* outStats) const = 0; }; class BnGpuService : public BnInterface { public: enum IGpuServiceTag { SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION, GET_GPU_STATS_GLOBAL_INFO, GET_GPU_STATS_APP_INFO, SET_TARGET_STATS, // Always append new enum to the end. }; status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; protected: virtual status_t shellCommand(int in, int out, int err, std::vector& args) = 0; }; } // namespace android libs/gui/0040755 0000000 0000000 00000000000 13756501735 011341 5ustar000000000 0000000 libs/gui/Android.bp0100644 0000000 0000000 00000012603 13756501735 013243 0ustar000000000 0000000 // Copyright 2010 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. cc_library_headers { name: "libgui_headers", vendor_available: true, export_include_dirs: ["include"], } cc_library_shared { name: "libgui", vendor_available: false, vndk: { enabled: true, }, double_loadable: true, defaults: ["libgui_bufferqueue-defaults"], srcs: [ "BitTube.cpp", "BufferHubConsumer.cpp", "BufferHubProducer.cpp", "BufferItemConsumer.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", "DebugEGLImageTracker.cpp", "DisplayEventReceiver.cpp", "GLConsumer.cpp", "GuiConfig.cpp", "IDisplayEventConnection.cpp", "IRegionSamplingListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", "ITransactionCompletedListener.cpp", "LayerDebugInfo.cpp", "LayerMetadata.cpp", "LayerState.cpp", "StreamSplitter.cpp", "Surface.cpp", "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", "view/Surface.cpp", ], shared_libs: [ "android.frameworks.bufferhub@1.0", "libbufferhub", "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. "libinput", "libpdx_default_transport", ], // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], exclude_srcs: [ "BufferHubConsumer.cpp", "BufferHubProducer.cpp", ], exclude_shared_libs: [ "android.frameworks.bufferhub@1.0", "libbufferhub", "libbufferhubqueue", "libinput", "libpdx_default_transport", ], }, }, header_libs: [ "libdvr_headers", "libpdx_headers", ], } // Used by media codec services exclusively as a static lib for // core bufferqueue support only. cc_library_static { name: "libgui_bufferqueue_static", vendor_available: true, cflags: [ "-DNO_BUFFERHUB", ], defaults: ["libgui_bufferqueue-defaults"], } // Common build config shared by libgui and libgui_bufferqueue_static. cc_defaults { name: "libgui_bufferqueue-defaults", clang: true, cflags: [ "-Wall", "-Werror", ], cppflags: [ "-Wextra", "-DDEBUG_ONLY_CODE=0", ], product_variables: { eng: { cppflags: [ "-UDEBUG_ONLY_CODE", "-DDEBUG_ONLY_CODE=1", ], }, }, srcs: [ "BufferItem.cpp", "BufferQueue.cpp", "BufferQueueConsumer.cpp", "BufferQueueCore.cpp", "BufferQueueProducer.cpp", "BufferQueueThreadState.cpp", "BufferSlot.cpp", "FrameTimestamps.cpp", "GLConsumerUtils.cpp", "HdrMetadata.cpp", "IConsumerListener.cpp", "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", "OccupancyTracker.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", "bufferqueue/1.0/Conversion.cpp", "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", "bufferqueue/1.0/H2BProducerListener.cpp", "bufferqueue/1.0/WProducerListener.cpp", "bufferqueue/2.0/B2HGraphicBufferProducer.cpp", "bufferqueue/2.0/B2HProducerListener.cpp", "bufferqueue/2.0/H2BGraphicBufferProducer.cpp", "bufferqueue/2.0/H2BProducerListener.cpp", "bufferqueue/2.0/types.cpp", ], shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", "android.hardware.graphics.common@1.2", "android.hidl.token@1.0-utils", "libbase", "libbinder", "libcutils", "libEGL", "libGLESv2", "libhidlbase", "libhidltransport", "libhwbinder", "liblog", "libnativewindow", "libsync", "libui", "libutils", "libvndksupport", ], header_libs: [ "libgui_headers", "libnativebase_headers", ], export_shared_lib_headers: [ "libbinder", "libEGL", "libnativewindow", "libui", "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", "android.hardware.graphics.common@1.2", "android.hidl.token@1.0-utils", ], export_header_lib_headers: [ "libgui_headers", ], export_include_dirs: [ "include", ], } subdirs = ["tests"] libs/gui/BitTube.cpp0100644 0000000 0000000 00000012156 13756501735 013405 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #include #include #include #include #include #include #include #include namespace android { namespace gui { // Socket buffer size. The default is typically about 128KB, which is much larger than we really // need. So we make it smaller. static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024; BitTube::BitTube(size_t bufsize) { init(bufsize, bufsize); } BitTube::BitTube(DefaultSizeType) : BitTube(DEFAULT_SOCKET_BUFFER_SIZE) {} BitTube::BitTube(const Parcel& data) { readFromParcel(&data); } void BitTube::init(size_t rcvbuf, size_t sndbuf) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { size_t size = DEFAULT_SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); // since we don't use the "return channel", we keep it small... setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); fcntl(sockets[0], F_SETFL, O_NONBLOCK); fcntl(sockets[1], F_SETFL, O_NONBLOCK); mReceiveFd.reset(sockets[0]); mSendFd.reset(sockets[1]); } else { mReceiveFd.reset(); ALOGE("BitTube: pipe creation failed (%s)", strerror(errno)); } } status_t BitTube::initCheck() const { if (mReceiveFd < 0) { return status_t(mReceiveFd); } return NO_ERROR; } int BitTube::getFd() const { return mReceiveFd; } int BitTube::getSendFd() const { return mSendFd; } base::unique_fd BitTube::moveReceiveFd() { return std::move(mReceiveFd); } void BitTube::setReceiveFd(base::unique_fd&& receiveFd) { mReceiveFd = std::move(receiveFd); } ssize_t BitTube::write(void const* vaddr, size_t size) { ssize_t err, len; do { len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); // cannot return less than size, since we're using SOCK_SEQPACKET err = len < 0 ? errno : 0; } while (err == EINTR); return err == 0 ? len : -err; } ssize_t BitTube::read(void* vaddr, size_t size) { ssize_t err, len; do { len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT); err = len < 0 ? errno : 0; } while (err == EINTR); if (err == EAGAIN || err == EWOULDBLOCK) { // EAGAIN means that we have non-blocking I/O but there was no data to be read. Nothing the // client should care about. return 0; } return err == 0 ? len : -err; } status_t BitTube::writeToParcel(Parcel* reply) const { if (mReceiveFd < 0) return -EINVAL; status_t result = reply->writeDupFileDescriptor(mReceiveFd); mReceiveFd.reset(); return result; } status_t BitTube::readFromParcel(const Parcel* parcel) { mReceiveFd.reset(dup(parcel->readFileDescriptor())); if (mReceiveFd < 0) { mReceiveFd.reset(); int error = errno; ALOGE("BitTube::readFromParcel: can't dup file descriptor (%s)", strerror(error)); return -error; } return NO_ERROR; } ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) { const char* vaddr = reinterpret_cast(events); ssize_t size = tube->write(vaddr, count * objSize); // should never happen because of SOCK_SEQPACKET LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast(objSize)), "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were " "sent!)", count, objSize, size); // ALOGE_IF(size<0, "error %d sending %d events", size, count); return size < 0 ? size : size / static_cast(objSize); } ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) { char* vaddr = reinterpret_cast(events); ssize_t size = tube->read(vaddr, count * objSize); // should never happen because of SOCK_SEQPACKET LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast(objSize)), "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were " "received!)", count, objSize, size); // ALOGE_IF(size<0, "error %d receiving %d events", size, count); return size < 0 ? size : size / static_cast(objSize); } } // namespace gui } // namespace android libs/gui/BufferHubConsumer.cpp0100644 0000000 0000000 00000013423 13756501735 015431 0ustar000000000 0000000 /* * Copyright 2018 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. */ #include namespace android { using namespace dvr; /* static */ sp BufferHubConsumer::Create(const std::shared_ptr& queue) { sp consumer = new BufferHubConsumer; consumer->mQueue = queue; return consumer; } /* static */ sp BufferHubConsumer::Create(ConsumerQueueParcelable parcelable) { if (!parcelable.IsValid()) { ALOGE("BufferHubConsumer::Create: Invalid consumer parcelable."); return nullptr; } sp consumer = new BufferHubConsumer; consumer->mQueue = ConsumerQueue::Import(parcelable.TakeChannelHandle()); return consumer; } status_t BufferHubConsumer::acquireBuffer(BufferItem* /*buffer*/, nsecs_t /*presentWhen*/, uint64_t /*maxFrameNumber*/) { ALOGE("BufferHubConsumer::acquireBuffer: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::detachBuffer(int /*slot*/) { ALOGE("BufferHubConsumer::detachBuffer: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::attachBuffer(int* /*outSlot*/, const sp& /*buffer*/) { ALOGE("BufferHubConsumer::attachBuffer: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::releaseBuffer(int /*buf*/, uint64_t /*frameNumber*/, EGLDisplay /*display*/, EGLSyncKHR /*fence*/, const sp& /*releaseFence*/) { ALOGE("BufferHubConsumer::releaseBuffer: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::consumerConnect(const sp& /*consumer*/, bool /*controlledByApp*/) { ALOGE("BufferHubConsumer::consumerConnect: not implemented."); // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to // make IGraphicBufferConsumer_test happy. return NO_ERROR; } status_t BufferHubConsumer::consumerDisconnect() { ALOGE("BufferHubConsumer::consumerDisconnect: not implemented."); // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to // make IGraphicBufferConsumer_test happy. return NO_ERROR; } status_t BufferHubConsumer::getReleasedBuffers(uint64_t* /*slotMask*/) { ALOGE("BufferHubConsumer::getReleasedBuffers: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) { ALOGE("BufferHubConsumer::setDefaultBufferSize: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setMaxBufferCount(int /*bufferCount*/) { ALOGE("BufferHubConsumer::setMaxBufferCount: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setMaxAcquiredBufferCount(int /*maxAcquiredBuffers*/) { ALOGE("BufferHubConsumer::setMaxAcquiredBufferCount: not implemented."); // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to // make IGraphicBufferConsumer_test happy. return NO_ERROR; } status_t BufferHubConsumer::setConsumerName(const String8& /*name*/) { ALOGE("BufferHubConsumer::setConsumerName: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setDefaultBufferFormat(PixelFormat /*defaultFormat*/) { ALOGE("BufferHubConsumer::setDefaultBufferFormat: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setDefaultBufferDataSpace(android_dataspace /*defaultDataSpace*/) { ALOGE("BufferHubConsumer::setDefaultBufferDataSpace: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setConsumerUsageBits(uint64_t /*usage*/) { ALOGE("BufferHubConsumer::setConsumerUsageBits: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setConsumerIsProtected(bool /*isProtected*/) { ALOGE("BufferHubConsumer::setConsumerIsProtected: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::setTransformHint(uint32_t /*hint*/) { ALOGE("BufferHubConsumer::setTransformHint: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::getSidebandStream(sp* /*outStream*/) const { ALOGE("BufferHubConsumer::getSidebandStream: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::getOccupancyHistory( bool /*forceFlush*/, std::vector* /*outHistory*/) { ALOGE("BufferHubConsumer::getOccupancyHistory: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::discardFreeBuffers() { ALOGE("BufferHubConsumer::discardFreeBuffers: not implemented."); return INVALID_OPERATION; } status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const { ALOGE("BufferHubConsumer::dumpState: not implemented."); return INVALID_OPERATION; } IBinder* BufferHubConsumer::onAsBinder() { ALOGE("BufferHubConsumer::onAsBinder: BufferHubConsumer should never be used as an Binder " "object."); return nullptr; } } // namespace android libs/gui/BufferHubProducer.cpp0100644 0000000 0000000 00000100276 13756501735 015424 0ustar000000000 0000000 /* * Copyright 2018 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. */ #include #include #include #include #include #include namespace android { using namespace dvr; /* static */ sp BufferHubProducer::Create(const std::shared_ptr& queue) { sp producer = new BufferHubProducer; producer->queue_ = queue; return producer; } /* static */ sp BufferHubProducer::Create(ProducerQueueParcelable parcelable) { if (!parcelable.IsValid()) { ALOGE("BufferHubProducer::Create: Invalid producer parcelable."); return nullptr; } sp producer = new BufferHubProducer; producer->queue_ = ProducerQueue::Import(parcelable.TakeChannelHandle()); return producer; } status_t BufferHubProducer::requestBuffer(int slot, sp* buf) { ALOGV("requestBuffer: slot=%d", slot); std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("requestBuffer: BufferHubProducer has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= max_buffer_count_) { ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_); return BAD_VALUE; } else if (!buffers_[slot].mBufferState.isDequeued()) { ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", slot, buffers_[slot].mBufferState.string()); return BAD_VALUE; } else if (buffers_[slot].mGraphicBuffer != nullptr) { ALOGE("requestBuffer: slot %d is not empty.", slot); return BAD_VALUE; } else if (buffers_[slot].mProducerBuffer == nullptr) { ALOGE("requestBuffer: slot %d is not dequeued.", slot); return BAD_VALUE; } const auto& producer_buffer = buffers_[slot].mProducerBuffer; sp graphic_buffer = producer_buffer->buffer()->buffer(); buffers_[slot].mGraphicBuffer = graphic_buffer; buffers_[slot].mRequestBufferCalled = true; *buf = graphic_buffer; return NO_ERROR; } status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers) { ALOGV("setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers); std::unique_lock lock(mutex_); if (max_dequeued_buffers <= 0 || max_dequeued_buffers > int(BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) { ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity); return BAD_VALUE; } // The new dequeued_buffers count should not be violated by the number // of currently dequeued buffers. int dequeued_count = 0; for (const auto& buf : buffers_) { if (buf.mBufferState.isDequeued()) { dequeued_count++; } } if (dequeued_count > max_dequeued_buffers) { ALOGE("setMaxDequeuedBufferCount: the requested dequeued_buffers" "count (%d) exceeds the current dequeued buffer count (%d)", max_dequeued_buffers, dequeued_count); return BAD_VALUE; } max_dequeued_buffer_count_ = max_dequeued_buffers; return NO_ERROR; } status_t BufferHubProducer::setAsyncMode(bool async) { if (async) { // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer // automatically and behaves differently from IGraphicBufferConsumer. Thus, // android::BufferQueue's async mode (a.k.a. allocating an additional buffer // to prevent dequeueBuffer from being blocking) technically does not apply // here. // // In Daydream, non-blocking producer side dequeue is guaranteed by careful // buffer consumer implementations. In another word, BufferHubQueue based // dequeueBuffer should never block whether setAsyncMode(true) is set or // not. // // See: IGraphicBufferProducer::setAsyncMode and // BufferQueueProducer::setAsyncMode for more about original implementation. ALOGW("BufferHubProducer::setAsyncMode: BufferHubQueue should always be " "asynchronous. This call makes no effact."); return NO_ERROR; } return NO_ERROR; } status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp* out_fence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* /*outBufferAge*/, FrameEventHistoryDelta* /* out_timestamps */) { ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage); status_t ret; std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("dequeueBuffer: BufferQueue has no connected producer"); return NO_INIT; } const uint32_t kLayerCount = 1; if (int32_t(queue_->capacity()) < max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) { // Lazy allocation. When the capacity of |queue_| has not reached // |max_dequeued_buffer_count_|, allocate new buffer. // TODO(jwcai) To save memory, the really reasonable thing to do is to go // over existing slots and find first existing one to dequeue. ret = AllocateBuffer(width, height, kLayerCount, format, usage); if (ret < 0) return ret; } size_t slot = 0; std::shared_ptr producer_buffer; for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) { LocalHandle fence; auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence); if (!buffer_status) return NO_MEMORY; producer_buffer = buffer_status.take(); if (!producer_buffer) return NO_MEMORY; if (width == producer_buffer->width() && height == producer_buffer->height() && uint32_t(format) == producer_buffer->format()) { // The producer queue returns a producer buffer matches the request. break; } // Needs reallocation. // TODO(jwcai) Consider use VLOG instead if we find this log is not useful. ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different " "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need " "re-allocattion.", width, height, format, slot, producer_buffer->width(), producer_buffer->height(), producer_buffer->format()); // Mark the slot as reallocating, so that later we can set // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued. buffers_[slot].mIsReallocating = true; // Remove the old buffer once the allocation before allocating its // replacement. RemoveBuffer(slot); // Allocate a new producer buffer with new buffer configs. Note that if // there are already multiple buffers in the queue, the next one returned // from |queue_->Dequeue| may not be the new buffer we just reallocated. // Retry up to BufferHubQueue::kMaxQueueCapacity times. ret = AllocateBuffer(width, height, kLayerCount, format, usage); if (ret < 0) return ret; } // With the BufferHub backed solution. Buffer slot returned from // |queue_->Dequeue| is guaranteed to avaiable for producer's use. // It's either in free state (if the buffer has never been used before) or // in queued state (if the buffer has been dequeued and queued back to // BufferHubQueue). LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()), "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot, buffers_[slot].mBufferState.string()); buffers_[slot].mBufferState.freeQueued(); buffers_[slot].mBufferState.dequeue(); ALOGV("dequeueBuffer: slot=%zu", slot); // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we // just need to exopose that through |BufferHubQueue| once we need fence. *out_fence = Fence::NO_FENCE; *out_slot = int(slot); ret = NO_ERROR; if (buffers_[slot].mIsReallocating) { ret |= BUFFER_NEEDS_REALLOCATION; buffers_[slot].mIsReallocating = false; } return ret; } status_t BufferHubProducer::detachBuffer(int slot) { ALOGV("detachBuffer: slot=%d", slot); std::unique_lock lock(mutex_); return DetachBufferLocked(static_cast(slot)); } status_t BufferHubProducer::DetachBufferLocked(size_t slot) { if (connected_api_ == kNoConnectedApi) { ALOGE("detachBuffer: BufferHubProducer is not connected."); return NO_INIT; } if (slot >= static_cast(max_buffer_count_)) { ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_); return BAD_VALUE; } else if (!buffers_[slot].mBufferState.isDequeued()) { ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot, buffers_[slot].mBufferState.string()); return BAD_VALUE; } else if (!buffers_[slot].mRequestBufferCalled) { ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot); return BAD_VALUE; } std::shared_ptr producer_buffer = queue_->GetBuffer(slot); if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) { ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot); return BAD_VALUE; } sp graphic_buffer = producer_buffer->buffer()->buffer(); if (graphic_buffer == nullptr) { ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot); return BAD_VALUE; } // Remove the ProducerBuffer from the ProducerQueue. status_t error = RemoveBuffer(slot); if (error != NO_ERROR) { ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error); return error; } // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject // the handle into the GraphicBuffer object at the requested slot. auto status_or_handle = producer_buffer->Detach(); if (!status_or_handle.ok()) { ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot, status_or_handle.error()); return BAD_VALUE; } // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can // be directly backed by BufferHub. return INVALID_OPERATION; } status_t BufferHubProducer::detachNextBuffer(sp* out_buffer, sp* out_fence) { ALOGV("detachNextBuffer."); if (out_buffer == nullptr || out_fence == nullptr) { ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer, out_fence); return BAD_VALUE; } std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("detachNextBuffer: BufferHubProducer is not connected."); return NO_INIT; } // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in // sequence, except for two things: // // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the // function just returns whatever ProducerBuffer is available from the ProducerQueue and no // buffer allocation or re-allocation will happen. // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return // an error instead. size_t slot = 0; LocalHandle fence; // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error // immediately if ProducerQueue::Dequeue() fails. auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence); if (!status_or_buffer.ok()) { ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error()); return NO_MEMORY; } std::shared_ptr producer_buffer = status_or_buffer.take(); if (producer_buffer == nullptr) { ALOGE("detachNextBuffer: Dequeued buffer is null."); return NO_MEMORY; } // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to // be available for producer's use. It's either in free state (if the buffer has never been used // before) or in queued state (if the buffer has been dequeued and queued back to // BufferHubQueue). if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) { ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot, buffers_[slot].mBufferState.string()); return BAD_VALUE; } if (buffers_[slot].mProducerBuffer == nullptr) { ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot); return BAD_VALUE; } if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) { ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: " "%d, expected: %d.", slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id()); return BAD_VALUE; } ALOGV("detachNextBuffer: slot=%zu", slot); buffers_[slot].mBufferState.freeQueued(); buffers_[slot].mBufferState.dequeue(); // Second, request the buffer. sp graphic_buffer = producer_buffer->buffer()->buffer(); buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer(); // Finally, detach the buffer and then return. status_t error = DetachBufferLocked(slot); if (error == NO_ERROR) { *out_fence = new Fence(fence.Release()); *out_buffer = graphic_buffer; } return error; } status_t BufferHubProducer::attachBuffer(int* out_slot, const sp& buffer) { // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer. ALOGV("queueBuffer: buffer=%p", buffer.get()); if (out_slot == nullptr) { ALOGE("attachBuffer: out_slot cannot be NULL."); return BAD_VALUE; } if (buffer == nullptr) { ALOGE("attachBuffer: invalid GraphicBuffer."); return BAD_VALUE; } std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("attachBuffer: BufferQueue has no connected producer"); return NO_INIT; } // Before attaching the buffer, caller is supposed to call // IGraphicBufferProducer::setGenerationNumber to inform the // BufferHubProducer the next generation number. if (buffer->getGenerationNumber() != generation_number_) { ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.", buffer->getGenerationNumber(), generation_number_); return BAD_VALUE; } // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can // be directly backed by BufferHub. return INVALID_OPERATION; } status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) { ALOGV("queueBuffer: slot %d", slot); if (output == nullptr) { return BAD_VALUE; } int64_t timestamp; bool is_auto_timestamp; android_dataspace dataspace; Rect crop(Rect::EMPTY_RECT); int scaling_mode; uint32_t transform; sp fence; input.deflate(×tamp, &is_auto_timestamp, &dataspace, &crop, &scaling_mode, &transform, &fence); // Check input scaling mode is valid. switch (scaling_mode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: break; default: ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode); return BAD_VALUE; } // Check input fence is valid. if (fence == nullptr) { ALOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("queueBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= max_buffer_count_) { ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_); return BAD_VALUE; } else if (!buffers_[slot].mBufferState.isDequeued()) { ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", slot, buffers_[slot].mBufferState.string()); return BAD_VALUE; } else if ((!buffers_[slot].mRequestBufferCalled || buffers_[slot].mGraphicBuffer == nullptr)) { ALOGE("queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, " "mGraphicBuffer=%p)", slot, buffers_[slot].mRequestBufferCalled, buffers_[slot].mGraphicBuffer.get()); return BAD_VALUE; } // Post the producer buffer with timestamp in the metadata. const auto& producer_buffer = buffers_[slot].mProducerBuffer; // Check input crop is not out of boundary of current buffer. Rect buffer_rect(producer_buffer->width(), producer_buffer->height()); Rect cropped_rect(Rect::EMPTY_RECT); crop.intersect(buffer_rect, &cropped_rect); if (cropped_rect != crop) { ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot); return BAD_VALUE; } LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1); DvrNativeBufferMetadata meta_data; meta_data.timestamp = timestamp; meta_data.is_auto_timestamp = int32_t(is_auto_timestamp); meta_data.dataspace = int32_t(dataspace); meta_data.crop_left = crop.left; meta_data.crop_top = crop.top; meta_data.crop_right = crop.right; meta_data.crop_bottom = crop.bottom; meta_data.scaling_mode = int32_t(scaling_mode); meta_data.transform = int32_t(transform); producer_buffer->PostAsync(&meta_data, fence_fd); buffers_[slot].mBufferState.queue(); output->width = producer_buffer->width(); output->height = producer_buffer->height(); output->transformHint = 0; // default value, we don't use it yet. // |numPendingBuffers| counts of the number of buffers that has been enqueued // by the producer but not yet acquired by the consumer. Due to the nature // of BufferHubQueue design, this is hard to trace from the producer's client // side, but it's safe to assume it's zero. output->numPendingBuffers = 0; // Note that we are not setting nextFrameNumber here as it seems to be only // used by surface flinger. See more at b/22802885, ag/791760. output->nextFrameNumber = 0; return NO_ERROR; } status_t BufferHubProducer::cancelBuffer(int slot, const sp& fence) { ALOGV(__FUNCTION__); std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("cancelBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= max_buffer_count_) { ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_); return BAD_VALUE; } else if (!buffers_[slot].mBufferState.isDequeued()) { ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", slot, buffers_[slot].mBufferState.string()); return BAD_VALUE; } else if (fence == nullptr) { ALOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } auto producer_buffer = buffers_[slot].mProducerBuffer; queue_->Enqueue(producer_buffer, size_t(slot), 0U); buffers_[slot].mBufferState.cancel(); buffers_[slot].mFence = fence; ALOGV("cancelBuffer: slot %d", slot); return NO_ERROR; } status_t BufferHubProducer::query(int what, int* out_value) { ALOGV(__FUNCTION__); std::unique_lock lock(mutex_); if (out_value == nullptr) { ALOGE("query: out_value was NULL"); return BAD_VALUE; } int value = 0; switch (what) { case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: // TODO(b/36187402) This should be the maximum number of buffers that this // producer queue's consumer can acquire. Set to be at least one. Need to // find a way to set from the consumer side. value = kDefaultUndequeuedBuffers; break; case NATIVE_WINDOW_BUFFER_AGE: value = 0; break; case NATIVE_WINDOW_WIDTH: value = int32_t(queue_->default_width()); break; case NATIVE_WINDOW_HEIGHT: value = int32_t(queue_->default_height()); break; case NATIVE_WINDOW_FORMAT: value = int32_t(queue_->default_format()); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: // BufferHubQueue is always operating in async mode, thus semantically // consumer can never be running behind. See BufferQueueCore.cpp core // for more information about the original meaning of this flag. value = 0; break; case NATIVE_WINDOW_CONSUMER_USAGE_BITS: // TODO(jwcai) This is currently not implement as we don't need // IGraphicBufferConsumer parity. value = 0; break; case NATIVE_WINDOW_DEFAULT_DATASPACE: // TODO(jwcai) Return the default value android::BufferQueue is using as // there is no way dvr::ConsumerQueue can set it. value = 0; // HAL_DATASPACE_UNKNOWN break; case NATIVE_WINDOW_STICKY_TRANSFORM: // TODO(jwcai) Return the default value android::BufferQueue is using as // there is no way dvr::ConsumerQueue can set it. value = 0; break; case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: // In Daydream's implementation, the consumer end (i.e. VR Compostior) // knows how to handle protected buffers. value = 1; break; default: return BAD_VALUE; } ALOGV("query: key=%d, v=%d", what, value); *out_value = value; return NO_ERROR; } status_t BufferHubProducer::connect(const sp& /* listener */, int api, bool /* producer_controlled_by_app */, QueueBufferOutput* output) { // Consumer interaction are actually handled by buffer hub, and we need // to maintain consumer operations here. We only need to perform basic input // parameter checks here. ALOGV(__FUNCTION__); if (output == nullptr) { return BAD_VALUE; } std::unique_lock lock(mutex_); if (connected_api_ != kNoConnectedApi) { return BAD_VALUE; } if (!queue_->is_connected()) { ALOGE("BufferHubProducer::connect: This BufferHubProducer is not " "connected to bufferhud. Has it been taken out as a parcelable?"); return BAD_VALUE; } switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: connected_api_ = api; output->width = queue_->default_width(); output->height = queue_->default_height(); // default values, we don't use them yet. output->transformHint = 0; output->numPendingBuffers = 0; output->nextFrameNumber = 0; output->bufferReplaced = false; break; default: ALOGE("BufferHubProducer::connect: unknow API %d", api); return BAD_VALUE; } return NO_ERROR; } status_t BufferHubProducer::disconnect(int api, DisconnectMode /*mode*/) { // Consumer interaction are actually handled by buffer hub, and we need // to maintain consumer operations here. We only need to perform basic input // parameter checks here. ALOGV(__FUNCTION__); std::unique_lock lock(mutex_); if (kNoConnectedApi == connected_api_) { return NO_INIT; } else if (api != connected_api_) { return BAD_VALUE; } FreeAllBuffers(); connected_api_ = kNoConnectedApi; return NO_ERROR; } status_t BufferHubProducer::setSidebandStream(const sp& stream) { if (stream != nullptr) { // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's // metadata. ALOGE("SidebandStream is not currently supported."); return INVALID_OPERATION; } return NO_ERROR; } void BufferHubProducer::allocateBuffers(uint32_t /* width */, uint32_t /* height */, PixelFormat /* format */, uint64_t /* usage */) { // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number // of buffers permitted by the current BufferQueue configuration (aka // |max_buffer_count_|). ALOGE("BufferHubProducer::allocateBuffers not implemented."); } status_t BufferHubProducer::allowAllocation(bool /* allow */) { ALOGE("BufferHubProducer::allowAllocation not implemented."); return INVALID_OPERATION; } status_t BufferHubProducer::setGenerationNumber(uint32_t generation_number) { ALOGV(__FUNCTION__); std::unique_lock lock(mutex_); generation_number_ = generation_number; return NO_ERROR; } String8 BufferHubProducer::getConsumerName() const { // BufferHub based implementation could have one to many producer/consumer // relationship, thus |getConsumerName| from the producer side does not // make any sense. ALOGE("BufferHubProducer::getConsumerName not supported."); return String8("BufferHubQueue::DummyConsumer"); } status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) { if (shared_buffer_mode) { ALOGE("BufferHubProducer::setSharedBufferMode(true) is not supported."); // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow. return INVALID_OPERATION; } // Setting to default should just work as a no-op. return NO_ERROR; } status_t BufferHubProducer::setAutoRefresh(bool auto_refresh) { if (auto_refresh) { ALOGE("BufferHubProducer::setAutoRefresh(true) is not supported."); return INVALID_OPERATION; } // Setting to default should just work as a no-op. return NO_ERROR; } status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) { ALOGV(__FUNCTION__); std::unique_lock lock(mutex_); dequeue_timeout_ms_ = static_cast(timeout / (1000 * 1000)); return NO_ERROR; } status_t BufferHubProducer::getLastQueuedBuffer(sp* /* out_buffer */, sp* /* out_fence */, float /*out_transform_matrix*/[16]) { ALOGE("BufferHubProducer::getLastQueuedBuffer not implemented."); return INVALID_OPERATION; } void BufferHubProducer::getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) { ALOGE("BufferHubProducer::getFrameTimestamps not implemented."); } status_t BufferHubProducer::getUniqueId(uint64_t* out_id) const { ALOGV(__FUNCTION__); *out_id = unique_id_; return NO_ERROR; } status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const { ALOGV(__FUNCTION__); // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS *out_usage = 0; return NO_ERROR; } status_t BufferHubProducer::TakeAsParcelable(ProducerQueueParcelable* out_parcelable) { if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE; if (connected_api_ != kNoConnectedApi) { ALOGE("BufferHubProducer::TakeAsParcelable: BufferHubProducer has " "connected client. Must disconnect first."); return BAD_VALUE; } if (!queue_->is_connected()) { ALOGE("BufferHubProducer::TakeAsParcelable: This BufferHubProducer " "is not connected to bufferhud. Has it been taken out as a " "parcelable?"); return BAD_VALUE; } auto status = queue_->TakeAsParcelable(); if (!status) { ALOGE("BufferHubProducer::TakeAsParcelable: Failed to take out " "ProducuerQueueParcelable from the producer queue, error: %s.", status.GetErrorMessage().c_str()); return BAD_VALUE; } *out_parcelable = status.take(); return NO_ERROR; } status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, PixelFormat format, uint64_t usage) { auto status = queue_->AllocateBuffer(width, height, layer_count, uint32_t(format), usage); if (!status) { ALOGE("BufferHubProducer::AllocateBuffer: Failed to allocate buffer: %s", status.GetErrorMessage().c_str()); return NO_MEMORY; } size_t slot = status.get(); auto producer_buffer = queue_->GetBuffer(slot); LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr, "Failed to get the producer buffer at slot: %zu", slot); buffers_[slot].mProducerBuffer = producer_buffer; return NO_ERROR; } status_t BufferHubProducer::RemoveBuffer(size_t slot) { auto status = queue_->RemoveBuffer(slot); if (!status) { ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.", slot, status.GetErrorMessage().c_str()); return INVALID_OPERATION; } // Reset in memory objects related the the buffer. buffers_[slot].mProducerBuffer = nullptr; buffers_[slot].mBufferState.detachProducer(); buffers_[slot].mFence = Fence::NO_FENCE; buffers_[slot].mGraphicBuffer = nullptr; buffers_[slot].mRequestBufferCalled = false; return NO_ERROR; } status_t BufferHubProducer::FreeAllBuffers() { for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { // Reset in memory objects related the the buffer. buffers_[slot].mProducerBuffer = nullptr; buffers_[slot].mBufferState.reset(); buffers_[slot].mFence = Fence::NO_FENCE; buffers_[slot].mGraphicBuffer = nullptr; buffers_[slot].mRequestBufferCalled = false; } auto status = queue_->FreeAllBuffers(); if (!status) { ALOGE("BufferHubProducer::FreeAllBuffers: Failed to free all buffers on " "the queue: %s", status.GetErrorMessage().c_str()); } if (queue_->capacity() != 0 || queue_->count() != 0) { LOG_ALWAYS_FATAL("BufferHubProducer::FreeAllBuffers: Not all buffers are freed."); } return NO_ERROR; } status_t BufferHubProducer::exportToParcel(Parcel* parcel) { status_t res = TakeAsParcelable(&pending_producer_parcelable_); if (res != NO_ERROR) return res; if (!pending_producer_parcelable_.IsValid()) { ALOGE("BufferHubProducer::exportToParcel: Invalid parcelable object."); return BAD_VALUE; } res = parcel->writeUint32(USE_BUFFER_HUB); if (res != NO_ERROR) { ALOGE("BufferHubProducer::exportToParcel: Cannot write magic, res=%d.", res); return res; } return pending_producer_parcelable_.writeToParcel(parcel); } IBinder* BufferHubProducer::onAsBinder() { ALOGE("BufferHubProducer::onAsBinder: BufferHubProducer should never be used as an Binder " "object."); return nullptr; } } // namespace android libs/gui/BufferItem.cpp0100644 0000000 0000000 00000020340 13756501735 014071 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #include namespace android { template static inline constexpr uint32_t low32(const T n) { return static_cast(static_cast(n)); } template static inline constexpr uint32_t high32(const T n) { return static_cast(static_cast(n)>>32); } template static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast(static_cast(hi)<<32 | lo); } BufferItem::BufferItem() : mGraphicBuffer(nullptr), mFence(nullptr), mCrop(Rect::INVALID_RECT), mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTimestamp(0), mIsAutoTimestamp(false), mDataSpace(HAL_DATASPACE_UNKNOWN), mFrameNumber(0), mSlot(INVALID_BUFFER_SLOT), mIsDroppable(false), mAcquireCalled(false), mTransformToDisplayInverse(false), mSurfaceDamage(), mAutoRefresh(false), mQueuedBuffer(true), mIsStale(false), mApi(0) { } BufferItem::~BufferItem() {} template static void addAligned(size_t& size, T /* value */) { size = FlattenableUtils::align(size); size += sizeof(T); } size_t BufferItem::getPodSize() const { size_t size = 0; addAligned(size, mCrop); addAligned(size, mTransform); addAligned(size, mScalingMode); addAligned(size, low32(mTimestamp)); addAligned(size, high32(mTimestamp)); addAligned(size, mIsAutoTimestamp); addAligned(size, mDataSpace); addAligned(size, low32(mFrameNumber)); addAligned(size, high32(mFrameNumber)); addAligned(size, mSlot); addAligned(size, mIsDroppable); addAligned(size, mAcquireCalled); addAligned(size, mTransformToDisplayInverse); addAligned(size, mAutoRefresh); addAligned(size, mQueuedBuffer); addAligned(size, mIsStale); addAligned(size, mApi); return size; } size_t BufferItem::getFlattenedSize() const { size_t size = sizeof(uint32_t); // Flags if (mGraphicBuffer != nullptr) { size += mGraphicBuffer->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } if (mFence != nullptr) { size += mFence->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } size += mSurfaceDamage.getFlattenedSize(); size += mHdrMetadata.getFlattenedSize(); size = FlattenableUtils::align<8>(size); return size + getPodSize(); } size_t BufferItem::getFdCount() const { size_t count = 0; if (mGraphicBuffer != nullptr) { count += mGraphicBuffer->getFdCount(); } if (mFence != nullptr) { count += mFence->getFdCount(); } return count; } template static void writeAligned(void*& buffer, size_t& size, T value) { size -= FlattenableUtils::align(buffer); FlattenableUtils::write(buffer, size, value); } status_t BufferItem::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { // make sure we have enough space if (size < BufferItem::getFlattenedSize()) { return NO_MEMORY; } // content flags are stored first uint32_t& flags = *static_cast(buffer); // advance the pointer FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); flags = 0; if (mGraphicBuffer != nullptr) { status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); flags |= 1; } if (mFence != nullptr) { status_t err = mFence->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); flags |= 2; } status_t err = mSurfaceDamage.flatten(buffer, size); if (err) return err; FlattenableUtils::advance(buffer, size, mSurfaceDamage.getFlattenedSize()); err = mHdrMetadata.flatten(buffer, size); if (err) return err; FlattenableUtils::advance(buffer, size, mHdrMetadata.getFlattenedSize()); // Check we still have enough space if (size < getPodSize()) { return NO_MEMORY; } writeAligned(buffer, size, mCrop); writeAligned(buffer, size, mTransform); writeAligned(buffer, size, mScalingMode); writeAligned(buffer, size, low32(mTimestamp)); writeAligned(buffer, size, high32(mTimestamp)); writeAligned(buffer, size, mIsAutoTimestamp); writeAligned(buffer, size, mDataSpace); writeAligned(buffer, size, low32(mFrameNumber)); writeAligned(buffer, size, high32(mFrameNumber)); writeAligned(buffer, size, mSlot); writeAligned(buffer, size, mIsDroppable); writeAligned(buffer, size, mAcquireCalled); writeAligned(buffer, size, mTransformToDisplayInverse); writeAligned(buffer, size, mAutoRefresh); writeAligned(buffer, size, mQueuedBuffer); writeAligned(buffer, size, mIsStale); writeAligned(buffer, size, mApi); return NO_ERROR; } template static void readAligned(const void*& buffer, size_t& size, T& value) { size -= FlattenableUtils::align(buffer); FlattenableUtils::read(buffer, size, value); } status_t BufferItem::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < sizeof(uint32_t)) { return NO_MEMORY; } uint32_t flags = 0; FlattenableUtils::read(buffer, size, flags); if (flags & 1) { mGraphicBuffer = new GraphicBuffer(); status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); } if (flags & 2) { mFence = new Fence(); status_t err = mFence->unflatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); mFenceTime = std::make_shared(mFence); } status_t err = mSurfaceDamage.unflatten(buffer, size); if (err) return err; FlattenableUtils::advance(buffer, size, mSurfaceDamage.getFlattenedSize()); err = mHdrMetadata.unflatten(buffer, size); if (err) return err; FlattenableUtils::advance(buffer, size, mHdrMetadata.getFlattenedSize()); // Check we still have enough space if (size < getPodSize()) { return NO_MEMORY; } uint32_t timestampLo = 0, timestampHi = 0; uint32_t frameNumberLo = 0, frameNumberHi = 0; readAligned(buffer, size, mCrop); readAligned(buffer, size, mTransform); readAligned(buffer, size, mScalingMode); readAligned(buffer, size, timestampLo); readAligned(buffer, size, timestampHi); mTimestamp = to64(timestampLo, timestampHi); readAligned(buffer, size, mIsAutoTimestamp); readAligned(buffer, size, mDataSpace); readAligned(buffer, size, frameNumberLo); readAligned(buffer, size, frameNumberHi); mFrameNumber = to64(frameNumberLo, frameNumberHi); readAligned(buffer, size, mSlot); readAligned(buffer, size, mIsDroppable); readAligned(buffer, size, mAcquireCalled); readAligned(buffer, size, mTransformToDisplayInverse); readAligned(buffer, size, mAutoRefresh); readAligned(buffer, size, mQueuedBuffer); readAligned(buffer, size, mIsStale); readAligned(buffer, size, mApi); return NO_ERROR; } const char* BufferItem::scalingModeName(uint32_t scalingMode) { switch (scalingMode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE"; case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW"; case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP"; default: return "Unknown"; } } } // namespace android libs/gui/BufferItemConsumer.cpp0100644 0000000 0000000 00000007612 13756501735 015614 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "BufferItemConsumer" //#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) //#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) //#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) //#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) #define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) namespace android { BufferItemConsumer::BufferItemConsumer( const sp& consumer, uint64_t consumerUsage, int bufferCount, bool controlledByApp) : ConsumerBase(consumer, controlledByApp) { status_t err = mConsumer->setConsumerUsageBits(consumerUsage); LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set consumer usage bits to %#" PRIx64, consumerUsage); if (bufferCount != DEFAULT_MAX_BUFFERS) { err = mConsumer->setMaxAcquiredBufferCount(bufferCount); LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set max acquired buffer count to %d", bufferCount); } } BufferItemConsumer::~BufferItemConsumer() {} void BufferItemConsumer::setBufferFreedListener( const wp& listener) { Mutex::Autolock _l(mMutex); mBufferFreedListener = listener; } status_t BufferItemConsumer::acquireBuffer(BufferItem *item, nsecs_t presentWhen, bool waitForFence) { status_t err; if (!item) return BAD_VALUE; Mutex::Autolock _l(mMutex); err = acquireBufferLocked(item, presentWhen); if (err != OK) { if (err != NO_BUFFER_AVAILABLE) { BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); } return err; } if (waitForFence) { err = item->mFence->waitForever("BufferItemConsumer::acquireBuffer"); if (err != OK) { BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", strerror(-err), err); return err; } } item->mGraphicBuffer = mSlots[item->mSlot].mGraphicBuffer; return OK; } status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, const sp& releaseFence) { status_t err; Mutex::Autolock _l(mMutex); err = addReleaseFenceLocked(item.mSlot, item.mGraphicBuffer, releaseFence); if (err != OK) { BI_LOGE("Failed to addReleaseFenceLocked"); } err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != OK && err != IGraphicBufferConsumer::STALE_BUFFER_SLOT) { BI_LOGE("Failed to release buffer: %s (%d)", strerror(-err), err); } return err; } void BufferItemConsumer::freeBufferLocked(int slotIndex) { sp listener = mBufferFreedListener.promote(); if (listener != nullptr && mSlots[slotIndex].mGraphicBuffer != nullptr) { // Fire callback if we have a listener registered and the buffer being freed is valid. BI_LOGV("actually calling onBufferFreed"); listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer); } ConsumerBase::freeBufferLocked(slotIndex); } } // namespace android libs/gui/BufferQueue.cpp0100644 0000000 0000000 00000011545 13756501735 014266 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #define LOG_TAG "BufferQueue" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #ifndef NO_BUFFERHUB #include #include #endif #include #include #include #include namespace android { BufferQueue::ProxyConsumerListener::ProxyConsumerListener( const wp& consumerListener): mConsumerListener(consumerListener) {} BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} void BufferQueue::ProxyConsumerListener::onDisconnect() { sp listener(mConsumerListener.promote()); if (listener != nullptr) { listener->onDisconnect(); } } void BufferQueue::ProxyConsumerListener::onFrameAvailable( const BufferItem& item) { sp listener(mConsumerListener.promote()); if (listener != nullptr) { listener->onFrameAvailable(item); } } void BufferQueue::ProxyConsumerListener::onFrameReplaced( const BufferItem& item) { sp listener(mConsumerListener.promote()); if (listener != nullptr) { listener->onFrameReplaced(item); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { sp listener(mConsumerListener.promote()); if (listener != nullptr) { listener->onBuffersReleased(); } } void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { sp listener(mConsumerListener.promote()); if (listener != nullptr) { listener->onSidebandStreamChanged(); } } void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { sp listener(mConsumerListener.promote()); if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } void BufferQueue::createBufferQueue(sp* outProducer, sp* outConsumer, bool consumerIsSurfaceFlinger) { LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp core(new BufferQueueCore()); LOG_ALWAYS_FATAL_IF(core == nullptr, "BufferQueue: failed to create BufferQueueCore"); sp producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); sp consumer(new BufferQueueConsumer(core)); LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; } #ifndef NO_BUFFERHUB void BufferQueue::createBufferHubQueue(sp* outProducer, sp* outConsumer) { LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp producer; sp consumer; dvr::ProducerQueueConfigBuilder configBuilder; std::shared_ptr producerQueue = dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{}); LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue."); std::shared_ptr consumerQueue = producerQueue->CreateConsumerQueue(); LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue."); producer = BufferHubProducer::Create(producerQueue); consumer = BufferHubConsumer::Create(consumerQueue); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; } #endif }; // namespace android libs/gui/BufferQueueConsumer.cpp0100644 0000000 0000000 00000073116 13756501735 016004 0ustar000000000 0000000 /* * Copyright 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. */ #include #include #include #define LOG_TAG "BufferQueueConsumer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #if DEBUG_ONLY_CODE #define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0) #else #define VALIDATE_CONSISTENCY() #endif #include #include #include #include #include #include #ifndef __ANDROID_VNDK__ #include #include #endif #include namespace android { BufferQueueConsumer::BufferQueueConsumer(const sp& core) : mCore(core), mSlots(core->mSlots), mConsumerName() {} BufferQueueConsumer::~BufferQueueConsumer() {} status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber) { ATRACE_CALL(); int numDroppedBuffers = 0; sp listener; { std::unique_lock lock(mCore->mMutex); // Check that the consumer doesn't currently have the maximum number of // buffers acquired. We allow the max buffer count to be exceeded by one // buffer so that the consumer can successfully set up the newly acquired // buffer before releasing the old one. int numAcquiredBuffers = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } } if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)", numAcquiredBuffers, mCore->mMaxAcquiredBufferCount); return INVALID_OPERATION; } bool sharedBufferAvailable = mCore->mSharedBufferMode && mCore->mAutoRefresh && mCore->mSharedBufferSlot != BufferQueueCore::INVALID_BUFFER_SLOT; // In asynchronous mode the list is guaranteed to be one buffer deep, // while in synchronous mode we use the oldest buffer. if (mCore->mQueue.empty() && !sharedBufferAvailable) { return NO_BUFFER_AVAILABLE; } BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); // If expectedPresent is specified, we may not want to return a buffer yet. // If it's specified and there's more than one buffer queued, we may want // to drop a buffer. // Skip this if we're in shared buffer mode and the queue is empty, // since in that case we'll just return the shared buffer. if (expectedPresent != 0 && !mCore->mQueue.empty()) { // The 'expectedPresent' argument indicates when the buffer is expected // to be presented on-screen. If the buffer's desired present time is // earlier (less) than expectedPresent -- meaning it will be displayed // on time or possibly late if we show it as soon as possible -- we // acquire and return it. If we don't want to display it until after the // expectedPresent time, we return PRESENT_LATER without acquiring it. // // To be safe, we don't defer acquisition if expectedPresent is more // than one second in the future beyond the desired present time // (i.e., we'd be holding the buffer for a long time). // // NOTE: Code assumes monotonic time values from the system clock // are positive. // Start by checking to see if we can drop frames. We skip this check if // the timestamps are being auto-generated by Surface. If the app isn't // generating timestamps explicitly, it probably doesn't want frames to // be discarded based on them. while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) { const BufferItem& bufferItem(mCore->mQueue[1]); // If dropping entry[0] would leave us with a buffer that the // consumer is not yet ready for, don't drop it. if (maxFrameNumber && bufferItem.mFrameNumber > maxFrameNumber) { break; } // If entry[1] is timely, drop entry[0] (and repeat). We apply an // additional criterion here: we only drop the earlier buffer if our // desiredPresent falls within +/- 1 second of the expected present. // Otherwise, bogus desiredPresent times (e.g., 0 or a small // relative timestamp), which normally mean "ignore the timestamp // and acquire immediately", would cause us to drop frames. // // We may want to add an additional criterion: don't drop the // earlier buffer if entry[1]'s fence hasn't signaled yet. nsecs_t desiredPresent = bufferItem.mTimestamp; if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC || desiredPresent > expectedPresent) { // This buffer is set to display in the near future, or // desiredPresent is garbage. Either way we don't want to drop // the previous buffer just to get this on the screen sooner. BQ_LOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%" PRId64 " (%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent, desiredPresent - expectedPresent, systemTime(CLOCK_MONOTONIC)); break; } BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64 " size=%zu", desiredPresent, expectedPresent, mCore->mQueue.size()); if (!front->mIsStale) { // Front buffer is still in mSlots, so mark the slot as free mSlots[front->mSlot].mBufferState.freeQueued(); // After leaving shared buffer mode, the shared buffer will // still be around. Mark it as no longer shared if this // operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[front->mSlot].mBufferState.isFree()) { mSlots[front->mSlot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list if (!mSlots[front->mSlot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(front->mSlot); mCore->mFreeBuffers.push_back(front->mSlot); } if (mCore->mBufferReleasedCbEnabled) { listener = mCore->mConnectedProducerListener; } ++numDroppedBuffers; } mCore->mQueue.erase(front); front = mCore->mQueue.begin(); } // See if the front buffer is ready to be acquired nsecs_t desiredPresent = front->mTimestamp; bool bufferIsDue = desiredPresent <= expectedPresent || desiredPresent > expectedPresent + MAX_REASONABLE_NSEC; bool consumerIsReady = maxFrameNumber > 0 ? front->mFrameNumber <= maxFrameNumber : true; if (!bufferIsDue || !consumerIsReady) { BQ_LOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64 " (%" PRId64 ") now=%" PRId64 " frame=%" PRIu64 " consumer=%" PRIu64, desiredPresent, expectedPresent, desiredPresent - expectedPresent, systemTime(CLOCK_MONOTONIC), front->mFrameNumber, maxFrameNumber); ATRACE_NAME("PRESENT_LATER"); return PRESENT_LATER; } BQ_LOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " " "(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent, desiredPresent - expectedPresent, systemTime(CLOCK_MONOTONIC)); } int slot = BufferQueueCore::INVALID_BUFFER_SLOT; if (sharedBufferAvailable && mCore->mQueue.empty()) { // make sure the buffer has finished allocating before acquiring it mCore->waitWhileAllocatingLocked(lock); slot = mCore->mSharedBufferSlot; // Recreate the BufferItem for the shared buffer from the data that // was cached when it was last queued. outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer; outBuffer->mFence = Fence::NO_FENCE; outBuffer->mFenceTime = FenceTime::NO_FENCE; outBuffer->mCrop = mCore->mSharedBufferCache.crop; outBuffer->mTransform = mCore->mSharedBufferCache.transform & ~static_cast( NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); outBuffer->mScalingMode = mCore->mSharedBufferCache.scalingMode; outBuffer->mDataSpace = mCore->mSharedBufferCache.dataspace; outBuffer->mFrameNumber = mCore->mFrameCounter; outBuffer->mSlot = slot; outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled; outBuffer->mTransformToDisplayInverse = (mCore->mSharedBufferCache.transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0; outBuffer->mSurfaceDamage = Region::INVALID_REGION; outBuffer->mQueuedBuffer = false; outBuffer->mIsStale = false; outBuffer->mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh; } else { slot = front->mSlot; *outBuffer = *front; } ATRACE_BUFFER_INDEX(slot); BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }", slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle); if (!outBuffer->mIsStale) { mSlots[slot].mAcquireCalled = true; // Don't decrease the queue count if the BufferItem wasn't // previously in the queue. This happens in shared buffer mode when // the queue is empty and the BufferItem is created above. if (mCore->mQueue.empty()) { mSlots[slot].mBufferState.acquireNotInQueue(); } else { mSlots[slot].mBufferState.acquire(); } mSlots[slot].mFence = Fence::NO_FENCE; } // If the buffer has previously been acquired by the consumer, set // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer // on the consumer side if (outBuffer->mAcquireCalled) { outBuffer->mGraphicBuffer = nullptr; } mCore->mQueue.erase(front); // We might have freed a slot while dropping old buffers, or the producer // may be blocked waiting for the number of buffers in the queue to // decrease. mCore->mDequeueCondition.notify_all(); ATRACE_INT(mCore->mConsumerName.string(), static_cast(mCore->mQueue.size())); mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); VALIDATE_CONSISTENCY(); } if (listener != nullptr) { for (int i = 0; i < numDroppedBuffers; ++i) { listener->onBufferReleased(); } } return NO_ERROR; } status_t BufferQueueConsumer::detachBuffer(int slot) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); BQ_LOGV("detachBuffer: slot %d", slot); std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mSharedBufferMode || slot == mCore->mSharedBufferSlot) { BQ_LOGE("detachBuffer: detachBuffer not allowed in shared buffer mode"); return BAD_VALUE; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isAcquired()) { BQ_LOGE("detachBuffer: slot %d is not owned by the consumer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } mSlots[slot].mBufferState.detachConsumer(); mCore->mActiveBuffers.erase(slot); mCore->mFreeSlots.insert(slot); mCore->clearBufferSlotLocked(slot); mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); return NO_ERROR; } status_t BufferQueueConsumer::attachBuffer(int* outSlot, const sp& buffer) { ATRACE_CALL(); if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } std::lock_guard lock(mCore->mMutex); if (mCore->mSharedBufferMode) { BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode"); return BAD_VALUE; } // Make sure we don't have too many acquired buffers int numAcquiredBuffers = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } } if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { BQ_LOGE("attachBuffer: max acquired buffer count reached: %d " "(max %d)", numAcquiredBuffers, mCore->mMaxAcquiredBufferCount); return INVALID_OPERATION; } if (buffer->getGenerationNumber() != mCore->mGenerationNumber) { BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] " "[queue %u]", buffer->getGenerationNumber(), mCore->mGenerationNumber); return BAD_VALUE; } // Find a free slot to put the buffer into int found = BufferQueueCore::INVALID_BUFFER_SLOT; if (!mCore->mFreeSlots.empty()) { auto slot = mCore->mFreeSlots.begin(); found = *slot; mCore->mFreeSlots.erase(slot); } else if (!mCore->mFreeBuffers.empty()) { found = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.remove(found); } if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("attachBuffer: could not find free buffer slot"); return NO_MEMORY; } mCore->mActiveBuffers.insert(found); *outSlot = found; ATRACE_BUFFER_INDEX(*outSlot); BQ_LOGV("attachBuffer: returning slot %d", *outSlot); mSlots[*outSlot].mGraphicBuffer = buffer; mSlots[*outSlot].mBufferState.attachConsumer(); mSlots[*outSlot].mNeedsReallocation = true; mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mFrameNumber = 0; // mAcquireCalled tells BufferQueue that it doesn't need to send a valid // GraphicBuffer pointer on the next acquireBuffer call, which decreases // Binder traffic by not un/flattening the GraphicBuffer. However, it // requires that the consumer maintain a cached copy of the slot <--> buffer // mappings, which is why the consumer doesn't need the valid pointer on // acquire. // // The StreamSplitter is one of the primary users of the attach/detach // logic, and while it is running, all buffers it acquires are immediately // detached, and all buffers it eventually releases are ones that were // attached (as opposed to having been obtained from acquireBuffer), so it // doesn't make sense to maintain the slot/buffer mappings, which would // become invalid for every buffer during detach/attach. By setting this to // false, the valid GraphicBuffer pointer will always be sent with acquire // for attached buffers. mSlots[*outSlot].mAcquireCalled = false; VALIDATE_CONSISTENCY(); return NO_ERROR; } status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, const sp& releaseFence, EGLDisplay eglDisplay, EGLSyncKHR eglFence) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || releaseFence == nullptr) { BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; } sp listener; { // Autolock scope std::lock_guard lock(mCore->mMutex); // If the frame number has changed because the buffer has been reallocated, // we can ignore this releaseBuffer for the old buffer. // Ignore this for the shared buffer where the frame number can easily // get out of sync due to the buffer being queued and acquired at the // same time. if (frameNumber != mSlots[slot].mFrameNumber && !mSlots[slot].mBufferState.isShared()) { return STALE_BUFFER_SLOT; } if (!mSlots[slot].mBufferState.isAcquired()) { BQ_LOGE("releaseBuffer: attempted to release buffer slot %d " "but its state was %s", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } mSlots[slot].mEglDisplay = eglDisplay; mSlots[slot].mEglFence = eglFence; mSlots[slot].mFence = releaseFence; mSlots[slot].mBufferState.release(); // After leaving shared buffer mode, the shared buffer will // still be around. Mark it as no longer shared if this // operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) { mSlots[slot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list. if (!mSlots[slot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(slot); mCore->mFreeBuffers.push_back(slot); } if (mCore->mBufferReleasedCbEnabled) { listener = mCore->mConnectedProducerListener; } BQ_LOGV("releaseBuffer: releasing slot %d", slot); mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBufferReleased(); } return NO_ERROR; } status_t BufferQueueConsumer::connect( const sp& consumerListener, bool controlledByApp) { ATRACE_CALL(); if (consumerListener == nullptr) { BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } BQ_LOGV("connect: controlledByApp=%s", controlledByApp ? "true" : "false"); std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("connect: BufferQueue has been abandoned"); return NO_INIT; } mCore->mConsumerListener = consumerListener; mCore->mConsumerControlledByApp = controlledByApp; return NO_ERROR; } status_t BufferQueueConsumer::disconnect() { ATRACE_CALL(); BQ_LOGV("disconnect"); std::lock_guard lock(mCore->mMutex); if (mCore->mConsumerListener == nullptr) { BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } mCore->mIsAbandoned = true; mCore->mConsumerListener = nullptr; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mDequeueCondition.notify_all(); return NO_ERROR; } status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { ATRACE_CALL(); if (outSlotMask == nullptr) { BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); return BAD_VALUE; } std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned"); return NO_INIT; } uint64_t mask = 0; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (!mSlots[s].mAcquireCalled) { mask |= (1ULL << s); } } // Remove from the mask queued buffers for which acquire has been called, // since the consumer will not receive their buffer addresses and so must // retain their cached information BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); while (current != mCore->mQueue.end()) { if (current->mAcquireCalled) { mask &= ~(1ULL << current->mSlot); } ++current; } BQ_LOGV("getReleasedBuffers: returning mask %#" PRIx64, mask); *outSlotMask = mask; return NO_ERROR; } status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width, uint32_t height) { ATRACE_CALL(); if (width == 0 || height == 0) { BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u " "height=%u)", width, height); return BAD_VALUE; } BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height); std::lock_guard lock(mCore->mMutex); mCore->mDefaultWidth = width; mCore->mDefaultHeight = height; return NO_ERROR; } status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { ATRACE_CALL(); if (bufferCount < 1 || bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("setMaxBufferCount: invalid count %d", bufferCount); return BAD_VALUE; } std::lock_guard lock(mCore->mMutex); if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("setMaxBufferCount: producer is already connected"); return INVALID_OPERATION; } if (bufferCount < mCore->mMaxAcquiredBufferCount) { BQ_LOGE("setMaxBufferCount: invalid buffer count (%d) less than" "mMaxAcquiredBufferCount (%d)", bufferCount, mCore->mMaxAcquiredBufferCount); return BAD_VALUE; } int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, mCore->mDequeueBufferCannotBlock, bufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of " "available slots. Delta = %d", delta); return BAD_VALUE; } mCore->mMaxBufferCount = bufferCount; return NO_ERROR; } status_t BufferQueueConsumer::setMaxAcquiredBufferCount( int maxAcquiredBuffers) { ATRACE_CALL(); if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) { BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers); return BAD_VALUE; } sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setMaxAcquiredBufferCount: consumer is abandoned"); return NO_INIT; } if (maxAcquiredBuffers == mCore->mMaxAcquiredBufferCount) { return NO_ERROR; } // The new maxAcquiredBuffers count should not be violated by the number // of currently acquired buffers int acquiredCount = 0; for (int slot : mCore->mActiveBuffers) { if (mSlots[slot].mBufferState.isAcquired()) { acquiredCount++; } } if (acquiredCount > maxAcquiredBuffers) { BQ_LOGE("setMaxAcquiredBufferCount: the requested maxAcquiredBuffer" "count (%d) exceeds the current acquired buffer count (%d)", maxAcquiredBuffers, acquiredCount); return BAD_VALUE; } if ((maxAcquiredBuffers + mCore->mMaxDequeuedBufferCount + (mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock ? 1 : 0)) > mCore->mMaxBufferCount) { BQ_LOGE("setMaxAcquiredBufferCount: %d acquired buffers would " "exceed the maxBufferCount (%d) (maxDequeued %d async %d)", maxAcquiredBuffers, mCore->mMaxBufferCount, mCore->mMaxDequeuedBufferCount, mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock); return BAD_VALUE; } int delta = maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount; if (!mCore->adjustAvailableSlotsLocked(delta)) { return BAD_VALUE; } BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; VALIDATE_CONSISTENCY(); if (delta < 0 && mCore->mBufferReleasedCbEnabled) { listener = mCore->mConsumerListener; } } // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueConsumer::setConsumerName(const String8& name) { ATRACE_CALL(); BQ_LOGV("setConsumerName: '%s'", name.string()); std::lock_guard lock(mCore->mMutex); mCore->mConsumerName = name; mConsumerName = name; return NO_ERROR; } status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) { ATRACE_CALL(); BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat); std::lock_guard lock(mCore->mMutex); mCore->mDefaultBufferFormat = defaultFormat; return NO_ERROR; } status_t BufferQueueConsumer::setDefaultBufferDataSpace( android_dataspace defaultDataSpace) { ATRACE_CALL(); BQ_LOGV("setDefaultBufferDataSpace: %u", defaultDataSpace); std::lock_guard lock(mCore->mMutex); mCore->mDefaultBufferDataSpace = defaultDataSpace; return NO_ERROR; } status_t BufferQueueConsumer::setConsumerUsageBits(uint64_t usage) { ATRACE_CALL(); BQ_LOGV("setConsumerUsageBits: %#" PRIx64, usage); std::lock_guard lock(mCore->mMutex); mCore->mConsumerUsageBits = usage; return NO_ERROR; } status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) { ATRACE_CALL(); BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false"); std::lock_guard lock(mCore->mMutex); mCore->mConsumerIsProtected = isProtected; return NO_ERROR; } status_t BufferQueueConsumer::setTransformHint(uint32_t hint) { ATRACE_CALL(); BQ_LOGV("setTransformHint: %#x", hint); std::lock_guard lock(mCore->mMutex); mCore->mTransformHint = hint; return NO_ERROR; } status_t BufferQueueConsumer::getSidebandStream(sp* outStream) const { std::lock_guard lock(mCore->mMutex); *outStream = mCore->mSidebandStream; return NO_ERROR; } status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush, std::vector* outHistory) { std::lock_guard lock(mCore->mMutex); *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush); return NO_ERROR; } status_t BufferQueueConsumer::discardFreeBuffers() { std::lock_guard lock(mCore->mMutex); mCore->discardFreeBuffersLocked(); return NO_ERROR; } status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const { struct passwd* pwd = getpwnam("shell"); uid_t shellUid = pwd ? pwd->pw_uid : 0; if (!shellUid) { int savedErrno = errno; BQ_LOGE("Cannot get AID_SHELL"); return savedErrno ? -savedErrno : UNKNOWN_ERROR; } bool denied = false; const uid_t uid = BufferQueueThreadState::getCallingUid(); #ifndef __ANDROID_VNDK__ // permission check can't be done for vendors as vendors have no access to // the PermissionController. We need to do a runtime check as well, since // the system variant of libgui can be loaded in a vendor process. For eg: // if a HAL uses an llndk library that depends on libgui (libmediandk etc). if (!android_is_in_vendor_process()) { const pid_t pid = BufferQueueThreadState::getCallingPid(); if ((uid != shellUid) && !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " "from pid=%d, uid=%d\n", pid, uid); denied = true; } } #else if (uid != shellUid) { denied = true; } #endif if (denied) { android_errorWriteWithInfoLog(0x534e4554, "27046057", static_cast(uid), nullptr, 0); return PERMISSION_DENIED; } mCore->dumpState(prefix, outResult); return NO_ERROR; } } // namespace android libs/gui/BufferQueueCore.cpp0100644 0000000 0000000 00000041030 13756501735 015067 0ustar000000000 0000000 /* * Copyright 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. */ #define LOG_TAG "BufferQueueCore" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #define EGL_EGLEXT_PROTOTYPES #if DEBUG_ONLY_CODE #define VALIDATE_CONSISTENCY() do { validateConsistencyLocked(); } while (0) #else #define VALIDATE_CONSISTENCY() #endif #include #include #include #include #include #include #include #include #include #include namespace android { static String8 getUniqueName() { static volatile int32_t counter = 0; return String8::format("unnamed-%d-%d", getpid(), android_atomic_inc(&counter)); } static uint64_t getUniqueId() { static std::atomic counter{0}; static uint64_t id = static_cast(getpid()) << 32; return id | counter++; } BufferQueueCore::BufferQueueCore() : mMutex(), mIsAbandoned(false), mConsumerControlledByApp(false), mConsumerName(getUniqueName()), mConsumerListener(), mConsumerUsageBits(0), mConsumerIsProtected(false), mConnectedApi(NO_CONNECTED_API), mLinkedToDeath(), mConnectedProducerListener(), mBufferReleasedCbEnabled(false), mSlots(), mQueue(), mFreeSlots(), mFreeBuffers(), mUnusedSlots(), mActiveBuffers(), mDequeueCondition(), mDequeueBufferCannotBlock(false), mQueueBufferCanDrop(false), mLegacyBufferDrop(true), mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), mDefaultWidth(1), mDefaultHeight(1), mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN), mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS), mMaxAcquiredBufferCount(1), mMaxDequeuedBufferCount(1), mBufferHasBeenQueued(false), mFrameCounter(0), mTransformHint(0), mIsAllocating(false), mIsAllocatingCondition(), mAllowAllocation(true), mBufferAge(0), mGenerationNumber(0), mAsyncMode(false), mSharedBufferMode(false), mAutoRefresh(false), mSharedBufferSlot(INVALID_BUFFER_SLOT), mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE, HAL_DATASPACE_UNKNOWN), mLastQueuedSlot(INVALID_BUFFER_SLOT), mUniqueId(getUniqueId()) { int numStartingBuffers = getMaxBufferCountLocked(); for (int s = 0; s < numStartingBuffers; s++) { mFreeSlots.insert(s); } for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS; s++) { mUnusedSlots.push_front(s); } } BufferQueueCore::~BufferQueueCore() {} void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const { std::lock_guard lock(mMutex); outResult->appendFormat("%s- BufferQueue ", prefix.string()); outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n", mMaxAcquiredBufferCount, mMaxDequeuedBufferCount); outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(), mDequeueBufferCannotBlock, mAsyncMode); outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(), mQueueBufferCanDrop, mLegacyBufferDrop); outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(), mDefaultWidth, mDefaultHeight, mDefaultBufferFormat); outResult->appendFormat("transform-hint=%02x frame-counter=%" PRIu64, mTransformHint, mFrameCounter); outResult->appendFormat("\n%sFIFO(%zu):\n", prefix.string(), mQueue.size()); Fifo::const_iterator current(mQueue.begin()); while (current != mQueue.end()) { double timestamp = current->mTimestamp / 1e9; outResult->appendFormat("%s %02d:%p ", prefix.string(), current->mSlot, current->mGraphicBuffer.get()); outResult->appendFormat("crop=[%d,%d,%d,%d] ", current->mCrop.left, current->mCrop.top, current->mCrop.right, current->mCrop.bottom); outResult->appendFormat("xform=0x%02x time=%.4f scale=%s\n", current->mTransform, timestamp, BufferItem::scalingModeName(current->mScalingMode)); ++current; } outResult->appendFormat("%sSlots:\n", prefix.string()); for (int s : mActiveBuffers) { const sp& buffer(mSlots[s].mGraphicBuffer); // A dequeued buffer might be null if it's still being allocated if (buffer.get()) { outResult->appendFormat("%s %s[%02d:%p] ", prefix.string(), (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s, buffer.get()); outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(), buffer->handle, mSlots[s].mFrameNumber); outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height, buffer->stride, buffer->format); } else { outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get()); outResult->appendFormat("state=%-8s frame=%" PRIu64 "\n", mSlots[s].mBufferState.string(), mSlots[s].mFrameNumber); } } for (int s : mFreeBuffers) { const sp& buffer(mSlots[s].mGraphicBuffer); outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get()); outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(), buffer->handle, mSlots[s].mFrameNumber); outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height, buffer->stride, buffer->format); } for (int s : mFreeSlots) { const sp& buffer(mSlots[s].mGraphicBuffer); outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(), mSlots[s].mBufferState.string()); } } int BufferQueueCore::getMinUndequeuedBufferCountLocked() const { // If dequeueBuffer is allowed to error out, we don't have to add an // extra buffer. if (mAsyncMode || mDequeueBufferCannotBlock) { return mMaxAcquiredBufferCount + 1; } return mMaxAcquiredBufferCount; } int BufferQueueCore::getMinMaxBufferCountLocked() const { return getMinUndequeuedBufferCountLocked() + 1; } int BufferQueueCore::getMaxBufferCountLocked(bool asyncMode, bool dequeueBufferCannotBlock, int maxBufferCount) const { int maxCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount + ((asyncMode || dequeueBufferCannotBlock) ? 1 : 0); maxCount = std::min(maxBufferCount, maxCount); return maxCount; } int BufferQueueCore::getMaxBufferCountLocked() const { int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount + ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0); // limit maxBufferCount by mMaxBufferCount always maxBufferCount = std::min(mMaxBufferCount, maxBufferCount); return maxBufferCount; } void BufferQueueCore::clearBufferSlotLocked(int slot) { BQ_LOGV("clearBufferSlotLocked: slot %d", slot); mSlots[slot].mGraphicBuffer.clear(); mSlots[slot].mBufferState.reset(); mSlots[slot].mRequestBufferCalled = false; mSlots[slot].mFrameNumber = 0; mSlots[slot].mAcquireCalled = false; mSlots[slot].mNeedsReallocation = true; // Destroy fence as BufferQueue now takes ownership if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) { eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence); mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } mSlots[slot].mFence = Fence::NO_FENCE; mSlots[slot].mEglDisplay = EGL_NO_DISPLAY; if (mLastQueuedSlot == slot) { mLastQueuedSlot = INVALID_BUFFER_SLOT; } } void BufferQueueCore::freeAllBuffersLocked() { for (int s : mFreeSlots) { clearBufferSlotLocked(s); } for (int s : mFreeBuffers) { mFreeSlots.insert(s); clearBufferSlotLocked(s); } mFreeBuffers.clear(); for (int s : mActiveBuffers) { mFreeSlots.insert(s); clearBufferSlotLocked(s); } mActiveBuffers.clear(); for (auto& b : mQueue) { b.mIsStale = true; // We set this to false to force the BufferQueue to resend the buffer // handle upon acquire, since if we're here due to a producer // disconnect, the consumer will have been told to purge its cache of // slot-to-buffer-handle mappings and will not be able to otherwise // obtain a valid buffer handle. b.mAcquireCalled = false; } VALIDATE_CONSISTENCY(); } void BufferQueueCore::discardFreeBuffersLocked() { // Notify producer about the discarded buffers. if (mConnectedProducerListener != nullptr && mFreeBuffers.size() > 0) { std::vector freeBuffers(mFreeBuffers.begin(), mFreeBuffers.end()); mConnectedProducerListener->onBuffersDiscarded(freeBuffers); } for (int s : mFreeBuffers) { mFreeSlots.insert(s); clearBufferSlotLocked(s); } mFreeBuffers.clear(); VALIDATE_CONSISTENCY(); } bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) { if (delta >= 0) { // If we're going to fail, do so before modifying anything if (delta > static_cast(mUnusedSlots.size())) { return false; } while (delta > 0) { if (mUnusedSlots.empty()) { return false; } int slot = mUnusedSlots.back(); mUnusedSlots.pop_back(); mFreeSlots.insert(slot); delta--; } } else { // If we're going to fail, do so before modifying anything if (-delta > static_cast(mFreeSlots.size() + mFreeBuffers.size())) { return false; } while (delta < 0) { if (!mFreeSlots.empty()) { auto slot = mFreeSlots.begin(); clearBufferSlotLocked(*slot); mUnusedSlots.push_back(*slot); mFreeSlots.erase(slot); } else if (!mFreeBuffers.empty()) { int slot = mFreeBuffers.back(); clearBufferSlotLocked(slot); mUnusedSlots.push_back(slot); mFreeBuffers.pop_back(); } else { return false; } delta++; } } return true; } void BufferQueueCore::waitWhileAllocatingLocked(std::unique_lock& lock) const { ATRACE_CALL(); while (mIsAllocating) { mIsAllocatingCondition.wait(lock); } } #if DEBUG_ONLY_CODE void BufferQueueCore::validateConsistencyLocked() const { static const useconds_t PAUSE_TIME = 0; int allocatedSlots = 0; for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { bool isInFreeSlots = mFreeSlots.count(slot) != 0; bool isInFreeBuffers = std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) != mFreeBuffers.cend(); bool isInActiveBuffers = mActiveBuffers.count(slot) != 0; bool isInUnusedSlots = std::find(mUnusedSlots.cbegin(), mUnusedSlots.cend(), slot) != mUnusedSlots.cend(); if (isInFreeSlots || isInFreeBuffers || isInActiveBuffers) { allocatedSlots++; } if (isInUnusedSlots) { if (isInFreeSlots) { BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeSlots", slot); usleep(PAUSE_TIME); } if (isInFreeBuffers) { BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeBuffers", slot); usleep(PAUSE_TIME); } if (isInActiveBuffers) { BQ_LOGE("Slot %d is in mUnusedSlots and in mActiveBuffers", slot); usleep(PAUSE_TIME); } if (!mSlots[slot].mBufferState.isFree()) { BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot); usleep(PAUSE_TIME); } if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer", slot); usleep(PAUSE_TIME); } } else if (isInFreeSlots) { if (isInUnusedSlots) { BQ_LOGE("Slot %d is in mFreeSlots and in mUnusedSlots", slot); usleep(PAUSE_TIME); } if (isInFreeBuffers) { BQ_LOGE("Slot %d is in mFreeSlots and in mFreeBuffers", slot); usleep(PAUSE_TIME); } if (isInActiveBuffers) { BQ_LOGE("Slot %d is in mFreeSlots and in mActiveBuffers", slot); usleep(PAUSE_TIME); } if (!mSlots[slot].mBufferState.isFree()) { BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot); usleep(PAUSE_TIME); } if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mFreeSlots but has a buffer", slot); usleep(PAUSE_TIME); } } else if (isInFreeBuffers) { if (isInUnusedSlots) { BQ_LOGE("Slot %d is in mFreeBuffers and in mUnusedSlots", slot); usleep(PAUSE_TIME); } if (isInFreeSlots) { BQ_LOGE("Slot %d is in mFreeBuffers and in mFreeSlots", slot); usleep(PAUSE_TIME); } if (isInActiveBuffers) { BQ_LOGE("Slot %d is in mFreeBuffers and in mActiveBuffers", slot); usleep(PAUSE_TIME); } if (!mSlots[slot].mBufferState.isFree()) { BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot); usleep(PAUSE_TIME); } if (mSlots[slot].mGraphicBuffer == nullptr) { BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot); usleep(PAUSE_TIME); } } else if (isInActiveBuffers) { if (isInUnusedSlots) { BQ_LOGE("Slot %d is in mActiveBuffers and in mUnusedSlots", slot); usleep(PAUSE_TIME); } if (isInFreeSlots) { BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeSlots", slot); usleep(PAUSE_TIME); } if (isInFreeBuffers) { BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeBuffers", slot); usleep(PAUSE_TIME); } if (mSlots[slot].mBufferState.isFree() && !mSlots[slot].mBufferState.isShared()) { BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot); usleep(PAUSE_TIME); } if (mSlots[slot].mGraphicBuffer == nullptr && !mIsAllocating) { BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot); usleep(PAUSE_TIME); } } else { BQ_LOGE("Slot %d isn't in any of mUnusedSlots, mFreeSlots, " "mFreeBuffers, or mActiveBuffers", slot); usleep(PAUSE_TIME); } } if (allocatedSlots != getMaxBufferCountLocked()) { BQ_LOGE("Number of allocated slots is incorrect. Allocated = %d, " "Should be %d (%zu free slots, %zu free buffers, " "%zu activeBuffers, %zu unusedSlots)", allocatedSlots, getMaxBufferCountLocked(), mFreeSlots.size(), mFreeBuffers.size(), mActiveBuffers.size(), mUnusedSlots.size()); } } #endif } // namespace android libs/gui/BufferQueueProducer.cpp0100644 0000000 0000000 00000165456 13756501735 016005 0ustar000000000 0000000 /* * Copyright 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. */ #include #define LOG_TAG "BufferQueueProducer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #if DEBUG_ONLY_CODE #define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0) #else #define VALIDATE_CONSISTENCY() #endif #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include namespace android { static constexpr uint32_t BQ_LAYER_COUNT = 1; BufferQueueProducer::BufferQueueProducer(const sp& core, bool consumerIsSurfaceFlinger) : mCore(core), mSlots(core->mSlots), mConsumerName(), mStickyTransform(0), mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger), mLastQueueBufferFence(Fence::NO_FENCE), mLastQueuedTransform(0), mCallbackMutex(), mNextCallbackTicket(0), mCurrentCallbackTicket(0), mCallbackCondition(), mDequeueTimeout(-1), mDequeueWaitingForAllocation(false) {} BufferQueueProducer::~BufferQueueProducer() {} status_t BufferQueueProducer::requestBuffer(int slot, sp* buf) { ATRACE_CALL(); BQ_LOGV("requestBuffer: slot %d", slot); std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("requestBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("requestBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("requestBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } mSlots[slot].mRequestBufferCalled = true; *buf = mSlots[slot].mGraphicBuffer; return NO_ERROR; } status_t BufferQueueProducer::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { ATRACE_CALL(); BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d", maxDequeuedBuffers); sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been " "abandoned"); return NO_INIT; } if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) { return NO_ERROR; } // The new maxDequeuedBuffer count should not be violated by the number // of currently dequeued buffers int dequeuedCount = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { dequeuedCount++; } } if (dequeuedCount > maxDequeuedBuffers) { BQ_LOGE("setMaxDequeuedBufferCount: the requested maxDequeuedBuffer" "count (%d) exceeds the current dequeued buffer count (%d)", maxDequeuedBuffers, dequeuedCount); return BAD_VALUE; } int bufferCount = mCore->getMinUndequeuedBufferCountLocked(); bufferCount += maxDequeuedBuffers; if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large " "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } const int minBufferSlots = mCore->getMinMaxBufferCountLocked(); if (bufferCount < minBufferSlots) { BQ_LOGE("setMaxDequeuedBufferCount: requested buffer count %d is " "less than minimum %d", bufferCount, minBufferSlots); return BAD_VALUE; } if (bufferCount > mCore->mMaxBufferCount) { BQ_LOGE("setMaxDequeuedBufferCount: %d dequeued buffers would " "exceed the maxBufferCount (%d) (maxAcquired %d async %d " "mDequeuedBufferCannotBlock %d)", maxDequeuedBuffers, mCore->mMaxBufferCount, mCore->mMaxAcquiredBufferCount, mCore->mAsyncMode, mCore->mDequeueBufferCannotBlock); return BAD_VALUE; } int delta = maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount; if (!mCore->adjustAvailableSlotsLocked(delta)) { return BAD_VALUE; } mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers; VALIDATE_CONSISTENCY(); if (delta < 0) { listener = mCore->mConsumerListener; } mCore->mDequeueCondition.notify_all(); } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::setAsyncMode(bool async) { ATRACE_CALL(); BQ_LOGV("setAsyncMode: async = %d", async); sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setAsyncMode: BufferQueue has been abandoned"); return NO_INIT; } if (async == mCore->mAsyncMode) { return NO_ERROR; } if ((mCore->mMaxAcquiredBufferCount + mCore->mMaxDequeuedBufferCount + (async || mCore->mDequeueBufferCannotBlock ? 1 : 0)) > mCore->mMaxBufferCount) { BQ_LOGE("setAsyncMode(%d): this call would cause the " "maxBufferCount (%d) to be exceeded (maxAcquired %d " "maxDequeued %d mDequeueBufferCannotBlock %d)", async, mCore->mMaxBufferCount, mCore->mMaxAcquiredBufferCount, mCore->mMaxDequeuedBufferCount, mCore->mDequeueBufferCannotBlock); return BAD_VALUE; } int delta = mCore->getMaxBufferCountLocked(async, mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of " "available slots. Delta = %d", delta); return BAD_VALUE; } mCore->mAsyncMode = async; VALIDATE_CONSISTENCY(); mCore->mDequeueCondition.notify_all(); if (delta < 0) { listener = mCore->mConsumerListener; } } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } int BufferQueueProducer::getFreeBufferLocked() const { if (mCore->mFreeBuffers.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.pop_front(); return slot; } int BufferQueueProducer::getFreeSlotLocked() const { if (mCore->mFreeSlots.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = *(mCore->mFreeSlots.begin()); mCore->mFreeSlots.erase(slot); return slot; } status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, std::unique_lock& lock, int* found) const { auto callerString = (caller == FreeSlotCaller::Dequeue) ? "dequeueBuffer" : "attachBuffer"; bool tryAgain = true; while (tryAgain) { if (mCore->mIsAbandoned) { BQ_LOGE("%s: BufferQueue has been abandoned", callerString); return NO_INIT; } int dequeuedCount = 0; int acquiredCount = 0; for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { ++dequeuedCount; } if (mSlots[s].mBufferState.isAcquired()) { ++acquiredCount; } } // Producers are not allowed to dequeue more than // mMaxDequeuedBufferCount buffers. // This check is only done if a buffer has already been queued if (mCore->mBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) { // Supress error logs when timeout is non-negative. if (mDequeueTimeout < 0) { BQ_LOGE("%s: attempting to exceed the max dequeued buffer " "count (%d)", callerString, mCore->mMaxDequeuedBufferCount); } return INVALID_OPERATION; } *found = BufferQueueCore::INVALID_BUFFER_SLOT; // If we disconnect and reconnect quickly, we can be in a state where // our slots are empty but we have many buffers in the queue. This can // cause us to run out of memory if we outrun the consumer. Wait here if // it looks like we have too many buffers queued up. const int maxBufferCount = mCore->getMaxBufferCountLocked(); bool tooManyBuffers = mCore->mQueue.size() > static_cast(maxBufferCount); if (tooManyBuffers) { BQ_LOGV("%s: queue size is %zu, waiting", callerString, mCore->mQueue.size()); } else { // If in shared buffer mode and a shared buffer exists, always // return it. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = mCore->mSharedBufferSlot; } else { if (caller == FreeSlotCaller::Dequeue) { // If we're calling this from dequeue, prefer free buffers int slot = getFreeBufferLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else if (mCore->mAllowAllocation) { *found = getFreeSlotLocked(); } } else { // If we're calling this from attach, prefer free slots int slot = getFreeSlotLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else { *found = getFreeBufferLocked(); } } } } // If no buffer is found, or if the queue has too many buffers // outstanding, wait for a buffer to be acquired or released, or for the // max buffer count to change. tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || tooManyBuffers; if (tryAgain) { // Return an error if we're in non-blocking mode (producer and // consumer are controlled by the application). // However, the consumer is allowed to briefly acquire an extra // buffer (which could cause us to have to wait here), which is // okay, since it is only used to implement an atomic acquire + // release (e.g., in GLConsumer::updateTexImage()) if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) && (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { return WOULD_BLOCK; } if (mDequeueTimeout >= 0) { std::cv_status result = mCore->mDequeueCondition.wait_for(lock, std::chrono::nanoseconds(mDequeueTimeout)); if (result == std::cv_status::timeout) { return TIMED_OUT; } } else { mCore->mDequeueCondition.wait(lock); } } } // while (tryAgain) return NO_ERROR; } status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp* outFence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { ATRACE_CALL(); { // Autolock scope std::lock_guard lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("dequeueBuffer: BufferQueue has no connected producer"); return NO_INIT; } } // Autolock scope BQ_LOGV("dequeueBuffer: w=%u h=%u format=%#x, usage=%#" PRIx64, width, height, format, usage); if ((width && !height) || (!width && height)) { BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); return BAD_VALUE; } status_t returnFlags = NO_ERROR; EGLDisplay eglDisplay = EGL_NO_DISPLAY; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; bool attachedByConsumer = false; { // Autolock scope std::unique_lock lock(mCore->mMutex); // If we don't have a free buffer, but we are currently allocating, we wait until allocation // is finished such that we don't allocate in parallel. if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) { mDequeueWaitingForAllocation = true; mCore->waitWhileAllocatingLocked(lock); mDequeueWaitingForAllocation = false; mDequeueWaitingForAllocationCondition.notify_all(); } if (format == 0) { format = mCore->mDefaultBufferFormat; } // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; } int found = BufferItem::INVALID_BUFFER_SLOT; while (found == BufferItem::INVALID_BUFFER_SLOT) { status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found); if (status != NO_ERROR) { return status; } // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } const sp& buffer(mSlots[found].mGraphicBuffer); // If we are not allowed to allocate new buffers, // waitForFreeSlotThenRelock must have returned a slot containing a // buffer. If this buffer would require reallocation to meet the // requested attributes, we free it and attempt to get another one. if (!mCore->mAllowAllocation) { if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { if (mCore->mSharedBufferSlot == found) { BQ_LOGE("dequeueBuffer: cannot re-allocate a sharedbuffer"); return BAD_VALUE; } mCore->mFreeSlots.insert(found); mCore->clearBufferSlotLocked(found); found = BufferItem::INVALID_BUFFER_SLOT; continue; } } } const sp& buffer(mSlots[found].mGraphicBuffer); if (mCore->mSharedBufferSlot == found && buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); return BAD_VALUE; } if (mCore->mSharedBufferSlot != found) { mCore->mActiveBuffers.insert(found); } *outSlot = found; ATRACE_BUFFER_INDEX(found); attachedByConsumer = mSlots[found].mNeedsReallocation; mSlots[found].mNeedsReallocation = false; mSlots[found].mBufferState.dequeue(); if ((buffer == nullptr) || buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = nullptr; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; mCore->mBufferAge = 0; mCore->mIsAllocating = true; returnFlags |= BUFFER_NEEDS_REALLOCATION; } else { // We add 1 because that will be the frame number when this buffer // is queued mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber; } BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge); if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) { BQ_LOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); } eglDisplay = mSlots[found].mEglDisplay; eglFence = mSlots[found].mEglFence; // Don't return a fence in shared buffer mode, except for the first // frame. *outFence = (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == found) ? Fence::NO_FENCE : mSlots[found].mFence; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; // If shared buffer mode has just been enabled, cache the slot of the // first buffer that is dequeued and mark it as the shared buffer. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == BufferQueueCore::INVALID_BUFFER_SLOT) { mCore->mSharedBufferSlot = found; mSlots[found].mBufferState.mShared = true; } } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp graphicBuffer = new GraphicBuffer( width, height, format, BQ_LAYER_COUNT, usage, {mConsumerName.string(), mConsumerName.size()}); status_t error = graphicBuffer->initCheck(); { // Autolock scope std::lock_guard lock(mCore->mMutex); if (error == NO_ERROR && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); if (error != NO_ERROR) { mCore->mFreeSlots.insert(*outSlot); mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); return error; } if (mCore->mIsAbandoned) { mCore->mFreeSlots.insert(*outSlot); mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } VALIDATE_CONSISTENCY(); } // Autolock scope } if (attachedByConsumer) { returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (eglFence != EGL_NO_SYNC_KHR) { EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, 1000000000); // If something goes wrong, log the error, but return the buffer without // synchronizing access to it. It's too late at this point to abort the // dequeue operation. if (result == EGL_FALSE) { BQ_LOGE("dequeueBuffer: error %#x waiting for fence", eglGetError()); } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { BQ_LOGE("dequeueBuffer: timeout waiting for fence"); } eglDestroySyncKHR(eglDisplay, eglFence); } BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); if (outBufferAge) { *outBufferAge = mCore->mBufferAge; } addAndGetFrameTimestamps(nullptr, outTimestamps); return returnFlags; } status_t BufferQueueProducer::detachBuffer(int slot) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); BQ_LOGV("detachBuffer: slot %d", slot); sp listener; { std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("detachBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode || mCore->mSharedBufferSlot == slot) { BQ_LOGE("detachBuffer: cannot detach a buffer in shared buffer mode"); return BAD_VALUE; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("detachBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("detachBuffer: buffer in slot %d has not been requested", slot); return BAD_VALUE; } mSlots[slot].mBufferState.detachProducer(); mCore->mActiveBuffers.erase(slot); mCore->mFreeSlots.insert(slot); mCore->clearBufferSlotLocked(slot); mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); listener = mCore->mConsumerListener; } if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::detachNextBuffer(sp* outBuffer, sp* outFence) { ATRACE_CALL(); if (outBuffer == nullptr) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; } else if (outFence == nullptr) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } sp listener; { std::unique_lock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("detachNextBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode) { BQ_LOGE("detachNextBuffer: cannot detach a buffer in shared buffer " "mode"); return BAD_VALUE; } mCore->waitWhileAllocatingLocked(lock); if (mCore->mFreeBuffers.empty()) { return NO_MEMORY; } int found = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.remove(found); mCore->mFreeSlots.insert(found); BQ_LOGV("detachNextBuffer detached slot %d", found); *outBuffer = mSlots[found].mGraphicBuffer; *outFence = mSlots[found].mFence; mCore->clearBufferSlotLocked(found); VALIDATE_CONSISTENCY(); listener = mCore->mConsumerListener; } if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::attachBuffer(int* outSlot, const sp& buffer) { ATRACE_CALL(); if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } std::unique_lock lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("attachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("attachBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode) { BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode"); return BAD_VALUE; } if (buffer->getGenerationNumber() != mCore->mGenerationNumber) { BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] " "[queue %u]", buffer->getGenerationNumber(), mCore->mGenerationNumber); return BAD_VALUE; } mCore->waitWhileAllocatingLocked(lock); status_t returnFlags = NO_ERROR; int found; status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, lock, &found); if (status != NO_ERROR) { return status; } // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { BQ_LOGE("attachBuffer: no available buffer slots"); return -EBUSY; } *outSlot = found; ATRACE_BUFFER_INDEX(*outSlot); BQ_LOGV("attachBuffer: returning slot %d flags=%#x", *outSlot, returnFlags); mSlots[*outSlot].mGraphicBuffer = buffer; mSlots[*outSlot].mBufferState.attachProducer(); mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mRequestBufferCalled = true; mSlots[*outSlot].mAcquireCalled = false; mSlots[*outSlot].mNeedsReallocation = false; mCore->mActiveBuffers.insert(found); VALIDATE_CONSISTENCY(); return returnFlags; } status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); int64_t requestedPresentTimestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop(Rect::EMPTY_RECT); int scalingMode; uint32_t transform; uint32_t stickyTransform; sp acquireFence; bool getFrameTimestamps = false; input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &acquireFence, &stickyTransform, &getFrameTimestamps); const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } auto acquireFenceTime = std::make_shared(acquireFence); switch (scalingMode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: break; default: BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode); return BAD_VALUE; } sp frameAvailableListener; sp frameReplacedListener; int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; { // Autolock scope std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("queueBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("queueBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("queueBuffer: slot %d was queued without requesting " "a buffer", slot); return BAD_VALUE; } // If shared buffer mode has just been enabled, cache the slot of the // first buffer that is queued and mark it as the shared buffer. if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == BufferQueueCore::INVALID_BUFFER_SLOT) { mCore->mSharedBufferSlot = slot; mSlots[slot].mBufferState.mShared = true; } BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d" " validHdrMetadataTypes=0x%x crop=[%d,%d,%d,%d] transform=%#x scale=%s", slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace, hdrMetadata.validTypes, crop.left, crop.top, crop.right, crop.bottom, transform, BufferItem::scalingModeName(static_cast(scalingMode))); const sp& graphicBuffer(mSlots[slot].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedRect(Rect::EMPTY_RECT); crop.intersect(bufferRect, &croppedRect); if (croppedRect != crop) { BQ_LOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", slot); return BAD_VALUE; } // Override UNKNOWN dataspace with consumer default if (dataSpace == HAL_DATASPACE_UNKNOWN) { dataSpace = mCore->mDefaultBufferDataSpace; } mSlots[slot].mFence = acquireFence; mSlots[slot].mBufferState.queue(); // Increment the frame counter and store a local version of it // for use outside the lock on mCore->mMutex. ++mCore->mFrameCounter; currentFrameNumber = mCore->mFrameCounter; mSlots[slot].mFrameNumber = currentFrameNumber; item.mAcquireCalled = mSlots[slot].mAcquireCalled; item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; item.mCrop = crop; item.mTransform = transform & ~static_cast(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); item.mTransformToDisplayInverse = (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0; item.mScalingMode = static_cast(scalingMode); item.mTimestamp = requestedPresentTimestamp; item.mIsAutoTimestamp = isAutoTimestamp; item.mDataSpace = dataSpace; item.mHdrMetadata = hdrMetadata; item.mFrameNumber = currentFrameNumber; item.mSlot = slot; item.mFence = acquireFence; item.mFenceTime = acquireFenceTime; item.mIsDroppable = mCore->mAsyncMode || (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) || (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) || (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot); item.mSurfaceDamage = surfaceDamage; item.mQueuedBuffer = true; item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh; item.mApi = mCore->mConnectedApi; mStickyTransform = stickyTransform; // Cache the shared buffer data so that the BufferItem can be recreated. if (mCore->mSharedBufferMode) { mCore->mSharedBufferCache.crop = crop; mCore->mSharedBufferCache.transform = transform; mCore->mSharedBufferCache.scalingMode = static_cast( scalingMode); mCore->mSharedBufferCache.dataspace = dataSpace; } output->bufferReplaced = false; if (mCore->mQueue.empty()) { // When the queue is empty, we can ignore mDequeueBufferCannotBlock // and simply queue this buffer mCore->mQueue.push_back(item); frameAvailableListener = mCore->mConsumerListener; } else { // When the queue is not empty, we need to look at the last buffer // in the queue to see if we need to replace it const BufferItem& last = mCore->mQueue.itemAt( mCore->mQueue.size() - 1); if (last.mIsDroppable) { if (!last.mIsStale) { mSlots[last.mSlot].mBufferState.freeQueued(); // After leaving shared buffer mode, the shared buffer will // still be around. Mark it as no longer shared if this // operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[last.mSlot].mBufferState.isFree()) { mSlots[last.mSlot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list. if (!mSlots[last.mSlot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(last.mSlot); mCore->mFreeBuffers.push_back(last.mSlot); output->bufferReplaced = true; } } // Make sure to merge the damage rect from the frame we're about // to drop into the new frame's damage rect. if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT || item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) { item.mSurfaceDamage = Region::INVALID_REGION; } else { item.mSurfaceDamage |= last.mSurfaceDamage; } // Overwrite the droppable buffer with the incoming one mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item; frameReplacedListener = mCore->mConsumerListener; } else { mCore->mQueue.push_back(item); frameAvailableListener = mCore->mConsumerListener; } } mCore->mBufferHasBeenQueued = true; mCore->mDequeueCondition.notify_all(); mCore->mLastQueuedSlot = slot; output->width = mCore->mDefaultWidth; output->height = mCore->mDefaultHeight; output->transformHint = mCore->mTransformHint; output->numPendingBuffers = static_cast(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; ATRACE_INT(mCore->mConsumerName.string(), static_cast(mCore->mQueue.size())); mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); // Take a ticket for the callback functions callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and // there will be no Binder call if (!mConsumerIsSurfaceFlinger) { item.mGraphicBuffer.clear(); } // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order int connectedApi; sp lastQueuedFence; { // scope for the lock std::unique_lock lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { mCallbackCondition.wait(lock); } if (frameAvailableListener != nullptr) { frameAvailableListener->onFrameAvailable(item); } else if (frameReplacedListener != nullptr) { frameReplacedListener->onFrameReplaced(item); } connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; ++mCurrentCallbackTicket; mCallbackCondition.notify_all(); } // Update and get FrameEventHistory. nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC); NewFrameEventsEntry newFrameEventsEntry = { currentFrameNumber, postedTime, requestedPresentTimestamp, std::move(acquireFenceTime) }; addAndGetFrameTimestamps(&newFrameEventsEntry, getFrameTimestamps ? &output->frameTimestamps : nullptr); // Wait without lock held if (connectedApi == NATIVE_WINDOW_API_EGL) { // Waiting here allows for two full buffers to be queued but not a // third. In the event that frames take varying time, this makes a // small trade-off in favor of latency rather than throughput. lastQueuedFence->waitForever("Throttling EGL Production"); } return NO_ERROR; } status_t BufferQueueProducer::cancelBuffer(int slot, const sp& fence) { ATRACE_CALL(); BQ_LOGV("cancelBuffer: slot %d", slot); std::lock_guard lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("cancelBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("cancelBuffer: BufferQueue has no connected producer"); return NO_INIT; } if (mCore->mSharedBufferMode) { BQ_LOGE("cancelBuffer: cannot cancel a buffer in shared buffer mode"); return BAD_VALUE; } if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (fence == nullptr) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } mSlots[slot].mBufferState.cancel(); // After leaving shared buffer mode, the shared buffer will still be around. // Mark it as no longer shared if this operation causes it to be free. if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) { mSlots[slot].mBufferState.mShared = false; } // Don't put the shared buffer on the free list. if (!mSlots[slot].mBufferState.isShared()) { mCore->mActiveBuffers.erase(slot); mCore->mFreeBuffers.push_back(slot); } mSlots[slot].mFence = fence; mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); return NO_ERROR; } int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); std::lock_guard lock(mCore->mMutex); if (outValue == nullptr) { BQ_LOGE("query: outValue was NULL"); return BAD_VALUE; } if (mCore->mIsAbandoned) { BQ_LOGE("query: BufferQueue has been abandoned"); return NO_INIT; } int value; switch (what) { case NATIVE_WINDOW_WIDTH: value = static_cast(mCore->mDefaultWidth); break; case NATIVE_WINDOW_HEIGHT: value = static_cast(mCore->mDefaultHeight); break; case NATIVE_WINDOW_FORMAT: value = static_cast(mCore->mDefaultBufferFormat); break; case NATIVE_WINDOW_LAYER_COUNT: // All BufferQueue buffers have a single layer. value = BQ_LAYER_COUNT; break; case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: value = mCore->getMinUndequeuedBufferCountLocked(); break; case NATIVE_WINDOW_STICKY_TRANSFORM: value = static_cast(mStickyTransform); break; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: value = (mCore->mQueue.size() > 1); break; case NATIVE_WINDOW_CONSUMER_USAGE_BITS: // deprecated; higher 32 bits are truncated value = static_cast(mCore->mConsumerUsageBits); break; case NATIVE_WINDOW_DEFAULT_DATASPACE: value = static_cast(mCore->mDefaultBufferDataSpace); break; case NATIVE_WINDOW_BUFFER_AGE: if (mCore->mBufferAge > INT32_MAX) { value = 0; } else { value = static_cast(mCore->mBufferAge); } break; case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: value = static_cast(mCore->mConsumerIsProtected); break; case NATIVE_WINDOW_MAX_BUFFER_COUNT: value = static_cast(mCore->mMaxBufferCount); break; default: return BAD_VALUE; } BQ_LOGV("query: %d? %d", what, value); *outValue = value; return NO_ERROR; } status_t BufferQueueProducer::connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput *output) { ATRACE_CALL(); std::lock_guard lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; BQ_LOGV("connect: api=%d producerControlledByApp=%s", api, producerControlledByApp ? "true" : "false"); if (mCore->mIsAbandoned) { BQ_LOGE("connect: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConsumerListener == nullptr) { BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } if (output == nullptr) { BQ_LOGE("connect: output was NULL"); return BAD_VALUE; } if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("connect: already connected (cur=%d req=%d)", mCore->mConnectedApi, api); return BAD_VALUE; } int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, mDequeueTimeout < 0 ? mCore->mConsumerControlledByApp && producerControlledByApp : false, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("connect: BufferQueue failed to adjust the number of available " "slots. Delta = %d", delta); return BAD_VALUE; } int status = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: mCore->mConnectedApi = api; output->width = mCore->mDefaultWidth; output->height = mCore->mDefaultHeight; output->transformHint = mCore->mTransformHint; output->numPendingBuffers = static_cast(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; if (listener != nullptr) { // Set up a death notification so that we can disconnect // automatically if the remote producer dies if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( static_cast(this)); if (status != NO_ERROR) { BQ_LOGE("connect: linkToDeath failed: %s (%d)", strerror(-status), status); } mCore->mLinkedToDeath = listener; } mCore->mConnectedProducerListener = listener; mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify(); } break; default: BQ_LOGE("connect: unknown API %d", api); status = BAD_VALUE; break; } mCore->mConnectedPid = BufferQueueThreadState::getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; mCore->mQueueBufferCanDrop = false; mCore->mLegacyBufferDrop = true; if (mCore->mConsumerControlledByApp && producerControlledByApp) { mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0; mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0; } mCore->mAllowAllocation = true; VALIDATE_CONSISTENCY(); return status; } status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { ATRACE_CALL(); BQ_LOGV("disconnect: api %d", api); int status = NO_ERROR; sp listener; { // Autolock scope std::unique_lock lock(mCore->mMutex); if (mode == DisconnectMode::AllLocal) { if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) { return NO_ERROR; } api = BufferQueueCore::CURRENTLY_CONNECTED_API; } mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { // It's not really an error to disconnect after the surface has // been abandoned; it should just be a no-op. return NO_ERROR; } if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) { if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) { ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode); } api = mCore->mConnectedApi; // If we're asked to disconnect the currently connected api but // nobody is connected, it's not really an error. if (api == BufferQueueCore::NO_CONNECTED_API) { return NO_ERROR; } } switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: if (mCore->mConnectedApi == api) { mCore->freeAllBuffersLocked(); // Remove our death notification callback if we have one if (mCore->mLinkedToDeath != nullptr) { sp token = IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death // notification, but we just ignore it token->unlinkToDeath( static_cast(this)); } mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mLinkedToDeath = nullptr; mCore->mConnectedProducerListener = nullptr; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); mCore->mDequeueCondition.notify_all(); listener = mCore->mConsumerListener; } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("disconnect: not connected (req=%d)", api); status = NO_INIT; } else { BQ_LOGE("disconnect: still connected to another API " "(cur=%d req=%d)", mCore->mConnectedApi, api); status = BAD_VALUE; } break; default: BQ_LOGE("disconnect: unknown API %d", api); status = BAD_VALUE; break; } } // Autolock scope // Call back without lock held if (listener != nullptr) { listener->onBuffersReleased(); listener->onDisconnect(); } return status; } status_t BufferQueueProducer::setSidebandStream(const sp& stream) { sp listener; { // Autolock scope std::lock_guard _l(mCore->mMutex); mCore->mSidebandStream = stream; listener = mCore->mConsumerListener; } // Autolock scope if (listener != nullptr) { listener->onSidebandStreamChanged(); } return NO_ERROR; } void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) { ATRACE_CALL(); while (true) { size_t newBufferCount = 0; uint32_t allocWidth = 0; uint32_t allocHeight = 0; PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN; uint64_t allocUsage = 0; std::string allocName; { // Autolock scope std::unique_lock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(lock); if (!mCore->mAllowAllocation) { BQ_LOGE("allocateBuffers: allocation is not allowed for this " "BufferQueue"); return; } // Only allocate one buffer at a time to reduce risks of overlapping an allocation from // both allocateBuffers and dequeueBuffer. newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1; if (newBufferCount == 0) { return; } allocWidth = width > 0 ? width : mCore->mDefaultWidth; allocHeight = height > 0 ? height : mCore->mDefaultHeight; allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat; allocUsage = usage | mCore->mConsumerUsageBits; allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size()); mCore->mIsAllocating = true; } // Autolock scope Vector> buffers; for (size_t i = 0; i < newBufferCount; ++i) { sp graphicBuffer = new GraphicBuffer( allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, allocUsage, allocName); status_t result = graphicBuffer->initCheck(); if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" " %u, usage %#" PRIx64 ")", width, height, format, usage); std::lock_guard lock(mCore->mMutex); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); return; } buffers.push_back(graphicBuffer); } { // Autolock scope std::unique_lock lock(mCore->mMutex); uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth; uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight; PixelFormat checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat; uint64_t checkUsage = usage | mCore->mConsumerUsageBits; if (checkWidth != allocWidth || checkHeight != allocHeight || checkFormat != allocFormat || checkUsage != allocUsage) { // Something changed while we released the lock. Retry. BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying."); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); continue; } for (size_t i = 0; i < newBufferCount; ++i) { if (mCore->mFreeSlots.empty()) { BQ_LOGV("allocateBuffers: a slot was occupied while " "allocating. Dropping allocated buffer."); continue; } auto slot = mCore->mFreeSlots.begin(); mCore->clearBufferSlotLocked(*slot); // Clean up the slot first mSlots[*slot].mGraphicBuffer = buffers[i]; mSlots[*slot].mFence = Fence::NO_FENCE; // freeBufferLocked puts this slot on the free slots list. Since // we then attached a buffer, move the slot to free buffer list. mCore->mFreeBuffers.push_front(*slot); BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", *slot); // Make sure the erase is done after all uses of the slot // iterator since it will be invalid after this point. mCore->mFreeSlots.erase(slot); } mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.notify_all(); VALIDATE_CONSISTENCY(); // If dequeue is waiting for to allocate a buffer, release the lock until it's not // waiting anymore so it can use the buffer we just allocated. while (mDequeueWaitingForAllocation) { mDequeueWaitingForAllocationCondition.wait(lock); } } // Autolock scope } } status_t BufferQueueProducer::allowAllocation(bool allow) { ATRACE_CALL(); BQ_LOGV("allowAllocation: %s", allow ? "true" : "false"); std::lock_guard lock(mCore->mMutex); mCore->mAllowAllocation = allow; return NO_ERROR; } status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) { ATRACE_CALL(); BQ_LOGV("setGenerationNumber: %u", generationNumber); std::lock_guard lock(mCore->mMutex); mCore->mGenerationNumber = generationNumber; return NO_ERROR; } String8 BufferQueueProducer::getConsumerName() const { ATRACE_CALL(); std::lock_guard lock(mCore->mMutex); BQ_LOGV("getConsumerName: %s", mConsumerName.string()); return mConsumerName; } status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) { ATRACE_CALL(); BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode); std::lock_guard lock(mCore->mMutex); if (!sharedBufferMode) { mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; } mCore->mSharedBufferMode = sharedBufferMode; return NO_ERROR; } status_t BufferQueueProducer::setAutoRefresh(bool autoRefresh) { ATRACE_CALL(); BQ_LOGV("setAutoRefresh: %d", autoRefresh); std::lock_guard lock(mCore->mMutex); mCore->mAutoRefresh = autoRefresh; return NO_ERROR; } status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) { ATRACE_CALL(); BQ_LOGV("setDequeueTimeout: %" PRId64, timeout); std::lock_guard lock(mCore->mMutex); bool dequeueBufferCannotBlock = timeout >= 0 ? false : mCore->mDequeueBufferCannotBlock; int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, dequeueBufferCannotBlock, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of " "available slots. Delta = %d", delta); return BAD_VALUE; } mDequeueTimeout = timeout; mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock; if (timeout > 0) { mCore->mQueueBufferCanDrop = false; } VALIDATE_CONSISTENCY(); return NO_ERROR; } status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { ATRACE_CALL(); BQ_LOGV("setLegacyBufferDrop: drop = %d", drop); std::lock_guard lock(mCore->mMutex); mCore->mLegacyBufferDrop = drop; return NO_ERROR; } status_t BufferQueueProducer::getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard lock(mCore->mMutex); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; } *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer; *outFence = mLastQueueBufferFence; // Currently only SurfaceFlinger internally ever changes // GLConsumer's filtering mode, so we just use 'true' here as // this is slightly specialized for the current client of this API, // which does want filtering. GLConsumer::computeTransformMatrix(outTransformMatrix, mSlots[mCore->mLastQueuedSlot].mGraphicBuffer, mLastQueuedCrop, mLastQueuedTransform, true /* filter */); return NO_ERROR; } void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { addAndGetFrameTimestamps(nullptr, outDelta); } void BufferQueueProducer::addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { if (newTimestamps == nullptr && outDelta == nullptr) { return; } ATRACE_CALL(); BQ_LOGV("addAndGetFrameTimestamps"); sp listener; { std::lock_guard lock(mCore->mMutex); listener = mCore->mConsumerListener; } if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } void BufferQueueProducer::binderDied(const wp& /* who */) { // If we're here, it means that a producer we were connected to died. // We're guaranteed that we are still connected to it because we remove // this callback upon disconnect. It's therefore safe to read mConnectedApi // without synchronization here. int api = mCore->mConnectedApi; disconnect(api); } status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const { BQ_LOGV("getUniqueId"); *outId = mCore->mUniqueId; return NO_ERROR; } status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const { BQ_LOGV("getConsumerUsage"); std::lock_guard lock(mCore->mMutex); *outUsage = mCore->mConsumerUsageBits; return NO_ERROR; } } // namespace android libs/gui/BufferQueueThreadState.cpp0100644 0000000 0000000 00000002372 13756501735 016415 0ustar000000000 0000000 /* * Copyright 2019 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. */ #include #include #include #include namespace android { uid_t BufferQueueThreadState::getCallingUid() { if (hardware::IPCThreadState::self()->isServingCall()) { return hardware::IPCThreadState::self()->getCallingUid(); } return IPCThreadState::self()->getCallingUid(); } pid_t BufferQueueThreadState::getCallingPid() { if (hardware::IPCThreadState::self()->isServingCall()) { return hardware::IPCThreadState::self()->getCallingPid(); } return IPCThreadState::self()->getCallingPid(); } } // namespace android libs/gui/BufferSlot.cpp0100644 0000000 0000000 00000002010 13756501735 014106 0ustar000000000 0000000 /* * Copyright 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. */ #include namespace android { const char* BufferState::string() const { if (isShared()) { return "SHARED"; } if (isFree()) { return "FREE"; } if (isAcquired()) { return "ACQUIRED"; } if (isDequeued()) { return "DEQUEUED"; } if (isQueued()) { return "QUEUED"; } return "UNKNOWN"; } } // namespace android libs/gui/ConsumerBase.cpp0100644 0000000 0000000 00000035027 13756501735 014437 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #include #define LOG_TAG "ConsumerBase" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include #include // Macros for including the ConsumerBase name in log messages #define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) //#define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) //#define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) //#define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) #define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) namespace android { // Get an ID that's unique within this process. static int32_t createProcessUniqueId() { static volatile int32_t globalCounter = 0; return android_atomic_inc(&globalCounter); } ConsumerBase::ConsumerBase(const sp& bufferQueue, bool controlledByApp) : mAbandoned(false), mConsumer(bufferQueue), mPrevFinalReleaseFence(Fence::NO_FENCE) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); // Note that we can't create an sp<...>(this) in a ctor that will not keep a // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. wp listener = static_cast(this); sp proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mConsumer->consumerConnect(proxy, controlledByApp); if (err != NO_ERROR) { CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } else { mConsumer->setConsumerName(mName); } } ConsumerBase::~ConsumerBase() { CB_LOGV("~ConsumerBase"); Mutex::Autolock lock(mMutex); // Verify that abandon() has been called before we get here. This should // be done by ConsumerBase::onLastStrongRef(), but it's possible for a // derived class to override that method and not call // ConsumerBase::onLastStrongRef(). LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the " "consumer is not abandoned!", mName.string()); } void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { abandon(); } void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); mSlots[slotIndex].mGraphicBuffer = nullptr; mSlots[slotIndex].mFence = Fence::NO_FENCE; mSlots[slotIndex].mFrameNumber = 0; } void ConsumerBase::onFrameAvailable(const BufferItem& item) { CB_LOGV("onFrameAvailable"); sp listener; { // scope for the lock Mutex::Autolock lock(mFrameAvailableMutex); listener = mFrameAvailableListener.promote(); } if (listener != nullptr) { CB_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(item); } } void ConsumerBase::onFrameReplaced(const BufferItem &item) { CB_LOGV("onFrameReplaced"); sp listener; { Mutex::Autolock lock(mFrameAvailableMutex); listener = mFrameAvailableListener.promote(); } if (listener != nullptr) { CB_LOGV("actually calling onFrameReplaced"); listener->onFrameReplaced(item); } } void ConsumerBase::onBuffersReleased() { Mutex::Autolock lock(mMutex); CB_LOGV("onBuffersReleased"); if (mAbandoned) { // Nothing to do if we're already abandoned. return; } uint64_t mask = 0; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1ULL << i)) { freeBufferLocked(i); } } } void ConsumerBase::onSidebandStreamChanged() { } void ConsumerBase::abandon() { CB_LOGV("abandon"); Mutex::Autolock lock(mMutex); if (!mAbandoned) { abandonLocked(); mAbandoned = true; } } void ConsumerBase::abandonLocked() { CB_LOGV("abandonLocked"); if (mAbandoned) { CB_LOGE("abandonLocked: ConsumerBase is abandoned!"); return; } for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } // disconnect from the BufferQueue mConsumer->consumerDisconnect(); mConsumer.clear(); } bool ConsumerBase::isAbandoned() { Mutex::Autolock _l(mMutex); return mAbandoned; } void ConsumerBase::setName(const String8& name) { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("setName: ConsumerBase is abandoned!"); return; } mName = name; mConsumer->setConsumerName(name); } void ConsumerBase::setFrameAvailableListener( const wp& listener) { CB_LOGV("setFrameAvailableListener"); Mutex::Autolock lock(mFrameAvailableMutex); mFrameAvailableListener = listener; } status_t ConsumerBase::detachBuffer(int slot) { CB_LOGV("detachBuffer"); Mutex::Autolock lock(mMutex); if (mAbandoned) { CB_LOGE("detachBuffer: ConsumerBase is abandoned!"); return NO_INIT; } status_t result = mConsumer->detachBuffer(slot); if (result != NO_ERROR) { CB_LOGE("Failed to detach buffer: %d", result); return result; } freeBufferLocked(slot); return result; } status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("setDefaultBufferSize: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->setDefaultBufferSize(width, height); } status_t ConsumerBase::setDefaultBufferFormat(PixelFormat defaultFormat) { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("setDefaultBufferFormat: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t ConsumerBase::setDefaultBufferDataSpace( android_dataspace defaultDataSpace) { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("setDefaultBufferDataSpace: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->setDefaultBufferDataSpace(defaultDataSpace); } status_t ConsumerBase::setConsumerUsageBits(uint64_t usage) { Mutex::Autolock lock(mMutex); if (mAbandoned) { CB_LOGE("setConsumerUsageBits: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->setConsumerUsageBits(usage); } status_t ConsumerBase::setTransformHint(uint32_t hint) { Mutex::Autolock lock(mMutex); if (mAbandoned) { CB_LOGE("setTransformHint: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->setTransformHint(hint); } status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { Mutex::Autolock lock(mMutex); if (mAbandoned) { CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers); } sp ConsumerBase::getSidebandStream() const { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("getSidebandStream: ConsumerBase is abandoned!"); return nullptr; } sp stream; status_t err = mConsumer->getSidebandStream(&stream); if (err != NO_ERROR) { CB_LOGE("failed to get sideband stream: %d", err); return nullptr; } return stream; } status_t ConsumerBase::getOccupancyHistory(bool forceFlush, std::vector* outHistory) { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("getOccupancyHistory: ConsumerBase is abandoned!"); return NO_INIT; } return mConsumer->getOccupancyHistory(forceFlush, outHistory); } status_t ConsumerBase::discardFreeBuffers() { Mutex::Autolock _l(mMutex); if (mAbandoned) { CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!"); return NO_INIT; } status_t err = mConsumer->discardFreeBuffers(); if (err != OK) { return err; } uint64_t mask; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1ULL << i)) { freeBufferLocked(i); } } return OK; } void ConsumerBase::dumpState(String8& result) const { dumpState(result, ""); } void ConsumerBase::dumpState(String8& result, const char* prefix) const { Mutex::Autolock _l(mMutex); dumpLocked(result, prefix); } void ConsumerBase::dumpLocked(String8& result, const char* prefix) const { result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); if (!mAbandoned) { String8 consumerState; mConsumer->dumpState(String8(prefix), &consumerState); result.append(consumerState); } } status_t ConsumerBase::acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber) { if (mAbandoned) { CB_LOGE("acquireBufferLocked: ConsumerBase is abandoned!"); return NO_INIT; } status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber); if (err != NO_ERROR) { return err; } if (item->mGraphicBuffer != nullptr) { if (mSlots[item->mSlot].mGraphicBuffer != nullptr) { freeBufferLocked(item->mSlot); } mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer; } mSlots[item->mSlot].mFrameNumber = item->mFrameNumber; mSlots[item->mSlot].mFence = item->mFence; CB_LOGV("acquireBufferLocked: -> slot=%d/%" PRIu64, item->mSlot, item->mFrameNumber); return OK; } status_t ConsumerBase::addReleaseFence(int slot, const sp graphicBuffer, const sp& fence) { Mutex::Autolock lock(mMutex); return addReleaseFenceLocked(slot, graphicBuffer, fence); } status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp graphicBuffer, const sp& fence) { CB_LOGV("addReleaseFenceLocked: slot=%d", slot); // If consumer no longer tracks this graphicBuffer, we can safely // drop this fence, as it will never be received by the producer. if (!stillTracking(slot, graphicBuffer)) { return OK; } if (!mSlots[slot].mFence.get()) { mSlots[slot].mFence = fence; return OK; } // Check status of fences first because merging is expensive. // Merging an invalid fence with any other fence results in an // invalid fence. auto currentStatus = mSlots[slot].mFence->getStatus(); if (currentStatus == Fence::Status::Invalid) { CB_LOGE("Existing fence has invalid state"); return BAD_VALUE; } auto incomingStatus = fence->getStatus(); if (incomingStatus == Fence::Status::Invalid) { CB_LOGE("New fence has invalid state"); mSlots[slot].mFence = fence; return BAD_VALUE; } // If both fences are signaled or both are unsignaled, we need to merge // them to get an accurate timestamp. if (currentStatus == incomingStatus) { char fenceName[32] = {}; snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot); sp mergedFence = Fence::merge( fenceName, mSlots[slot].mFence, fence); if (!mergedFence.get()) { CB_LOGE("failed to merge release fences"); // synchronization is broken, the best we can do is hope fences // signal in order so the new fence will act like a union mSlots[slot].mFence = fence; return BAD_VALUE; } mSlots[slot].mFence = mergedFence; } else if (incomingStatus == Fence::Status::Unsignaled) { // If one fence has signaled and the other hasn't, the unsignaled // fence will approximately correspond with the correct timestamp. // There's a small race if both fences signal at about the same time // and their statuses are retrieved with unfortunate timing. However, // by this point, they will have both signaled and only the timestamp // will be slightly off; any dependencies after this point will // already have been met. mSlots[slot].mFence = fence; } // else if (currentStatus == Fence::Status::Unsignaled) is a no-op. return OK; } status_t ConsumerBase::releaseBufferLocked( int slot, const sp graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { if (mAbandoned) { CB_LOGE("releaseBufferLocked: ConsumerBase is abandoned!"); return NO_INIT; } // If consumer no longer tracks this graphicBuffer (we received a new // buffer on the same slot), the buffer producer is definitely no longer // tracking it. if (!stillTracking(slot, graphicBuffer)) { return OK; } CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64, slot, mSlots[slot].mFrameNumber); status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, display, eglFence, mSlots[slot].mFence); if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } mPrevFinalReleaseFence = mSlots[slot].mFence; mSlots[slot].mFence = Fence::NO_FENCE; return err; } bool ConsumerBase::stillTracking(int slot, const sp graphicBuffer) { if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { return false; } return (mSlots[slot].mGraphicBuffer != nullptr && mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); } } // namespace android libs/gui/CpuConsumer.cpp0100644 0000000 0000000 00000017475 13756501735 014323 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "CpuConsumer" //#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) //#define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) //#define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) #define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) #define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) namespace android { CpuConsumer::CpuConsumer(const sp& bq, size_t maxLockedBuffers, bool controlledByApp) : ConsumerBase(bq, controlledByApp), mMaxLockedBuffers(maxLockedBuffers), mCurrentLockedBuffers(0) { // Create tracking entries for locked buffers mAcquiredBuffers.insertAt(0, maxLockedBuffers); mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); mConsumer->setMaxAcquiredBufferCount(static_cast(maxLockedBuffers)); } size_t CpuConsumer::findAcquiredBufferLocked(uintptr_t id) const { for (size_t i = 0; i < mMaxLockedBuffers; i++) { const auto& ab = mAcquiredBuffers[i]; // note that this finds AcquiredBuffer::kUnusedId as well if (ab.mLockedBufferId == id) { return i; } } return mMaxLockedBuffers; // an invalid index } static uintptr_t getLockedBufferId(const CpuConsumer::LockedBuffer& buffer) { return reinterpret_cast(buffer.data); } static bool isPossiblyYUV(PixelFormat format) { switch (static_cast(format)) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_RGBA_FP16: case HAL_PIXEL_FORMAT_RGBA_1010102: case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_BGRA_8888: case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_Y16: case HAL_PIXEL_FORMAT_RAW16: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: return false; case HAL_PIXEL_FORMAT_YV12: case HAL_PIXEL_FORMAT_YCbCr_420_888: case HAL_PIXEL_FORMAT_YCbCr_422_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_422_I: default: return true; } } status_t CpuConsumer::lockBufferItem(const BufferItem& item, LockedBuffer* outBuffer) const { android_ycbcr ycbcr = android_ycbcr(); PixelFormat format = item.mGraphicBuffer->getPixelFormat(); PixelFormat flexFormat = format; if (isPossiblyYUV(format)) { int fenceFd = item.mFence.get() ? item.mFence->dup() : -1; status_t err = item.mGraphicBuffer->lockAsyncYCbCr(GraphicBuffer::USAGE_SW_READ_OFTEN, item.mCrop, &ycbcr, fenceFd); if (err == OK) { flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888; if (format != HAL_PIXEL_FORMAT_YCbCr_420_888) { CC_LOGV("locking buffer of format %#x as flex YUV", format); } } else if (format == HAL_PIXEL_FORMAT_YCbCr_420_888) { CC_LOGE("Unable to lock YCbCr buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } } if (ycbcr.y != nullptr) { outBuffer->data = reinterpret_cast(ycbcr.y); outBuffer->stride = static_cast(ycbcr.ystride); outBuffer->dataCb = reinterpret_cast(ycbcr.cb); outBuffer->dataCr = reinterpret_cast(ycbcr.cr); outBuffer->chromaStride = static_cast(ycbcr.cstride); outBuffer->chromaStep = static_cast(ycbcr.chroma_step); } else { // not flexible YUV; try lockAsync void* bufferPointer = nullptr; int fenceFd = item.mFence.get() ? item.mFence->dup() : -1; status_t err = item.mGraphicBuffer->lockAsync(GraphicBuffer::USAGE_SW_READ_OFTEN, item.mCrop, &bufferPointer, fenceFd); if (err != OK) { CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } outBuffer->data = reinterpret_cast(bufferPointer); outBuffer->stride = item.mGraphicBuffer->getStride(); outBuffer->dataCb = nullptr; outBuffer->dataCr = nullptr; outBuffer->chromaStride = 0; outBuffer->chromaStep = 0; } outBuffer->width = item.mGraphicBuffer->getWidth(); outBuffer->height = item.mGraphicBuffer->getHeight(); outBuffer->format = format; outBuffer->flexFormat = flexFormat; outBuffer->crop = item.mCrop; outBuffer->transform = item.mTransform; outBuffer->scalingMode = item.mScalingMode; outBuffer->timestamp = item.mTimestamp; outBuffer->dataSpace = item.mDataSpace; outBuffer->frameNumber = item.mFrameNumber; return OK; } status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { status_t err; if (!nativeBuffer) return BAD_VALUE; Mutex::Autolock _l(mMutex); if (mCurrentLockedBuffers == mMaxLockedBuffers) { CC_LOGW("Max buffers have been locked (%zd), cannot lock anymore.", mMaxLockedBuffers); return NOT_ENOUGH_DATA; } BufferItem b; err = acquireBufferLocked(&b, 0); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; } else { CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); return err; } } if (b.mGraphicBuffer == nullptr) { b.mGraphicBuffer = mSlots[b.mSlot].mGraphicBuffer; } err = lockBufferItem(b, nativeBuffer); if (err != OK) { return err; } // find an unused AcquiredBuffer size_t lockedIdx = findAcquiredBufferLocked(AcquiredBuffer::kUnusedId); ALOG_ASSERT(lockedIdx < mMaxLockedBuffers); AcquiredBuffer& ab = mAcquiredBuffers.editItemAt(lockedIdx); ab.mSlot = b.mSlot; ab.mGraphicBuffer = b.mGraphicBuffer; ab.mLockedBufferId = getLockedBufferId(*nativeBuffer); mCurrentLockedBuffers++; return OK; } status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) { Mutex::Autolock _l(mMutex); uintptr_t id = getLockedBufferId(nativeBuffer); size_t lockedIdx = (id != AcquiredBuffer::kUnusedId) ? findAcquiredBufferLocked(id) : mMaxLockedBuffers; if (lockedIdx == mMaxLockedBuffers) { CC_LOGE("%s: Can't find buffer to free", __FUNCTION__); return BAD_VALUE; } AcquiredBuffer& ab = mAcquiredBuffers.editItemAt(lockedIdx); int fenceFd = -1; status_t err = ab.mGraphicBuffer->unlockAsync(&fenceFd); if (err != OK) { CC_LOGE("%s: Unable to unlock graphic buffer %zd", __FUNCTION__, lockedIdx); return err; } sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); addReleaseFenceLocked(ab.mSlot, ab.mGraphicBuffer, fence); releaseBufferLocked(ab.mSlot, ab.mGraphicBuffer); ab.reset(); mCurrentLockedBuffers--; return OK; } } // namespace android libs/gui/DebugEGLImageTracker.cpp0100644 0000000 0000000 00000006255 13756501735 015707 0ustar000000000 0000000 /* * Copyright 2019 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. */ #include #include #include #include #include using android::base::StringAppendF; std::mutex DebugEGLImageTracker::mInstanceLock; std::atomic DebugEGLImageTracker::mInstance; class DebugEGLImageTrackerNoOp : public DebugEGLImageTracker { public: DebugEGLImageTrackerNoOp() = default; ~DebugEGLImageTrackerNoOp() override = default; void create(const char * /*from*/) override {} void destroy(const char * /*from*/) override {} void dump(std::string & /*result*/) override {} }; class DebugEGLImageTrackerImpl : public DebugEGLImageTracker { public: DebugEGLImageTrackerImpl() = default; ~DebugEGLImageTrackerImpl() override = default; void create(const char * /*from*/) override; void destroy(const char * /*from*/) override; void dump(std::string & /*result*/) override; private: std::mutex mLock; std::unordered_map mCreateTracker; std::unordered_map mDestroyTracker; int64_t mTotalCreated = 0; int64_t mTotalDestroyed = 0; }; DebugEGLImageTracker *DebugEGLImageTracker::getInstance() { std::lock_guard lock(mInstanceLock); if (mInstance == nullptr) { char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.enable_egl_image_tracker", value, "0"); const bool enabled = static_cast(atoi(value)); if (enabled) { mInstance = new DebugEGLImageTrackerImpl(); } else { mInstance = new DebugEGLImageTrackerNoOp(); } } return mInstance; } void DebugEGLImageTrackerImpl::create(const char *from) { std::lock_guard lock(mLock); mCreateTracker[from]++; mTotalCreated++; } void DebugEGLImageTrackerImpl::destroy(const char *from) { std::lock_guard lock(mLock); mDestroyTracker[from]++; mTotalDestroyed++; } void DebugEGLImageTrackerImpl::dump(std::string &result) { std::lock_guard lock(mLock); StringAppendF(&result, "Live EGL Image objects: %" PRIi64 "\n", mTotalCreated - mTotalDestroyed); StringAppendF(&result, "Total EGL Image created: %" PRIi64 "\n", mTotalCreated); for (const auto &[from, count] : mCreateTracker) { StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count); } StringAppendF(&result, "Total EGL Image destroyed: %" PRIi64 "\n", mTotalDestroyed); for (const auto &[from, count] : mDestroyTracker) { StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count); } } libs/gui/DisplayEventReceiver.cpp0100644 0000000 0000000 00000005677 13756501735 016155 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #include #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { // --------------------------------------------------------------------------- DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) { sp sf(ComposerService::getComposerService()); if (sf != nullptr) { mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged); if (mEventConnection != nullptr) { mDataChannel = std::make_unique(); mEventConnection->stealReceiveChannel(mDataChannel.get()); } } } DisplayEventReceiver::~DisplayEventReceiver() { } status_t DisplayEventReceiver::initCheck() const { if (mDataChannel != nullptr) return NO_ERROR; return NO_INIT; } int DisplayEventReceiver::getFd() const { if (mDataChannel == nullptr) return NO_INIT; return mDataChannel->getFd(); } status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { if (int32_t(count) < 0) return BAD_VALUE; if (mEventConnection != nullptr) { mEventConnection->setVsyncRate(count); return NO_ERROR; } return NO_INIT; } status_t DisplayEventReceiver::requestNextVsync() { if (mEventConnection != nullptr) { mEventConnection->requestNextVsync(); return NO_ERROR; } return NO_INIT; } ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events, size_t count) { return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count); } ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel, Event* events, size_t count) { return gui::BitTube::recvObjects(dataChannel, events, count); } ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count) { return gui::BitTube::sendObjects(dataChannel, events, count); } // --------------------------------------------------------------------------- }; // namespace android libs/gui/FrameTimestamps.cpp0100644 0000000 0000000 00000060343 13756501735 015151 0ustar000000000 0000000 /* * Copyright 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. */ #include #define LOG_TAG "FrameEvents" #include #include // For CC_[UN]LIKELY #include #include #include #include #include namespace android { using base::StringAppendF; // ============================================================================ // FrameEvents // ============================================================================ bool FrameEvents::hasPostedInfo() const { return FrameEvents::isValidTimestamp(postedTime); } bool FrameEvents::hasRequestedPresentInfo() const { return FrameEvents::isValidTimestamp(requestedPresentTime); } bool FrameEvents::hasLatchInfo() const { return FrameEvents::isValidTimestamp(latchTime); } bool FrameEvents::hasFirstRefreshStartInfo() const { return FrameEvents::isValidTimestamp(firstRefreshStartTime); } bool FrameEvents::hasLastRefreshStartInfo() const { // The last refresh start time may continue to update until a new frame // is latched. We know we have the final value once the release info is set. return addReleaseCalled; } bool FrameEvents::hasDequeueReadyInfo() const { return FrameEvents::isValidTimestamp(dequeueReadyTime); } bool FrameEvents::hasAcquireInfo() const { return acquireFence->isValid(); } bool FrameEvents::hasGpuCompositionDoneInfo() const { // We may not get a gpuCompositionDone in addPostComposite if // client/gles compositing isn't needed. return addPostCompositeCalled; } bool FrameEvents::hasDisplayPresentInfo() const { // We may not get a displayPresent in addPostComposite for HWC1. return addPostCompositeCalled; } bool FrameEvents::hasReleaseInfo() const { return addReleaseCalled; } void FrameEvents::checkFencesForCompletion() { acquireFence->getSignalTime(); gpuCompositionDoneFence->getSignalTime(); displayPresentFence->getSignalTime(); releaseFence->getSignalTime(); } static void dumpFenceTime(std::string& outString, const char* name, bool pending, const FenceTime& fenceTime) { StringAppendF(&outString, "--- %s", name); nsecs_t signalTime = fenceTime.getCachedSignalTime(); if (Fence::isValidTimestamp(signalTime)) { StringAppendF(&outString, "%" PRId64 "\n", signalTime); } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) { outString.append("Pending\n"); } else if (&fenceTime == FenceTime::NO_FENCE.get()){ outString.append("N/A\n"); } else { outString.append("Error\n"); } } void FrameEvents::dump(std::string& outString) const { if (!valid) { return; } StringAppendF(&outString, "-- Frame %" PRIu64 "\n", frameNumber); StringAppendF(&outString, "--- Posted \t%" PRId64 "\n", postedTime); StringAppendF(&outString, "--- Req. Present\t%" PRId64 "\n", requestedPresentTime); outString.append("--- Latched \t"); if (FrameEvents::isValidTimestamp(latchTime)) { StringAppendF(&outString, "%" PRId64 "\n", latchTime); } else { outString.append("Pending\n"); } outString.append("--- Refresh (First)\t"); if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) { StringAppendF(&outString, "%" PRId64 "\n", firstRefreshStartTime); } else { outString.append("Pending\n"); } outString.append("--- Refresh (Last)\t"); if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) { StringAppendF(&outString, "%" PRId64 "\n", lastRefreshStartTime); } else { outString.append("Pending\n"); } dumpFenceTime(outString, "Acquire \t", true, *acquireFence); dumpFenceTime(outString, "GPU Composite Done\t", !addPostCompositeCalled, *gpuCompositionDoneFence); dumpFenceTime(outString, "Display Present \t", !addPostCompositeCalled, *displayPresentFence); outString.append("--- DequeueReady \t"); if (FrameEvents::isValidTimestamp(dequeueReadyTime)) { StringAppendF(&outString, "%" PRId64 "\n", dequeueReadyTime); } else { outString.append("Pending\n"); } dumpFenceTime(outString, "Release \t", true, *releaseFence); } // ============================================================================ // FrameEventHistory // ============================================================================ namespace { struct FrameNumberEqual { explicit FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {} bool operator()(const FrameEvents& frame) { return frame.valid && mFrameNumber == frame.frameNumber; } const uint64_t mFrameNumber; }; } // namespace FrameEventHistory::~FrameEventHistory() = default; FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) { auto frame = std::find_if( mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber)); return frame == mFrames.end() ? nullptr : &(*frame); } FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) { *iHint = std::min(*iHint, mFrames.size()); auto hint = mFrames.begin() + *iHint; auto frame = std::find_if( hint, mFrames.end(), FrameNumberEqual(frameNumber)); if (frame == mFrames.end()) { frame = std::find_if( mFrames.begin(), hint, FrameNumberEqual(frameNumber)); if (frame == hint) { return nullptr; } } *iHint = static_cast(std::distance(mFrames.begin(), frame)); return &(*frame); } void FrameEventHistory::checkFencesForCompletion() { for (auto& frame : mFrames) { frame.checkFencesForCompletion(); } } // Uses !|valid| as the MSB. static bool FrameNumberLessThan( const FrameEvents& lhs, const FrameEvents& rhs) { if (lhs.valid == rhs.valid) { return lhs.frameNumber < rhs.frameNumber; } return lhs.valid; } void FrameEventHistory::dump(std::string& outString) const { auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); if (!earliestFrame->valid) { outString.append("-- N/A\n"); return; } for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { frame->dump(outString); } for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) { frame->dump(outString); } } // ============================================================================ // ProducerFrameEventHistory // ============================================================================ ProducerFrameEventHistory::~ProducerFrameEventHistory() = default; nsecs_t ProducerFrameEventHistory::snapToNextTick( nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) { nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval; // Integer modulo rounds towards 0 and not -inf before taking the remainder, // so adjust the offset if it is negative. if (tickOffset < 0) { tickOffset += tickInterval; } return timestamp + tickOffset; } nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline( const nsecs_t now) const{ return snapToNextTick( now, mCompositorTiming.deadline, mCompositorTiming.interval); } void ProducerFrameEventHistory::updateAcquireFence( uint64_t frameNumber, std::shared_ptr&& acquire) { FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset); if (frame == nullptr) { ALOGE("updateAcquireFence: Did not find frame."); return; } if (acquire->isValid()) { mAcquireTimeline.push(acquire); frame->acquireFence = std::move(acquire); } else { // If there isn't an acquire fence, assume that buffer was // ready for the consumer when posted. frame->acquireFence = std::make_shared(frame->postedTime); } } void ProducerFrameEventHistory::applyDelta( const FrameEventHistoryDelta& delta) { mCompositorTiming = delta.mCompositorTiming; for (auto& d : delta.mDeltas) { // Avoid out-of-bounds access. if (CC_UNLIKELY(d.mIndex >= mFrames.size())) { ALOGE("applyDelta: Bad index."); return; } FrameEvents& frame = mFrames[d.mIndex]; frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0; frame.addReleaseCalled = d.mAddReleaseCalled != 0; frame.postedTime = d.mPostedTime; frame.requestedPresentTime = d.mRequestedPresentTime; frame.latchTime = d.mLatchTime; frame.firstRefreshStartTime = d.mFirstRefreshStartTime; frame.lastRefreshStartTime = d.mLastRefreshStartTime; frame.dequeueReadyTime = d.mDequeueReadyTime; if (frame.frameNumber != d.mFrameNumber) { // We got a new frame. Initialize some of the fields. frame.frameNumber = d.mFrameNumber; frame.acquireFence = FenceTime::NO_FENCE; frame.gpuCompositionDoneFence = FenceTime::NO_FENCE; frame.displayPresentFence = FenceTime::NO_FENCE; frame.releaseFence = FenceTime::NO_FENCE; // The consumer only sends valid frames. frame.valid = true; } applyFenceDelta(&mGpuCompositionDoneTimeline, &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence); applyFenceDelta(&mPresentTimeline, &frame.displayPresentFence, d.mDisplayPresentFence); applyFenceDelta(&mReleaseTimeline, &frame.releaseFence, d.mReleaseFence); } } void ProducerFrameEventHistory::updateSignalTimes() { mAcquireTimeline.updateSignalTimes(); mGpuCompositionDoneTimeline.updateSignalTimes(); mPresentTimeline.updateSignalTimes(); mReleaseTimeline.updateSignalTimes(); } void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline, std::shared_ptr* dst, const FenceTime::Snapshot& src) const { if (CC_UNLIKELY(dst == nullptr || dst->get() == nullptr)) { ALOGE("applyFenceDelta: dst is null."); return; } switch (src.state) { case FenceTime::Snapshot::State::EMPTY: return; case FenceTime::Snapshot::State::FENCE: ALOGE_IF((*dst)->isValid(), "applyFenceDelta: Unexpected fence."); *dst = createFenceTime(src.fence); timeline->push(*dst); return; case FenceTime::Snapshot::State::SIGNAL_TIME: if ((*dst)->isValid()) { (*dst)->applyTrustedSnapshot(src); } else { *dst = std::make_shared(src.signalTime); } return; } } std::shared_ptr ProducerFrameEventHistory::createFenceTime( const sp& fence) const { return std::make_shared(fence); } // ============================================================================ // ConsumerFrameEventHistory // ============================================================================ ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; void ConsumerFrameEventHistory::onDisconnect() { mCurrentConnectId++; mProducerWantsEvents = false; } void ConsumerFrameEventHistory::initializeCompositorTiming( const CompositorTiming& compositorTiming) { mCompositorTiming = compositorTiming; } void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) { // Overwrite all fields of the frame with default values unless set here. FrameEvents newTimestamps; newTimestamps.connectId = mCurrentConnectId; newTimestamps.frameNumber = newEntry.frameNumber; newTimestamps.postedTime = newEntry.postedTime; newTimestamps.requestedPresentTime = newEntry.requestedPresentTime; newTimestamps.acquireFence = newEntry.acquireFence; newTimestamps.valid = true; mFrames[mQueueOffset] = newTimestamps; // Note: We avoid sending the acquire fence back to the caller since // they have the original one already, so there is no need to set the // acquire dirty bit. mFramesDirty[mQueueOffset].setDirty(); mQueueOffset = (mQueueOffset + 1) % mFrames.size(); } void ConsumerFrameEventHistory::addLatch( uint64_t frameNumber, nsecs_t latchTime) { FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame."); return; } frame->latchTime = latchTime; mFramesDirty[mCompositionOffset].setDirty(); } void ConsumerFrameEventHistory::addPreComposition( uint64_t frameNumber, nsecs_t refreshStartTime) { FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addPreComposition: Did not find frame."); return; } frame->lastRefreshStartTime = refreshStartTime; mFramesDirty[mCompositionOffset].setDirty(); if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) { frame->firstRefreshStartTime = refreshStartTime; mFramesDirty[mCompositionOffset].setDirty(); } } void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber, const std::shared_ptr& gpuCompositionDone, const std::shared_ptr& displayPresent, const CompositorTiming& compositorTiming) { mCompositorTiming = compositorTiming; FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addPostComposition: Did not find frame."); return; } // Only get GPU and present info for the first composite. if (!frame->addPostCompositeCalled) { frame->addPostCompositeCalled = true; frame->gpuCompositionDoneFence = gpuCompositionDone; mFramesDirty[mCompositionOffset].setDirty(); if (!frame->displayPresentFence->isValid()) { frame->displayPresentFence = displayPresent; mFramesDirty[mCompositionOffset].setDirty(); } } } void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime, std::shared_ptr&& release) { FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, "addRelease: Did not find frame."); return; } frame->addReleaseCalled = true; frame->dequeueReadyTime = dequeueReadyTime; frame->releaseFence = std::move(release); mFramesDirty[mReleaseOffset].setDirty(); } void ConsumerFrameEventHistory::getFrameDelta( FrameEventHistoryDelta* delta, const std::array::iterator& frame) { mProducerWantsEvents = true; size_t i = static_cast(std::distance(mFrames.begin(), frame)); if (mFramesDirty[i].anyDirty()) { // Make sure only to send back deltas for the current connection // since the producer won't have the correct state to apply a delta // from a previous connection. if (mFrames[i].connectId == mCurrentConnectId) { delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]); } mFramesDirty[i].reset(); } } void ConsumerFrameEventHistory::getAndResetDelta( FrameEventHistoryDelta* delta) { mProducerWantsEvents = true; delta->mCompositorTiming = mCompositorTiming; // Write these in order of frame number so that it is easy to // add them to a FenceTimeline in the proper order producer side. delta->mDeltas.reserve(mFramesDirty.size()); auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { getFrameDelta(delta, frame); } for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) { getFrameDelta(delta, frame); } } // ============================================================================ // FrameEventsDelta // ============================================================================ FrameEventsDelta::FrameEventsDelta( size_t index, const FrameEvents& frameTimestamps, const FrameEventDirtyFields& dirtyFields) : mIndex(index), mFrameNumber(frameTimestamps.frameNumber), mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled), mAddReleaseCalled(frameTimestamps.addReleaseCalled), mPostedTime(frameTimestamps.postedTime), mRequestedPresentTime(frameTimestamps.requestedPresentTime), mLatchTime(frameTimestamps.latchTime), mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime), mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime), mDequeueReadyTime(frameTimestamps.dequeueReadyTime) { if (dirtyFields.isDirty()) { mGpuCompositionDoneFence = frameTimestamps.gpuCompositionDoneFence->getSnapshot(); } if (dirtyFields.isDirty()) { mDisplayPresentFence = frameTimestamps.displayPresentFence->getSnapshot(); } if (dirtyFields.isDirty()) { mReleaseFence = frameTimestamps.releaseFence->getSnapshot(); } } constexpr size_t FrameEventsDelta::minFlattenedSize() { return sizeof(FrameEventsDelta::mFrameNumber) + sizeof(uint16_t) + // mIndex sizeof(uint8_t) + // mAddPostCompositeCalled sizeof(uint8_t) + // mAddReleaseCalled sizeof(FrameEventsDelta::mPostedTime) + sizeof(FrameEventsDelta::mRequestedPresentTime) + sizeof(FrameEventsDelta::mLatchTime) + sizeof(FrameEventsDelta::mFirstRefreshStartTime) + sizeof(FrameEventsDelta::mLastRefreshStartTime) + sizeof(FrameEventsDelta::mDequeueReadyTime); } // Flattenable implementation size_t FrameEventsDelta::getFlattenedSize() const { auto fences = allFences(this); return minFlattenedSize() + std::accumulate(fences.begin(), fences.end(), size_t(0), [](size_t a, const FenceTime::Snapshot* fence) { return a + fence->getFlattenedSize(); }); } size_t FrameEventsDelta::getFdCount() const { auto fences = allFences(this); return std::accumulate(fences.begin(), fences.end(), size_t(0), [](size_t a, const FenceTime::Snapshot* fence) { return a + fence->getFdCount(); }); } status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { if (size < getFlattenedSize() || count < getFdCount()) { return NO_MEMORY; } if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY || mIndex > std::numeric_limits::max()) { return BAD_VALUE; } FlattenableUtils::write(buffer, size, mFrameNumber); // These are static_cast to uint16_t/uint8_t for alignment. FlattenableUtils::write(buffer, size, static_cast(mIndex)); FlattenableUtils::write( buffer, size, static_cast(mAddPostCompositeCalled)); FlattenableUtils::write( buffer, size, static_cast(mAddReleaseCalled)); FlattenableUtils::write(buffer, size, mPostedTime); FlattenableUtils::write(buffer, size, mRequestedPresentTime); FlattenableUtils::write(buffer, size, mLatchTime); FlattenableUtils::write(buffer, size, mFirstRefreshStartTime); FlattenableUtils::write(buffer, size, mLastRefreshStartTime); FlattenableUtils::write(buffer, size, mDequeueReadyTime); // Fences for (auto fence : allFences(this)) { status_t status = fence->flatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < minFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, mFrameNumber); // These were written as uint16_t/uint8_t for alignment. uint16_t temp16 = 0; FlattenableUtils::read(buffer, size, temp16); mIndex = temp16; if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } uint8_t temp8 = 0; FlattenableUtils::read(buffer, size, temp8); mAddPostCompositeCalled = static_cast(temp8); FlattenableUtils::read(buffer, size, temp8); mAddReleaseCalled = static_cast(temp8); FlattenableUtils::read(buffer, size, mPostedTime); FlattenableUtils::read(buffer, size, mRequestedPresentTime); FlattenableUtils::read(buffer, size, mLatchTime); FlattenableUtils::read(buffer, size, mFirstRefreshStartTime); FlattenableUtils::read(buffer, size, mLastRefreshStartTime); FlattenableUtils::read(buffer, size, mDequeueReadyTime); // Fences for (auto fence : allFences(this)) { status_t status = fence->unflatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } // ============================================================================ // FrameEventHistoryDelta // ============================================================================ FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( FrameEventHistoryDelta&& src) noexcept { mCompositorTiming = src.mCompositorTiming; if (CC_UNLIKELY(!mDeltas.empty())) { ALOGE("FrameEventHistoryDelta assign clobbering history."); } mDeltas = std::move(src.mDeltas); return *this; } constexpr size_t FrameEventHistoryDelta::minFlattenedSize() { return sizeof(uint32_t) + // mDeltas.size() sizeof(mCompositorTiming); } size_t FrameEventHistoryDelta::getFlattenedSize() const { return minFlattenedSize() + std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0), [](size_t a, const FrameEventsDelta& delta) { return a + delta.getFlattenedSize(); }); } size_t FrameEventHistoryDelta::getFdCount() const { return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0), [](size_t a, const FrameEventsDelta& delta) { return a + delta.getFdCount(); }); } status_t FrameEventHistoryDelta::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } if (size < getFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, mCompositorTiming); FlattenableUtils::write( buffer, size, static_cast(mDeltas.size())); for (auto& d : mDeltas) { status_t status = d.flatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } status_t FrameEventHistoryDelta::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < minFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, mCompositorTiming); uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } mDeltas.resize(deltaCount); for (auto& d : mDeltas) { status_t status = d.unflatten(buffer, size, fds, count); if (status != NO_ERROR) { return status; } } return NO_ERROR; } } // namespace android libs/gui/GLConsumer.cpp0100644 0000000 0000000 00000103665 13756501735 014073 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #define LOG_TAG "GLConsumer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #define GL_GLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" #define EGL_PROTECTED_CONTENT_EXT 0x32C0 namespace android { // Macros for including the GLConsumer name in log messages #define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) #define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) //#define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) #define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) #define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) static const struct { uint32_t width, height; char const* bits; } kDebugData = { 15, 12, "_______________" "_______________" "_____XX_XX_____" "__X_X_____X_X__" "__X_XXXXXXX_X__" "__XXXXXXXXXXX__" "___XX_XXX_XX___" "____XXXXXXX____" "_____X___X_____" "____X_____X____" "_______________" "_______________" }; static const mat4 mtxIdentity; Mutex GLConsumer::sStaticInitLock; sp GLConsumer::sReleasedTexImageBuffer; static bool hasEglProtectedContentImpl() { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); size_t extsLen = strlen(exts); bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen+1); bool atEnd = (cropExtLen+1) < extsLen && !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen+1)); bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); return equal || atStart || atEnd || inMiddle; } static bool hasEglProtectedContent() { // Only compute whether the extension is present once the first time this // function is called. static bool hasIt = hasEglProtectedContentImpl(); return hasIt; } GLConsumer::GLConsumer(const sp& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), mCurrentCrop(Rect::EMPTY_RECT), mCurrentTransform(0), mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentFence(Fence::NO_FENCE), mCurrentTimestamp(0), mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), mCurrentFrameNumber(0), mDefaultWidth(1), mDefaultHeight(1), mFilteringEnabled(true), mTexName(tex), mUseFenceSync(useFenceSync), mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { GLC_LOGV("GLConsumer"); memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } GLConsumer::GLConsumer(const sp& bq, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), mCurrentCrop(Rect::EMPTY_RECT), mCurrentTransform(0), mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mCurrentFence(Fence::NO_FENCE), mCurrentTimestamp(0), mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), mCurrentFrameNumber(0), mDefaultWidth(1), mDefaultHeight(1), mFilteringEnabled(true), mTexName(0), mUseFenceSync(useFenceSync), mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(false) { GLC_LOGV("GLConsumer"); memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); } status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setDefaultBufferSize: GLConsumer is abandoned!"); return NO_INIT; } mDefaultWidth = w; mDefaultHeight = h; return mConsumer->setDefaultBufferSize(w, h); } status_t GLConsumer::updateTexImage() { ATRACE_CALL(); GLC_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("updateTexImage: GLConsumer is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } BufferItem item; // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item, 0); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. GLC_LOGV("updateTexImage: no buffers were available"); glBindTexture(mTexTarget, mTexName); err = NO_ERROR; } else { GLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // Release the previous buffer. err = updateAndReleaseLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); return err; } // Bind the new buffer to the GL texture, and wait until it's ready. return bindTextureImageLocked(); } status_t GLConsumer::releaseTexImage() { ATRACE_CALL(); GLC_LOGV("releaseTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("releaseTexImage: GLConsumer is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = NO_ERROR; if (mAttached) { err = checkAndUpdateEglStateLocked(true); if (err != NO_ERROR) { return err; } } else { // if we're detached, no need to validate EGL's state -- we won't use it. } // Update the GLConsumer state. int buf = mCurrentTexture; if (buf != BufferQueue::INVALID_BUFFER_SLOT) { GLC_LOGV("releaseTexImage: (slot=%d, mAttached=%d)", buf, mAttached); if (mAttached) { // Do whatever sync ops we need to do before releasing the slot. err = syncForReleaseLocked(mEglDisplay); if (err != NO_ERROR) { GLC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); return err; } } else { // if we're detached, we just use the fence that was created in detachFromContext() // so... basically, nothing more to do here. } err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); if (err < NO_ERROR) { GLC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); return err; } if (mReleasedTexImage == nullptr) { mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); } mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; mCurrentTextureImage = mReleasedTexImage; mCurrentCrop.makeInvalid(); mCurrentTransform = 0; mCurrentTimestamp = 0; mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; mCurrentFence = Fence::NO_FENCE; mCurrentFenceTime = FenceTime::NO_FENCE; if (mAttached) { // This binds a dummy buffer (mReleasedTexImage). status_t result = bindTextureImageLocked(); if (result != NO_ERROR) { return result; } } else { // detached, don't touch the texture (and we may not even have an // EGLDisplay here. } } return NO_ERROR; } sp GLConsumer::getDebugTexImageBuffer() { Mutex::Autolock _l(sStaticInitLock); if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { // The first time, create the debug texture in case the application // continues to use it. sp buffer = new GraphicBuffer( kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, GraphicBuffer::USAGE_SW_WRITE_RARELY, "[GLConsumer debug texture]"); uint32_t* bits; buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); uint32_t stride = buffer->getStride(); uint32_t height = buffer->getHeight(); memset(bits, 0, stride * height * 4); for (uint32_t y = 0; y < kDebugData.height; y++) { for (uint32_t x = 0; x < kDebugData.width; x++) { bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 : 0xFFFFFFFF; } bits += stride; } buffer->unlock(); sReleasedTexImageBuffer = buffer; } return sReleasedTexImageBuffer; } status_t GLConsumer::acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber) { status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); if (err != NO_ERROR) { return err; } // If item->mGraphicBuffer is not null, this buffer has not been acquired // before, so any prior EglImage created is using a stale buffer. This // replaces any old EglImage with a new one (using the new buffer). if (item->mGraphicBuffer != nullptr) { int slot = item->mSlot; mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); } return NO_ERROR; } status_t GLConsumer::releaseBufferLocked(int buf, sp graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { // release the buffer if it hasn't already been discarded by the // BufferQueue. This can happen, for example, when the producer of this // buffer has reallocated the original buffer slot after this buffer // was acquired. status_t err = ConsumerBase::releaseBufferLocked( buf, graphicBuffer, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; return err; } status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease) { status_t err = NO_ERROR; int slot = item.mSlot; if (!mAttached) { GLC_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL " "ES context"); releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); return INVALID_OPERATION; } // Confirm state. err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); return err; } // Ensure we have a valid EglImageKHR for the slot, creating an EglImage // if nessessary, for the gralloc buffer currently in the slot in // ConsumerBase. // We may have to do this even when item.mGraphicBuffer == NULL (which // means the buffer was previously acquired). err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); return UNKNOWN_ERROR; } // Do whatever sync ops we need to do before releasing the old slot. if (slot != mCurrentTexture) { err = syncForReleaseLocked(mEglDisplay); if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. // As we are still under lock since acquireBuffer, it is safe to // release by slot. releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); return err; } } GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, slot, mSlots[slot].mGraphicBuffer->handle); // Hang onto the pointer so that it isn't freed in the call to // releaseBufferLocked() if we're in shared buffer mode and both buffers are // the same. sp nextTextureImage = mEglSlots[slot].mEglImage; // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { status_t status = releaseBufferLocked( mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); if (status < NO_ERROR) { GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); err = status; // keep going, with error raised [?] } } else { pendingRelease->currentTexture = mCurrentTexture; pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); pendingRelease->display = mEglDisplay; pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence; pendingRelease->isPending = true; } } // Update the GLConsumer state. mCurrentTexture = slot; mCurrentTextureImage = nextTextureImage; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentDataSpace = item.mDataSpace; mCurrentFence = item.mFence; mCurrentFenceTime = item.mFenceTime; mCurrentFrameNumber = item.mFrameNumber; computeCurrentTransformMatrixLocked(); return err; } status_t GLConsumer::bindTextureImageLocked() { if (mEglDisplay == EGL_NO_DISPLAY) { ALOGE("bindTextureImage: invalid display"); return INVALID_OPERATION; } GLenum error; while ((error = glGetError()) != GL_NO_ERROR) { GLC_LOGW("bindTextureImage: clearing GL error: %#04x", error); } glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { GLC_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); return UNKNOWN_ERROR; } mCurrentTextureImage->bindToTextureTarget(mTexTarget); // In the rare case that the display is terminated and then initialized // again, we can't detect that the display changed (it didn't), but the // image is invalid. In this case, repeat the exact same steps while // forcing the creation of a new image. if ((error = glGetError()) != GL_NO_ERROR) { glBindTexture(mTexTarget, mTexName); status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); if (result != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); return UNKNOWN_ERROR; } mCurrentTextureImage->bindToTextureTarget(mTexTarget); if ((error = glGetError()) != GL_NO_ERROR) { GLC_LOGE("bindTextureImage: error binding external image: %#04x", error); return UNKNOWN_ERROR; } } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); } status_t GLConsumer::checkAndUpdateEglStateLocked(bool contextCheck) { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (!contextCheck) { // if this is the first time we're called, mEglDisplay/mEglContext have // never been set, so don't error out (below). if (mEglDisplay == EGL_NO_DISPLAY) { mEglDisplay = dpy; } if (mEglContext == EGL_NO_CONTEXT) { mEglContext = ctx; } } if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { GLC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { GLC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; return NO_ERROR; } void GLConsumer::setReleaseFence(const sp& fence) { if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t err = addReleaseFence(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), fence); if (err != OK) { GLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); } } } status_t GLConsumer::detachFromContext() { ATRACE_CALL(); GLC_LOGV("detachFromContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("detachFromContext: abandoned GLConsumer"); return NO_INIT; } if (!mAttached) { GLC_LOGE("detachFromContext: GLConsumer is not attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { GLC_LOGE("detachFromContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { GLC_LOGE("detachFromContext: invalid current EGLContext"); return INVALID_OPERATION; } if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { status_t err = syncForReleaseLocked(dpy); if (err != OK) { return err; } glDeleteTextures(1, &mTexName); } mEglDisplay = EGL_NO_DISPLAY; mEglContext = EGL_NO_CONTEXT; mAttached = false; return OK; } status_t GLConsumer::attachToContext(uint32_t tex) { ATRACE_CALL(); GLC_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("attachToContext: abandoned GLConsumer"); return NO_INIT; } if (mAttached) { GLC_LOGE("attachToContext: GLConsumer is already attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (dpy == EGL_NO_DISPLAY) { GLC_LOGE("attachToContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (ctx == EGL_NO_CONTEXT) { GLC_LOGE("attachToContext: invalid current EGLContext"); return INVALID_OPERATION; } // We need to bind the texture regardless of whether there's a current // buffer. glBindTexture(mTexTarget, GLuint(tex)); mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; if (mCurrentTextureImage != nullptr) { // This may wait for a buffer a second time. This is likely required if // this is a different context, since otherwise the wait could be skipped // by bouncing through another context. For the same context the extra // wait is redundant. status_t err = bindTextureImageLocked(); if (err != NO_ERROR) { return err; } } return OK; } status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { GLC_LOGV("syncForReleaseLocked"); if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (SyncFeatures::getInstance().useNativeFenceSync()) { EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); eglDestroySyncKHR(dpy, sync); if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { GLC_LOGE("syncForReleaseLocked: error dup'ing native fence " "fd: %#x", eglGetError()); return UNKNOWN_ERROR; } sp fence(new Fence(fenceFd)); status_t err = addReleaseFenceLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), fence); if (err != OK) { GLC_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", strerror(-err), err); return err; } } else if (mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to // wait on that before replacing it with another fence to // ensure that all outstanding buffer accesses have completed // before the producer accesses it. EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); if (result == EGL_FALSE) { GLC_LOGE("syncForReleaseLocked: error waiting for previous " "fence: %#x", eglGetError()); return UNKNOWN_ERROR; } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { GLC_LOGE("syncForReleaseLocked: timeout waiting for previous " "fence"); return TIMED_OUT; } eglDestroySyncKHR(dpy, fence); } // Create a fence for the outstanding accesses in the current // OpenGL ES context. fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); if (fence == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; } } return OK; } uint32_t GLConsumer::getCurrentTextureTarget() const { return mTexTarget; } void GLConsumer::getTransformMatrix(float mtx[16]) { Mutex::Autolock lock(mMutex); memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); } void GLConsumer::setFilteringEnabled(bool enabled) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setFilteringEnabled: GLConsumer is abandoned!"); return; } bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; if (needsRecompute && mCurrentTextureImage==nullptr) { GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); } if (needsRecompute && mCurrentTextureImage != nullptr) { computeCurrentTransformMatrixLocked(); } } void GLConsumer::computeCurrentTransformMatrixLocked() { GLC_LOGV("computeCurrentTransformMatrixLocked"); sp buf = (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer(); if (buf == nullptr) { GLC_LOGD("computeCurrentTransformMatrixLocked: " "mCurrentTextureImage is NULL"); } computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, mFilteringEnabled); } Rect GLConsumer::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { Rect outCrop = crop; uint32_t newWidth = static_cast(crop.width()); uint32_t newHeight = static_cast(crop.height()); if (newWidth * bufferHeight > newHeight * bufferWidth) { newWidth = newHeight * bufferWidth / bufferHeight; ALOGV("too wide: newWidth = %d", newWidth); } else if (newWidth * bufferHeight < newHeight * bufferWidth) { newHeight = newWidth * bufferHeight / bufferWidth; ALOGV("too tall: newHeight = %d", newHeight); } uint32_t currentWidth = static_cast(crop.width()); uint32_t currentHeight = static_cast(crop.height()); // The crop is too wide if (newWidth < currentWidth) { uint32_t dw = currentWidth - newWidth; auto halfdw = dw / 2; outCrop.left += halfdw; // Not halfdw because it would subtract 1 too few when dw is odd outCrop.right -= (dw - halfdw); // The crop is too tall } else if (newHeight < currentHeight) { uint32_t dh = currentHeight - newHeight; auto halfdh = dh / 2; outCrop.top += halfdh; // Not halfdh because it would subtract 1 too few when dh is odd outCrop.bottom -= (dh - halfdh); } ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,outCrop.bottom); return outCrop; } nsecs_t GLConsumer::getTimestamp() { GLC_LOGV("getTimestamp"); Mutex::Autolock lock(mMutex); return mCurrentTimestamp; } android_dataspace GLConsumer::getCurrentDataSpace() { GLC_LOGV("getCurrentDataSpace"); Mutex::Autolock lock(mMutex); return mCurrentDataSpace; } uint64_t GLConsumer::getFrameNumber() { GLC_LOGV("getFrameNumber"); Mutex::Autolock lock(mMutex); return mCurrentFrameNumber; } sp GLConsumer::getCurrentBuffer(int* outSlot) const { Mutex::Autolock lock(mMutex); if (outSlot != nullptr) { *outSlot = mCurrentTexture; } return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer(); } Rect GLConsumer::getCurrentCrop() const { Mutex::Autolock lock(mMutex); return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) : mCurrentCrop; } uint32_t GLConsumer::getCurrentTransform() const { Mutex::Autolock lock(mMutex); return mCurrentTransform; } uint32_t GLConsumer::getCurrentScalingMode() const { Mutex::Autolock lock(mMutex); return mCurrentScalingMode; } sp GLConsumer::getCurrentFence() const { Mutex::Autolock lock(mMutex); return mCurrentFence; } std::shared_ptr GLConsumer::getCurrentFenceTime() const { Mutex::Autolock lock(mMutex); return mCurrentFenceTime; } status_t GLConsumer::doGLFenceWaitLocked() const { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { GLC_LOGE("doGLFenceWait: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { GLC_LOGE("doGLFenceWait: invalid current EGLContext"); return INVALID_OPERATION; } if (mCurrentFence->isValid()) { if (SyncFeatures::getInstance().useWaitSync() && SyncFeatures::getInstance().useNativeFenceSync()) { // Create an EGLSyncKHR from the current fence. int fenceFd = mCurrentFence->dup(); if (fenceFd == -1) { GLC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); return -errno; } EGLint attribs[] = { EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE }; EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (sync == EGL_NO_SYNC_KHR) { close(fenceFd); GLC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); return UNKNOWN_ERROR; } // XXX: The spec draft is inconsistent as to whether this should // return an EGLint or void. Ignore the return value for now, as // it's not strictly needed. eglWaitSyncKHR(dpy, sync, 0); EGLint eglErr = eglGetError(); eglDestroySyncKHR(dpy, sync); if (eglErr != EGL_SUCCESS) { GLC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); return UNKNOWN_ERROR; } } else { status_t err = mCurrentFence->waitForever( "GLConsumer::doGLFenceWaitLocked"); if (err != NO_ERROR) { GLC_LOGE("doGLFenceWait: error waiting for fence: %d", err); return err; } } } return NO_ERROR; } void GLConsumer::freeBufferLocked(int slotIndex) { GLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); if (slotIndex == mCurrentTexture) { mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; } mEglSlots[slotIndex].mEglImage.clear(); ConsumerBase::freeBufferLocked(slotIndex); } void GLConsumer::abandonLocked() { GLC_LOGV("abandonLocked"); mCurrentTextureImage.clear(); ConsumerBase::abandonLocked(); } status_t GLConsumer::setConsumerUsageBits(uint64_t usage) { return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); } void GLConsumer::dumpLocked(String8& result, const char* prefix) const { result.appendFormat( "%smTexName=%d mCurrentTexture=%d\n" "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); ConsumerBase::dumpLocked(result, prefix); } GLConsumer::EglImage::EglImage(sp graphicBuffer) : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) { } GLConsumer::EglImage::~EglImage() { if (mEglImage != EGL_NO_IMAGE_KHR) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("~EglImage: eglDestroyImageKHR failed"); } DEBUG_EGL_IMAGE_TRACKER_DESTROY(); eglTerminate(mEglDisplay); } } status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { // If there's an image and it's no longer valid, destroy it. bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; bool displayInvalid = mEglDisplay != eglDisplay; if (haveImage && (displayInvalid || forceCreation)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } DEBUG_EGL_IMAGE_TRACKER_DESTROY(); eglTerminate(mEglDisplay); mEglImage = EGL_NO_IMAGE_KHR; mEglDisplay = EGL_NO_DISPLAY; } // If there's no image, create one. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = eglDisplay; mEglImage = createImage(mEglDisplay, mGraphicBuffer); } // Fail if we can't create a valid image. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = EGL_NO_DISPLAY; const sp& buffer = mGraphicBuffer; ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), buffer->getPixelFormat()); return UNKNOWN_ERROR; } return OK; } void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); } EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, const sp& graphicBuffer) { EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); const bool createProtectedImage = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, createProtectedImage ? EGL_TRUE : EGL_NONE, EGL_NONE, }; eglInitialize(dpy, nullptr, nullptr); EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { EGLint error = eglGetError(); ALOGE("error creating EGLImage: %#x", error); eglTerminate(dpy); } else { DEBUG_EGL_IMAGE_TRACKER_CREATE(); } return image; } }; // namespace android ���������������������������������������������������������������������������libs/gui/GLConsumerUtils.cpp������������������������������������������������������������������������0100644 0000000 0000000 00000010262 13756501735 015102� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #define LOG_TAG "GLConsumerUtils" //#define LOG_NDEBUG 0 #include #include #include #include namespace android { void GLConsumer::computeTransformMatrix(float outTransform[16], const sp& buf, const Rect& cropRect, uint32_t transform, bool filtering) { // Transform matrices static const mat4 mtxFlipH( -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1 ); static const mat4 mtxFlipV( 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ); static const mat4 mtxRot90( 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1 ); mat4 xform; if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { xform *= mtxFlipH; } if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { xform *= mtxFlipV; } if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { xform *= mtxRot90; } if (!cropRect.isEmpty()) { float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; float bufferWidth = buf->getWidth(); float bufferHeight = buf->getHeight(); float shrinkAmount = 0.0f; if (filtering) { // In order to prevent bilinear sampling beyond the edge of the // crop rectangle we may need to shrink it by 2 texels in each // dimension. Normally this would just need to take 1/2 a texel // off each end, but because the chroma channels of YUV420 images // are subsampled we may need to shrink the crop region by a whole // texel on each side. switch (buf->getPixelFormat()) { case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_RGBA_FP16: case PIXEL_FORMAT_RGBA_1010102: case PIXEL_FORMAT_RGB_888: case PIXEL_FORMAT_RGB_565: case PIXEL_FORMAT_BGRA_8888: // We know there's no subsampling of any channels, so we // only need to shrink by a half a pixel. shrinkAmount = 0.5; break; default: // If we don't recognize the format, we must assume the // worst case (that we care about), which is YUV420. shrinkAmount = 1.0; break; } } // Only shrink the dimensions that are not the size of the buffer. if (cropRect.width() < bufferWidth) { tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; } if (cropRect.height() < bufferHeight) { ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; } mat4 crop( sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1 ); xform = crop * xform; } // GLConsumer uses the GL convention where (0, 0) is the bottom-left // corner and (1, 1) is the top-right corner. Add an additional vertical // flip after all other transforms to map from GL convention to buffer // queue memory layout, where (0, 0) is the top-left corner. xform = mtxFlipV * xform; memcpy(outTransform, xform.asArray(), sizeof(xform)); } }; // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/GuiConfig.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000001631 13756501735 013715� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2012 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. */ #include namespace android { void appendGuiConfigString(std::string& configStr) { static const char* config = " [libgui" #ifdef DONT_USE_FENCE_SYNC " DONT_USE_FENCE_SYNC" #endif "]"; configStr.append(config); } }; // namespace android �������������������������������������������������������������������������������������������������������libs/gui/HdrMetadata.cpp����������������������������������������������������������������������������0100644 0000000 0000000 00000010032 13756501735 014214� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #include #include namespace android { size_t HdrMetadata::getFlattenedSize() const { size_t size = sizeof(validTypes); if (validTypes & SMPTE2086) { size += sizeof(smpte2086); } if (validTypes & CTA861_3) { size += sizeof(cta8613); } if (validTypes & HDR10PLUS) { size += sizeof(size_t); size += hdr10plus.size(); } return size; } status_t HdrMetadata::flatten(void* buffer, size_t size) const { if (size < getFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, validTypes); if (validTypes & SMPTE2086) { FlattenableUtils::write(buffer, size, smpte2086); } if (validTypes & CTA861_3) { FlattenableUtils::write(buffer, size, cta8613); } if (validTypes & HDR10PLUS) { size_t metadataSize = hdr10plus.size(); FlattenableUtils::write(buffer, size, metadataSize); memcpy(buffer, hdr10plus.data(), metadataSize); FlattenableUtils::advance(buffer, size, metadataSize); } return NO_ERROR; } status_t HdrMetadata::unflatten(void const* buffer, size_t size) { if (size < sizeof(validTypes)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, validTypes); if (validTypes & SMPTE2086) { if (size < sizeof(smpte2086)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, smpte2086); } if (validTypes & CTA861_3) { if (size < sizeof(cta8613)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, cta8613); } if (validTypes & HDR10PLUS) { if (size < sizeof(size_t)) { return NO_MEMORY; } size_t metadataSize; FlattenableUtils::read(buffer, size, metadataSize); if (size < metadataSize) { return NO_MEMORY; } hdr10plus.resize(metadataSize); memcpy(hdr10plus.data(), buffer, metadataSize); FlattenableUtils::advance(buffer, size, metadataSize); } return NO_ERROR; } bool HdrMetadata::operator==(const HdrMetadata& rhs) const { if (validTypes != rhs.validTypes) return false; if ((validTypes & SMPTE2086) == SMPTE2086) { if (smpte2086.displayPrimaryRed.x != rhs.smpte2086.displayPrimaryRed.x || smpte2086.displayPrimaryRed.y != rhs.smpte2086.displayPrimaryRed.y || smpte2086.displayPrimaryGreen.x != rhs.smpte2086.displayPrimaryGreen.x || smpte2086.displayPrimaryGreen.y != rhs.smpte2086.displayPrimaryGreen.y || smpte2086.displayPrimaryBlue.x != rhs.smpte2086.displayPrimaryBlue.x || smpte2086.displayPrimaryBlue.y != rhs.smpte2086.displayPrimaryBlue.y || smpte2086.whitePoint.x != rhs.smpte2086.whitePoint.x || smpte2086.whitePoint.y != rhs.smpte2086.whitePoint.y || smpte2086.maxLuminance != rhs.smpte2086.maxLuminance || smpte2086.minLuminance != rhs.smpte2086.minLuminance) { return false; } } if ((validTypes & CTA861_3) == CTA861_3) { if (cta8613.maxFrameAverageLightLevel != rhs.cta8613.maxFrameAverageLightLevel || cta8613.maxContentLightLevel != rhs.cta8613.maxContentLightLevel) { return false; } } if ((validTypes & HDR10PLUS) == HDR10PLUS) { if (hdr10plus != rhs.hdr10plus) return false; } return true; } } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/IConsumerListener.cpp����������������������������������������������������������������������0100644 0000000 0000000 00000007312 13756501735 015457� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #include #include namespace android { namespace { // Anonymous enum class Tag : uint32_t { ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, ON_FRAME_AVAILABLE, ON_FRAME_REPLACED, ON_BUFFERS_RELEASED, ON_SIDEBAND_STREAM_CHANGED, LAST = ON_SIDEBAND_STREAM_CHANGED, }; } // Anonymous namespace class BpConsumerListener : public SafeBpInterface { public: explicit BpConsumerListener(const sp& impl) : SafeBpInterface(impl, "BpConsumerListener") {} ~BpConsumerListener() override; void onDisconnect() override { callRemoteAsync(Tag::ON_DISCONNECT); } void onFrameAvailable(const BufferItem& item) override { callRemoteAsync(Tag::ON_FRAME_AVAILABLE, item); } void onFrameReplaced(const BufferItem& item) override { callRemoteAsync(Tag::ON_FRAME_REPLACED, item); } void onBuffersReleased() override { callRemoteAsync(Tag::ON_BUFFERS_RELEASED); } void onSidebandStreamChanged() override { callRemoteAsync( Tag::ON_SIDEBAND_STREAM_CHANGED); } void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/, FrameEventHistoryDelta* /*outDelta*/) override { LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied"); } }; // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see // clang warning -Wweak-vtables) BpConsumerListener::~BpConsumerListener() = default; ConsumerListener::~ConsumerListener() = default; IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener"); status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast(Tag::LAST)) { return BBinder::onTransact(code, data, reply, flags); } auto tag = static_cast(code); switch (tag) { case Tag::ON_DISCONNECT: return callLocalAsync(data, reply, &IConsumerListener::onDisconnect); case Tag::ON_FRAME_AVAILABLE: return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable); case Tag::ON_FRAME_REPLACED: return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced); case Tag::ON_BUFFERS_RELEASED: return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased); case Tag::ON_SIDEBAND_STREAM_CHANGED: return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged); } } } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/IDisplayEventConnection.cpp����������������������������������������������������������������0100644 0000000 0000000 00000005662 13756501735 016613� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2011 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. */ #include #include namespace android { namespace { // Anonymous enum class Tag : uint32_t { STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, SET_VSYNC_RATE, REQUEST_NEXT_VSYNC, LAST = REQUEST_NEXT_VSYNC, }; } // Anonymous namespace class BpDisplayEventConnection : public SafeBpInterface { public: explicit BpDisplayEventConnection(const sp& impl) : SafeBpInterface(impl, "BpDisplayEventConnection") {} ~BpDisplayEventConnection() override; status_t stealReceiveChannel(gui::BitTube* outChannel) override { return callRemote(Tag::STEAL_RECEIVE_CHANNEL, outChannel); } status_t setVsyncRate(uint32_t count) override { return callRemote(Tag::SET_VSYNC_RATE, count); } void requestNextVsync() override { callRemoteAsync( Tag::REQUEST_NEXT_VSYNC); } }; // Out-of-line virtual method definition to trigger vtable emission in this translation unit (see // clang warning -Wweak-vtables) BpDisplayEventConnection::~BpDisplayEventConnection() = default; IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection"); status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast(Tag::LAST)) { return BBinder::onTransact(code, data, reply, flags); } auto tag = static_cast(code); switch (tag) { case Tag::STEAL_RECEIVE_CHANNEL: return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel); case Tag::SET_VSYNC_RATE: return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate); case Tag::REQUEST_NEXT_VSYNC: return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync); } } } // namespace android ������������������������������������������������������������������������������libs/gui/IGraphicBufferConsumer.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000023610 13756501735 016400� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #include #include #include #include #include #include #include #include namespace android { namespace { // Anonymous namespace enum class Tag : uint32_t { ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, DETACH_BUFFER, ATTACH_BUFFER, RELEASE_BUFFER, CONSUMER_CONNECT, CONSUMER_DISCONNECT, GET_RELEASED_BUFFERS, SET_DEFAULT_BUFFER_SIZE, SET_MAX_BUFFER_COUNT, SET_MAX_ACQUIRED_BUFFER_COUNT, SET_CONSUMER_NAME, SET_DEFAULT_BUFFER_FORMAT, SET_DEFAULT_BUFFER_DATA_SPACE, SET_CONSUMER_USAGE_BITS, SET_CONSUMER_IS_PROTECTED, SET_TRANSFORM_HINT, GET_SIDEBAND_STREAM, GET_OCCUPANCY_HISTORY, DISCARD_FREE_BUFFERS, DUMP_STATE, LAST = DUMP_STATE, }; } // Anonymous namespace class BpGraphicBufferConsumer : public SafeBpInterface { public: explicit BpGraphicBufferConsumer(const sp& impl) : SafeBpInterface(impl, "BpGraphicBufferConsumer") {} ~BpGraphicBufferConsumer() override; status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, uint64_t maxFrameNumber) override { using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer); return callRemote(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber); } status_t detachBuffer(int slot) override { using Signature = decltype(&IGraphicBufferConsumer::detachBuffer); return callRemote(Tag::DETACH_BUFFER, slot); } status_t attachBuffer(int* slot, const sp& buffer) override { using Signature = decltype(&IGraphicBufferConsumer::attachBuffer); return callRemote(Tag::ATTACH_BUFFER, slot, buffer); } status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), const sp& releaseFence) override { return callRemote(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); } status_t consumerConnect(const sp& consumer, bool controlledByApp) override { using Signature = decltype(&IGraphicBufferConsumer::consumerConnect); return callRemote(Tag::CONSUMER_CONNECT, consumer, controlledByApp); } status_t consumerDisconnect() override { return callRemote( Tag::CONSUMER_DISCONNECT); } status_t getReleasedBuffers(uint64_t* slotMask) override { using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers); return callRemote(Tag::GET_RELEASED_BUFFERS, slotMask); } status_t setDefaultBufferSize(uint32_t width, uint32_t height) override { using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize); return callRemote(Tag::SET_DEFAULT_BUFFER_SIZE, width, height); } status_t setMaxBufferCount(int bufferCount) override { using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount); return callRemote(Tag::SET_MAX_BUFFER_COUNT, bufferCount); } status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override { using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount); return callRemote(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers); } status_t setConsumerName(const String8& name) override { using Signature = decltype(&IGraphicBufferConsumer::setConsumerName); return callRemote(Tag::SET_CONSUMER_NAME, name); } status_t setDefaultBufferFormat(PixelFormat defaultFormat) override { using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat); return callRemote(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat); } status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override { using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace); return callRemote(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace); } status_t setConsumerUsageBits(uint64_t usage) override { using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits); return callRemote(Tag::SET_CONSUMER_USAGE_BITS, usage); } status_t setConsumerIsProtected(bool isProtected) override { using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected); return callRemote(Tag::SET_CONSUMER_IS_PROTECTED, isProtected); } status_t setTransformHint(uint32_t hint) override { using Signature = decltype(&IGraphicBufferConsumer::setTransformHint); return callRemote(Tag::SET_TRANSFORM_HINT, hint); } status_t getSidebandStream(sp* outStream) const override { using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream); return callRemote(Tag::GET_SIDEBAND_STREAM, outStream); } status_t getOccupancyHistory(bool forceFlush, std::vector* outHistory) override { using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory); return callRemote(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory); } status_t discardFreeBuffers() override { return callRemote( Tag::DISCARD_FREE_BUFFERS); } status_t dumpState(const String8& prefix, String8* outResult) const override { using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; return callRemote(Tag::DUMP_STATE, prefix, outResult); } }; // Out-of-line virtual method definition to trigger vtable emission in this translation unit // (see clang warning -Wweak-vtables) BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default; IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer"); status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast(Tag::LAST)) { return BBinder::onTransact(code, data, reply, flags); } auto tag = static_cast(code); switch (tag) { case Tag::ACQUIRE_BUFFER: return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer); case Tag::DETACH_BUFFER: return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer); case Tag::ATTACH_BUFFER: return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer); case Tag::RELEASE_BUFFER: return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper); case Tag::CONSUMER_CONNECT: return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect); case Tag::CONSUMER_DISCONNECT: return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect); case Tag::GET_RELEASED_BUFFERS: return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers); case Tag::SET_DEFAULT_BUFFER_SIZE: return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize); case Tag::SET_MAX_BUFFER_COUNT: return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount); case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT: return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount); case Tag::SET_CONSUMER_NAME: return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName); case Tag::SET_DEFAULT_BUFFER_FORMAT: return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat); case Tag::SET_DEFAULT_BUFFER_DATA_SPACE: return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace); case Tag::SET_CONSUMER_USAGE_BITS: return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits); case Tag::SET_CONSUMER_IS_PROTECTED: return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected); case Tag::SET_TRANSFORM_HINT: return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint); case Tag::GET_SIDEBAND_STREAM: return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream); case Tag::GET_OCCUPANCY_HISTORY: return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory); case Tag::DISCARD_FREE_BUFFERS: return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers); case Tag::DUMP_STATE: { using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; return callLocal(data, reply, &IGraphicBufferConsumer::dumpState); } } } } // namespace android ������������������������������������������������������������������������������������������������������������������������libs/gui/IGraphicBufferProducer.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000122216 13756501735 016372� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #include #include #include #include #include #include #include #include #include #include #ifndef NO_BUFFERHUB #include #endif #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- using H2BGraphicBufferProducerV1_0 = ::android::hardware::graphics::bufferqueue::V1_0::utils:: H2BGraphicBufferProducer; using H2BGraphicBufferProducerV2_0 = ::android::hardware::graphics::bufferqueue::V2_0::utils:: H2BGraphicBufferProducer; enum { REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, DEQUEUE_BUFFER, DETACH_BUFFER, DETACH_NEXT_BUFFER, ATTACH_BUFFER, QUEUE_BUFFER, CANCEL_BUFFER, QUERY, CONNECT, DISCONNECT, SET_SIDEBAND_STREAM, ALLOCATE_BUFFERS, ALLOW_ALLOCATION, SET_GENERATION_NUMBER, GET_CONSUMER_NAME, SET_MAX_DEQUEUED_BUFFER_COUNT, SET_ASYNC_MODE, SET_SHARED_BUFFER_MODE, SET_AUTO_REFRESH, SET_DEQUEUE_TIMEOUT, GET_LAST_QUEUED_BUFFER, GET_FRAME_TIMESTAMPS, GET_UNIQUE_ID, GET_CONSUMER_USAGE, SET_LEGACY_BUFFER_DROP, }; class BpGraphicBufferProducer : public BpInterface { public: explicit BpGraphicBufferProducer(const sp& impl) : BpInterface(impl) { } ~BpGraphicBufferProducer() override; virtual status_t requestBuffer(int bufferIdx, sp* buf) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(bufferIdx); status_t result =remote()->transact(REQUEST_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } bool nonNull = reply.readInt32(); if (nonNull) { *buf = new GraphicBuffer(); result = reply.read(**buf); if(result != NO_ERROR) { (*buf).clear(); return result; } } result = reply.readInt32(); return result; } virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) { Parcel data, reply; data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(maxDequeuedBuffers); status_t result = remote()->transact(SET_MAX_DEQUEUED_BUFFER_COUNT, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t setAsyncMode(bool async) { Parcel data, reply; data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(async); status_t result = remote()->transact(SET_ASYNC_MODE, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t dequeueBuffer(int* buf, sp* fence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { Parcel data, reply; bool getFrameTimestamps = (outTimestamps != nullptr); data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeUint32(width); data.writeUint32(height); data.writeInt32(static_cast(format)); data.writeUint64(usage); data.writeBool(getFrameTimestamps); status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } *buf = reply.readInt32(); *fence = new Fence(); result = reply.read(**fence); if (result != NO_ERROR) { fence->clear(); return result; } if (outBufferAge) { result = reply.readUint64(outBufferAge); } else { // Read the value even if outBufferAge is nullptr: uint64_t bufferAge; result = reply.readUint64(&bufferAge); } if (result != NO_ERROR) { ALOGE("IGBP::dequeueBuffer failed to read buffer age: %d", result); return result; } if (getFrameTimestamps) { result = reply.read(*outTimestamps); if (result != NO_ERROR) { ALOGE("IGBP::dequeueBuffer failed to read timestamps: %d", result); return result; } } result = reply.readInt32(); return result; } virtual status_t detachBuffer(int slot) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(slot); status_t result = remote()->transact(DETACH_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t detachNextBuffer(sp* outBuffer, sp* outFence) { if (outBuffer == nullptr) { ALOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; } else if (outFence == nullptr) { ALOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); status_t result = remote()->transact(DETACH_NEXT_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); if (result == NO_ERROR) { bool nonNull = reply.readInt32(); if (nonNull) { *outBuffer = new GraphicBuffer; result = reply.read(**outBuffer); if (result != NO_ERROR) { outBuffer->clear(); return result; } } nonNull = reply.readInt32(); if (nonNull) { *outFence = new Fence; result = reply.read(**outFence); if (result != NO_ERROR) { outBuffer->clear(); outFence->clear(); return result; } } } return result; } virtual status_t attachBuffer(int* slot, const sp& buffer) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.write(*buffer.get()); status_t result = remote()->transact(ATTACH_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } *slot = reply.readInt32(); result = reply.readInt32(); if (result == NO_ERROR && (*slot < 0 || *slot >= BufferQueueDefs::NUM_BUFFER_SLOTS)) { ALOGE("attachBuffer returned invalid slot %d", *slot); android_errorWriteLog(0x534e4554, "37478824"); return UNKNOWN_ERROR; } return result; } virtual status_t queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(buf); data.write(input); status_t result = remote()->transact(QUEUE_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } result = reply.read(*output); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t cancelBuffer(int buf, const sp& fence) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(buf); data.write(*fence.get()); status_t result = remote()->transact(CANCEL_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual int query(int what, int* value) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(what); status_t result = remote()->transact(QUERY, data, &reply); if (result != NO_ERROR) { return result; } value[0] = reply.readInt32(); result = reply.readInt32(); return result; } virtual status_t connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); if (listener != nullptr) { data.writeInt32(1); data.writeStrongBinder(IInterface::asBinder(listener)); } else { data.writeInt32(0); } data.writeInt32(api); data.writeInt32(producerControlledByApp); status_t result = remote()->transact(CONNECT, data, &reply); if (result != NO_ERROR) { return result; } reply.read(*output); result = reply.readInt32(); return result; } virtual status_t disconnect(int api, DisconnectMode mode) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(api); data.writeInt32(static_cast(mode)); status_t result =remote()->transact(DISCONNECT, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t setSidebandStream(const sp& stream) { Parcel data, reply; status_t result; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); if (stream.get()) { data.writeInt32(true); data.writeNativeHandle(stream->handle()); } else { data.writeInt32(false); } if ((result = remote()->transact(SET_SIDEBAND_STREAM, data, &reply)) == NO_ERROR) { result = reply.readInt32(); } return result; } virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeUint32(width); data.writeUint32(height); data.writeInt32(static_cast(format)); data.writeUint64(usage); status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, IBinder::FLAG_ONEWAY); if (result != NO_ERROR) { ALOGE("allocateBuffers failed to transact: %d", result); } } virtual status_t allowAllocation(bool allow) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(static_cast(allow)); status_t result = remote()->transact(ALLOW_ALLOCATION, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t setGenerationNumber(uint32_t generationNumber) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeUint32(generationNumber); status_t result = remote()->transact(SET_GENERATION_NUMBER, data, &reply); if (result == NO_ERROR) { result = reply.readInt32(); } return result; } virtual String8 getConsumerName() const { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); status_t result = remote()->transact(GET_CONSUMER_NAME, data, &reply); if (result != NO_ERROR) { ALOGE("getConsumerName failed to transact: %d", result); return String8("TransactFailed"); } return reply.readString8(); } virtual status_t setSharedBufferMode(bool sharedBufferMode) { Parcel data, reply; data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(sharedBufferMode); status_t result = remote()->transact(SET_SHARED_BUFFER_MODE, data, &reply); if (result == NO_ERROR) { result = reply.readInt32(); } return result; } virtual status_t setAutoRefresh(bool autoRefresh) { Parcel data, reply; data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(autoRefresh); status_t result = remote()->transact(SET_AUTO_REFRESH, data, &reply); if (result == NO_ERROR) { result = reply.readInt32(); } return result; } virtual status_t setDequeueTimeout(nsecs_t timeout) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt64(timeout); status_t result = remote()->transact(SET_DEQUEUE_TIMEOUT, data, &reply); if (result != NO_ERROR) { ALOGE("setDequeueTimeout failed to transact: %d", result); return result; } return reply.readInt32(); } virtual status_t setLegacyBufferDrop(bool drop) { Parcel data, reply; data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(drop); status_t result = remote()->transact(SET_LEGACY_BUFFER_DROP, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readInt32(); return result; } virtual status_t getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) override { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER, data, &reply); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to transact: %d", result); return result; } result = reply.readInt32(); if (result != NO_ERROR) { return result; } bool hasBuffer = reply.readBool(); sp buffer; if (hasBuffer) { buffer = new GraphicBuffer(); result = reply.read(*buffer); if (result == NO_ERROR) { result = reply.read(outTransformMatrix, sizeof(float) * 16); } } if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); return result; } sp fence(new Fence); result = reply.read(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to read fence: %d", result); return result; } *outBuffer = buffer; *outFence = fence; return result; } virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) { Parcel data, reply; status_t result = data.writeInterfaceToken( IGraphicBufferProducer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("IGBP::getFrameTimestamps failed to write token: %d", result); return; } result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply); if (result != NO_ERROR) { ALOGE("IGBP::getFrameTimestamps failed to transact: %d", result); return; } result = reply.read(*outDelta); if (result != NO_ERROR) { ALOGE("IGBP::getFrameTimestamps failed to read timestamps: %d", result); } } virtual status_t getUniqueId(uint64_t* outId) const { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); status_t result = remote()->transact(GET_UNIQUE_ID, data, &reply); if (result != NO_ERROR) { ALOGE("getUniqueId failed to transact: %d", result); } status_t actualResult = NO_ERROR; result = reply.readInt32(&actualResult); if (result != NO_ERROR) { return result; } result = reply.readUint64(outId); if (result != NO_ERROR) { return result; } return actualResult; } virtual status_t getConsumerUsage(uint64_t* outUsage) const { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); status_t result = remote()->transact(GET_CONSUMER_USAGE, data, &reply); if (result != NO_ERROR) { ALOGE("getConsumerUsage failed to transact: %d", result); } status_t actualResult = NO_ERROR; result = reply.readInt32(&actualResult); if (result != NO_ERROR) { return result; } result = reply.readUint64(outUsage); if (result != NO_ERROR) { return result; } return actualResult; } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpGraphicBufferProducer::~BpGraphicBufferProducer() {} class HpGraphicBufferProducer : public HpInterface< BpGraphicBufferProducer, H2BGraphicBufferProducerV1_0, H2BGraphicBufferProducerV2_0> { public: explicit HpGraphicBufferProducer(const sp& base) : PBase(base) {} status_t requestBuffer(int slot, sp* buf) override { return mBase->requestBuffer(slot, buf); } status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override { return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers); } status_t setAsyncMode(bool async) override { return mBase->setAsyncMode(async); } status_t dequeueBuffer(int* slot, sp* fence, uint32_t w, uint32_t h, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override { return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); } status_t detachBuffer(int slot) override { return mBase->detachBuffer(slot); } status_t detachNextBuffer( sp* outBuffer, sp* outFence) override { return mBase->detachNextBuffer(outBuffer, outFence); } status_t attachBuffer( int* outSlot, const sp& buffer) override { return mBase->attachBuffer(outSlot, buffer); } status_t queueBuffer( int slot, const QueueBufferInput& input, QueueBufferOutput* output) override { return mBase->queueBuffer(slot, input, output); } status_t cancelBuffer(int slot, const sp& fence) override { return mBase->cancelBuffer(slot, fence); } int query(int what, int* value) override { return mBase->query(what, value); } status_t connect( const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override { return mBase->connect(listener, api, producerControlledByApp, output); } status_t disconnect( int api, DisconnectMode mode = DisconnectMode::Api) override { return mBase->disconnect(api, mode); } status_t setSidebandStream(const sp& stream) override { return mBase->setSidebandStream(stream); } void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) override { return mBase->allocateBuffers(width, height, format, usage); } status_t allowAllocation(bool allow) override { return mBase->allowAllocation(allow); } status_t setGenerationNumber(uint32_t generationNumber) override { return mBase->setGenerationNumber(generationNumber); } String8 getConsumerName() const override { return mBase->getConsumerName(); } status_t setSharedBufferMode(bool sharedBufferMode) override { return mBase->setSharedBufferMode(sharedBufferMode); } status_t setAutoRefresh(bool autoRefresh) override { return mBase->setAutoRefresh(autoRefresh); } status_t setDequeueTimeout(nsecs_t timeout) override { return mBase->setDequeueTimeout(timeout); } status_t setLegacyBufferDrop(bool drop) override { return mBase->setLegacyBufferDrop(drop); } status_t getLastQueuedBuffer( sp* outBuffer, sp* outFence, float outTransformMatrix[16]) override { return mBase->getLastQueuedBuffer( outBuffer, outFence, outTransformMatrix); } void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override { return mBase->getFrameTimestamps(outDelta); } status_t getUniqueId(uint64_t* outId) const override { return mBase->getUniqueId(outId); } status_t getConsumerUsage(uint64_t* outUsage) const override { return mBase->getConsumerUsage(outUsage); } }; IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer"); // ---------------------------------------------------------------------- status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) { // No-op for IGBP other than BufferQueue. (void) drop; return INVALID_OPERATION; } status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) { status_t res = OK; res = parcel->writeUint32(USE_BUFFER_QUEUE); if (res != NO_ERROR) { ALOGE("exportToParcel: Cannot write magic, res=%d.", res); return res; } return parcel->writeStrongBinder(IInterface::asBinder(this)); } /* static */ status_t IGraphicBufferProducer::exportToParcel(const sp& producer, Parcel* parcel) { if (parcel == nullptr) { ALOGE("exportToParcel: Invalid parcel object."); return BAD_VALUE; } if (producer == nullptr) { status_t res = OK; res = parcel->writeUint32(IGraphicBufferProducer::USE_BUFFER_QUEUE); if (res != NO_ERROR) return res; return parcel->writeStrongBinder(nullptr); } else { return producer->exportToParcel(parcel); } } /* static */ sp IGraphicBufferProducer::createFromParcel(const Parcel* parcel) { uint32_t outMagic = 0; status_t res = NO_ERROR; res = parcel->readUint32(&outMagic); if (res != NO_ERROR) { ALOGE("createFromParcel: Failed to read magic, error=%d.", res); return nullptr; } switch (outMagic) { case USE_BUFFER_QUEUE: { sp binder; res = parcel->readNullableStrongBinder(&binder); if (res != NO_ERROR) { ALOGE("createFromParcel: Can't read strong binder."); return nullptr; } return interface_cast(binder); } case USE_BUFFER_HUB: { ALOGE("createFromParcel: BufferHub not implemented."); #ifndef NO_BUFFERHUB dvr::ProducerQueueParcelable producerParcelable; res = producerParcelable.readFromParcel(parcel); if (res != NO_ERROR) { ALOGE("createFromParcel: Failed to read from parcel, error=%d", res); return nullptr; } return BufferHubProducer::Create(std::move(producerParcelable)); #else return nullptr; #endif } default: { ALOGE("createFromParcel: Unexpected mgaic: 0x%x.", outMagic); return nullptr; } } } // ---------------------------------------------------------------------------- status_t BnGraphicBufferProducer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case REQUEST_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int bufferIdx = data.readInt32(); sp buffer; int result = requestBuffer(bufferIdx, &buffer); reply->writeInt32(buffer != nullptr); if (buffer != nullptr) { reply->write(*buffer); } reply->writeInt32(result); return NO_ERROR; } case SET_MAX_DEQUEUED_BUFFER_COUNT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int maxDequeuedBuffers = data.readInt32(); int result = setMaxDequeuedBufferCount(maxDequeuedBuffers); reply->writeInt32(result); return NO_ERROR; } case SET_ASYNC_MODE: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); bool async = data.readInt32(); int result = setAsyncMode(async); reply->writeInt32(result); return NO_ERROR; } case DEQUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); uint32_t width = data.readUint32(); uint32_t height = data.readUint32(); PixelFormat format = static_cast(data.readInt32()); uint64_t usage = data.readUint64(); uint64_t bufferAge = 0; bool getTimestamps = data.readBool(); int buf = 0; sp fence = Fence::NO_FENCE; FrameEventHistoryDelta frameTimestamps; int result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge, getTimestamps ? &frameTimestamps : nullptr); if (fence == nullptr) { ALOGE("dequeueBuffer returned a NULL fence, setting to Fence::NO_FENCE"); fence = Fence::NO_FENCE; } reply->writeInt32(buf); reply->write(*fence); reply->writeUint64(bufferAge); if (getTimestamps) { reply->write(frameTimestamps); } reply->writeInt32(result); return NO_ERROR; } case DETACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int slot = data.readInt32(); int result = detachBuffer(slot); reply->writeInt32(result); return NO_ERROR; } case DETACH_NEXT_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp buffer; sp fence; int32_t result = detachNextBuffer(&buffer, &fence); reply->writeInt32(result); if (result == NO_ERROR) { reply->writeInt32(buffer != nullptr); if (buffer != nullptr) { reply->write(*buffer); } reply->writeInt32(fence != nullptr); if (fence != nullptr) { reply->write(*fence); } } return NO_ERROR; } case ATTACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp buffer = new GraphicBuffer(); status_t result = data.read(*buffer.get()); int slot = 0; if (result == NO_ERROR) { result = attachBuffer(&slot, buffer); } reply->writeInt32(slot); reply->writeInt32(result); return NO_ERROR; } case QUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); QueueBufferInput input(data); QueueBufferOutput output; status_t result = queueBuffer(buf, input, &output); reply->write(output); reply->writeInt32(result); return NO_ERROR; } case CANCEL_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); sp fence = new Fence(); status_t result = data.read(*fence.get()); if (result == NO_ERROR) { result = cancelBuffer(buf, fence); } reply->writeInt32(result); return NO_ERROR; } case QUERY: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int value = 0; int what = data.readInt32(); int res = query(what, &value); reply->writeInt32(value); reply->writeInt32(res); return NO_ERROR; } case CONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp listener; if (data.readInt32() == 1) { listener = IProducerListener::asInterface(data.readStrongBinder()); } int api = data.readInt32(); bool producerControlledByApp = data.readInt32(); QueueBufferOutput output; status_t res = connect(listener, api, producerControlledByApp, &output); reply->write(output); reply->writeInt32(res); return NO_ERROR; } case DISCONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int api = data.readInt32(); DisconnectMode mode = static_cast(data.readInt32()); status_t res = disconnect(api, mode); reply->writeInt32(res); return NO_ERROR; } case SET_SIDEBAND_STREAM: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp stream; if (data.readInt32()) { stream = NativeHandle::create(data.readNativeHandle(), true); } status_t result = setSidebandStream(stream); reply->writeInt32(result); return NO_ERROR; } case ALLOCATE_BUFFERS: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); uint32_t width = data.readUint32(); uint32_t height = data.readUint32(); PixelFormat format = static_cast(data.readInt32()); uint64_t usage = data.readUint64(); allocateBuffers(width, height, format, usage); return NO_ERROR; } case ALLOW_ALLOCATION: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); bool allow = static_cast(data.readInt32()); status_t result = allowAllocation(allow); reply->writeInt32(result); return NO_ERROR; } case SET_GENERATION_NUMBER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); uint32_t generationNumber = data.readUint32(); status_t result = setGenerationNumber(generationNumber); reply->writeInt32(result); return NO_ERROR; } case GET_CONSUMER_NAME: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); reply->writeString8(getConsumerName()); return NO_ERROR; } case SET_SHARED_BUFFER_MODE: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); bool sharedBufferMode = data.readInt32(); status_t result = setSharedBufferMode(sharedBufferMode); reply->writeInt32(result); return NO_ERROR; } case SET_AUTO_REFRESH: { CHECK_INTERFACE(IGraphicBuffer, data, reply); bool autoRefresh = data.readInt32(); status_t result = setAutoRefresh(autoRefresh); reply->writeInt32(result); return NO_ERROR; } case SET_DEQUEUE_TIMEOUT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); nsecs_t timeout = data.readInt64(); status_t result = setDequeueTimeout(timeout); reply->writeInt32(result); return NO_ERROR; } case GET_LAST_QUEUED_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp buffer(nullptr); sp fence(Fence::NO_FENCE); float transform[16] = {}; status_t result = getLastQueuedBuffer(&buffer, &fence, transform); reply->writeInt32(result); if (result != NO_ERROR) { return result; } if (!buffer.get()) { reply->writeBool(false); } else { reply->writeBool(true); result = reply->write(*buffer); if (result == NO_ERROR) { reply->write(transform, sizeof(float) * 16); } } if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to write buffer: %d", result); return result; } if (fence == nullptr) { ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE"); fence = Fence::NO_FENCE; } result = reply->write(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to write fence: %d", result); return result; } return NO_ERROR; } case GET_FRAME_TIMESTAMPS: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); FrameEventHistoryDelta frameTimestamps; getFrameTimestamps(&frameTimestamps); status_t result = reply->write(frameTimestamps); if (result != NO_ERROR) { ALOGE("BnGBP::GET_FRAME_TIMESTAMPS failed to write buffer: %d", result); return result; } return NO_ERROR; } case GET_UNIQUE_ID: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); uint64_t outId = 0; status_t actualResult = getUniqueId(&outId); status_t result = reply->writeInt32(actualResult); if (result != NO_ERROR) { return result; } result = reply->writeUint64(outId); if (result != NO_ERROR) { return result; } return NO_ERROR; } case GET_CONSUMER_USAGE: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); uint64_t outUsage = 0; status_t actualResult = getConsumerUsage(&outUsage); status_t result = reply->writeInt32(actualResult); if (result != NO_ERROR) { return result; } result = reply->writeUint64(outUsage); if (result != NO_ERROR) { return result; } return NO_ERROR; } case SET_LEGACY_BUFFER_DROP: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); bool drop = data.readInt32(); int result = setLegacyBufferDrop(drop); reply->writeInt32(result); return NO_ERROR; } } return BBinder::onTransact(code, data, reply, flags); } // ---------------------------------------------------------------------------- IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { parcel.read(*this); } constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { return sizeof(timestamp) + sizeof(isAutoTimestamp) + sizeof(dataSpace) + sizeof(crop) + sizeof(scalingMode) + sizeof(transform) + sizeof(stickyTransform) + sizeof(getFrameTimestamps); } size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { return minFlattenedSize() + fence->getFlattenedSize() + surfaceDamage.getFlattenedSize() + hdrMetadata.getFlattenedSize(); } size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { return fence->getFdCount(); } status_t IGraphicBufferProducer::QueueBufferInput::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { if (size < getFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, timestamp); FlattenableUtils::write(buffer, size, isAutoTimestamp); FlattenableUtils::write(buffer, size, dataSpace); FlattenableUtils::write(buffer, size, crop); FlattenableUtils::write(buffer, size, scalingMode); FlattenableUtils::write(buffer, size, transform); FlattenableUtils::write(buffer, size, stickyTransform); FlattenableUtils::write(buffer, size, getFrameTimestamps); status_t result = fence->flatten(buffer, size, fds, count); if (result != NO_ERROR) { return result; } result = surfaceDamage.flatten(buffer, size); if (result != NO_ERROR) { return result; } FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); return hdrMetadata.flatten(buffer, size); } status_t IGraphicBufferProducer::QueueBufferInput::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < minFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, timestamp); FlattenableUtils::read(buffer, size, isAutoTimestamp); FlattenableUtils::read(buffer, size, dataSpace); FlattenableUtils::read(buffer, size, crop); FlattenableUtils::read(buffer, size, scalingMode); FlattenableUtils::read(buffer, size, transform); FlattenableUtils::read(buffer, size, stickyTransform); FlattenableUtils::read(buffer, size, getFrameTimestamps); fence = new Fence(); status_t result = fence->unflatten(buffer, size, fds, count); if (result != NO_ERROR) { return result; } result = surfaceDamage.unflatten(buffer, size); if (result != NO_ERROR) { return result; } FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); return hdrMetadata.unflatten(buffer, size); } // ---------------------------------------------------------------------------- constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + sizeof(nextFrameNumber) + sizeof(bufferReplaced); } size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { return minFlattenedSize() + frameTimestamps.getFlattenedSize(); } size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const { return frameTimestamps.getFdCount(); } status_t IGraphicBufferProducer::QueueBufferOutput::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { if (size < getFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, width); FlattenableUtils::write(buffer, size, height); FlattenableUtils::write(buffer, size, transformHint); FlattenableUtils::write(buffer, size, numPendingBuffers); FlattenableUtils::write(buffer, size, nextFrameNumber); FlattenableUtils::write(buffer, size, bufferReplaced); return frameTimestamps.flatten(buffer, size, fds, count); } status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < minFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, width); FlattenableUtils::read(buffer, size, height); FlattenableUtils::read(buffer, size, transformHint); FlattenableUtils::read(buffer, size, numPendingBuffers); FlattenableUtils::read(buffer, size, nextFrameNumber); FlattenableUtils::read(buffer, size, bufferReplaced); return frameTimestamps.unflatten(buffer, size, fds, count); } }; // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/IProducerListener.cpp����������������������������������������������������������������������0100644 0000000 0000000 00000010675 13756501735 015455� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include #include #include #include namespace android { enum { ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION, NEEDS_RELEASE_NOTIFY, ON_BUFFERS_DISCARDED, }; class BpProducerListener : public BpInterface { public: explicit BpProducerListener(const sp& impl) : BpInterface(impl) {} virtual ~BpProducerListener(); virtual void onBufferReleased() { Parcel data, reply; data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY); } virtual bool needsReleaseNotify() { bool result; Parcel data, reply; data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); status_t err = remote()->transact(NEEDS_RELEASE_NOTIFY, data, &reply); if (err != NO_ERROR) { ALOGE("IProducerListener: binder call \'needsReleaseNotify\' failed"); return true; } err = reply.readBool(&result); if (err != NO_ERROR) { ALOGE("IProducerListener: malformed binder reply"); return true; } return result; } virtual void onBuffersDiscarded(const std::vector& discardedSlots) { Parcel data, reply; data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); data.writeInt32Vector(discardedSlots); remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY); } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpProducerListener::~BpProducerListener() {} class HpProducerListener : public HpInterface< BpProducerListener, hardware::graphics::bufferqueue::V1_0::utils::H2BProducerListener, hardware::graphics::bufferqueue::V2_0::utils::H2BProducerListener> { public: explicit HpProducerListener(const sp& base) : PBase{base} {} virtual void onBufferReleased() override { mBase->onBufferReleased(); } virtual bool needsReleaseNotify() override { return mBase->needsReleaseNotify(); } virtual void onBuffersDiscarded(const std::vector& discardedSlots) override { return mBase->onBuffersDiscarded(discardedSlots); } }; IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener, "android.gui.IProducerListener") status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { case ON_BUFFER_RELEASED: CHECK_INTERFACE(IProducerListener, data, reply); onBufferReleased(); return NO_ERROR; case NEEDS_RELEASE_NOTIFY: CHECK_INTERFACE(IProducerListener, data, reply); reply->writeBool(needsReleaseNotify()); return NO_ERROR; case ON_BUFFERS_DISCARDED: { CHECK_INTERFACE(IProducerListener, data, reply); std::vector discardedSlots; status_t result = data.readInt32Vector(&discardedSlots); if (result != NO_ERROR) { ALOGE("ON_BUFFERS_DISCARDED failed to read discardedSlots: %d", result); return result; } onBuffersDiscarded(discardedSlots); return NO_ERROR; } } return BBinder::onTransact(code, data, reply, flags); } ProducerListener::~ProducerListener() = default; DummyProducerListener::~DummyProducerListener() = default; bool BnProducerListener::needsReleaseNotify() { return true; } void BnProducerListener::onBuffersDiscarded(const std::vector& /*discardedSlots*/) { } } // namespace android �������������������������������������������������������������������libs/gui/IRegionSamplingListener.cpp����������������������������������������������������������������0100644 0000000 0000000 00000004301 13756501735 016575� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #define LOG_TAG "IRegionSamplingListener" //#define LOG_NDEBUG 0 #include namespace android { namespace { // Anonymous enum class Tag : uint32_t { ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION, LAST = ON_SAMPLE_COLLECTED, }; } // Anonymous namespace class BpRegionSamplingListener : public SafeBpInterface { public: explicit BpRegionSamplingListener(const sp& impl) : SafeBpInterface(impl, "BpRegionSamplingListener") {} ~BpRegionSamplingListener() override; void onSampleCollected(float medianLuma) override { callRemoteAsync(Tag::ON_SAMPLE_COLLECTED, medianLuma); } }; // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see // clang warning -Wweak-vtables) BpRegionSamplingListener::~BpRegionSamplingListener() = default; IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener"); status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast(Tag::LAST)) { return BBinder::onTransact(code, data, reply, flags); } auto tag = static_cast(code); switch (tag) { case Tag::ON_SAMPLE_COLLECTED: return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected); } } } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/ISurfaceComposer.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000176315 13756501735 015270� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ // tag as surfaceflinger #define LOG_TAG "SurfaceFlinger" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { using ui::ColorMode; class BpSurfaceComposer : public BpInterface { public: explicit BpSurfaceComposer(const sp& impl) : BpInterface(impl) { } virtual ~BpSurfaceComposer(); virtual sp createConnection() { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); return interface_cast(reply.readStrongBinder()); } virtual void setTransactionState(const Vector& state, const Vector& displays, uint32_t flags, const sp& applyToken, const InputWindowCommands& commands, int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, const std::vector& listenerCallbacks) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeUint32(static_cast(state.size())); for (const auto& s : state) { s.write(data); } data.writeUint32(static_cast(displays.size())); for (const auto& d : displays) { d.write(data); } data.writeUint32(flags); data.writeStrongBinder(applyToken); commands.write(data); data.writeInt64(desiredPresentTime); data.writeWeakBinder(uncacheBuffer.token); data.writeUint64(uncacheBuffer.id); if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { for (const auto& [listener, callbackIds] : listenerCallbacks) { data.writeStrongBinder(IInterface::asBinder(listener)); data.writeInt64Vector(callbackIds); } } remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } virtual void bootFinished() { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); } virtual status_t captureScreen(const sp& display, sp* outBuffer, bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation, bool captureSecureLayers) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); data.writeInt32(static_cast(reqDataspace)); data.writeInt32(static_cast(reqPixelFormat)); data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); data.writeInt32(static_cast(useIdentityTransform)); data.writeInt32(static_cast(rotation)); data.writeInt32(static_cast(captureSecureLayers)); status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); if (result != NO_ERROR) { ALOGE("captureScreen failed to transact: %d", result); return result; } result = reply.readInt32(); if (result != NO_ERROR) { ALOGE("captureScreen failed to readInt32: %d", result); return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); outCapturedSecureLayers = reply.readBool(); return result; } virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, sp* outBuffer) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeUint64(displayOrLayerStack); status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply); if (result != NO_ERROR) { ALOGE("captureScreen failed to transact: %d", result); return result; } result = reply.readInt32(); if (result != NO_ERROR) { ALOGE("captureScreen failed to readInt32: %d", result); return result; } *outDataspace = static_cast(reply.readInt32()); *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); return result; } virtual status_t captureLayers( const sp& layerHandleBinder, sp* outBuffer, const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, const std::unordered_set, SpHash>& excludeLayers, float frameScale, bool childrenOnly) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(layerHandleBinder); data.writeInt32(static_cast(reqDataspace)); data.writeInt32(static_cast(reqPixelFormat)); data.write(sourceCrop); data.writeInt32(excludeLayers.size()); for (auto el : excludeLayers) { data.writeStrongBinder(el); } data.writeFloat(frameScale); data.writeBool(childrenOnly); status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); if (result != NO_ERROR) { ALOGE("captureLayers failed to transact: %d", result); return result; } result = reply.readInt32(); if (result != NO_ERROR) { ALOGE("captureLayers failed to readInt32: %d", result); return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); return result; } virtual bool authenticateSurfaceTexture( const sp& bufferProducer) const { Parcel data, reply; int err = NO_ERROR; err = data.writeInterfaceToken( ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing " "interface descriptor: %s (%d)", strerror(-err), -err); return false; } err = data.writeStrongBinder(IInterface::asBinder(bufferProducer)); if (err != NO_ERROR) { ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing " "strong binder to parcel: %s (%d)", strerror(-err), -err); return false; } err = remote()->transact(BnSurfaceComposer::AUTHENTICATE_SURFACE, data, &reply); if (err != NO_ERROR) { ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error " "performing transaction: %s (%d)", strerror(-err), -err); return false; } int32_t result = 0; err = reply.readInt32(&result); if (err != NO_ERROR) { ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error " "retrieving result: %s (%d)", strerror(-err), -err); return false; } return result != 0; } virtual status_t getSupportedFrameTimestamps( std::vector* outSupported) const { if (!outSupported) { return UNEXPECTED_NULL; } outSupported->clear(); Parcel data, reply; status_t err = data.writeInterfaceToken( ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { return err; } err = remote()->transact( BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS, data, &reply); if (err != NO_ERROR) { return err; } int32_t result = 0; err = reply.readInt32(&result); if (err != NO_ERROR) { return err; } if (result != NO_ERROR) { return result; } std::vector supported; err = reply.readInt32Vector(&supported); if (err != NO_ERROR) { return err; } outSupported->reserve(supported.size()); for (int32_t s : supported) { outSupported->push_back(static_cast(s)); } return NO_ERROR; } virtual sp createDisplayEventConnection(VsyncSource vsyncSource, ConfigChanged configChanged) { Parcel data, reply; sp result; int err = data.writeInterfaceToken( ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { return result; } data.writeInt32(static_cast(vsyncSource)); data.writeInt32(static_cast(configChanged)); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); if (err != NO_ERROR) { ALOGE("ISurfaceComposer::createDisplayEventConnection: error performing " "transaction: %s (%d)", strerror(-err), -err); return result; } result = interface_cast(reply.readStrongBinder()); return result; } virtual sp createDisplay(const String8& displayName, bool secure) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeString8(displayName); data.writeInt32(secure ? 1 : 0); remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply); return reply.readStrongBinder(); } virtual void destroyDisplay(const sp& display) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply); } virtual std::vector getPhysicalDisplayIds() const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) == NO_ERROR) { std::vector displayIds; if (reply.readUint64Vector(&displayIds) == NO_ERROR) { return displayIds; } } return {}; } virtual sp getPhysicalDisplayToken(PhysicalDisplayId displayId) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeUint64(displayId); remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply); return reply.readStrongBinder(); } virtual void setPowerMode(const sp& display, int mode) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); data.writeInt32(mode); remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply); } virtual status_t getDisplayConfigs(const sp& display, Vector* configs) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply); status_t result = reply.readInt32(); if (result == NO_ERROR) { size_t numConfigs = reply.readUint32(); configs->clear(); configs->resize(numConfigs); for (size_t c = 0; c < numConfigs; ++c) { memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo)); } } return result; } virtual status_t getDisplayStats(const sp& display, DisplayStatInfo* stats) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::GET_DISPLAY_STATS, data, &reply); status_t result = reply.readInt32(); if (result == NO_ERROR) { memcpy(stats, reply.readInplace(sizeof(DisplayStatInfo)), sizeof(DisplayStatInfo)); } return result; } virtual int getActiveConfig(const sp& display) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); return reply.readInt32(); } virtual status_t setActiveConfig(const sp& display, int id) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { ALOGE("setActiveConfig failed to writeStrongBinder: %d", result); return result; } result = data.writeInt32(id); if (result != NO_ERROR) { ALOGE("setActiveConfig failed to writeInt32: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); if (result != NO_ERROR) { ALOGE("setActiveConfig failed to transact: %d", result); return result; } return reply.readInt32(); } virtual status_t getDisplayColorModes(const sp& display, Vector* outColorModes) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("getDisplayColorModes failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { ALOGE("getDisplayColorModes failed to writeStrongBinder: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, data, &reply); if (result != NO_ERROR) { ALOGE("getDisplayColorModes failed to transact: %d", result); return result; } result = static_cast(reply.readInt32()); if (result == NO_ERROR) { size_t numModes = reply.readUint32(); outColorModes->clear(); outColorModes->resize(numModes); for (size_t i = 0; i < numModes; ++i) { outColorModes->replaceAt(static_cast(reply.readInt32()), i); } } return result; } virtual status_t getDisplayNativePrimaries(const sp& display, ui::DisplayPrimaries& primaries) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply); if (result != NO_ERROR) { ALOGE("getDisplayNativePrimaries failed to transact: %d", result); return result; } result = reply.readInt32(); if (result == NO_ERROR) { memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)), sizeof(ui::DisplayPrimaries)); } return result; } virtual ColorMode getActiveColorMode(const sp& display) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("getActiveColorMode failed to writeInterfaceToken: %d", result); return static_cast(result); } result = data.writeStrongBinder(display); if (result != NO_ERROR) { ALOGE("getActiveColorMode failed to writeStrongBinder: %d", result); return static_cast(result); } result = remote()->transact(BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, data, &reply); if (result != NO_ERROR) { ALOGE("getActiveColorMode failed to transact: %d", result); return static_cast(result); } return static_cast(reply.readInt32()); } virtual status_t setActiveColorMode(const sp& display, ColorMode colorMode) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("setActiveColorMode failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { ALOGE("setActiveColorMode failed to writeStrongBinder: %d", result); return result; } result = data.writeInt32(static_cast(colorMode)); if (result != NO_ERROR) { ALOGE("setActiveColorMode failed to writeInt32: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, data, &reply); if (result != NO_ERROR) { ALOGE("setActiveColorMode failed to transact: %d", result); return result; } return static_cast(reply.readInt32()); } virtual status_t clearAnimationFrameStats() { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); if (result != NO_ERROR) { ALOGE("clearAnimationFrameStats failed to transact: %d", result); return result; } return reply.readInt32(); } virtual status_t getAnimationFrameStats(FrameStats* outStats) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::GET_ANIMATION_FRAME_STATS, data, &reply); reply.read(*outStats); return reply.readInt32(); } virtual status_t getHdrCapabilities(const sp& display, HdrCapabilities* outCapabilities) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); status_t result = data.writeStrongBinder(display); if (result != NO_ERROR) { ALOGE("getHdrCapabilities failed to writeStrongBinder: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::GET_HDR_CAPABILITIES, data, &reply); if (result != NO_ERROR) { ALOGE("getHdrCapabilities failed to transact: %d", result); return result; } result = reply.readInt32(); if (result == NO_ERROR) { result = reply.read(*outCapabilities); } return result; } virtual status_t enableVSyncInjections(bool enable) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result); return result; } result = data.writeBool(enable); if (result != NO_ERROR) { ALOGE("enableVSyncInjections failed to writeBool: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, data, &reply, IBinder::FLAG_ONEWAY); if (result != NO_ERROR) { ALOGE("enableVSyncInjections failed to transact: %d", result); return result; } return result; } virtual status_t injectVSync(nsecs_t when) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("injectVSync failed to writeInterfaceToken: %d", result); return result; } result = data.writeInt64(when); if (result != NO_ERROR) { ALOGE("injectVSync failed to writeInt64: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY); if (result != NO_ERROR) { ALOGE("injectVSync failed to transact: %d", result); return result; } return result; } virtual status_t getLayerDebugInfo(std::vector* outLayers) const { if (!outLayers) { return UNEXPECTED_NULL; } Parcel data, reply; status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { return err; } err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply); if (err != NO_ERROR) { return err; } int32_t result = 0; err = reply.readInt32(&result); if (err != NO_ERROR) { return err; } if (result != NO_ERROR) { return result; } outLayers->clear(); return reply.readParcelableVector(outLayers); } virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat, ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) const { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { return error; } error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply); if (error != NO_ERROR) { return error; } error = static_cast(reply.readInt32()); if (error == NO_ERROR) { *defaultDataspace = static_cast(reply.readInt32()); *defaultPixelFormat = static_cast(reply.readInt32()); *wideColorGamutDataspace = static_cast(reply.readInt32()); *wideColorGamutPixelFormat = static_cast(reply.readInt32()); } return error; } virtual status_t getColorManagement(bool* outGetColorManagement) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply); bool result; status_t err = reply.readBool(&result); if (err == NO_ERROR) { *outGetColorManagement = result; } return err; } virtual status_t getDisplayedContentSamplingAttributes(const sp& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, uint8_t* outComponentMask) const { if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE; Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); status_t error = remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, data, &reply); if (error != NO_ERROR) { return error; } uint32_t value = 0; error = reply.readUint32(&value); if (error != NO_ERROR) { return error; } *outFormat = static_cast(value); error = reply.readUint32(&value); if (error != NO_ERROR) { return error; } *outDataspace = static_cast(value); error = reply.readUint32(&value); if (error != NO_ERROR) { return error; } *outComponentMask = static_cast(value); return error; } virtual status_t setDisplayContentSamplingEnabled(const sp& display, bool enable, uint8_t componentMask, uint64_t maxFrames) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); data.writeBool(enable); data.writeByte(static_cast(componentMask)); data.writeUint64(maxFrames); status_t result = remote()->transact(BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, data, &reply); return result; } virtual status_t getDisplayedContentSample(const sp& display, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) const { if (!outStats) return BAD_VALUE; Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); data.writeUint64(maxFrames); data.writeUint64(timestamp); status_t result = remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply); if (result != NO_ERROR) { return result; } result = reply.readUint64(&outStats->numFrames); if (result != NO_ERROR) { return result; } result = reply.readUint64Vector(&outStats->component_0_sample); if (result != NO_ERROR) { return result; } result = reply.readUint64Vector(&outStats->component_1_sample); if (result != NO_ERROR) { return result; } result = reply.readUint64Vector(&outStats->component_2_sample); if (result != NO_ERROR) { return result; } result = reply.readUint64Vector(&outStats->component_3_sample); return result; } virtual status_t getProtectedContentSupport(bool* outSupported) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); status_t error = remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply); if (error != NO_ERROR) { return error; } error = reply.readBool(outSupported); return error; } virtual status_t isWideColorDisplay(const sp& token, bool* outIsWideColorDisplay) const { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { return error; } error = data.writeStrongBinder(token); if (error != NO_ERROR) { return error; } error = remote()->transact(BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, data, &reply); if (error != NO_ERROR) { return error; } error = reply.readBool(outIsWideColorDisplay); return error; } virtual status_t addRegionSamplingListener(const Rect& samplingArea, const sp& stopLayerHandle, const sp& listener) { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to write interface token"); return error; } error = data.write(samplingArea); if (error != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to write sampling area"); return error; } error = data.writeStrongBinder(stopLayerHandle); if (error != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to write stop layer handle"); return error; } error = data.writeStrongBinder(IInterface::asBinder(listener)); if (error != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to write listener"); return error; } error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply); if (error != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to transact"); } return error; } virtual status_t removeRegionSamplingListener(const sp& listener) { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { ALOGE("removeRegionSamplingListener: Failed to write interface token"); return error; } error = data.writeStrongBinder(IInterface::asBinder(listener)); if (error != NO_ERROR) { ALOGE("removeRegionSamplingListener: Failed to write listener"); return error; } error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data, &reply); if (error != NO_ERROR) { ALOGE("removeRegionSamplingListener: Failed to transact"); } return error; } virtual status_t setAllowedDisplayConfigs(const sp& displayToken, const std::vector& allowedConfigs) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(displayToken); if (result != NO_ERROR) { ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result); return result; } result = data.writeInt32Vector(allowedConfigs); if (result != NO_ERROR) { ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply); if (result != NO_ERROR) { ALOGE("setAllowedDisplayConfigs failed to transact: %d", result); return result; } return reply.readInt32(); } virtual status_t getAllowedDisplayConfigs(const sp& displayToken, std::vector* outAllowedConfigs) { if (!outAllowedConfigs) return BAD_VALUE; Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(displayToken); if (result != NO_ERROR) { ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result); return result; } result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply); if (result != NO_ERROR) { ALOGE("getAllowedDisplayConfigs failed to transact: %d", result); return result; } result = reply.readInt32Vector(outAllowedConfigs); if (result != NO_ERROR) { ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result); return result; } return reply.readInt32(); } virtual status_t getDisplayBrightnessSupport(const sp& displayToken, bool* outSupport) const { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { ALOGE("getDisplayBrightnessSupport: failed to write interface token: %d", error); return error; } error = data.writeStrongBinder(displayToken); if (error != NO_ERROR) { ALOGE("getDisplayBrightnessSupport: failed to write display token: %d", error); return error; } error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT, data, &reply); if (error != NO_ERROR) { ALOGE("getDisplayBrightnessSupport: failed to transact: %d", error); return error; } bool support; error = reply.readBool(&support); if (error != NO_ERROR) { ALOGE("getDisplayBrightnessSupport: failed to read support: %d", error); return error; } *outSupport = support; return NO_ERROR; } virtual status_t setDisplayBrightness(const sp& displayToken, float brightness) const { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { ALOGE("setDisplayBrightness: failed to write interface token: %d", error); return error; } error = data.writeStrongBinder(displayToken); if (error != NO_ERROR) { ALOGE("setDisplayBrightness: failed to write display token: %d", error); return error; } error = data.writeFloat(brightness); if (error != NO_ERROR) { ALOGE("setDisplayBrightness: failed to write brightness: %d", error); return error; } error = remote()->transact(BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS, data, &reply); if (error != NO_ERROR) { ALOGE("setDisplayBrightness: failed to transact: %d", error); return error; } return NO_ERROR; } virtual status_t notifyPowerHint(int32_t hintId) { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { ALOGE("notifyPowerHint: failed to write interface token: %d", error); return error; } error = data.writeInt32(hintId); if (error != NO_ERROR) { ALOGE("notifyPowerHint: failed to write hintId: %d", error); return error; } error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY); if (error != NO_ERROR) { ALOGE("notifyPowerHint: failed to transact: %d", error); return error; } return NO_ERROR; } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpSurfaceComposer::~BpSurfaceComposer() {} IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); // ---------------------------------------------------------------------- status_t BnSurfaceComposer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case CREATE_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp b = IInterface::asBinder(createConnection()); reply->writeStrongBinder(b); return NO_ERROR; } case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); size_t count = data.readUint32(); if (count > data.dataSize()) { return BAD_VALUE; } Vector state; state.setCapacity(count); for (size_t i = 0; i < count; i++) { ComposerState s; if (s.read(data) == BAD_VALUE) { return BAD_VALUE; } state.add(s); } count = data.readUint32(); if (count > data.dataSize()) { return BAD_VALUE; } DisplayState d; Vector displays; displays.setCapacity(count); for (size_t i = 0; i < count; i++) { if (d.read(data) == BAD_VALUE) { return BAD_VALUE; } displays.add(d); } uint32_t stateFlags = data.readUint32(); sp applyToken = data.readStrongBinder(); InputWindowCommands inputWindowCommands; inputWindowCommands.read(data); int64_t desiredPresentTime = data.readInt64(); client_cache_t uncachedBuffer; uncachedBuffer.token = data.readWeakBinder(); uncachedBuffer.id = data.readUint64(); std::vector listenerCallbacks; int32_t listenersSize = data.readInt32(); for (int32_t i = 0; i < listenersSize; i++) { auto listener = interface_cast(data.readStrongBinder()); std::vector callbackIds; data.readInt64Vector(&callbackIds); listenerCallbacks.emplace_back(listener, callbackIds); } setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands, desiredPresentTime, uncachedBuffer, listenerCallbacks); return NO_ERROR; } case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); return NO_ERROR; } case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); ui::Dataspace reqDataspace = static_cast(data.readInt32()); ui::PixelFormat reqPixelFormat = static_cast(data.readInt32()); sp outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); uint32_t reqWidth = data.readUint32(); uint32_t reqHeight = data.readUint32(); bool useIdentityTransform = static_cast(data.readInt32()); int32_t rotation = data.readInt32(); bool captureSecureLayers = static_cast(data.readInt32()); bool capturedSecureLayers = false; status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform, static_cast(rotation), captureSecureLayers); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); reply->writeBool(capturedSecureLayers); } return NO_ERROR; } case CAPTURE_SCREEN_BY_ID: { CHECK_INTERFACE(ISurfaceComposer, data, reply); uint64_t displayOrLayerStack = data.readUint64(); ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB; sp outBuffer; status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer); reply->writeInt32(res); if (res == NO_ERROR) { reply->writeInt32(static_cast(outDataspace)); reply->write(*outBuffer); } return NO_ERROR; } case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp layerHandleBinder = data.readStrongBinder(); ui::Dataspace reqDataspace = static_cast(data.readInt32()); ui::PixelFormat reqPixelFormat = static_cast(data.readInt32()); sp outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); std::unordered_set, SpHash> excludeHandles; int numExcludeHandles = data.readInt32(); excludeHandles.reserve(numExcludeHandles); for (int i = 0; i < numExcludeHandles; i++) { excludeHandles.emplace(data.readStrongBinder()); } float frameScale = data.readFloat(); bool childrenOnly = data.readBool(); status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat, sourceCrop, excludeHandles, frameScale, childrenOnly); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); } return NO_ERROR; } case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp bufferProducer = interface_cast(data.readStrongBinder()); int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0; reply->writeInt32(result); return NO_ERROR; } case GET_SUPPORTED_FRAME_TIMESTAMPS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); std::vector supportedTimestamps; status_t result = getSupportedFrameTimestamps(&supportedTimestamps); status_t err = reply->writeInt32(result); if (err != NO_ERROR) { return err; } if (result != NO_ERROR) { return result; } std::vector supported; supported.reserve(supportedTimestamps.size()); for (FrameEvent s : supportedTimestamps) { supported.push_back(static_cast(s)); } return reply->writeInt32Vector(supported); } case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); auto vsyncSource = static_cast(data.readInt32()); auto configChanged = static_cast(data.readInt32()); sp connection( createDisplayEventConnection(vsyncSource, configChanged)); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } case CREATE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); String8 displayName = data.readString8(); bool secure = bool(data.readInt32()); sp display(createDisplay(displayName, secure)); reply->writeStrongBinder(display); return NO_ERROR; } case DESTROY_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); destroyDisplay(display); return NO_ERROR; } case GET_PHYSICAL_DISPLAY_TOKEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); PhysicalDisplayId displayId = data.readUint64(); sp display = getPhysicalDisplayToken(displayId); reply->writeStrongBinder(display); return NO_ERROR; } case GET_DISPLAY_CONFIGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); Vector configs; sp display = data.readStrongBinder(); status_t result = getDisplayConfigs(display, &configs); reply->writeInt32(result); if (result == NO_ERROR) { reply->writeUint32(static_cast(configs.size())); for (size_t c = 0; c < configs.size(); ++c) { memcpy(reply->writeInplace(sizeof(DisplayInfo)), &configs[c], sizeof(DisplayInfo)); } } return NO_ERROR; } case GET_DISPLAY_STATS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayStatInfo stats; sp display = data.readStrongBinder(); status_t result = getDisplayStats(display, &stats); reply->writeInt32(result); if (result == NO_ERROR) { memcpy(reply->writeInplace(sizeof(DisplayStatInfo)), &stats, sizeof(DisplayStatInfo)); } return NO_ERROR; } case GET_ACTIVE_CONFIG: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); int id = getActiveConfig(display); reply->writeInt32(id); return NO_ERROR; } case SET_ACTIVE_CONFIG: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); int id = data.readInt32(); status_t result = setActiveConfig(display, id); reply->writeInt32(result); return NO_ERROR; } case GET_DISPLAY_COLOR_MODES: { CHECK_INTERFACE(ISurfaceComposer, data, reply); Vector colorModes; sp display = nullptr; status_t result = data.readStrongBinder(&display); if (result != NO_ERROR) { ALOGE("getDisplayColorModes failed to readStrongBinder: %d", result); return result; } result = getDisplayColorModes(display, &colorModes); reply->writeInt32(result); if (result == NO_ERROR) { reply->writeUint32(static_cast(colorModes.size())); for (size_t i = 0; i < colorModes.size(); ++i) { reply->writeInt32(static_cast(colorModes[i])); } } return NO_ERROR; } case GET_DISPLAY_NATIVE_PRIMARIES: { CHECK_INTERFACE(ISurfaceComposer, data, reply); ui::DisplayPrimaries primaries; sp display = nullptr; status_t result = data.readStrongBinder(&display); if (result != NO_ERROR) { ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result); return result; } result = getDisplayNativePrimaries(display, primaries); reply->writeInt32(result); if (result == NO_ERROR) { memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries, sizeof(ui::DisplayPrimaries)); } return NO_ERROR; } case GET_ACTIVE_COLOR_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = nullptr; status_t result = data.readStrongBinder(&display); if (result != NO_ERROR) { ALOGE("getActiveColorMode failed to readStrongBinder: %d", result); return result; } ColorMode colorMode = getActiveColorMode(display); result = reply->writeInt32(static_cast(colorMode)); return result; } case SET_ACTIVE_COLOR_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = nullptr; status_t result = data.readStrongBinder(&display); if (result != NO_ERROR) { ALOGE("getActiveColorMode failed to readStrongBinder: %d", result); return result; } int32_t colorModeInt = 0; result = data.readInt32(&colorModeInt); if (result != NO_ERROR) { ALOGE("setActiveColorMode failed to readInt32: %d", result); return result; } result = setActiveColorMode(display, static_cast(colorModeInt)); result = reply->writeInt32(result); return result; } case CLEAR_ANIMATION_FRAME_STATS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); status_t result = clearAnimationFrameStats(); reply->writeInt32(result); return NO_ERROR; } case GET_ANIMATION_FRAME_STATS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); FrameStats stats; status_t result = getAnimationFrameStats(&stats); reply->write(stats); reply->writeInt32(result); return NO_ERROR; } case SET_POWER_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); int32_t mode = data.readInt32(); setPowerMode(display, mode); return NO_ERROR; } case GET_HDR_CAPABILITIES: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = nullptr; status_t result = data.readStrongBinder(&display); if (result != NO_ERROR) { ALOGE("getHdrCapabilities failed to readStrongBinder: %d", result); return result; } HdrCapabilities capabilities; result = getHdrCapabilities(display, &capabilities); reply->writeInt32(result); if (result == NO_ERROR) { reply->write(capabilities); } return NO_ERROR; } case ENABLE_VSYNC_INJECTIONS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bool enable = false; status_t result = data.readBool(&enable); if (result != NO_ERROR) { ALOGE("enableVSyncInjections failed to readBool: %d", result); return result; } return enableVSyncInjections(enable); } case INJECT_VSYNC: { CHECK_INTERFACE(ISurfaceComposer, data, reply); int64_t when = 0; status_t result = data.readInt64(&when); if (result != NO_ERROR) { ALOGE("enableVSyncInjections failed to readInt64: %d", result); return result; } return injectVSync(when); } case GET_LAYER_DEBUG_INFO: { CHECK_INTERFACE(ISurfaceComposer, data, reply); std::vector outLayers; status_t result = getLayerDebugInfo(&outLayers); reply->writeInt32(result); if (result == NO_ERROR) { result = reply->writeParcelableVector(outLayers); } return result; } case GET_COMPOSITION_PREFERENCE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); ui::Dataspace defaultDataspace; ui::PixelFormat defaultPixelFormat; ui::Dataspace wideColorGamutDataspace; ui::PixelFormat wideColorGamutPixelFormat; status_t error = getCompositionPreference(&defaultDataspace, &defaultPixelFormat, &wideColorGamutDataspace, &wideColorGamutPixelFormat); reply->writeInt32(error); if (error == NO_ERROR) { reply->writeInt32(static_cast(defaultDataspace)); reply->writeInt32(static_cast(defaultPixelFormat)); reply->writeInt32(static_cast(wideColorGamutDataspace)); reply->writeInt32(static_cast(wideColorGamutPixelFormat)); } return error; } case GET_COLOR_MANAGEMENT: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bool result; status_t error = getColorManagement(&result); if (error == NO_ERROR) { reply->writeBool(result); } return error; } case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); ui::PixelFormat format; ui::Dataspace dataspace; uint8_t component = 0; auto result = getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component); if (result == NO_ERROR) { reply->writeUint32(static_cast(format)); reply->writeUint32(static_cast(dataspace)); reply->writeUint32(static_cast(component)); } return result; } case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = nullptr; bool enable = false; int8_t componentMask = 0; uint64_t maxFrames = 0; status_t result = data.readStrongBinder(&display); if (result != NO_ERROR) { ALOGE("setDisplayContentSamplingEnabled failure in reading Display token: %d", result); return result; } result = data.readBool(&enable); if (result != NO_ERROR) { ALOGE("setDisplayContentSamplingEnabled failure in reading enable: %d", result); return result; } result = data.readByte(static_cast(&componentMask)); if (result != NO_ERROR) { ALOGE("setDisplayContentSamplingEnabled failure in reading component mask: %d", result); return result; } result = data.readUint64(&maxFrames); if (result != NO_ERROR) { ALOGE("setDisplayContentSamplingEnabled failure in reading max frames: %d", result); return result; } return setDisplayContentSamplingEnabled(display, enable, static_cast(componentMask), maxFrames); } case GET_DISPLAYED_CONTENT_SAMPLE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = data.readStrongBinder(); uint64_t maxFrames = 0; uint64_t timestamp = 0; status_t result = data.readUint64(&maxFrames); if (result != NO_ERROR) { ALOGE("getDisplayedContentSample failure in reading max frames: %d", result); return result; } result = data.readUint64(×tamp); if (result != NO_ERROR) { ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result); return result; } DisplayedFrameStats stats; result = getDisplayedContentSample(display, maxFrames, timestamp, &stats); if (result == NO_ERROR) { reply->writeUint64(stats.numFrames); reply->writeUint64Vector(stats.component_0_sample); reply->writeUint64Vector(stats.component_1_sample); reply->writeUint64Vector(stats.component_2_sample); reply->writeUint64Vector(stats.component_3_sample); } return result; } case GET_PROTECTED_CONTENT_SUPPORT: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bool result; status_t error = getProtectedContentSupport(&result); if (error == NO_ERROR) { reply->writeBool(result); } return error; } case IS_WIDE_COLOR_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp display = nullptr; status_t error = data.readStrongBinder(&display); if (error != NO_ERROR) { return error; } bool result; error = isWideColorDisplay(display, &result); if (error == NO_ERROR) { reply->writeBool(result); } return error; } case GET_PHYSICAL_DISPLAY_IDS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); return reply->writeUint64Vector(getPhysicalDisplayIds()); } case ADD_REGION_SAMPLING_LISTENER: { CHECK_INTERFACE(ISurfaceComposer, data, reply); Rect samplingArea; status_t result = data.read(samplingArea); if (result != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to read sampling area"); return result; } sp stopLayerHandle; result = data.readNullableStrongBinder(&stopLayerHandle); if (result != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to read stop layer handle"); return result; } sp listener; result = data.readNullableStrongBinder(&listener); if (result != NO_ERROR) { ALOGE("addRegionSamplingListener: Failed to read listener"); return result; } return addRegionSamplingListener(samplingArea, stopLayerHandle, listener); } case REMOVE_REGION_SAMPLING_LISTENER: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp listener; status_t result = data.readNullableStrongBinder(&listener); if (result != NO_ERROR) { ALOGE("removeRegionSamplingListener: Failed to read listener"); return result; } return removeRegionSamplingListener(listener); } case SET_ALLOWED_DISPLAY_CONFIGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp displayToken = data.readStrongBinder(); std::vector allowedConfigs; data.readInt32Vector(&allowedConfigs); status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs); reply->writeInt32(result); return result; } case GET_ALLOWED_DISPLAY_CONFIGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp displayToken = data.readStrongBinder(); std::vector allowedConfigs; status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs); reply->writeInt32Vector(allowedConfigs); reply->writeInt32(result); return result; } case GET_DISPLAY_BRIGHTNESS_SUPPORT: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp displayToken; status_t error = data.readNullableStrongBinder(&displayToken); if (error != NO_ERROR) { ALOGE("getDisplayBrightnessSupport: failed to read display token: %d", error); return error; } bool support = false; error = getDisplayBrightnessSupport(displayToken, &support); reply->writeBool(support); return error; } case SET_DISPLAY_BRIGHTNESS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp displayToken; status_t error = data.readNullableStrongBinder(&displayToken); if (error != NO_ERROR) { ALOGE("setDisplayBrightness: failed to read display token: %d", error); return error; } float brightness = -1.0f; error = data.readFloat(&brightness); if (error != NO_ERROR) { ALOGE("setDisplayBrightness: failed to read brightness: %d", error); return error; } return setDisplayBrightness(displayToken, brightness); } case NOTIFY_POWER_HINT: { CHECK_INTERFACE(ISurfaceComposer, data, reply); int32_t hintId; status_t error = data.readInt32(&hintId); if (error != NO_ERROR) { ALOGE("notifyPowerHint: failed to read hintId: %d", error); return error; } return notifyPowerHint(hintId); } default: { return BBinder::onTransact(code, data, reply, flags); } } } } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/ISurfaceComposerClient.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000011511 13756501735 016411� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ // tag as surfaceflinger #define LOG_TAG "SurfaceFlinger" #include #include #include #include namespace android { namespace { // Anonymous enum class Tag : uint32_t { CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, CREATE_WITH_SURFACE_PARENT, CLEAR_LAYER_FRAME_STATS, GET_LAYER_FRAME_STATS, LAST = GET_LAYER_FRAME_STATS, }; } // Anonymous namespace class BpSurfaceComposerClient : public SafeBpInterface { public: explicit BpSurfaceComposerClient(const sp& impl) : SafeBpInterface(impl, "BpSurfaceComposerClient") {} ~BpSurfaceComposerClient() override; status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format, uint32_t flags, const sp& parent, LayerMetadata metadata, sp* handle, sp* gbp) override { return callRemote(Tag::CREATE_SURFACE, name, width, height, format, flags, parent, std::move(metadata), handle, gbp); } status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height, PixelFormat format, uint32_t flags, const sp& parent, LayerMetadata metadata, sp* handle, sp* gbp) override { return callRemote(Tag::CREATE_WITH_SURFACE_PARENT, name, width, height, format, flags, parent, std::move(metadata), handle, gbp); } status_t clearLayerFrameStats(const sp& handle) const override { return callRemote(Tag::CLEAR_LAYER_FRAME_STATS, handle); } status_t getLayerFrameStats(const sp& handle, FrameStats* outStats) const override { return callRemote(Tag::GET_LAYER_FRAME_STATS, handle, outStats); } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpSurfaceComposerClient::~BpSurfaceComposerClient() {} IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); // ---------------------------------------------------------------------- status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast(Tag::LAST)) { return BBinder::onTransact(code, data, reply, flags); } auto tag = static_cast(code); switch (tag) { case Tag::CREATE_SURFACE: return callLocal(data, reply, &ISurfaceComposerClient::createSurface); case Tag::CREATE_WITH_SURFACE_PARENT: return callLocal(data, reply, &ISurfaceComposerClient::createWithSurfaceParent); case Tag::CLEAR_LAYER_FRAME_STATS: return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats); case Tag::GET_LAYER_FRAME_STATS: return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats); } } } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/ITransactionCompletedListener.cpp����������������������������������������������������������0100644 0000000 0000000 00000013557 13756501735 020016� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #define LOG_TAG "ITransactionCompletedListener" //#define LOG_NDEBUG 0 #include namespace android { namespace { // Anonymous enum class Tag : uint32_t { ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION, LAST = ON_TRANSACTION_COMPLETED, }; } // Anonymous namespace status_t SurfaceStats::writeToParcel(Parcel* output) const { status_t err = output->writeStrongBinder(surfaceControl); if (err != NO_ERROR) { return err; } err = output->writeInt64(acquireTime); if (err != NO_ERROR) { return err; } if (previousReleaseFence) { err = output->writeBool(true); if (err != NO_ERROR) { return err; } err = output->write(*previousReleaseFence); } else { err = output->writeBool(false); } return err; } status_t SurfaceStats::readFromParcel(const Parcel* input) { status_t err = input->readStrongBinder(&surfaceControl); if (err != NO_ERROR) { return err; } err = input->readInt64(&acquireTime); if (err != NO_ERROR) { return err; } bool hasFence = false; err = input->readBool(&hasFence); if (err != NO_ERROR) { return err; } if (hasFence) { previousReleaseFence = new Fence(); err = input->read(*previousReleaseFence); if (err != NO_ERROR) { return err; } } return NO_ERROR; } status_t TransactionStats::writeToParcel(Parcel* output) const { status_t err = output->writeInt64Vector(callbackIds); if (err != NO_ERROR) { return err; } err = output->writeInt64(latchTime); if (err != NO_ERROR) { return err; } if (presentFence) { err = output->writeBool(true); if (err != NO_ERROR) { return err; } err = output->write(*presentFence); } else { err = output->writeBool(false); } if (err != NO_ERROR) { return err; } return output->writeParcelableVector(surfaceStats); } status_t TransactionStats::readFromParcel(const Parcel* input) { status_t err = input->readInt64Vector(&callbackIds); if (err != NO_ERROR) { return err; } err = input->readInt64(&latchTime); if (err != NO_ERROR) { return err; } bool hasFence = false; err = input->readBool(&hasFence); if (err != NO_ERROR) { return err; } if (hasFence) { presentFence = new Fence(); err = input->read(*presentFence); if (err != NO_ERROR) { return err; } } return input->readParcelableVector(&surfaceStats); } status_t ListenerStats::writeToParcel(Parcel* output) const { status_t err = output->writeInt32(static_cast(transactionStats.size())); if (err != NO_ERROR) { return err; } for (const auto& stats : transactionStats) { err = output->writeParcelable(stats); if (err != NO_ERROR) { return err; } } return NO_ERROR; } status_t ListenerStats::readFromParcel(const Parcel* input) { int32_t transactionStats_size = input->readInt32(); for (int i = 0; i < transactionStats_size; i++) { TransactionStats stats; status_t err = input->readParcelable(&stats); if (err != NO_ERROR) { return err; } transactionStats.push_back(stats); } return NO_ERROR; } ListenerStats ListenerStats::createEmpty(const sp& listener, const std::unordered_set& callbackIds) { ListenerStats listenerStats; listenerStats.listener = listener; listenerStats.transactionStats.emplace_back(callbackIds); return listenerStats; } class BpTransactionCompletedListener : public SafeBpInterface { public: explicit BpTransactionCompletedListener(const sp& impl) : SafeBpInterface(impl, "BpTransactionCompletedListener") { } ~BpTransactionCompletedListener() override; void onTransactionCompleted(ListenerStats stats) override { callRemoteAsync(Tag::ON_TRANSACTION_COMPLETED, stats); } }; // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see // clang warning -Wweak-vtables) BpTransactionCompletedListener::~BpTransactionCompletedListener() = default; IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener"); status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast(Tag::LAST)) { return BBinder::onTransact(code, data, reply, flags); } auto tag = static_cast(code); switch (tag) { case Tag::ON_TRANSACTION_COMPLETED: return callLocalAsync(data, reply, &ITransactionCompletedListener::onTransactionCompleted); } } }; // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/LayerDebugInfo.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000015146 13756501735 014710� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 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. */ #include #include #include #include using namespace android; using android::base::StringAppendF; #define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) namespace android { status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { RETURN_ON_ERROR(parcel->writeCString(mName.c_str())); RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str())); RETURN_ON_ERROR(parcel->writeCString(mType.c_str())); RETURN_ON_ERROR(parcel->write(mTransparentRegion)); RETURN_ON_ERROR(parcel->write(mVisibleRegion)); RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion)); RETURN_ON_ERROR(parcel->writeUint32(mLayerStack)); RETURN_ON_ERROR(parcel->writeFloat(mX)); RETURN_ON_ERROR(parcel->writeFloat(mY)); RETURN_ON_ERROR(parcel->writeUint32(mZ)); RETURN_ON_ERROR(parcel->writeInt32(mWidth)); RETURN_ON_ERROR(parcel->writeInt32(mHeight)); RETURN_ON_ERROR(parcel->write(mCrop)); RETURN_ON_ERROR(parcel->writeFloat(mColor.r)); RETURN_ON_ERROR(parcel->writeFloat(mColor.g)); RETURN_ON_ERROR(parcel->writeFloat(mColor.b)); RETURN_ON_ERROR(parcel->writeFloat(mColor.a)); RETURN_ON_ERROR(parcel->writeUint32(mFlags)); RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat)); RETURN_ON_ERROR(parcel->writeUint32(static_cast(mDataSpace))); for (size_t index = 0; index < 4; index++) { RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2])); } RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth)); RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight)); RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride)); RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat)); RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames)); RETURN_ON_ERROR(parcel->writeBool(mRefreshPending)); RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); return NO_ERROR; } status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { mName = parcel->readCString(); RETURN_ON_ERROR(parcel->errorCheck()); mParentName = parcel->readCString(); RETURN_ON_ERROR(parcel->errorCheck()); mType = parcel->readCString(); RETURN_ON_ERROR(parcel->errorCheck()); RETURN_ON_ERROR(parcel->read(mTransparentRegion)); RETURN_ON_ERROR(parcel->read(mVisibleRegion)); RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion)); RETURN_ON_ERROR(parcel->readUint32(&mLayerStack)); RETURN_ON_ERROR(parcel->readFloat(&mX)); RETURN_ON_ERROR(parcel->readFloat(&mY)); RETURN_ON_ERROR(parcel->readUint32(&mZ)); RETURN_ON_ERROR(parcel->readInt32(&mWidth)); RETURN_ON_ERROR(parcel->readInt32(&mHeight)); RETURN_ON_ERROR(parcel->read(mCrop)); mColor.r = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); mColor.g = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); mColor.b = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); mColor.a = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); RETURN_ON_ERROR(parcel->readUint32(&mFlags)); RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat)); // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways? mDataSpace = static_cast(parcel->readUint32()); RETURN_ON_ERROR(parcel->errorCheck()); for (size_t index = 0; index < 4; index++) { RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2])); } RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth)); RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight)); RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride)); RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat)); RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames)); RETURN_ON_ERROR(parcel->readBool(&mRefreshPending)); RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); return NO_ERROR; } std::string to_string(const LayerDebugInfo& info) { std::string result; StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); info.mTransparentRegion.dump(result, "TransparentRegion"); info.mVisibleRegion.dump(result, "VisibleRegion"); info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", info.mLayerStack, info.mZ, static_cast(info.mX), static_cast(info.mY), info.mWidth, info.mHeight); StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str()); StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", static_cast(info.mColor.r), static_cast(info.mColor.g), static_cast(info.mColor.b), static_cast(info.mColor.a), info.mFlags); StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast(info.mMatrix[0][0]), static_cast(info.mMatrix[0][1]), static_cast(info.mMatrix[1][0]), static_cast(info.mMatrix[1][1])); result.append("\n"); StringAppendF(&result, " parent=%s\n", info.mParentName.c_str()); StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth, info.mActiveBufferHeight, info.mActiveBufferStride, decodePixelFormat(info.mActiveBufferFormat).c_str()); StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", info.mNumQueuedFrames, info.mRefreshPending); result.append("\n"); return result; } } // android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/LayerMetadata.cpp��������������������������������������������������������������������������0100644 0000000 0000000 00000007614 13756501735 014567� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2019 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. */ #include #include #include using android::base::StringPrintf; namespace android { LayerMetadata::LayerMetadata() = default; LayerMetadata::LayerMetadata(std::unordered_map> map) : mMap(std::move(map)) {} LayerMetadata::LayerMetadata(const LayerMetadata& other) = default; LayerMetadata::LayerMetadata(LayerMetadata&& other) = default; bool LayerMetadata::merge(const LayerMetadata& other, bool eraseEmpty) { bool changed = false; for (const auto& entry : other.mMap) { auto it = mMap.find(entry.first); if (it != mMap.cend() && it->second != entry.second) { if (eraseEmpty && entry.second.empty()) { mMap.erase(it); } else { it->second = entry.second; } changed = true; } else if (it == mMap.cend() && !entry.second.empty()) { mMap[entry.first] = entry.second; changed = true; } } return changed; } status_t LayerMetadata::writeToParcel(Parcel* parcel) const { parcel->writeInt32(static_cast(mMap.size())); status_t status = OK; for (const auto& entry : mMap) { status = parcel->writeUint32(entry.first); if (status != OK) { break; } status = parcel->writeByteVector(entry.second); if (status != OK) { break; } } return status; } status_t LayerMetadata::readFromParcel(const Parcel* parcel) { int size = parcel->readInt32(); status_t status = OK; mMap.clear(); for (int i = 0; i < size; ++i) { uint32_t key = parcel->readUint32(); status = parcel->readByteVector(&mMap[key]); if (status != OK) { break; } } return status; } LayerMetadata& LayerMetadata::operator=(const LayerMetadata& other) { mMap = other.mMap; return *this; } LayerMetadata& LayerMetadata::operator=(LayerMetadata&& other) { mMap = std::move(other.mMap); return *this; } bool LayerMetadata::has(uint32_t key) const { return mMap.count(key); } int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const { if (!has(key)) return fallback; const std::vector& data = mMap.at(key); if (data.size() < sizeof(uint32_t)) return fallback; Parcel p; p.setData(data.data(), data.size()); return p.readInt32(); } void LayerMetadata::setInt32(uint32_t key, int32_t value) { std::vector& data = mMap[key]; Parcel p; p.writeInt32(value); data.resize(p.dataSize()); memcpy(data.data(), p.data(), p.dataSize()); } std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const { if (!has(key)) return std::string(); switch (key) { case METADATA_OWNER_UID: return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0)); case METADATA_WINDOW_TYPE: return StringPrintf("windowType%s%d", separator, getInt32(key, 0)); case METADATA_TASK_ID: return StringPrintf("taskId%s%d", separator, getInt32(key, 0)); default: return StringPrintf("%d%s%dbytes", key, separator, static_cast(mMap.at(key).size())); } } } // namespace android ��������������������������������������������������������������������������������������������������������������������libs/gui/LayerState.cpp�����������������������������������������������������������������������������0100644 0000000 0000000 00000033321 13756501735 014121� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2008 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. */ #define LOG_TAG "LayerState" #include #include #include #include #include #include namespace android { status_t layer_state_t::write(Parcel& output) const { output.writeStrongBinder(surface); output.writeUint64(what); output.writeFloat(x); output.writeFloat(y); output.writeInt32(z); output.writeUint32(w); output.writeUint32(h); output.writeUint32(layerStack); output.writeFloat(alpha); output.writeUint32(flags); output.writeUint32(mask); *reinterpret_cast( output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; output.write(crop_legacy); output.writeStrongBinder(barrierHandle_legacy); output.writeStrongBinder(reparentHandle); output.writeUint64(frameNumber_legacy); output.writeInt32(overrideScalingMode); output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); output.writeStrongBinder(relativeLayerHandle); output.writeStrongBinder(parentHandleForChild); output.writeFloat(color.r); output.writeFloat(color.g); output.writeFloat(color.b); #ifndef NO_INPUT inputInfo.write(output); #endif output.write(transparentRegion); output.writeUint32(transform); output.writeBool(transformToDisplayInverse); output.write(crop); output.write(frame); if (buffer) { output.writeBool(true); output.write(*buffer); } else { output.writeBool(false); } if (acquireFence) { output.writeBool(true); output.write(*acquireFence); } else { output.writeBool(false); } output.writeUint32(static_cast(dataspace)); output.write(hdrMetadata); output.write(surfaceDamageRegion); output.writeInt32(api); if (sidebandStream) { output.writeBool(true); output.writeNativeHandle(sidebandStream->handle()); } else { output.writeBool(false); } memcpy(output.writeInplace(16 * sizeof(float)), colorTransform.asArray(), 16 * sizeof(float)); output.writeFloat(cornerRadius); output.writeBool(hasListenerCallbacks); output.writeWeakBinder(cachedBuffer.token); output.writeUint64(cachedBuffer.id); output.writeParcelable(metadata); output.writeFloat(bgColorAlpha); output.writeUint32(static_cast(bgColorDataspace)); output.writeBool(colorSpaceAgnostic); return NO_ERROR; } status_t layer_state_t::read(const Parcel& input) { surface = input.readStrongBinder(); what = input.readUint64(); x = input.readFloat(); y = input.readFloat(); z = input.readInt32(); w = input.readUint32(); h = input.readUint32(); layerStack = input.readUint32(); alpha = input.readFloat(); flags = static_cast(input.readUint32()); mask = static_cast(input.readUint32()); const void* matrix_data = input.readInplace(sizeof(layer_state_t::matrix22_t)); if (matrix_data) { matrix = *reinterpret_cast(matrix_data); } else { return BAD_VALUE; } input.read(crop_legacy); barrierHandle_legacy = input.readStrongBinder(); reparentHandle = input.readStrongBinder(); frameNumber_legacy = input.readUint64(); overrideScalingMode = input.readInt32(); barrierGbp_legacy = interface_cast(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); parentHandleForChild = input.readStrongBinder(); color.r = input.readFloat(); color.g = input.readFloat(); color.b = input.readFloat(); #ifndef NO_INPUT inputInfo = InputWindowInfo::read(input); #endif input.read(transparentRegion); transform = input.readUint32(); transformToDisplayInverse = input.readBool(); input.read(crop); input.read(frame); buffer = new GraphicBuffer(); if (input.readBool()) { input.read(*buffer); } acquireFence = new Fence(); if (input.readBool()) { input.read(*acquireFence); } dataspace = static_cast(input.readUint32()); input.read(hdrMetadata); input.read(surfaceDamageRegion); api = input.readInt32(); if (input.readBool()) { sidebandStream = NativeHandle::create(input.readNativeHandle(), true); } colorTransform = mat4(static_cast(input.readInplace(16 * sizeof(float)))); cornerRadius = input.readFloat(); hasListenerCallbacks = input.readBool(); cachedBuffer.token = input.readWeakBinder(); cachedBuffer.id = input.readUint64(); input.readParcelable(&metadata); bgColorAlpha = input.readFloat(); bgColorDataspace = static_cast(input.readUint32()); colorSpaceAgnostic = input.readBool(); return NO_ERROR; } status_t ComposerState::write(Parcel& output) const { output.writeStrongBinder(IInterface::asBinder(client)); return state.write(output); } status_t ComposerState::read(const Parcel& input) { client = interface_cast(input.readStrongBinder()); return state.read(input); } DisplayState::DisplayState() : what(0), layerStack(0), orientation(eOrientationDefault), viewport(Rect::EMPTY_RECT), frame(Rect::EMPTY_RECT), width(0), height(0) { } status_t DisplayState::write(Parcel& output) const { output.writeStrongBinder(token); output.writeStrongBinder(IInterface::asBinder(surface)); output.writeUint32(what); output.writeUint32(layerStack); output.writeUint32(orientation); output.write(viewport); output.write(frame); output.writeUint32(width); output.writeUint32(height); return NO_ERROR; } status_t DisplayState::read(const Parcel& input) { token = input.readStrongBinder(); surface = interface_cast(input.readStrongBinder()); what = input.readUint32(); layerStack = input.readUint32(); orientation = input.readUint32(); input.read(viewport); input.read(frame); width = input.readUint32(); height = input.readUint32(); return NO_ERROR; } void DisplayState::merge(const DisplayState& other) { if (other.what & eSurfaceChanged) { what |= eSurfaceChanged; surface = other.surface; } if (other.what & eLayerStackChanged) { what |= eLayerStackChanged; layerStack = other.layerStack; } if (other.what & eDisplayProjectionChanged) { what |= eDisplayProjectionChanged; orientation = other.orientation; viewport = other.viewport; frame = other.frame; } if (other.what & eDisplaySizeChanged) { what |= eDisplaySizeChanged; width = other.width; height = other.height; } } void layer_state_t::merge(const layer_state_t& other) { if (other.what & ePositionChanged) { what |= ePositionChanged; x = other.x; y = other.y; } if (other.what & eLayerChanged) { what |= eLayerChanged; what &= ~eRelativeLayerChanged; z = other.z; } if (other.what & eSizeChanged) { what |= eSizeChanged; w = other.w; h = other.h; } if (other.what & eAlphaChanged) { what |= eAlphaChanged; alpha = other.alpha; } if (other.what & eMatrixChanged) { what |= eMatrixChanged; matrix = other.matrix; } if (other.what & eTransparentRegionChanged) { what |= eTransparentRegionChanged; transparentRegion = other.transparentRegion; } if (other.what & eFlagsChanged) { what |= eFlagsChanged; flags = other.flags; mask = other.mask; } if (other.what & eLayerStackChanged) { what |= eLayerStackChanged; layerStack = other.layerStack; } if (other.what & eCropChanged_legacy) { what |= eCropChanged_legacy; crop_legacy = other.crop_legacy; } if (other.what & eCornerRadiusChanged) { what |= eCornerRadiusChanged; cornerRadius = other.cornerRadius; } if (other.what & eDeferTransaction_legacy) { what |= eDeferTransaction_legacy; barrierHandle_legacy = other.barrierHandle_legacy; barrierGbp_legacy = other.barrierGbp_legacy; frameNumber_legacy = other.frameNumber_legacy; } if (other.what & eOverrideScalingModeChanged) { what |= eOverrideScalingModeChanged; overrideScalingMode = other.overrideScalingMode; } if (other.what & eGeometryAppliesWithResize) { what |= eGeometryAppliesWithResize; } if (other.what & eReparentChildren) { what |= eReparentChildren; reparentHandle = other.reparentHandle; } if (other.what & eDetachChildren) { what |= eDetachChildren; } if (other.what & eRelativeLayerChanged) { what |= eRelativeLayerChanged; what &= ~eLayerChanged; z = other.z; relativeLayerHandle = other.relativeLayerHandle; } if (other.what & eReparent) { what |= eReparent; parentHandleForChild = other.parentHandleForChild; } if (other.what & eDestroySurface) { what |= eDestroySurface; } if (other.what & eTransformChanged) { what |= eTransformChanged; transform = other.transform; } if (other.what & eTransformToDisplayInverseChanged) { what |= eTransformToDisplayInverseChanged; transformToDisplayInverse = other.transformToDisplayInverse; } if (other.what & eCropChanged) { what |= eCropChanged; crop = other.crop; } if (other.what & eFrameChanged) { what |= eFrameChanged; frame = other.frame; } if (other.what & eBufferChanged) { what |= eBufferChanged; buffer = other.buffer; } if (other.what & eAcquireFenceChanged) { what |= eAcquireFenceChanged; acquireFence = other.acquireFence; } if (other.what & eDataspaceChanged) { what |= eDataspaceChanged; dataspace = other.dataspace; } if (other.what & eHdrMetadataChanged) { what |= eHdrMetadataChanged; hdrMetadata = other.hdrMetadata; } if (other.what & eSurfaceDamageRegionChanged) { what |= eSurfaceDamageRegionChanged; surfaceDamageRegion = other.surfaceDamageRegion; } if (other.what & eApiChanged) { what |= eApiChanged; api = other.api; } if (other.what & eSidebandStreamChanged) { what |= eSidebandStreamChanged; sidebandStream = other.sidebandStream; } if (other.what & eColorTransformChanged) { what |= eColorTransformChanged; colorTransform = other.colorTransform; } if (other.what & eHasListenerCallbacksChanged) { what |= eHasListenerCallbacksChanged; hasListenerCallbacks = other.hasListenerCallbacks; } #ifndef NO_INPUT if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; inputInfo = other.inputInfo; } #endif if (other.what & eCachedBufferChanged) { what |= eCachedBufferChanged; cachedBuffer = other.cachedBuffer; } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; color = other.color; bgColorAlpha = other.bgColorAlpha; bgColorDataspace = other.bgColorDataspace; } if (other.what & eMetadataChanged) { what |= eMetadataChanged; metadata.merge(other.metadata); } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, other.what, what); } } // ------------------------------- InputWindowCommands ---------------------------------------- void InputWindowCommands::merge(const InputWindowCommands& other) { transferTouchFocusCommands .insert(transferTouchFocusCommands.end(), std::make_move_iterator(other.transferTouchFocusCommands.begin()), std::make_move_iterator(other.transferTouchFocusCommands.end())); syncInputWindows |= other.syncInputWindows; } void InputWindowCommands::clear() { transferTouchFocusCommands.clear(); syncInputWindows = false; } void InputWindowCommands::write(Parcel& output) const { output.writeUint32(static_cast(transferTouchFocusCommands.size())); for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) { output.writeStrongBinder(transferTouchFocusCommand.fromToken); output.writeStrongBinder(transferTouchFocusCommand.toToken); } output.writeBool(syncInputWindows); } void InputWindowCommands::read(const Parcel& input) { size_t count = input.readUint32(); transferTouchFocusCommands.clear(); for (size_t i = 0; i < count; i++) { TransferTouchFocusCommand transferTouchFocusCommand; transferTouchFocusCommand.fromToken = input.readStrongBinder(); transferTouchFocusCommand.toToken = input.readStrongBinder(); transferTouchFocusCommands.emplace_back(transferTouchFocusCommand); } syncInputWindows = input.readBool(); } }; // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/OWNERS�������������������������������������������������������������������������������������0100644 0000000 0000000 00000000177 13756501735 012303� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������jessehall@google.com jwcai@google.com lpy@google.com marissaw@google.com mathias@google.com racarr@google.com stoza@google.com �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/OccupancyTracker.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000007303 13756501735 015305� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #undef LOG_TAG #define LOG_TAG "OccupancyTracker" #include #include #include #include #include namespace android { status_t OccupancyTracker::Segment::writeToParcel(Parcel* parcel) const { status_t result = parcel->writeInt64(totalTime); if (result != OK) { return result; } result = parcel->writeUint64(static_cast(numFrames)); if (result != OK) { return result; } result = parcel->writeFloat(occupancyAverage); if (result != OK) { return result; } return parcel->writeBool(usedThirdBuffer); } status_t OccupancyTracker::Segment::readFromParcel(const Parcel* parcel) { status_t result = parcel->readInt64(&totalTime); if (result != OK) { return result; } uint64_t uintNumFrames = 0; result = parcel->readUint64(&uintNumFrames); if (result != OK) { return result; } numFrames = static_cast(uintNumFrames); result = parcel->readFloat(&occupancyAverage); if (result != OK) { return result; } return parcel->readBool(&usedThirdBuffer); } void OccupancyTracker::registerOccupancyChange(size_t occupancy) { ATRACE_CALL(); nsecs_t now = systemTime(); nsecs_t delta = now - mLastOccupancyChangeTime; if (delta > NEW_SEGMENT_DELAY) { recordPendingSegment(); } else { mPendingSegment.totalTime += delta; if (mPendingSegment.mOccupancyTimes.count(mLastOccupancy)) { mPendingSegment.mOccupancyTimes[mLastOccupancy] += delta; } else { mPendingSegment.mOccupancyTimes[mLastOccupancy] = delta; } } if (occupancy > mLastOccupancy) { ++mPendingSegment.numFrames; } mLastOccupancyChangeTime = now; mLastOccupancy = occupancy; } std::vector OccupancyTracker::getSegmentHistory( bool forceFlush) { if (forceFlush) { recordPendingSegment(); } std::vector segments(mSegmentHistory.cbegin(), mSegmentHistory.cend()); mSegmentHistory.clear(); return segments; } void OccupancyTracker::recordPendingSegment() { // Only record longer segments to get a better measurement of actual double- // vs. triple-buffered time if (mPendingSegment.numFrames > LONG_SEGMENT_THRESHOLD) { float occupancyAverage = 0.0f; bool usedThirdBuffer = false; for (const auto& timePair : mPendingSegment.mOccupancyTimes) { size_t occupancy = timePair.first; float timeRatio = static_cast(timePair.second) / mPendingSegment.totalTime; occupancyAverage += timeRatio * occupancy; usedThirdBuffer = usedThirdBuffer || (occupancy > 1); } mSegmentHistory.push_front({mPendingSegment.totalTime, mPendingSegment.numFrames, occupancyAverage, usedThirdBuffer}); if (mSegmentHistory.size() > MAX_HISTORY_SIZE) { mSegmentHistory.pop_back(); } } mPendingSegment.clear(); } } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/StreamSplitter.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000024166 13756501735 015035� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include #define LOG_TAG "StreamSplitter" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include namespace android { status_t StreamSplitter::createSplitter( const sp& inputQueue, sp* outSplitter) { if (inputQueue == nullptr) { ALOGE("createSplitter: inputQueue must not be NULL"); return BAD_VALUE; } if (outSplitter == nullptr) { ALOGE("createSplitter: outSplitter must not be NULL"); return BAD_VALUE; } sp splitter(new StreamSplitter(inputQueue)); status_t status = splitter->mInput->consumerConnect(splitter, false); if (status == NO_ERROR) { splitter->mInput->setConsumerName(String8("StreamSplitter")); *outSplitter = splitter; } return status; } StreamSplitter::StreamSplitter(const sp& inputQueue) : mIsAbandoned(false), mMutex(), mReleaseCondition(), mOutstandingBuffers(0), mInput(inputQueue), mOutputs(), mBuffers() {} StreamSplitter::~StreamSplitter() { mInput->consumerDisconnect(); Vector >::iterator output = mOutputs.begin(); for (; output != mOutputs.end(); ++output) { (*output)->disconnect(NATIVE_WINDOW_API_CPU); } if (mBuffers.size() > 0) { ALOGE("%zu buffers still being tracked", mBuffers.size()); } } status_t StreamSplitter::addOutput( const sp& outputQueue) { if (outputQueue == nullptr) { ALOGE("addOutput: outputQueue must not be NULL"); return BAD_VALUE; } Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; sp listener(new OutputListener(this, outputQueue)); IInterface::asBinder(outputQueue)->linkToDeath(listener); status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU, /* producerControlledByApp */ false, &queueBufferOutput); if (status != NO_ERROR) { ALOGE("addOutput: failed to connect (%d)", status); return status; } mOutputs.push_back(outputQueue); return NO_ERROR; } void StreamSplitter::setName(const String8 &name) { Mutex::Autolock lock(mMutex); mInput->setConsumerName(name); } void StreamSplitter::onFrameAvailable(const BufferItem& /* item */) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); // The current policy is that if any one consumer is consuming buffers too // slowly, the splitter will stall the rest of the outputs by not acquiring // any more buffers from the input. This will cause back pressure on the // input queue, slowing down its producer. // If there are too many outstanding buffers, we block until a buffer is // released back to the input in onBufferReleased while (mOutstandingBuffers >= MAX_OUTSTANDING_BUFFERS) { mReleaseCondition.wait(mMutex); // If the splitter is abandoned while we are waiting, the release // condition variable will be broadcast, and we should just return // without attempting to do anything more (since the input queue will // also be abandoned). if (mIsAbandoned) { return; } } ++mOutstandingBuffers; // Acquire and detach the buffer from the input BufferItem bufferItem; status_t status = mInput->acquireBuffer(&bufferItem, /* presentWhen */ 0); LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "acquiring buffer from input failed (%d)", status); ALOGV("acquired buffer %#" PRIx64 " from input", bufferItem.mGraphicBuffer->getId()); status = mInput->detachBuffer(bufferItem.mSlot); LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "detaching buffer from input failed (%d)", status); // Initialize our reference count for this buffer mBuffers.add(bufferItem.mGraphicBuffer->getId(), new BufferTracker(bufferItem.mGraphicBuffer)); IGraphicBufferProducer::QueueBufferInput queueInput( bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp, bufferItem.mDataSpace, bufferItem.mCrop, static_cast(bufferItem.mScalingMode), bufferItem.mTransform, bufferItem.mFence); // Attach and queue the buffer to each of the outputs Vector >::iterator output = mOutputs.begin(); for (; output != mOutputs.end(); ++output) { int slot; status = (*output)->attachBuffer(&slot, bufferItem.mGraphicBuffer); if (status == NO_INIT) { // If we just discovered that this output has been abandoned, note // that, increment the release count so that we still release this // buffer eventually, and move on to the next output onAbandonedLocked(); mBuffers.editValueFor(bufferItem.mGraphicBuffer->getId())-> incrementReleaseCountLocked(); continue; } else { LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "attaching buffer to output failed (%d)", status); } IGraphicBufferProducer::QueueBufferOutput queueOutput; status = (*output)->queueBuffer(slot, queueInput, &queueOutput); if (status == NO_INIT) { // If we just discovered that this output has been abandoned, note // that, increment the release count so that we still release this // buffer eventually, and move on to the next output onAbandonedLocked(); mBuffers.editValueFor(bufferItem.mGraphicBuffer->getId())-> incrementReleaseCountLocked(); continue; } else { LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "queueing buffer to output failed (%d)", status); } ALOGV("queued buffer %#" PRIx64 " to output %p", bufferItem.mGraphicBuffer->getId(), output->get()); } } void StreamSplitter::onBufferReleasedByOutput( const sp& from) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); sp buffer; sp fence; status_t status = from->detachNextBuffer(&buffer, &fence); if (status == NO_INIT) { // If we just discovered that this output has been abandoned, note that, // but we can't do anything else, since buffer is invalid onAbandonedLocked(); return; } else { LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "detaching buffer from output failed (%d)", status); } ALOGV("detached buffer %#" PRIx64 " from output %p", buffer->getId(), from.get()); const sp& tracker = mBuffers.editValueFor(buffer->getId()); // Merge the release fence of the incoming buffer so that the fence we send // back to the input includes all of the outputs' fences tracker->mergeFence(fence); // Check to see if this is the last outstanding reference to this buffer size_t releaseCount = tracker->incrementReleaseCountLocked(); ALOGV("buffer %#" PRIx64 " reference count %zu (of %zu)", buffer->getId(), releaseCount, mOutputs.size()); if (releaseCount < mOutputs.size()) { return; } // If we've been abandoned, we can't return the buffer to the input, so just // stop tracking it and move on if (mIsAbandoned) { mBuffers.removeItem(buffer->getId()); return; } // Attach and release the buffer back to the input int consumerSlot; status = mInput->attachBuffer(&consumerSlot, tracker->getBuffer()); LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status); status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker->getMergedFence()); LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "releasing buffer to input failed (%d)", status); ALOGV("released buffer %#" PRIx64 " to input", buffer->getId()); // We no longer need to track the buffer once it has been returned to the // input mBuffers.removeItem(buffer->getId()); // Notify any waiting onFrameAvailable calls --mOutstandingBuffers; mReleaseCondition.signal(); } void StreamSplitter::onAbandonedLocked() { ALOGE("one of my outputs has abandoned me"); if (!mIsAbandoned) { mInput->consumerDisconnect(); } mIsAbandoned = true; mReleaseCondition.broadcast(); } StreamSplitter::OutputListener::OutputListener( const sp& splitter, const sp& output) : mSplitter(splitter), mOutput(output) {} StreamSplitter::OutputListener::~OutputListener() {} void StreamSplitter::OutputListener::onBufferReleased() { mSplitter->onBufferReleasedByOutput(mOutput); } void StreamSplitter::OutputListener::binderDied(const wp& /* who */) { Mutex::Autolock lock(mSplitter->mMutex); mSplitter->onAbandonedLocked(); } StreamSplitter::BufferTracker::BufferTracker(const sp& buffer) : mBuffer(buffer), mMergedFence(Fence::NO_FENCE), mReleaseCount(0) {} StreamSplitter::BufferTracker::~BufferTracker() {} void StreamSplitter::BufferTracker::mergeFence(const sp& with) { mMergedFence = Fence::merge(String8("StreamSplitter"), mMergedFence, with); } } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/Surface.cpp��������������������������������������������������������������������������������0100644 0000000 0000000 00000200434 13756501735 013435� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #define LOG_TAG "Surface" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { using ui::ColorMode; using ui::Dataspace; Surface::Surface(const sp& bufferProducer, bool controlledByApp) : mGraphicBufferProducer(bufferProducer), mCrop(Rect::EMPTY_RECT), mBufferAge(0), mGenerationNumber(0), mSharedBufferMode(false), mAutoRefresh(false), mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), mSharedBufferHasBeenQueued(false), mQueriedSupportedTimestamps(false), mFrameTimestampsSupportsPresent(false), mEnableFrameTimestamps(false), mFrameEventHistory(std::make_unique()) { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; ANativeWindow::dequeueBuffer = hook_dequeueBuffer; ANativeWindow::cancelBuffer = hook_cancelBuffer; ANativeWindow::queueBuffer = hook_queueBuffer; ANativeWindow::query = hook_query; ANativeWindow::perform = hook_perform; ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; const_cast(ANativeWindow::minSwapInterval) = 0; const_cast(ANativeWindow::maxSwapInterval) = 1; mReqWidth = 0; mReqHeight = 0; mReqFormat = 0; mReqUsage = 0; mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; mDataSpace = Dataspace::UNKNOWN; mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mTransform = 0; mStickyTransform = 0; mDefaultWidth = 0; mDefaultHeight = 0; mUserWidth = 0; mUserHeight = 0; mTransformHint = 0; mConsumerRunningBehind = false; mConnectedToCpu = false; mProducerControlledByApp = controlledByApp; mSwapIntervalZero = false; } Surface::~Surface() { if (mConnectedToCpu) { Surface::disconnect(NATIVE_WINDOW_API_CPU); } } sp Surface::composerService() const { return ComposerService::getComposerService(); } nsecs_t Surface::now() const { return systemTime(); } sp Surface::getIGraphicBufferProducer() const { return mGraphicBufferProducer; } void Surface::setSidebandStream(const sp& stream) { mGraphicBufferProducer->setSidebandStream(stream); } void Surface::allocateBuffers() { uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; mGraphicBufferProducer->allocateBuffers(reqWidth, reqHeight, mReqFormat, mReqUsage); } status_t Surface::setGenerationNumber(uint32_t generation) { status_t result = mGraphicBufferProducer->setGenerationNumber(generation); if (result == NO_ERROR) { mGenerationNumber = generation; } return result; } uint64_t Surface::getNextFrameNumber() const { Mutex::Autolock lock(mMutex); return mNextFrameNumber; } String8 Surface::getConsumerName() const { return mGraphicBufferProducer->getConsumerName(); } status_t Surface::setDequeueTimeout(nsecs_t timeout) { return mGraphicBufferProducer->setDequeueTimeout(timeout); } status_t Surface::getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) { return mGraphicBufferProducer->getLastQueuedBuffer(outBuffer, outFence, outTransformMatrix); } status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { ATRACE_CALL(); DisplayStatInfo stats; status_t result = composerService()->getDisplayStats(nullptr, &stats); if (result != NO_ERROR) { return result; } *outRefreshDuration = stats.vsyncPeriod; return NO_ERROR; } void Surface::enableFrameTimestamps(bool enable) { Mutex::Autolock lock(mMutex); // If going from disabled to enabled, get the initial values for // compositor and display timing. if (!mEnableFrameTimestamps && enable) { FrameEventHistoryDelta delta; mGraphicBufferProducer->getFrameTimestamps(&delta); mFrameEventHistory->applyDelta(delta); } mEnableFrameTimestamps = enable; } status_t Surface::getCompositorTiming( nsecs_t* compositeDeadline, nsecs_t* compositeInterval, nsecs_t* compositeToPresentLatency) { Mutex::Autolock lock(mMutex); if (!mEnableFrameTimestamps) { return INVALID_OPERATION; } if (compositeDeadline != nullptr) { *compositeDeadline = mFrameEventHistory->getNextCompositeDeadline(now()); } if (compositeInterval != nullptr) { *compositeInterval = mFrameEventHistory->getCompositeInterval(); } if (compositeToPresentLatency != nullptr) { *compositeToPresentLatency = mFrameEventHistory->getCompositeToPresentLatency(); } return NO_ERROR; } static bool checkConsumerForUpdates( const FrameEvents* e, const uint64_t lastFrameNumber, const nsecs_t* outLatchTime, const nsecs_t* outFirstRefreshStartTime, const nsecs_t* outLastRefreshStartTime, const nsecs_t* outGpuCompositionDoneTime, const nsecs_t* outDisplayPresentTime, const nsecs_t* outDequeueReadyTime, const nsecs_t* outReleaseTime) { bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo(); bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) && !e->hasFirstRefreshStartInfo(); bool checkForGpuCompositionDone = (outGpuCompositionDoneTime != nullptr) && !e->hasGpuCompositionDoneInfo(); bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) && !e->hasDisplayPresentInfo(); // LastRefreshStart, DequeueReady, and Release are never available for the // last frame. bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) && !e->hasLastRefreshStartInfo() && (e->frameNumber != lastFrameNumber); bool checkForDequeueReady = (outDequeueReadyTime != nullptr) && !e->hasDequeueReadyInfo() && (e->frameNumber != lastFrameNumber); bool checkForRelease = (outReleaseTime != nullptr) && !e->hasReleaseInfo() && (e->frameNumber != lastFrameNumber); // RequestedPresent and Acquire info are always available producer-side. return checkForLatch || checkForFirstRefreshStart || checkForLastRefreshStart || checkForGpuCompositionDone || checkForDisplayPresent || checkForDequeueReady || checkForRelease; } static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) { if (dst != nullptr) { // We always get valid timestamps for these eventually. *dst = (src == FrameEvents::TIMESTAMP_PENDING) ? NATIVE_WINDOW_TIMESTAMP_PENDING : src; } } static void getFrameTimestampFence(nsecs_t *dst, const std::shared_ptr& src, bool fenceShouldBeKnown) { if (dst != nullptr) { if (!fenceShouldBeKnown) { *dst = NATIVE_WINDOW_TIMESTAMP_PENDING; return; } nsecs_t signalTime = src->getSignalTime(); *dst = (signalTime == Fence::SIGNAL_TIME_PENDING) ? NATIVE_WINDOW_TIMESTAMP_PENDING : (signalTime == Fence::SIGNAL_TIME_INVALID) ? NATIVE_WINDOW_TIMESTAMP_INVALID : signalTime; } } status_t Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime, nsecs_t* outLastRefreshStartTime, nsecs_t* outGpuCompositionDoneTime, nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime) { ATRACE_CALL(); Mutex::Autolock lock(mMutex); if (!mEnableFrameTimestamps) { return INVALID_OPERATION; } // Verify the requested timestamps are supported. querySupportedTimestampsLocked(); if (outDisplayPresentTime != nullptr && !mFrameTimestampsSupportsPresent) { return BAD_VALUE; } FrameEvents* events = mFrameEventHistory->getFrame(frameNumber); if (events == nullptr) { // If the entry isn't available in the producer, it's definitely not // available in the consumer. return NAME_NOT_FOUND; } // Update our cache of events if the requested events are not available. if (checkConsumerForUpdates(events, mLastFrameNumber, outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, outGpuCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime)) { FrameEventHistoryDelta delta; mGraphicBufferProducer->getFrameTimestamps(&delta); mFrameEventHistory->applyDelta(delta); events = mFrameEventHistory->getFrame(frameNumber); } if (events == nullptr) { // The entry was available before the update, but was overwritten // after the update. Make sure not to send the wrong frame's data. return NAME_NOT_FOUND; } getFrameTimestamp(outRequestedPresentTime, events->requestedPresentTime); getFrameTimestamp(outLatchTime, events->latchTime); getFrameTimestamp(outFirstRefreshStartTime, events->firstRefreshStartTime); getFrameTimestamp(outLastRefreshStartTime, events->lastRefreshStartTime); getFrameTimestamp(outDequeueReadyTime, events->dequeueReadyTime); getFrameTimestampFence(outAcquireTime, events->acquireFence, events->hasAcquireInfo()); getFrameTimestampFence(outGpuCompositionDoneTime, events->gpuCompositionDoneFence, events->hasGpuCompositionDoneInfo()); getFrameTimestampFence(outDisplayPresentTime, events->displayPresentFence, events->hasDisplayPresentInfo()); getFrameTimestampFence(outReleaseTime, events->releaseFence, events->hasReleaseInfo()); return NO_ERROR; } status_t Surface::getWideColorSupport(bool* supported) { ATRACE_CALL(); const sp display = composerService()->getInternalDisplayToken(); if (display == nullptr) { return NAME_NOT_FOUND; } *supported = false; status_t error = composerService()->isWideColorDisplay(display, supported); return error; } status_t Surface::getHdrSupport(bool* supported) { ATRACE_CALL(); const sp display = composerService()->getInternalDisplayToken(); if (display == nullptr) { return NAME_NOT_FOUND; } HdrCapabilities hdrCapabilities; status_t err = composerService()->getHdrCapabilities(display, &hdrCapabilities); if (err) return err; *supported = !hdrCapabilities.getSupportedHdrTypes().empty(); return NO_ERROR; } int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) { Surface* c = getSelf(window); return c->setSwapInterval(interval); } int Surface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) { Surface* c = getSelf(window); return c->dequeueBuffer(buffer, fenceFd); } int Surface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) { Surface* c = getSelf(window); return c->cancelBuffer(buffer, fenceFd); } int Surface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) { Surface* c = getSelf(window); return c->queueBuffer(buffer, fenceFd); } int Surface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer) { Surface* c = getSelf(window); ANativeWindowBuffer* buf; int fenceFd = -1; int result = c->dequeueBuffer(&buf, &fenceFd); if (result != OK) { return result; } sp fence(new Fence(fenceFd)); int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); if (waitResult != OK) { ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult); c->cancelBuffer(buf, -1); return waitResult; } *buffer = buf; return result; } int Surface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { Surface* c = getSelf(window); return c->cancelBuffer(buffer, -1); } int Surface::hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { Surface* c = getSelf(window); return c->lockBuffer_DEPRECATED(buffer); } int Surface::hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer) { Surface* c = getSelf(window); return c->queueBuffer(buffer, -1); } int Surface::hook_query(const ANativeWindow* window, int what, int* value) { const Surface* c = getSelf(window); return c->query(what, value); } int Surface::hook_perform(ANativeWindow* window, int operation, ...) { va_list args; va_start(args, operation); Surface* c = getSelf(window); int result = c->perform(operation, args); va_end(args); return result; } int Surface::setSwapInterval(int interval) { ATRACE_CALL(); // EGL specification states: // interval is silently clamped to minimum and maximum implementation // dependent values before being stored. if (interval < minSwapInterval) interval = minSwapInterval; if (interval > maxSwapInterval) interval = maxSwapInterval; const bool wasSwapIntervalZero = mSwapIntervalZero; mSwapIntervalZero = (interval == 0); if (mSwapIntervalZero != wasSwapIntervalZero) { mGraphicBufferProducer->setAsyncMode(mSwapIntervalZero); } return NO_ERROR; } class FenceMonitor { public: explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) { std::thread thread(&FenceMonitor::loop, this); pthread_setname_np(thread.native_handle(), mName); thread.detach(); } void queueFence(const sp& fence) { char message[64]; std::lock_guard lock(mMutex); if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued); ATRACE_NAME(message); // Need an increment on both to make the trace number correct. mFencesQueued++; mFencesSignaled++; return; } snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued); ATRACE_NAME(message); mQueue.push_back(fence); mCondition.notify_one(); mFencesQueued++; ATRACE_INT(mName, int32_t(mQueue.size())); } private: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" void loop() { while (true) { threadLoop(); } } #pragma clang diagnostic pop void threadLoop() { sp fence; uint32_t fenceNum; { std::unique_lock lock(mMutex); while (mQueue.empty()) { mCondition.wait(lock); } fence = mQueue[0]; fenceNum = mFencesSignaled; } { char message[64]; snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum); ATRACE_NAME(message); status_t result = fence->waitForever(message); if (result != OK) { ALOGE("Error waiting for fence: %d", result); } } { std::lock_guard lock(mMutex); mQueue.pop_front(); mFencesSignaled++; ATRACE_INT(mName, int32_t(mQueue.size())); } } const char* mName; uint32_t mFencesQueued; uint32_t mFencesSignaled; std::deque> mQueue; std::condition_variable mCondition; std::mutex mMutex; }; int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); uint32_t reqWidth; uint32_t reqHeight; PixelFormat reqFormat; uint64_t reqUsage; bool enableFrameTimestamps; { Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } reqWidth = mReqWidth ? mReqWidth : mUserWidth; reqHeight = mReqHeight ? mReqHeight : mUserHeight; reqFormat = mReqFormat; reqUsage = mReqUsage; enableFrameTimestamps = mEnableFrameTimestamps; if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { sp& gbuf(mSlots[mSharedBufferSlot].buffer); if (gbuf != nullptr) { *buffer = gbuf.get(); *fenceFd = -1; return OK; } } } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer int buf = -1; sp fence; nsecs_t startTime = systemTime(); FrameEventHistoryDelta frameTimestamps; status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, reqFormat, reqUsage, &mBufferAge, enableFrameTimestamps ? &frameTimestamps : nullptr); mLastDequeueDuration = systemTime() - startTime; if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer" "(%d, %d, %d, %#" PRIx64 ") failed: %d", reqWidth, reqHeight, reqFormat, reqUsage, result); return result; } if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf); android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging return FAILED_TRANSACTION; } Mutex::Autolock lock(mMutex); // Write this while holding the mutex mLastDequeueStartTime = startTime; sp& gbuf(mSlots[buf].buffer); // this should never happen ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { static FenceMonitor hwcReleaseThread("HWC release"); hwcReleaseThread.queueFence(fence); } if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); } if (enableFrameTimestamps) { mFrameEventHistory->applyDelta(frameTimestamps); } if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) { if (mReportRemovedBuffers && (gbuf != nullptr)) { mRemovedBuffers.push_back(gbuf); } result = mGraphicBufferProducer->requestBuffer(buf, &gbuf); if (result != NO_ERROR) { ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); mGraphicBufferProducer->cancelBuffer(buf, fence); return result; } } if (fence->isValid()) { *fenceFd = fence->dup(); if (*fenceFd == -1) { ALOGE("dequeueBuffer: error duping fence: %d", errno); // dup() should never fail; something is badly wrong. Soldier on // and hope for the best; the worst that should happen is some // visible corruption that lasts until the next frame. } } else { *fenceFd = -1; } *buffer = gbuf.get(); if (mSharedBufferMode && mAutoRefresh) { mSharedBufferSlot = buf; mSharedBufferHasBeenQueued = false; } else if (mSharedBufferSlot == buf) { mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; mSharedBufferHasBeenQueued = false; } return OK; } int Surface::cancelBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); ALOGV("Surface::cancelBuffer"); Mutex::Autolock lock(mMutex); int i = getSlotFromBufferLocked(buffer); if (i < 0) { if (fenceFd >= 0) { close(fenceFd); } return i; } if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { if (fenceFd >= 0) { close(fenceFd); } return OK; } sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); mGraphicBufferProducer->cancelBuffer(i, fence); if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { mSharedBufferHasBeenQueued = true; } return OK; } int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } } ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); return BAD_VALUE; } int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__((unused))) { ALOGV("Surface::lockBuffer"); Mutex::Autolock lock(mMutex); return OK; } int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); ALOGV("Surface::queueBuffer"); Mutex::Autolock lock(mMutex); int64_t timestamp; bool isAutoTimestamp = false; if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { timestamp = systemTime(SYSTEM_TIME_MONOTONIC); isAutoTimestamp = true; ALOGV("Surface::queueBuffer making up timestamp: %.2f ms", timestamp / 1000000.0); } else { timestamp = mTimestamp; } int i = getSlotFromBufferLocked(buffer); if (i < 0) { if (fenceFd >= 0) { close(fenceFd); } return i; } if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { if (fenceFd >= 0) { close(fenceFd); } return OK; } // Make sure the crop rectangle is entirely inside the buffer. Rect crop(Rect::EMPTY_RECT); mCrop.intersect(Rect(buffer->width, buffer->height), &crop); sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, mEnableFrameTimestamps); // we should send HDR metadata as needed if this becomes a bottleneck input.setHdrMetadata(mHdrMetadata); if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) { input.setSurfaceDamage(Region::INVALID_REGION); } else { // Here we do two things: // 1) The surface damage was specified using the OpenGL ES convention of // the origin being in the bottom-left corner. Here we flip to the // convention that the rest of the system uses (top-left corner) by // subtracting all top/bottom coordinates from the buffer height. // 2) If the buffer is coming in rotated (for example, because the EGL // implementation is reacting to the transform hint coming back from // SurfaceFlinger), the surface damage needs to be rotated the // opposite direction, since it was generated assuming an unrotated // buffer (the app doesn't know that the EGL implementation is // reacting to the transform hint behind its back). The // transformations in the switch statement below apply those // complementary rotations (e.g., if 90 degrees, rotate 270 degrees). int width = buffer->width; int height = buffer->height; bool rotated90 = (mTransform ^ mStickyTransform) & NATIVE_WINDOW_TRANSFORM_ROT_90; if (rotated90) { std::swap(width, height); } Region flippedRegion; for (auto rect : mDirtyRegion) { int left = rect.left; int right = rect.right; int top = height - rect.bottom; // Flip from OpenGL convention int bottom = height - rect.top; // Flip from OpenGL convention switch (mTransform ^ mStickyTransform) { case NATIVE_WINDOW_TRANSFORM_ROT_90: { // Rotate 270 degrees Rect flippedRect{top, width - right, bottom, width - left}; flippedRegion.orSelf(flippedRect); break; } case NATIVE_WINDOW_TRANSFORM_ROT_180: { // Rotate 180 degrees Rect flippedRect{width - right, height - bottom, width - left, height - top}; flippedRegion.orSelf(flippedRect); break; } case NATIVE_WINDOW_TRANSFORM_ROT_270: { // Rotate 90 degrees Rect flippedRect{height - bottom, left, height - top, right}; flippedRegion.orSelf(flippedRect); break; } default: { Rect flippedRect{left, top, right, bottom}; flippedRegion.orSelf(flippedRect); break; } } } input.setSurfaceDamage(flippedRegion); } nsecs_t now = systemTime(); status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); mLastQueueDuration = systemTime() - now; if (err != OK) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); } if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); // Update timestamps with the local acquire fence. // The consumer doesn't send it back to prevent us from having two // file descriptors of the same fence. mFrameEventHistory->updateAcquireFence(mNextFrameNumber, std::make_shared(fence)); // Cache timestamps of signaled fences so we can close their file // descriptors. mFrameEventHistory->updateSignalTimes(); } mLastFrameNumber = mNextFrameNumber; mDefaultWidth = output.width; mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; // Ignore transform hint if sticky transform is set or transform to display inverse flag is // set. if (mStickyTransform == 0 && !transformToDisplayInverse()) { mTransformHint = output.transformHint; } mConsumerRunningBehind = (output.numPendingBuffers >= 2); if (!mConnectedToCpu) { // Clear surface damage back to full-buffer mDirtyRegion = Region::INVALID_REGION; } if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { mSharedBufferHasBeenQueued = true; } mQueueBufferCondition.broadcast(); if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { static FenceMonitor gpuCompletionThread("GPU completion"); gpuCompletionThread.queueFence(fence); } return err; } void Surface::querySupportedTimestampsLocked() const { // mMutex must be locked when calling this method. if (mQueriedSupportedTimestamps) { return; } mQueriedSupportedTimestamps = true; std::vector supportedFrameTimestamps; status_t err = composerService()->getSupportedFrameTimestamps( &supportedFrameTimestamps); if (err != NO_ERROR) { return; } for (auto sft : supportedFrameTimestamps) { if (sft == FrameEvent::DISPLAY_PRESENT) { mFrameTimestampsSupportsPresent = true; } } } int Surface::query(int what, int* value) const { ATRACE_CALL(); ALOGV("Surface::query"); { // scope for the lock Mutex::Autolock lock(mMutex); switch (what) { case NATIVE_WINDOW_FORMAT: if (mReqFormat) { *value = static_cast(mReqFormat); return NO_ERROR; } break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { if (composerService()->authenticateSurfaceTexture( mGraphicBufferProducer)) { *value = 1; } else { *value = 0; } return NO_ERROR; } case NATIVE_WINDOW_CONCRETE_TYPE: *value = NATIVE_WINDOW_SURFACE; return NO_ERROR; case NATIVE_WINDOW_DEFAULT_WIDTH: *value = static_cast( mUserWidth ? mUserWidth : mDefaultWidth); return NO_ERROR; case NATIVE_WINDOW_DEFAULT_HEIGHT: *value = static_cast( mUserHeight ? mUserHeight : mDefaultHeight); return NO_ERROR; case NATIVE_WINDOW_TRANSFORM_HINT: *value = static_cast(mTransformHint); return NO_ERROR; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: { status_t err = NO_ERROR; if (!mConsumerRunningBehind) { *value = 0; } else { err = mGraphicBufferProducer->query(what, value); if (err == NO_ERROR) { mConsumerRunningBehind = *value; } } return err; } case NATIVE_WINDOW_BUFFER_AGE: { if (mBufferAge > INT32_MAX) { *value = 0; } else { *value = static_cast(mBufferAge); } return NO_ERROR; } case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: { int64_t durationUs = mLastDequeueDuration / 1000; *value = durationUs > std::numeric_limits::max() ? std::numeric_limits::max() : static_cast(durationUs); return NO_ERROR; } case NATIVE_WINDOW_LAST_QUEUE_DURATION: { int64_t durationUs = mLastQueueDuration / 1000; *value = durationUs > std::numeric_limits::max() ? std::numeric_limits::max() : static_cast(durationUs); return NO_ERROR; } case NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT: { querySupportedTimestampsLocked(); *value = mFrameTimestampsSupportsPresent ? 1 : 0; return NO_ERROR; } case NATIVE_WINDOW_IS_VALID: { *value = mGraphicBufferProducer != nullptr ? 1 : 0; return NO_ERROR; } case NATIVE_WINDOW_DATASPACE: { *value = static_cast(mDataSpace); return NO_ERROR; } } } return mGraphicBufferProducer->query(what, value); } int Surface::perform(int operation, va_list args) { int res = NO_ERROR; switch (operation) { case NATIVE_WINDOW_CONNECT: // deprecated. must return NO_ERROR. break; case NATIVE_WINDOW_DISCONNECT: // deprecated. must return NO_ERROR. break; case NATIVE_WINDOW_SET_USAGE: res = dispatchSetUsage(args); break; case NATIVE_WINDOW_SET_CROP: res = dispatchSetCrop(args); break; case NATIVE_WINDOW_SET_BUFFER_COUNT: res = dispatchSetBufferCount(args); break; case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: res = dispatchSetBuffersGeometry(args); break; case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: res = dispatchSetBuffersTransform(args); break; case NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM: res = dispatchSetBuffersStickyTransform(args); break; case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: res = dispatchSetBuffersTimestamp(args); break; case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: res = dispatchSetBuffersDimensions(args); break; case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS: res = dispatchSetBuffersUserDimensions(args); break; case NATIVE_WINDOW_SET_BUFFERS_FORMAT: res = dispatchSetBuffersFormat(args); break; case NATIVE_WINDOW_LOCK: res = dispatchLock(args); break; case NATIVE_WINDOW_UNLOCK_AND_POST: res = dispatchUnlockAndPost(args); break; case NATIVE_WINDOW_SET_SCALING_MODE: res = dispatchSetScalingMode(args); break; case NATIVE_WINDOW_API_CONNECT: res = dispatchConnect(args); break; case NATIVE_WINDOW_API_DISCONNECT: res = dispatchDisconnect(args); break; case NATIVE_WINDOW_SET_SIDEBAND_STREAM: res = dispatchSetSidebandStream(args); break; case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: res = dispatchSetBuffersDataSpace(args); break; case NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA: res = dispatchSetBuffersSmpte2086Metadata(args); break; case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA: res = dispatchSetBuffersCta8613Metadata(args); break; case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA: res = dispatchSetBuffersHdr10PlusMetadata(args); break; case NATIVE_WINDOW_SET_SURFACE_DAMAGE: res = dispatchSetSurfaceDamage(args); break; case NATIVE_WINDOW_SET_SHARED_BUFFER_MODE: res = dispatchSetSharedBufferMode(args); break; case NATIVE_WINDOW_SET_AUTO_REFRESH: res = dispatchSetAutoRefresh(args); break; case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION: res = dispatchGetDisplayRefreshCycleDuration(args); break; case NATIVE_WINDOW_GET_NEXT_FRAME_ID: res = dispatchGetNextFrameId(args); break; case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS: res = dispatchEnableFrameTimestamps(args); break; case NATIVE_WINDOW_GET_COMPOSITOR_TIMING: res = dispatchGetCompositorTiming(args); break; case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS: res = dispatchGetFrameTimestamps(args); break; case NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT: res = dispatchGetWideColorSupport(args); break; case NATIVE_WINDOW_GET_HDR_SUPPORT: res = dispatchGetHdrSupport(args); break; case NATIVE_WINDOW_SET_USAGE64: res = dispatchSetUsage64(args); break; case NATIVE_WINDOW_GET_CONSUMER_USAGE64: res = dispatchGetConsumerUsage64(args); break; default: res = NAME_NOT_FOUND; break; } return res; } int Surface::dispatchConnect(va_list args) { int api = va_arg(args, int); return connect(api); } int Surface::dispatchDisconnect(va_list args) { int api = va_arg(args, int); return disconnect(api); } int Surface::dispatchSetUsage(va_list args) { uint64_t usage = va_arg(args, uint32_t); return setUsage(usage); } int Surface::dispatchSetUsage64(va_list args) { uint64_t usage = va_arg(args, uint64_t); return setUsage(usage); } int Surface::dispatchSetCrop(va_list args) { android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); return setCrop(reinterpret_cast(rect)); } int Surface::dispatchSetBufferCount(va_list args) { size_t bufferCount = va_arg(args, size_t); return setBufferCount(static_cast(bufferCount)); } int Surface::dispatchSetBuffersGeometry(va_list args) { uint32_t width = va_arg(args, uint32_t); uint32_t height = va_arg(args, uint32_t); PixelFormat format = va_arg(args, PixelFormat); int err = setBuffersDimensions(width, height); if (err != 0) { return err; } return setBuffersFormat(format); } int Surface::dispatchSetBuffersDimensions(va_list args) { uint32_t width = va_arg(args, uint32_t); uint32_t height = va_arg(args, uint32_t); return setBuffersDimensions(width, height); } int Surface::dispatchSetBuffersUserDimensions(va_list args) { uint32_t width = va_arg(args, uint32_t); uint32_t height = va_arg(args, uint32_t); return setBuffersUserDimensions(width, height); } int Surface::dispatchSetBuffersFormat(va_list args) { PixelFormat format = va_arg(args, PixelFormat); return setBuffersFormat(format); } int Surface::dispatchSetScalingMode(va_list args) { int mode = va_arg(args, int); return setScalingMode(mode); } int Surface::dispatchSetBuffersTransform(va_list args) { uint32_t transform = va_arg(args, uint32_t); return setBuffersTransform(transform); } int Surface::dispatchSetBuffersStickyTransform(va_list args) { uint32_t transform = va_arg(args, uint32_t); return setBuffersStickyTransform(transform); } int Surface::dispatchSetBuffersTimestamp(va_list args) { int64_t timestamp = va_arg(args, int64_t); return setBuffersTimestamp(timestamp); } int Surface::dispatchLock(va_list args) { ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*); ARect* inOutDirtyBounds = va_arg(args, ARect*); return lock(outBuffer, inOutDirtyBounds); } int Surface::dispatchUnlockAndPost(va_list args __attribute__((unused))) { return unlockAndPost(); } int Surface::dispatchSetSidebandStream(va_list args) { native_handle_t* sH = va_arg(args, native_handle_t*); sp sidebandHandle = NativeHandle::create(sH, false); setSidebandStream(sidebandHandle); return OK; } int Surface::dispatchSetBuffersDataSpace(va_list args) { Dataspace dataspace = static_cast(va_arg(args, int)); return setBuffersDataSpace(dataspace); } int Surface::dispatchSetBuffersSmpte2086Metadata(va_list args) { const android_smpte2086_metadata* metadata = va_arg(args, const android_smpte2086_metadata*); return setBuffersSmpte2086Metadata(metadata); } int Surface::dispatchSetBuffersCta8613Metadata(va_list args) { const android_cta861_3_metadata* metadata = va_arg(args, const android_cta861_3_metadata*); return setBuffersCta8613Metadata(metadata); } int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) { const size_t size = va_arg(args, size_t); const uint8_t* metadata = va_arg(args, const uint8_t*); return setBuffersHdr10PlusMetadata(size, metadata); } int Surface::dispatchSetSurfaceDamage(va_list args) { android_native_rect_t* rects = va_arg(args, android_native_rect_t*); size_t numRects = va_arg(args, size_t); setSurfaceDamage(rects, numRects); return NO_ERROR; } int Surface::dispatchSetSharedBufferMode(va_list args) { bool sharedBufferMode = va_arg(args, int); return setSharedBufferMode(sharedBufferMode); } int Surface::dispatchSetAutoRefresh(va_list args) { bool autoRefresh = va_arg(args, int); return setAutoRefresh(autoRefresh); } int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) { nsecs_t* outRefreshDuration = va_arg(args, int64_t*); return getDisplayRefreshCycleDuration(outRefreshDuration); } int Surface::dispatchGetNextFrameId(va_list args) { uint64_t* nextFrameId = va_arg(args, uint64_t*); *nextFrameId = getNextFrameNumber(); return NO_ERROR; } int Surface::dispatchEnableFrameTimestamps(va_list args) { bool enable = va_arg(args, int); enableFrameTimestamps(enable); return NO_ERROR; } int Surface::dispatchGetCompositorTiming(va_list args) { nsecs_t* compositeDeadline = va_arg(args, int64_t*); nsecs_t* compositeInterval = va_arg(args, int64_t*); nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*); return getCompositorTiming(compositeDeadline, compositeInterval, compositeToPresentLatency); } int Surface::dispatchGetFrameTimestamps(va_list args) { uint64_t frameId = va_arg(args, uint64_t); nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*); nsecs_t* outAcquireTime = va_arg(args, int64_t*); nsecs_t* outLatchTime = va_arg(args, int64_t*); nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*); nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*); nsecs_t* outGpuCompositionDoneTime = va_arg(args, int64_t*); nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*); nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*); nsecs_t* outReleaseTime = va_arg(args, int64_t*); return getFrameTimestamps(frameId, outRequestedPresentTime, outAcquireTime, outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, outGpuCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime); } int Surface::dispatchGetWideColorSupport(va_list args) { bool* outSupport = va_arg(args, bool*); return getWideColorSupport(outSupport); } int Surface::dispatchGetHdrSupport(va_list args) { bool* outSupport = va_arg(args, bool*); return getHdrSupport(outSupport); } int Surface::dispatchGetConsumerUsage64(va_list args) { uint64_t* usage = va_arg(args, uint64_t*); return getConsumerUsage(usage); } bool Surface::transformToDisplayInverse() { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; } int Surface::connect(int api) { static sp listener = new DummyProducerListener(); return connect(api, listener); } int Surface::connect(int api, const sp& listener) { return connect(api, listener, false); } int Surface::connect( int api, bool reportBufferRemoval, const sp& sListener) { if (sListener != nullptr) { mListenerProxy = new ProducerListenerProxy(this, sListener); } return connect(api, mListenerProxy, reportBufferRemoval); } int Surface::connect( int api, const sp& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; mReportRemovedBuffers = reportBufferRemoval; int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output); if (err == NO_ERROR) { mDefaultWidth = output.width; mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; // Ignore transform hint if sticky transform is set or transform to display inverse flag is // set. Transform hint should be ignored if the client is expected to always submit buffers // in the same orientation. if (mStickyTransform == 0 && !transformToDisplayInverse()) { mTransformHint = output.transformHint; } mConsumerRunningBehind = (output.numPendingBuffers >= 2); } if (!err && api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = true; // Clear the dirty region in case we're switching from a non-CPU API mDirtyRegion.clear(); } else if (!err) { // Initialize the dirty region for tracking surface damage mDirtyRegion = Region::INVALID_REGION; } return err; } int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { ATRACE_CALL(); ALOGV("Surface::disconnect"); Mutex::Autolock lock(mMutex); mRemovedBuffers.clear(); mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; mSharedBufferHasBeenQueued = false; freeAllBuffers(); int err = mGraphicBufferProducer->disconnect(api, mode); if (!err) { mReqFormat = 0; mReqWidth = 0; mReqHeight = 0; mReqUsage = 0; mCrop.clear(); mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mTransform = 0; mStickyTransform = 0; if (api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = false; } } return err; } int Surface::detachNextBuffer(sp* outBuffer, sp* outFence) { ATRACE_CALL(); ALOGV("Surface::detachNextBuffer"); if (outBuffer == nullptr || outFence == nullptr) { return BAD_VALUE; } Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } sp buffer(nullptr); sp fence(nullptr); status_t result = mGraphicBufferProducer->detachNextBuffer( &buffer, &fence); if (result != NO_ERROR) { return result; } *outBuffer = buffer; if (fence != nullptr && fence->isValid()) { *outFence = fence; } else { *outFence = Fence::NO_FENCE; } for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != nullptr && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { mRemovedBuffers.push_back(mSlots[i].buffer); } mSlots[i].buffer = nullptr; } } return NO_ERROR; } int Surface::attachBuffer(ANativeWindowBuffer* buffer) { ATRACE_CALL(); ALOGV("Surface::attachBuffer"); Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } sp graphicBuffer(static_cast(buffer)); uint32_t priorGeneration = graphicBuffer->mGenerationNumber; graphicBuffer->mGenerationNumber = mGenerationNumber; int32_t attachedSlot = -1; status_t result = mGraphicBufferProducer->attachBuffer(&attachedSlot, graphicBuffer); if (result != NO_ERROR) { ALOGE("attachBuffer: IGraphicBufferProducer call failed (%d)", result); graphicBuffer->mGenerationNumber = priorGeneration; return result; } if (mReportRemovedBuffers && (mSlots[attachedSlot].buffer != nullptr)) { mRemovedBuffers.push_back(mSlots[attachedSlot].buffer); } mSlots[attachedSlot].buffer = graphicBuffer; return NO_ERROR; } int Surface::setUsage(uint64_t reqUsage) { ALOGV("Surface::setUsage"); Mutex::Autolock lock(mMutex); if (reqUsage != mReqUsage) { mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; } mReqUsage = reqUsage; return OK; } int Surface::setCrop(Rect const* rect) { ATRACE_CALL(); Rect realRect(Rect::EMPTY_RECT); if (rect == nullptr || rect->isEmpty()) { realRect.clear(); } else { realRect = *rect; } ALOGV("Surface::setCrop rect=[%d %d %d %d]", realRect.left, realRect.top, realRect.right, realRect.bottom); Mutex::Autolock lock(mMutex); mCrop = realRect; return NO_ERROR; } int Surface::setBufferCount(int bufferCount) { ATRACE_CALL(); ALOGV("Surface::setBufferCount"); Mutex::Autolock lock(mMutex); status_t err = NO_ERROR; if (bufferCount == 0) { err = mGraphicBufferProducer->setMaxDequeuedBufferCount(1); } else { int minUndequeuedBuffers = 0; err = mGraphicBufferProducer->query( NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers); if (err == NO_ERROR) { err = mGraphicBufferProducer->setMaxDequeuedBufferCount( bufferCount - minUndequeuedBuffers); } } ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s", bufferCount, strerror(-err)); return err; } int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) { ATRACE_CALL(); ALOGV("Surface::setMaxDequeuedBufferCount"); Mutex::Autolock lock(mMutex); status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount( maxDequeuedBuffers); ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) " "returned %s", maxDequeuedBuffers, strerror(-err)); return err; } int Surface::setAsyncMode(bool async) { ATRACE_CALL(); ALOGV("Surface::setAsyncMode"); Mutex::Autolock lock(mMutex); status_t err = mGraphicBufferProducer->setAsyncMode(async); ALOGE_IF(err, "IGraphicBufferProducer::setAsyncMode(%d) returned %s", async, strerror(-err)); return err; } int Surface::setSharedBufferMode(bool sharedBufferMode) { ATRACE_CALL(); ALOGV("Surface::setSharedBufferMode (%d)", sharedBufferMode); Mutex::Autolock lock(mMutex); status_t err = mGraphicBufferProducer->setSharedBufferMode( sharedBufferMode); if (err == NO_ERROR) { mSharedBufferMode = sharedBufferMode; } ALOGE_IF(err, "IGraphicBufferProducer::setSharedBufferMode(%d) returned" "%s", sharedBufferMode, strerror(-err)); return err; } int Surface::setAutoRefresh(bool autoRefresh) { ATRACE_CALL(); ALOGV("Surface::setAutoRefresh (%d)", autoRefresh); Mutex::Autolock lock(mMutex); status_t err = mGraphicBufferProducer->setAutoRefresh(autoRefresh); if (err == NO_ERROR) { mAutoRefresh = autoRefresh; } ALOGE_IF(err, "IGraphicBufferProducer::setAutoRefresh(%d) returned %s", autoRefresh, strerror(-err)); return err; } int Surface::setBuffersDimensions(uint32_t width, uint32_t height) { ATRACE_CALL(); ALOGV("Surface::setBuffersDimensions"); if ((width && !height) || (!width && height)) return BAD_VALUE; Mutex::Autolock lock(mMutex); if (width != mReqWidth || height != mReqHeight) { mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; } mReqWidth = width; mReqHeight = height; return NO_ERROR; } int Surface::setBuffersUserDimensions(uint32_t width, uint32_t height) { ATRACE_CALL(); ALOGV("Surface::setBuffersUserDimensions"); if ((width && !height) || (!width && height)) return BAD_VALUE; Mutex::Autolock lock(mMutex); if (width != mUserWidth || height != mUserHeight) { mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; } mUserWidth = width; mUserHeight = height; return NO_ERROR; } int Surface::setBuffersFormat(PixelFormat format) { ALOGV("Surface::setBuffersFormat"); Mutex::Autolock lock(mMutex); if (format != mReqFormat) { mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; } mReqFormat = format; return NO_ERROR; } int Surface::setScalingMode(int mode) { ATRACE_CALL(); ALOGV("Surface::setScalingMode(%d)", mode); switch (mode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: break; default: ALOGE("unknown scaling mode: %d", mode); return BAD_VALUE; } Mutex::Autolock lock(mMutex); mScalingMode = mode; return NO_ERROR; } int Surface::setBuffersTransform(uint32_t transform) { ATRACE_CALL(); ALOGV("Surface::setBuffersTransform"); Mutex::Autolock lock(mMutex); // Ensure NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY is sticky. If the client sets the flag, do not // override it until the surface is disconnected. This is a temporary workaround for camera // until they switch to using Buffer State Layers. Currently if client sets the buffer transform // it may be overriden by the buffer producer when the producer sets the buffer transform. if (transformToDisplayInverse()) { transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; } mTransform = transform; return NO_ERROR; } int Surface::setBuffersStickyTransform(uint32_t transform) { ATRACE_CALL(); ALOGV("Surface::setBuffersStickyTransform"); Mutex::Autolock lock(mMutex); mStickyTransform = transform; return NO_ERROR; } int Surface::setBuffersTimestamp(int64_t timestamp) { ALOGV("Surface::setBuffersTimestamp"); Mutex::Autolock lock(mMutex); mTimestamp = timestamp; return NO_ERROR; } int Surface::setBuffersDataSpace(Dataspace dataSpace) { ALOGV("Surface::setBuffersDataSpace"); Mutex::Autolock lock(mMutex); mDataSpace = dataSpace; return NO_ERROR; } int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) { ALOGV("Surface::setBuffersSmpte2086Metadata"); Mutex::Autolock lock(mMutex); if (metadata) { mHdrMetadata.smpte2086 = *metadata; mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086; } else { mHdrMetadata.validTypes &= ~HdrMetadata::SMPTE2086; } return NO_ERROR; } int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) { ALOGV("Surface::setBuffersCta8613Metadata"); Mutex::Autolock lock(mMutex); if (metadata) { mHdrMetadata.cta8613 = *metadata; mHdrMetadata.validTypes |= HdrMetadata::CTA861_3; } else { mHdrMetadata.validTypes &= ~HdrMetadata::CTA861_3; } return NO_ERROR; } int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) { ALOGV("Surface::setBuffersBlobMetadata"); Mutex::Autolock lock(mMutex); if (size > 0) { mHdrMetadata.hdr10plus.assign(metadata, metadata + size); mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS; } else { mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS; mHdrMetadata.hdr10plus.clear(); } return NO_ERROR; } Dataspace Surface::getBuffersDataSpace() { ALOGV("Surface::getBuffersDataSpace"); Mutex::Autolock lock(mMutex); return mDataSpace; } void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].buffer = nullptr; } } status_t Surface::getAndFlushBuffersFromSlots(const std::vector& slots, std::vector>* outBuffers) { ALOGV("Surface::getAndFlushBuffersFromSlots"); for (int32_t i : slots) { if (i < 0 || i >= NUM_BUFFER_SLOTS) { ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i); return BAD_VALUE; } } Mutex::Autolock lock(mMutex); for (int32_t i : slots) { if (mSlots[i].buffer == nullptr) { ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i); continue; } outBuffers->push_back(mSlots[i].buffer); mSlots[i].buffer = nullptr; } return OK; } void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) { ATRACE_CALL(); ALOGV("Surface::setSurfaceDamage"); Mutex::Autolock lock(mMutex); if (mConnectedToCpu || numRects == 0) { mDirtyRegion = Region::INVALID_REGION; return; } mDirtyRegion.clear(); for (size_t r = 0; r < numRects; ++r) { // We intentionally flip top and bottom here, since because they're // specified with a bottom-left origin, top > bottom, which fails // validation in the Region class. We will fix this up when we flip to a // top-left origin in queueBuffer. Rect rect(rects[r].left, rects[r].bottom, rects[r].right, rects[r].top); mDirtyRegion.orSelf(rect); } } // ---------------------------------------------------------------------- // the lock/unlock APIs must be used from the same thread static status_t copyBlt( const sp& dst, const sp& src, const Region& reg, int *dstFenceFd) { if (dst->getId() == src->getId()) return OK; // src and dst with, height and format must be identical. no verification // is done here. status_t err; uint8_t* src_bits = nullptr; err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), reinterpret_cast(&src_bits)); ALOGE_IF(err, "error locking src buffer %s", strerror(-err)); uint8_t* dst_bits = nullptr; err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), reinterpret_cast(&dst_bits), *dstFenceFd); ALOGE_IF(err, "error locking dst buffer %s", strerror(-err)); *dstFenceFd = -1; Region::const_iterator head(reg.begin()); Region::const_iterator tail(reg.end()); if (head != tail && src_bits && dst_bits) { const size_t bpp = bytesPerPixel(src->format); const size_t dbpr = static_cast(dst->stride) * bpp; const size_t sbpr = static_cast(src->stride) * bpp; while (head != tail) { const Rect& r(*head++); int32_t h = r.height(); if (h <= 0) continue; size_t size = static_cast(r.width()) * bpp; uint8_t const * s = src_bits + static_cast(r.left + src->stride * r.top) * bpp; uint8_t * d = dst_bits + static_cast(r.left + dst->stride * r.top) * bpp; if (dbpr==sbpr && size==sbpr) { size *= static_cast(h); h = 1; } do { memcpy(d, s, size); d += dbpr; s += sbpr; } while (--h > 0); } } if (src_bits) src->unlock(); if (dst_bits) dst->unlockAsync(dstFenceFd); return err; } // ---------------------------------------------------------------------------- status_t Surface::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { if (mLockedBuffer != nullptr) { ALOGE("Surface::lock failed, already locked"); return INVALID_OPERATION; } if (!mConnectedToCpu) { int err = Surface::connect(NATIVE_WINDOW_API_CPU); if (err) { return err; } // we're intending to do software rendering from this point setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); } ANativeWindowBuffer* out; int fenceFd = -1; status_t err = dequeueBuffer(&out, &fenceFd); ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { sp backBuffer(GraphicBuffer::getSelf(out)); const Rect bounds(backBuffer->width, backBuffer->height); Region newDirtyRegion; if (inOutDirtyBounds) { newDirtyRegion.set(static_cast(*inOutDirtyBounds)); newDirtyRegion.andSelf(bounds); } else { newDirtyRegion.set(bounds); } // figure out if we can copy the frontbuffer back const sp& frontBuffer(mPostedBuffer); const bool canCopyBack = (frontBuffer != nullptr && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); if (canCopyBack) { // copy the area that is invalid and not repainted this round const Region copyback(mDirtyRegion.subtract(newDirtyRegion)); if (!copyback.isEmpty()) { copyBlt(backBuffer, frontBuffer, copyback, &fenceFd); } } else { // if we can't copy-back anything, modify the user's dirty // region to make sure they redraw the whole buffer newDirtyRegion.set(bounds); mDirtyRegion.clear(); Mutex::Autolock lock(mMutex); for (size_t i=0 ; i= 0) { Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion); mDirtyRegion.subtract(dirtyRegion); dirtyRegion = newDirtyRegion; } } mDirtyRegion.orSelf(newDirtyRegion); if (inOutDirtyBounds) { *inOutDirtyBounds = newDirtyRegion.getBounds(); } void* vaddr; status_t res = backBuffer->lockAsync( GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd); ALOGW_IF(res, "failed locking buffer (handle = %p)", backBuffer->handle); if (res != 0) { err = INVALID_OPERATION; } else { mLockedBuffer = backBuffer; outBuffer->width = backBuffer->width; outBuffer->height = backBuffer->height; outBuffer->stride = backBuffer->stride; outBuffer->format = backBuffer->format; outBuffer->bits = vaddr; } } return err; } status_t Surface::unlockAndPost() { if (mLockedBuffer == nullptr) { ALOGE("Surface::unlockAndPost failed, no locked buffer"); return INVALID_OPERATION; } int fd = -1; status_t err = mLockedBuffer->unlockAsync(&fd); ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); err = queueBuffer(mLockedBuffer.get(), fd); ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)", mLockedBuffer->handle, strerror(-err)); mPostedBuffer = mLockedBuffer; mLockedBuffer = nullptr; return err; } bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) { Mutex::Autolock lock(mMutex); if (mNextFrameNumber > lastFrame) { return true; } return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK; } status_t Surface::getUniqueId(uint64_t* outId) const { Mutex::Autolock lock(mMutex); return mGraphicBufferProducer->getUniqueId(outId); } int Surface::getConsumerUsage(uint64_t* outUsage) const { Mutex::Autolock lock(mMutex); return mGraphicBufferProducer->getConsumerUsage(outUsage); } nsecs_t Surface::getLastDequeueStartTime() const { Mutex::Autolock lock(mMutex); return mLastDequeueStartTime; } status_t Surface::getAndFlushRemovedBuffers(std::vector>* out) { if (out == nullptr) { ALOGE("%s: out must not be null!", __FUNCTION__); return BAD_VALUE; } Mutex::Autolock lock(mMutex); *out = mRemovedBuffers; mRemovedBuffers.clear(); return OK; } status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp buffer, Dataspace dataspace) { if (buffer == nullptr) { return BAD_VALUE; } int err = static_cast(surface)->perform(surface, NATIVE_WINDOW_API_CONNECT, NATIVE_WINDOW_API_CPU); if (err != OK) { return err; } ui::Dataspace tmpDataspace = surface->getBuffersDataSpace(); err = surface->setBuffersDataSpace(dataspace); if (err != OK) { return err; } err = surface->attachBuffer(buffer->getNativeBuffer()); if (err != OK) { return err; } err = static_cast(surface)->queueBuffer(surface, buffer->getNativeBuffer(), -1); if (err != OK) { return err; } err = surface->setBuffersDataSpace(tmpDataspace); if (err != OK) { return err; } err = surface->disconnect(NATIVE_WINDOW_API_CPU); return err; } void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector& slots) { ATRACE_CALL(); sp parent = mParent.promote(); if (parent == nullptr) { return; } std::vector> discardedBufs; status_t res = parent->getAndFlushBuffersFromSlots(slots, &discardedBufs); if (res != OK) { ALOGE("%s: Failed to get buffers from slots: %s(%d)", __FUNCTION__, strerror(-res), res); return; } mSurfaceListener->onBuffersDiscarded(discardedBufs); } }; // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/gui/SurfaceComposerClient.cpp������������������������������������������������������������������0100644 0000000 0000000 00000160165 13756501735 016312� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ #define LOG_TAG "SurfaceComposerClient" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_INPUT #include

* This function requires the ___ permission. * * @param callingUid The original application that requested the report. This function * returns via the callback whether the application should be trusted. It is up * to the caller to actually implement the restriction to take or not take * the incident or bug report. * @param receiverClass The class that will be the eventual broacast receiver for the * INCIDENT_REPORT_READY message. Used as part of the id in incidentd. * @param reportId The incident report ID. Incidentd should call with this parameter, but * everyone else should pass null or empty string. * @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise * a dialog will be shown as a notification. * @param callback Interface to receive results. The results may not come back for * a long (user's choice) time, or ever (if they never respond to the notification). * Authorization requests are not persisted across reboot. It is up to the calling * service to request another authorization after reboot if they still would like * to send their report. */ oneway void authorizeReport(int callingUid, String callingPackage, String receiverClass, String reportId, int flags, IIncidentAuthListener callback); /** * Cancel an authorization. */ oneway void cancelAuthorization(IIncidentAuthListener callback); /** * Send the report ready broadcast on behalf of incidentd. */ oneway void sendReportReadyBroadcast(String pkg, String cls); /** * Return the list of pending approvals. */ List getPendingReports(); /** * The user has authorized the report to be shared. * * @param uri the report. */ void approveReport(String uri); /** * The user has denied the report from being shared. * * @param uri the report. */ void denyReport(String uri); /** * List the incident reports for the given ComponentName. The receiver * must be for a package inside the caller. */ List getIncidentReportList(String pkg, String cls); /** * Get the IncidentReport object. */ IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id); /** * Signal that the client is done with this incident report and it can be deleted. */ void deleteIncidentReports(String pkg, String cls, String id); /** * Signal that the client is done with all incident reports from this package. * Especially useful for testing. */ void deleteAllIncidentReports(String pkg); } libs/incidentcompanion/binder/android/os/IncidentManager.aidl0100644 0000000 0000000 00000001330 13756501735 023437 0ustar000000000 0000000 /* * 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 android.os; parcelable IncidentManager.IncidentReport cpp_header "android/os/IncidentManager.h"; libs/incidentcompanion/include/0040755 0000000 0000000 00000000000 13756501735 015701 5ustar000000000 0000000 libs/incidentcompanion/include/android/0040755 0000000 0000000 00000000000 13756501735 017321 5ustar000000000 0000000 libs/incidentcompanion/include/android/os/0040755 0000000 0000000 00000000000 13756501735 017742 5ustar000000000 0000000 libs/incidentcompanion/include/android/os/IncidentManager.h0100644 0000000 0000000 00000004344 13756501735 023145 0ustar000000000 0000000 /** * Copyright (c) 2019, 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. */ #pragma once #include #include #include #include #include namespace android { namespace os { class IncidentManager : public virtual RefBase { public: class IncidentReport : public Parcelable { public: IncidentReport(); virtual ~IncidentReport(); virtual status_t writeToParcel(Parcel* out) const; virtual status_t readFromParcel(const Parcel* in); void setTimestampNs(int64_t val) { mTimestampNs = val; } int64_t getTimestampNs() const { return mTimestampNs; } int64_t getTimestampMs() const { return mTimestampNs / 1000000; } void setPrivacyPolicy(int32_t val) { mPrivacyPolicy = val; } // This was accidentally published as a long in the java api. int64_t getPrivacyPolicy() const { return mPrivacyPolicy; } // Dups the fd, so you retain ownership of the original one. If there is a // previously set fd, closes that, since this object owns its own fd. status_t setFileDescriptor(int fd); // Does not dup the fd, so ownership is passed to this object. If there is a // previously set fd, closes that, since this object owns its own fd. void takeFileDescriptor(int fd); // Returns the fd, which you don't own. Call dup if you need a copy. int getFileDescriptor() const { return mFileDescriptor; } private: int64_t mTimestampNs; int32_t mPrivacyPolicy; int mFileDescriptor; }; private: // Not implemented for now. IncidentManager(); virtual ~IncidentManager(); }; } } libs/incidentcompanion/src/0040755 0000000 0000000 00000000000 13756501735 015045 5ustar000000000 0000000 libs/incidentcompanion/src/IncidentManager.cpp0100644 0000000 0000000 00000005767 13756501735 020615 0ustar000000000 0000000 /** * 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. */ #include namespace android { namespace os { // ============================================================ IncidentManager::IncidentReport::IncidentReport() :mTimestampNs(0), mPrivacyPolicy(0), mFileDescriptor(-1) { } IncidentManager::IncidentReport::~IncidentReport() { if (mFileDescriptor >= 0) { close(mFileDescriptor); } } status_t IncidentManager::IncidentReport::writeToParcel(Parcel* out) const { status_t err; err = out->writeInt64(mTimestampNs); if (err != NO_ERROR) { return err; } err = out->writeInt32(mPrivacyPolicy); if (err != NO_ERROR) { return err; } if (mFileDescriptor >= 0) { err = out->writeInt32(1); if (err != NO_ERROR) { return err; } err = out->writeDupParcelFileDescriptor(mFileDescriptor); if (err != NO_ERROR) { return err; } } else { err = out->writeInt32(0); if (err != NO_ERROR) { return err; } } return NO_ERROR; } status_t IncidentManager::IncidentReport::readFromParcel(const Parcel* in) { status_t err; int32_t hasField; err = in->readInt64(&mTimestampNs); if (err != NO_ERROR) { return err; } err = in->readInt32(&mPrivacyPolicy); if (err != NO_ERROR) { return err; } err = in->readInt32(&hasField); if (err != NO_ERROR) { return err; } if (hasField) { int fd = in->readParcelFileDescriptor(); if (fd >= 0) { mFileDescriptor = dup(fd); if (mFileDescriptor < 0) { return -errno; } } } return NO_ERROR; } status_t IncidentManager::IncidentReport::setFileDescriptor(int fd) { if (mFileDescriptor >= 0) { close(mFileDescriptor); } if (fd < 0) { mFileDescriptor = -1; } else { mFileDescriptor = dup(fd); if (mFileDescriptor < 0) { return -errno; } } return NO_ERROR; } void IncidentManager::IncidentReport::takeFileDescriptor(int fd) { if (mFileDescriptor >= 0) { close(mFileDescriptor); } if (fd < 0) { mFileDescriptor = -1; } else { mFileDescriptor = fd; } } // ============================================================ IncidentManager::~IncidentManager() { } } } libs/input/0040755 0000000 0000000 00000000000 13756501735 011714 5ustar000000000 0000000 libs/input/Android.bp0100644 0000000 0000000 00000003464 13756501735 013623 0ustar000000000 0000000 // 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. // libinput is partially built for the host (used by build time keymap validation tool) cc_library { name: "libinput", host_supported: true, cflags: [ "-Wall", "-Wextra", "-Werror", ], srcs: [ "Input.cpp", "InputDevice.cpp", "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", "TouchVideoFrame.cpp", "VirtualKeyMap.cpp", ], clang: true, shared_libs: [ "libbase", "liblog", "libcutils", ], target: { android: { srcs: [ "IInputFlinger.cpp", "InputApplication.cpp", "InputTransport.cpp", "InputWindow.cpp", "ISetInputWindowsListener.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], shared_libs: [ "libutils", "libbinder", "libui" ], sanitize: { misc_undefined: ["integer"], }, }, host: { shared: { enabled: false, }, }, }, } subdirs = ["tests"] libs/input/IInputFlinger.cpp0100644 0000000 0000000 00000007077 13756501735 015147 0ustar000000000 0000000 /* * 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. */ #include #include #include #include #include #include namespace android { class BpInputFlinger : public BpInterface { public: explicit BpInputFlinger(const sp& impl) : BpInterface(impl) { } virtual void setInputWindows(const std::vector& inputInfo, const sp& setInputWindowsListener) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); data.writeUint32(static_cast(inputInfo.size())); for (const auto& info : inputInfo) { info.write(data); } data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener)); remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } virtual void registerInputChannel(const sp& channel) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); channel->write(data); remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); } virtual void unregisterInputChannel(const sp& channel) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); channel->write(data); remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); } }; IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); status_t BnInputFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case SET_INPUT_WINDOWS_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); size_t count = data.readUint32(); if (count > data.dataSize()) { return BAD_VALUE; } std::vector handles; for (size_t i = 0; i < count; i++) { handles.push_back(InputWindowInfo::read(data)); } const sp setInputWindowsListener = ISetInputWindowsListener::asInterface(data.readStrongBinder()); setInputWindows(handles, setInputWindowsListener); break; } case REGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); sp channel = new InputChannel(); channel->read(data); registerInputChannel(channel); break; } case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); sp channel = new InputChannel(); channel->read(data); unregisterInputChannel(channel); break; } default: return BBinder::onTransact(code, data, reply, flags); } return NO_ERROR; } }; libs/input/ISetInputWindowsListener.cpp0100644 0000000 0000000 00000003436 13756501735 017370 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #include namespace android { class BpSetInputWindowsListener : public BpInterface { public: explicit BpSetInputWindowsListener(const sp& impl) : BpInterface(impl) { } virtual ~BpSetInputWindowsListener() = default; virtual void onSetInputWindowsFinished() { Parcel data, reply; data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor()); remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply, IBinder::FLAG_ONEWAY); } }; IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener"); status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case ON_SET_INPUT_WINDOWS_FINISHED: { CHECK_INTERFACE(ISetInputWindowsListener, data, reply); onSetInputWindowsFinished(); return NO_ERROR; } default: { return BBinder::onTransact(code, data, reply, flags); } } } } // namespace android libs/input/Input.cpp0100644 0000000 0000000 00000046130 13756501735 013520 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 #include #include #include #include #ifdef __ANDROID__ #include #endif namespace android { const char* motionClassificationToString(MotionClassification classification) { switch (classification) { case MotionClassification::NONE: return "NONE"; case MotionClassification::AMBIGUOUS_GESTURE: return "AMBIGUOUS_GESTURE"; case MotionClassification::DEEP_PRESS: return "DEEP_PRESS"; } } // --- InputEvent --- void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; mDisplayId = displayId; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; } // --- KeyEvent --- const char* KeyEvent::getLabel(int32_t keyCode) { return getLabelByKeyCode(keyCode); } int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } void KeyEvent::initialize( int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { InputEvent::initialize(deviceId, source, displayId); mAction = action; mFlags = flags; mKeyCode = keyCode; mScanCode = scanCode; mMetaState = metaState; mRepeatCount = repeatCount; mDownTime = downTime; mEventTime = eventTime; } void KeyEvent::initialize(const KeyEvent& from) { InputEvent::initialize(from); mAction = from.mAction; mFlags = from.mFlags; mKeyCode = from.mKeyCode; mScanCode = from.mScanCode; mMetaState = from.mMetaState; mRepeatCount = from.mRepeatCount; mDownTime = from.mDownTime; mEventTime = from.mEventTime; } // --- PointerCoords --- float PointerCoords::getAxisValue(int32_t axis) const { if (axis < 0 || axis > 63 || !BitSet64::hasBit(bits, axis)){ return 0; } return values[BitSet64::getIndexOfBit(bits, axis)]; } status_t PointerCoords::setAxisValue(int32_t axis, float value) { if (axis < 0 || axis > 63) { return NAME_NOT_FOUND; } uint32_t index = BitSet64::getIndexOfBit(bits, axis); if (!BitSet64::hasBit(bits, axis)) { if (value == 0) { return OK; // axes with value 0 do not need to be stored } uint32_t count = BitSet64::count(bits); if (count >= MAX_AXES) { tooManyAxes(axis); return NO_MEMORY; } BitSet64::markBit(bits, axis); for (uint32_t i = count; i > index; i--) { values[i] = values[i - 1]; } } values[index] = value; return OK; } static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { float value = c.getAxisValue(axis); if (value != 0) { c.setAxisValue(axis, value * scaleFactor); } } void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) { // No need to scale pressure or size since they are normalized. // No need to scale orientation since it is meaningless to do so. // If there is a global scale factor, it is included in the windowX/YScale // so we don't need to apply it twice to the X/Y axes. // However we don't want to apply any windowXYScale not included in the global scale // to the TOUCH_MAJOR/MINOR coordinates. scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale); scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor); } void PointerCoords::scale(float globalScaleFactor) { scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); } void PointerCoords::applyOffset(float xOffset, float yOffset) { setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); } #ifdef __ANDROID__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); uint32_t count = BitSet64::count(bits); if (count > MAX_AXES) { return BAD_VALUE; } for (uint32_t i = 0; i < count; i++) { values[i] = parcel->readFloat(); } return OK; } status_t PointerCoords::writeToParcel(Parcel* parcel) const { parcel->writeInt64(bits); uint32_t count = BitSet64::count(bits); for (uint32_t i = 0; i < count; i++) { parcel->writeFloat(values[i]); } return OK; } #endif void PointerCoords::tooManyAxes(int axis) { ALOGW("Could not set value for axis %d because the PointerCoords structure is full and " "cannot contain more than %d axis values.", axis, int(MAX_AXES)); } bool PointerCoords::operator==(const PointerCoords& other) const { if (bits != other.bits) { return false; } uint32_t count = BitSet64::count(bits); for (uint32_t i = 0; i < count; i++) { if (values[i] != other.values[i]) { return false; } } return true; } void PointerCoords::copyFrom(const PointerCoords& other) { bits = other.bits; uint32_t count = BitSet64::count(bits); for (uint32_t i = 0; i < count; i++) { values[i] = other.values[i]; } } // --- PointerProperties --- bool PointerProperties::operator==(const PointerProperties& other) const { return id == other.id && toolType == other.toolType; } void PointerProperties::copyFrom(const PointerProperties& other) { id = other.id; toolType = other.toolType; } // --- MotionEvent --- void MotionEvent::initialize( int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; mFlags = flags; mEdgeFlags = edgeFlags; mMetaState = metaState; mButtonState = buttonState; mClassification = classification; mXOffset = xOffset; mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); mSampleEventTimes.clear(); mSamplePointerCoords.clear(); addSample(eventTime, pointerCoords); } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; mEdgeFlags = other->mEdgeFlags; mMetaState = other->mMetaState; mButtonState = other->mButtonState; mClassification = other->mClassification; mXOffset = other->mXOffset; mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; if (keepHistory) { mSampleEventTimes = other->mSampleEventTimes; mSamplePointerCoords = other->mSamplePointerCoords; } else { mSampleEventTimes.clear(); mSampleEventTimes.push(other->getEventTime()); mSamplePointerCoords.clear(); size_t pointerCount = other->getPointerCount(); size_t historySize = other->getHistorySize(); mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() + (historySize * pointerCount), pointerCount); } } void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { mSampleEventTimes.push(eventTime); mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { return getRawPointerCoords(pointerIndex)->getAxisValue(axis); } float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: return value + mXOffset; case AMOTION_EVENT_AXIS_Y: return value + mYOffset; } return value; } const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( size_t pointerIndex, size_t historicalIndex) const { return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; } float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: return value + mXOffset; case AMOTION_EVENT_AXIS_Y: return value + mYOffset; } return value; } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { size_t pointerCount = mPointerProperties.size(); for (size_t i = 0; i < pointerCount; i++) { if (mPointerProperties.itemAt(i).id == pointerId) { return i; } } return -1; } void MotionEvent::offsetLocation(float xOffset, float yOffset) { mXOffset += xOffset; mYOffset += yOffset; } void MotionEvent::scale(float globalScaleFactor) { mXOffset *= globalScaleFactor; mYOffset *= globalScaleFactor; mXPrecision *= globalScaleFactor; mYPrecision *= globalScaleFactor; size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor); } } static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) { // Apply perspective transform like Skia. float newX = matrix[0] * x + matrix[1] * y + matrix[2]; float newY = matrix[3] * x + matrix[4] * y + matrix[5]; float newZ = matrix[6] * x + matrix[7] * y + matrix[8]; if (newZ) { newZ = 1.0f / newZ; } *outX = newX * newZ; *outY = newY * newZ; } static float transformAngle(const float matrix[9], float angleRadians, float originX, float originY) { // Construct and transform a vector oriented at the specified clockwise angle from vertical. // Coordinate system: down is increasing Y, right is increasing X. float x = sinf(angleRadians); float y = -cosf(angleRadians); transformPoint(matrix, x, y, &x, &y); x -= originX; y -= originY; // Derive the transformed vector's clockwise angle from vertical. float result = atan2f(x, -y); if (result < - M_PI_2) { result += M_PI; } else if (result > M_PI_2) { result -= M_PI; } return result; } void MotionEvent::transform(const float matrix[9]) { // The tricky part of this implementation is to preserve the value of // rawX and rawY. So we apply the transformation to the first point // then derive an appropriate new X/Y offset that will preserve rawX // and rawY for that point. float oldXOffset = mXOffset; float oldYOffset = mYOffset; float newX, newY; float rawX = getRawX(0); float rawY = getRawY(0); transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY); mXOffset = newX - rawX; mYOffset = newY - rawY; // Determine how the origin is transformed by the matrix so that we // can transform orientation vectors. float originX, originY; transformPoint(matrix, 0, 0, &originX, &originY); // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset; float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset; transformPoint(matrix, x, y, &x, &y); c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset); c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset); float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation, originX, originY)); } } #ifdef __ANDROID__ status_t MotionEvent::readFromParcel(Parcel* parcel) { size_t pointerCount = parcel->readInt32(); size_t sampleCount = parcel->readInt32(); if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0 || sampleCount > MAX_SAMPLES) { return BAD_VALUE; } mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); mDisplayId = parcel->readInt32(); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); mEdgeFlags = parcel->readInt32(); mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); mClassification = static_cast(parcel->readByte()); mXOffset = parcel->readFloat(); mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); mPointerProperties.setCapacity(pointerCount); mSampleEventTimes.clear(); mSampleEventTimes.setCapacity(sampleCount); mSamplePointerCoords.clear(); mSamplePointerCoords.setCapacity(sampleCount * pointerCount); for (size_t i = 0; i < pointerCount; i++) { mPointerProperties.push(); PointerProperties& properties = mPointerProperties.editTop(); properties.id = parcel->readInt32(); properties.toolType = parcel->readInt32(); } while (sampleCount > 0) { sampleCount--; mSampleEventTimes.push(parcel->readInt64()); for (size_t i = 0; i < pointerCount; i++) { mSamplePointerCoords.push(); status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); if (status) { return status; } } } return OK; } status_t MotionEvent::writeToParcel(Parcel* parcel) const { size_t pointerCount = mPointerProperties.size(); size_t sampleCount = mSampleEventTimes.size(); parcel->writeInt32(pointerCount); parcel->writeInt32(sampleCount); parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); parcel->writeInt32(mDisplayId); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); parcel->writeInt32(mEdgeFlags); parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); parcel->writeByte(static_cast(mClassification)); parcel->writeFloat(mXOffset); parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { const PointerProperties& properties = mPointerProperties.itemAt(i); parcel->writeInt32(properties.id); parcel->writeInt32(properties.toolType); } const PointerCoords* pc = mSamplePointerCoords.array(); for (size_t h = 0; h < sampleCount; h++) { parcel->writeInt64(mSampleEventTimes.itemAt(h)); for (size_t i = 0; i < pointerCount; i++) { status_t status = (pc++)->writeToParcel(parcel); if (status) { return status; } } } return OK; } #endif bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { if (source & AINPUT_SOURCE_CLASS_POINTER) { // Specifically excludes HOVER_MOVE and SCROLL. switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_OUTSIDE: return true; } } return false; } const char* MotionEvent::getLabel(int32_t axis) { return getAxisLabel(axis); } int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } // --- PooledInputEventFactory --- PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : mMaxPoolSize(maxPoolSize) { } PooledInputEventFactory::~PooledInputEventFactory() { for (size_t i = 0; i < mKeyEventPool.size(); i++) { delete mKeyEventPool.itemAt(i); } for (size_t i = 0; i < mMotionEventPool.size(); i++) { delete mMotionEventPool.itemAt(i); } } KeyEvent* PooledInputEventFactory::createKeyEvent() { if (!mKeyEventPool.isEmpty()) { KeyEvent* event = mKeyEventPool.top(); mKeyEventPool.pop(); return event; } return new KeyEvent(); } MotionEvent* PooledInputEventFactory::createMotionEvent() { if (!mMotionEventPool.isEmpty()) { MotionEvent* event = mMotionEventPool.top(); mMotionEventPool.pop(); return event; } return new MotionEvent(); } void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: if (mKeyEventPool.size() < mMaxPoolSize) { mKeyEventPool.push(static_cast(event)); return; } break; case AINPUT_EVENT_TYPE_MOTION: if (mMotionEventPool.size() < mMaxPoolSize) { mMotionEventPool.push(static_cast(event)); return; } break; } delete event; } } // namespace android libs/input/InputApplication.cpp0100644 0000000 0000000 00000002533 13756501735 015703 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #define LOG_TAG "InputApplication" #include #include namespace android { // --- InputApplicationHandle --- InputApplicationHandle::InputApplicationHandle() { } InputApplicationHandle::~InputApplicationHandle() { } InputApplicationInfo InputApplicationInfo::read(const Parcel& from) { InputApplicationInfo ret; ret.token = from.readStrongBinder(); ret.name = from.readString8().c_str(); ret.dispatchingTimeout = from.readInt64(); return ret; } status_t InputApplicationInfo::write(Parcel& output) const { output.writeStrongBinder(token); output.writeString8(String8(name.c_str())); output.writeInt64(dispatchingTimeout); return OK; } } // namespace android libs/input/InputDevice.cpp0100644 0000000 0000000 00000015066 13756501735 014644 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #define LOG_TAG "InputDevice" #include #include #include #include #include #include using android::base::StringPrintf; namespace android { static const char* CONFIGURATION_FILE_DIR[] = { "idc/", "keylayout/", "keychars/", }; static const char* CONFIGURATION_FILE_EXTENSION[] = { ".idc", ".kl", ".kcm", }; static bool isValidNameChar(char ch) { return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); } static void appendInputDeviceConfigurationFileRelativePath(std::string& path, const std::string& name, InputDeviceConfigurationFileType type) { path += CONFIGURATION_FILE_DIR[type]; path += name; path += CONFIGURATION_FILE_EXTENSION[type]; } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. std::string versionPath = getInputDeviceConfigurationFilePathByName( StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", deviceIdentifier.vendor, deviceIdentifier.product, deviceIdentifier.version), type); if (!versionPath.empty()) { return versionPath; } } // Try vendor product. std::string productPath = getInputDeviceConfigurationFilePathByName( StringPrintf("Vendor_%04x_Product_%04x", deviceIdentifier.vendor, deviceIdentifier.product), type); if (!productPath.empty()) { return productPath; } } // Try device name. return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); } std::string getInputDeviceConfigurationFilePathByName( const std::string& name, InputDeviceConfigurationFileType type) { // Search system repository. std::string path; // Treblized input device config files will be located /odm/usr or /vendor/usr. const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")}; for (size_t i = 0; i < size(rootsForPartition); i++) { if (rootsForPartition[i] == nullptr) { continue; } path = rootsForPartition[i]; path += "/usr/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system provided input device configuration file: path='%s'", path.c_str()); #endif if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif return path; } } // Search user repository. // TODO Should only look here if not in safe mode. path = ""; char *androidData = getenv("ANDROID_DATA"); if (androidData != nullptr) { path += androidData; } path += "/system/devices/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str()); #endif if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif return path; } // Not found. #if DEBUG_PROBE ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", name.c_str(), type); #endif return ""; } // --- InputDeviceIdentifier std::string InputDeviceIdentifier::getCanonicalName() const { std::string replacedName = name; for (char& ch : replacedName) { if (!isValidNameChar(ch)) { ch = '_'; } } return replacedName; } // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false); } InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber), mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal), mHasMic(other.mHasMic), mSources(other.mSources), mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap), mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad), mMotionRanges(other.mMotionRanges) { } InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; mIdentifier = identifier; mAlias = alias; mIsExternal = isExternal; mHasMic = hasMic; mSources = 0; mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; mHasVibrator = false; mHasButtonUnderPad = false; mMotionRanges.clear(); } const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( int32_t axis, uint32_t source) const { size_t numRanges = mMotionRanges.size(); for (size_t i = 0; i < numRanges; i++) { const MotionRange& range = mMotionRanges[i]; if (range.axis == axis && range.source == source) { return ⦥ } } return nullptr; } void InputDeviceInfo::addSource(uint32_t source) { mSources |= source; } void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, float flat, float fuzz, float resolution) { MotionRange range = { axis, source, min, max, flat, fuzz, resolution }; mMotionRanges.push_back(range); } void InputDeviceInfo::addMotionRange(const MotionRange& range) { mMotionRanges.push_back(range); } } // namespace android libs/input/InputTransport.cpp0100644 0000000 0000000 00000123610 13756501735 015434 0ustar000000000 0000000 // // Copyright 2010 The Android Open Source Project // // Provides a shared memory transport for input events. // #define LOG_TAG "InputTransport" //#define LOG_NDEBUG 0 // Log debug messages about channel messages (send message, receive message) #define DEBUG_CHANNEL_MESSAGES 0 // Log debug messages whenever InputChannel objects are created/destroyed #define DEBUG_CHANNEL_LIFECYCLE 0 // Log debug messages about transport actions #define DEBUG_TRANSPORT_ACTIONS 0 // Log debug messages about touch event resampling #define DEBUG_RESAMPLING 0 #include #include #include #include #include #include #include #include #include #include #include #include #include using android::base::StringPrintf; namespace android { // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. It just needs to be big enough to hold // a few dozen large multi-finger motion events in the case where an application gets // behind processing touches. static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; // Nanoseconds per milliseconds. static const nsecs_t NANOS_PER_MS = 1000000; // Latency added during resampling. A few milliseconds doesn't hurt much but // reduces the impact of mispredicted touch positions. static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS; // Minimum time difference between consecutive samples before attempting to resample. static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; // Maximum time difference between consecutive samples before attempting to resample // by extrapolation. static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // Maximum time to predict forward from the last known state, to avoid predicting too // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; /** * System property for enabling / disabling touch resampling. * Resampling extrapolates / interpolates the reported touch event coordinates to better * align them to the VSYNC signal, thus resulting in smoother scrolling performance. * Resampling is not needed (and should be disabled) on hardware that already * has touch events triggered by VSYNC. * Set to "1" to enable resampling (default). * Set to "0" to disable resampling. * Resampling is enabled by default. */ static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; template inline static T min(const T& a, const T& b) { return a < b ? a : b; } inline static float lerp(float a, float b, float alpha) { return a + alpha * (b - a); } inline static bool isPointerEvent(int32_t source) { return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; } // --- InputMessage --- bool InputMessage::isValid(size_t actualSize) const { if (size() == actualSize) { switch (header.type) { case TYPE_KEY: return true; case TYPE_MOTION: return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; case TYPE_FINISHED: return true; } } return false; } size_t InputMessage::size() const { switch (header.type) { case TYPE_KEY: return sizeof(Header) + body.key.size(); case TYPE_MOTION: return sizeof(Header) + body.motion.size(); case TYPE_FINISHED: return sizeof(Header) + body.finished.size(); } return sizeof(Header); } /** * There could be non-zero bytes in-between InputMessage fields. Force-initialize the entire * memory to zero, then only copy the valid bytes on a per-field basis. */ void InputMessage::getSanitizedCopy(InputMessage* msg) const { memset(msg, 0, sizeof(*msg)); // Write the header msg->header.type = header.type; // Write the body switch(header.type) { case InputMessage::TYPE_KEY: { // uint32_t seq msg->body.key.seq = body.key.seq; // nsecs_t eventTime msg->body.key.eventTime = body.key.eventTime; // int32_t deviceId msg->body.key.deviceId = body.key.deviceId; // int32_t source msg->body.key.source = body.key.source; // int32_t displayId msg->body.key.displayId = body.key.displayId; // int32_t action msg->body.key.action = body.key.action; // int32_t flags msg->body.key.flags = body.key.flags; // int32_t keyCode msg->body.key.keyCode = body.key.keyCode; // int32_t scanCode msg->body.key.scanCode = body.key.scanCode; // int32_t metaState msg->body.key.metaState = body.key.metaState; // int32_t repeatCount msg->body.key.repeatCount = body.key.repeatCount; // nsecs_t downTime msg->body.key.downTime = body.key.downTime; break; } case InputMessage::TYPE_MOTION: { // uint32_t seq msg->body.motion.seq = body.motion.seq; // nsecs_t eventTime msg->body.motion.eventTime = body.motion.eventTime; // int32_t deviceId msg->body.motion.deviceId = body.motion.deviceId; // int32_t source msg->body.motion.source = body.motion.source; // int32_t displayId msg->body.motion.displayId = body.motion.displayId; // int32_t action msg->body.motion.action = body.motion.action; // int32_t actionButton msg->body.motion.actionButton = body.motion.actionButton; // int32_t flags msg->body.motion.flags = body.motion.flags; // int32_t metaState msg->body.motion.metaState = body.motion.metaState; // int32_t buttonState msg->body.motion.buttonState = body.motion.buttonState; // MotionClassification classification msg->body.motion.classification = body.motion.classification; // int32_t edgeFlags msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime msg->body.motion.downTime = body.motion.downTime; // float xOffset msg->body.motion.xOffset = body.motion.xOffset; // float yOffset msg->body.motion.yOffset = body.motion.yOffset; // float xPrecision msg->body.motion.xPrecision = body.motion.xPrecision; // float yPrecision msg->body.motion.yPrecision = body.motion.yPrecision; // uint32_t pointerCount msg->body.motion.pointerCount = body.motion.pointerCount; //struct Pointer pointers[MAX_POINTERS] for (size_t i = 0; i < body.motion.pointerCount; i++) { // PointerProperties properties msg->body.motion.pointers[i].properties.id = body.motion.pointers[i].properties.id; msg->body.motion.pointers[i].properties.toolType = body.motion.pointers[i].properties.toolType, // PointerCoords coords msg->body.motion.pointers[i].coords.bits = body.motion.pointers[i].coords.bits; const uint32_t count = BitSet64::count(body.motion.pointers[i].coords.bits); memcpy(&msg->body.motion.pointers[i].coords.values[0], &body.motion.pointers[i].coords.values[0], count * (sizeof(body.motion.pointers[i].coords.values[0]))); } break; } case InputMessage::TYPE_FINISHED: { msg->body.finished.seq = body.finished.seq; msg->body.finished.handled = body.finished.handled; break; } default: { LOG_FATAL("Unexpected message type %i", header.type); break; } } } // --- InputChannel --- InputChannel::InputChannel(const std::string& name, int fd) : mName(name) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), fd); #endif setFd(fd); } InputChannel::~InputChannel() { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd); #endif ::close(mFd); } void InputChannel::setFd(int fd) { if (mFd > 0) { ::close(mFd); } mFd = fd; if (mFd > 0) { int result = fcntl(mFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " "non-blocking. errno=%d", mName.c_str(), errno); } } status_t InputChannel::openInputChannelPair(const std::string& name, sp& outServerChannel, sp& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.c_str(), errno); outServerChannel.clear(); outClientChannel.clear(); return result; } int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); std::string serverChannelName = name; serverChannelName += " (server)"; outServerChannel = new InputChannel(serverChannelName, sockets[0]); std::string clientChannelName = name; clientChannelName += " (client)"; outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; } status_t InputChannel::sendMessage(const InputMessage* msg) { const size_t msgLength = msg->size(); InputMessage cleanMsg; msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(), msg->header.type, error); #endif if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) { return DEAD_OBJECT; } return -error; } if (size_t(nWrite) != msgLength) { #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", mName.c_str(), msg->header.type); #endif return DEAD_OBJECT; } #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type); #endif return OK; } status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { int error = errno; #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno); #endif if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) { return DEAD_OBJECT; } return -error; } if (nRead == 0) { // check for EOF #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str()); #endif return DEAD_OBJECT; } if (!msg->isValid(nRead)) { #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ received invalid message", mName.c_str()); #endif return BAD_VALUE; } #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type); #endif return OK; } sp InputChannel::dup() const { int fd = ::dup(getFd()); return fd >= 0 ? new InputChannel(getName(), fd) : nullptr; } status_t InputChannel::write(Parcel& out) const { status_t s = out.writeString8(String8(getName().c_str())); if (s != OK) { return s; } s = out.writeStrongBinder(mToken); if (s != OK) { return s; } s = out.writeDupFileDescriptor(getFd()); return s; } status_t InputChannel::read(const Parcel& from) { mName = from.readString8(); mToken = from.readStrongBinder(); int rawFd = from.readFileDescriptor(); setFd(::dup(rawFd)); if (mFd < 0) { return BAD_VALUE; } return OK; } sp InputChannel::getToken() const { return mToken; } void InputChannel::setToken(const sp& token) { if (mToken != nullptr) { ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str()); } mToken = token; } // --- InputPublisher --- InputPublisher::InputPublisher(const sp& channel) : mChannel(channel) { } InputPublisher::~InputPublisher() { } status_t InputPublisher::publishKeyEvent( uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", mChannel->getName().c_str(), keyCode); ATRACE_NAME(message.c_str()); } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," "downTime=%" PRId64 ", eventTime=%" PRId64, mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); #endif if (!seq) { ALOGE("Attempted to publish a key event with sequence number 0."); return BAD_VALUE; } InputMessage msg; msg.header.type = InputMessage::TYPE_KEY; msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; msg.body.key.scanCode = scanCode; msg.body.key.metaState = metaState; msg.body.key.repeatCount = repeatCount; msg.body.key.downTime = downTime; msg.body.key.eventTime = eventTime; return mChannel->sendMessage(&msg); } status_t InputPublisher::publishMotionEvent( uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", mChannel->getName().c_str(), action); ATRACE_NAME(message.c_str()); } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, motionClassificationToString(classification), xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); #endif if (!seq) { ALOGE("Attempted to publish a motion event with sequence number 0."); return BAD_VALUE; } if (pointerCount > MAX_POINTERS || pointerCount < 1) { ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %" PRIu32 ".", mChannel->getName().c_str(), pointerCount); return BAD_VALUE; } InputMessage msg; msg.header.type = InputMessage::TYPE_MOTION; msg.body.motion.seq = seq; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; msg.body.motion.edgeFlags = edgeFlags; msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; for (uint32_t i = 0; i < pointerCount; i++) { msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } return mChannel->sendMessage(&msg); } status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); #endif InputMessage msg; status_t result = mChannel->receiveMessage(&msg); if (result) { *outSeq = 0; *outHandled = false; return result; } if (msg.header.type != InputMessage::TYPE_FINISHED) { ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", mChannel->getName().c_str(), msg.header.type); return UNKNOWN_ERROR; } *outSeq = msg.body.finished.seq; *outHandled = msg.body.finished.handled; return OK; } // --- InputConsumer --- InputConsumer::InputConsumer(const sp& channel) : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) { } InputConsumer::~InputConsumer() { } bool InputConsumer::isTouchResamplingEnabled() { return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); } status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime); #endif *outSeq = 0; *outEvent = nullptr; // Fetch the next input message. // Loop until an event can be returned or no additional events are received. while (!*outEvent) { if (mMsgDeferred) { // mMsg contains a valid input message from the previous call to consume // that has not yet been processed. mMsgDeferred = false; } else { // Receive a fresh message. status_t result = mChannel->receiveMessage(&mMsg); if (result) { // Consume the next batched event unless batches are being held for later. if (consumeBatches || result != WOULD_BLOCK) { result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", mChannel->getName().c_str(), *outSeq); #endif break; } } return result; } } switch (mMsg.header.type) { case InputMessage::TYPE_KEY: { KeyEvent* keyEvent = factory->createKeyEvent(); if (!keyEvent) return NO_MEMORY; initializeKeyEvent(keyEvent, &mMsg); *outSeq = mMsg.body.key.seq; *outEvent = keyEvent; #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", mChannel->getName().c_str(), *outSeq); #endif break; } case InputMessage::TYPE_MOTION: { ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); if (batchIndex >= 0) { Batch& batch = mBatches.editItemAt(batchIndex); if (canAddSample(batch, &mMsg)) { batch.samples.push(mMsg); #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ appended to batch event", mChannel->getName().c_str()); #endif break; } else if (isPointerEvent(mMsg.body.motion.source) && mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { // No need to process events that we are going to cancel anyways const size_t count = batch.samples.size(); for (size_t i = 0; i < count; i++) { const InputMessage& msg = batch.samples.itemAt(i); sendFinishedSignal(msg.body.motion.seq, false); } batch.samples.removeItemsAt(0, count); mBatches.removeAt(batchIndex); } else { // We cannot append to the batch in progress, so we need to consume // the previous batch right now and defer the new message until later. mMsgDeferred = true; status_t result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(batchIndex); if (result) { return result; } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event and " "deferred current event, seq=%u", mChannel->getName().c_str(), *outSeq); #endif break; } } // Start a new batch if needed. if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { mBatches.push(); Batch& batch = mBatches.editTop(); batch.samples.push(mMsg); #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ started batch event", mChannel->getName().c_str()); #endif break; } MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; updateTouchState(mMsg); initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", mChannel->getName().c_str(), *outSeq); #endif break; } default: ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", mChannel->getName().c_str(), mMsg.header.type); return UNKNOWN_ERROR; } } return OK; } status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { status_t result; for (size_t i = mBatches.size(); i > 0; ) { i--; Batch& batch = mBatches.editItemAt(i); if (frameTime < 0) { result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(i); return result; } nsecs_t sampleTime = frameTime; if (mResampleTouch) { sampleTime -= RESAMPLE_LATENCY; } ssize_t split = findSampleNoLaterThan(batch, sampleTime); if (split < 0) { continue; } result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); const InputMessage* next; if (batch.samples.isEmpty()) { mBatches.removeAt(i); next = nullptr; } else { next = &batch.samples.itemAt(0); } if (!result && mResampleTouch) { resampleTouchState(sampleTime, static_cast(*outEvent), next); } return result; } return WOULD_BLOCK; } status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; uint32_t chain = 0; for (size_t i = 0; i < count; i++) { InputMessage& msg = batch.samples.editItemAt(i); updateTouchState(msg); if (i) { SeqChain seqChain; seqChain.seq = msg.body.motion.seq; seqChain.chain = chain; mSeqChains.push(seqChain); addSample(motionEvent, &msg); } else { initializeMotionEvent(motionEvent, &msg); } chain = msg.body.motion.seq; } batch.samples.removeItemsAt(0, count); *outSeq = chain; *outEvent = motionEvent; return OK; } void InputConsumer::updateTouchState(InputMessage& msg) { if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) { return; } int32_t deviceId = msg.body.motion.deviceId; int32_t source = msg.body.motion.source; // Update the touch state history to incorporate the new input message. // If the message is in the past relative to the most recently produced resampled // touch, then use the resampled time and coordinates instead. switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index < 0) { mTouchStates.push(); index = mTouchStates.size() - 1; } TouchState& touchState = mTouchStates.editItemAt(index); touchState.initialize(deviceId, source); touchState.addHistory(msg); break; } case AMOTION_EVENT_ACTION_MOVE: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); touchState.addHistory(msg); rewriteMessage(touchState, msg); } break; } case AMOTION_EVENT_ACTION_POINTER_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); rewriteMessage(touchState, msg); } break; } case AMOTION_EVENT_ACTION_POINTER_UP: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); rewriteMessage(touchState, msg); touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); } break; } case AMOTION_EVENT_ACTION_SCROLL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); rewriteMessage(touchState, msg); } break; } case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_CANCEL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { TouchState& touchState = mTouchStates.editItemAt(index); rewriteMessage(touchState, msg); mTouchStates.removeAt(index); } break; } } } /** * Replace the coordinates in msg with the coordinates in lastResample, if necessary. * * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time * is in the past relative to msg and the past two events do not contain identical coordinates), * then invalidate the lastResample data for that pointer. * If the two past events have identical coordinates, then lastResample data for that pointer will * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is * resampled to the new value x1, then x1 will always be used to replace x0 until some new value * not equal to x0 is received. */ void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { nsecs_t eventTime = msg.body.motion.eventTime; for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { uint32_t id = msg.body.motion.pointers[i].properties.id; if (state.lastResample.idBits.hasBit(id)) { if (eventTime < state.lastResample.eventTime || state.recentCoordinatesAreIdentical(id)) { PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); #if DEBUG_RESAMPLING ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(), msgCoords.getY()); #endif msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); } else { state.lastResample.idBits.clearBit(id); } } } } void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, const InputMessage* next) { if (!mResampleTouch || !(isPointerEvent(event->getSource())) || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { return; } ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); if (index < 0) { #if DEBUG_RESAMPLING ALOGD("Not resampled, no touch state for device."); #endif return; } TouchState& touchState = mTouchStates.editItemAt(index); if (touchState.historySize < 1) { #if DEBUG_RESAMPLING ALOGD("Not resampled, no history for device."); #endif return; } // Ensure that the current sample has all of the pointers that need to be reported. const History* current = touchState.getHistory(0); size_t pointerCount = event->getPointerCount(); for (size_t i = 0; i < pointerCount; i++) { uint32_t id = event->getPointerId(i); if (!current->idBits.hasBit(id)) { #if DEBUG_RESAMPLING ALOGD("Not resampled, missing id %d", id); #endif return; } } // Find the data to use for resampling. const History* other; History future; float alpha; if (next) { // Interpolate between current sample and future sample. // So current->eventTime <= sampleTime <= future.eventTime. future.initializeFrom(*next); other = &future; nsecs_t delta = future.eventTime - current->eventTime; if (delta < RESAMPLE_MIN_DELTA) { #if DEBUG_RESAMPLING ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta); #endif return; } alpha = float(sampleTime - current->eventTime) / delta; } else if (touchState.historySize >= 2) { // Extrapolate future sample using current sample and past sample. // So other->eventTime <= current->eventTime <= sampleTime. other = touchState.getHistory(1); nsecs_t delta = current->eventTime - other->eventTime; if (delta < RESAMPLE_MIN_DELTA) { #if DEBUG_RESAMPLING ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta); #endif return; } else if (delta > RESAMPLE_MAX_DELTA) { #if DEBUG_RESAMPLING ALOGD("Not resampled, delta time is too large: %" PRId64 " ns.", delta); #endif return; } nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); if (sampleTime > maxPredict) { #if DEBUG_RESAMPLING ALOGD("Sample time is too far in the future, adjusting prediction " "from %" PRId64 " to %" PRId64 " ns.", sampleTime - current->eventTime, maxPredict - current->eventTime); #endif sampleTime = maxPredict; } alpha = float(current->eventTime - sampleTime) / delta; } else { #if DEBUG_RESAMPLING ALOGD("Not resampled, insufficient data."); #endif return; } // Resample touch coordinates. History oldLastResample; oldLastResample.initializeFrom(touchState.lastResample); touchState.lastResample.eventTime = sampleTime; touchState.lastResample.idBits.clear(); for (size_t i = 0; i < pointerCount; i++) { uint32_t id = event->getPointerId(i); touchState.lastResample.idToIndex[id] = i; touchState.lastResample.idBits.markBit(id); if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) { // We maintain the previously resampled value for this pointer (stored in // oldLastResample) when the coordinates for this pointer haven't changed since then. // This way we don't introduce artificial jitter when pointers haven't actually moved. // We know here that the coordinates for the pointer haven't changed because we // would've cleared the resampled bit in rewriteMessage if they had. We can't modify // lastResample in place becasue the mapping from pointer ID to index may have changed. touchState.lastResample.pointers[i].copyFrom(oldLastResample.getPointerById(id)); continue; } PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; const PointerCoords& currentCoords = current->getPointerById(id); resampledCoords.copyFrom(currentCoords); if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { const PointerCoords& otherCoords = other->getPointerById(id); resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(currentCoords.getX(), otherCoords.getX(), alpha)); resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(currentCoords.getY(), otherCoords.getY(), alpha)); #if DEBUG_RESAMPLING ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " "other (%0.3f, %0.3f), alpha %0.3f", id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); #endif } else { #if DEBUG_RESAMPLING ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), currentCoords.getY()); #endif } } event->addSample(sampleTime, touchState.lastResample.pointers); } bool InputConsumer::shouldResampleTool(int32_t toolType) { return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN; } status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", mChannel->getName().c_str(), seq, handled ? "true" : "false"); #endif if (!seq) { ALOGE("Attempted to send a finished signal with sequence number 0."); return BAD_VALUE; } // Send finished signals for the batch sequence chain first. size_t seqChainCount = mSeqChains.size(); if (seqChainCount) { uint32_t currentSeq = seq; uint32_t chainSeqs[seqChainCount]; size_t chainIndex = 0; for (size_t i = seqChainCount; i > 0; ) { i--; const SeqChain& seqChain = mSeqChains.itemAt(i); if (seqChain.seq == currentSeq) { currentSeq = seqChain.chain; chainSeqs[chainIndex++] = currentSeq; mSeqChains.removeAt(i); } } status_t status = OK; while (!status && chainIndex > 0) { chainIndex--; status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); } if (status) { // An error occurred so at least one signal was not sent, reconstruct the chain. for (;;) { SeqChain seqChain; seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; seqChain.chain = chainSeqs[chainIndex]; mSeqChains.push(seqChain); if (!chainIndex) break; chainIndex--; } return status; } } // Send finished signal for the last message in the batch. return sendUnchainedFinishedSignal(seq, handled); } status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; msg.header.type = InputMessage::TYPE_FINISHED; msg.body.finished.seq = seq; msg.body.finished.handled = handled; return mChannel->sendMessage(&msg); } bool InputConsumer::hasDeferredEvent() const { return mMsgDeferred; } bool InputConsumer::hasPendingBatch() const { return !mBatches.isEmpty(); } ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { const Batch& batch = mBatches.itemAt(i); const InputMessage& head = batch.samples.itemAt(0); if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { return i; } } return -1; } ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mTouchStates.size(); i++) { const TouchState& touchState = mTouchStates.itemAt(i); if (touchState.deviceId == deviceId && touchState.source == source) { return i; } } return -1; } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { event->initialize( msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, msg->body.key.action, msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); } void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (uint32_t i = 0; i < pointerCount; i++) { pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } event->initialize( msg->body.motion.deviceId, msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, msg->body.motion.classification, msg->body.motion.xOffset, msg->body.motion.yOffset, msg->body.motion.xPrecision, msg->body.motion.yPrecision, msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerCoords pointerCoords[pointerCount]; for (uint32_t i = 0; i < pointerCount; i++) { pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } event->setMetaState(event->getMetaState() | msg->body.motion.metaState); event->addSample(msg->body.motion.eventTime, pointerCoords); } bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { const InputMessage& head = batch.samples.itemAt(0); uint32_t pointerCount = msg->body.motion.pointerCount; if (head.body.motion.pointerCount != pointerCount || head.body.motion.action != msg->body.motion.action) { return false; } for (size_t i = 0; i < pointerCount; i++) { if (head.body.motion.pointers[i].properties != msg->body.motion.pointers[i].properties) { return false; } } return true; } ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { size_t numSamples = batch.samples.size(); size_t index = 0; while (index < numSamples && batch.samples.itemAt(index).body.motion.eventTime <= time) { index += 1; } return ssize_t(index) - 1; } } // namespace android libs/input/InputWindow.cpp0100644 0000000 0000000 00000012441 13756501735 014706 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #define LOG_TAG "InputWindow" #define LOG_NDEBUG 0 #include #include #include #include #include #include namespace android { // --- InputWindowInfo --- void InputWindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { return touchableRegion.contains(x,y); } bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { return x >= frameLeft && x < frameRight && y >= frameTop && y < frameBottom; } bool InputWindowInfo::isTrustedOverlay() const { return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || layoutParamsType == TYPE_NAVIGATION_BAR || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || layoutParamsType == TYPE_DOCK_DIVIDER || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || layoutParamsType == TYPE_INPUT_CONSUMER; } bool InputWindowInfo::supportsSplitTouch() const { return layoutParamsFlags & FLAG_SPLIT_TOUCH; } bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { return frameLeft < other->frameRight && frameRight > other->frameLeft && frameTop < other->frameBottom && frameBottom > other->frameTop; } status_t InputWindowInfo::write(Parcel& output) const { if (token == nullptr) { output.writeInt32(0); return OK; } output.writeInt32(1); status_t s = output.writeStrongBinder(token); if (s != OK) return s; output.writeString8(String8(name.c_str())); output.writeInt32(layoutParamsFlags); output.writeInt32(layoutParamsType); output.writeInt64(dispatchingTimeout); output.writeInt32(frameLeft); output.writeInt32(frameTop); output.writeInt32(frameRight); output.writeInt32(frameBottom); output.writeInt32(surfaceInset); output.writeFloat(globalScaleFactor); output.writeFloat(windowXScale); output.writeFloat(windowYScale); output.writeBool(visible); output.writeBool(canReceiveKeys); output.writeBool(hasFocus); output.writeBool(hasWallpaper); output.writeBool(paused); output.writeInt32(layer); output.writeInt32(ownerPid); output.writeInt32(ownerUid); output.writeInt32(inputFeatures); output.writeInt32(displayId); output.writeInt32(portalToDisplayId); applicationInfo.write(output); output.write(touchableRegion); output.writeBool(replaceTouchableRegionWithCrop); output.writeWeakBinder(touchableRegionCropHandle); return OK; } InputWindowInfo InputWindowInfo::read(const Parcel& from) { InputWindowInfo ret; if (from.readInt32() == 0) { return ret; } sp token = from.readStrongBinder(); if (token == nullptr) { return ret; } ret.token = token; ret.name = from.readString8().c_str(); ret.layoutParamsFlags = from.readInt32(); ret.layoutParamsType = from.readInt32(); ret.dispatchingTimeout = from.readInt64(); ret.frameLeft = from.readInt32(); ret.frameTop = from.readInt32(); ret.frameRight = from.readInt32(); ret.frameBottom = from.readInt32(); ret.surfaceInset = from.readInt32(); ret.globalScaleFactor = from.readFloat(); ret.windowXScale = from.readFloat(); ret.windowYScale = from.readFloat(); ret.visible = from.readBool(); ret.canReceiveKeys = from.readBool(); ret.hasFocus = from.readBool(); ret.hasWallpaper = from.readBool(); ret.paused = from.readBool(); ret.layer = from.readInt32(); ret.ownerPid = from.readInt32(); ret.ownerUid = from.readInt32(); ret.inputFeatures = from.readInt32(); ret.displayId = from.readInt32(); ret.portalToDisplayId = from.readInt32(); ret.applicationInfo = InputApplicationInfo::read(from); from.read(ret.touchableRegion); ret.replaceTouchableRegionWithCrop = from.readBool(); ret.touchableRegionCropHandle = from.readWeakBinder(); return ret; } InputWindowInfo::InputWindowInfo(const Parcel& from) { *this = read(from); } // --- InputWindowHandle --- InputWindowHandle::InputWindowHandle() { } InputWindowHandle::~InputWindowHandle() { } void InputWindowHandle::releaseChannel() { mInfo.token.clear(); } sp InputWindowHandle::getToken() const { return mInfo.token; } void InputWindowHandle::updateFrom(sp handle) { mInfo = handle->mInfo; } } // namespace android libs/input/KeyCharacterMap.cpp0100644 0000000 0000000 00000130175 13756501735 015427 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #define LOG_TAG "KeyCharacterMap" #include #include #ifdef __ANDROID__ #include #endif #include #include #include #include #include #include #include #include // Enables debug output for the parser. #define DEBUG_PARSER 0 // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 // Enables debug output for mapping. #define DEBUG_MAPPING 0 namespace android { static const char* WHITESPACE = " \t\r"; static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; struct Modifier { const char* label; int32_t metaState; }; static const Modifier modifiers[] = { { "shift", AMETA_SHIFT_ON }, { "lshift", AMETA_SHIFT_LEFT_ON }, { "rshift", AMETA_SHIFT_RIGHT_ON }, { "alt", AMETA_ALT_ON }, { "lalt", AMETA_ALT_LEFT_ON }, { "ralt", AMETA_ALT_RIGHT_ON }, { "ctrl", AMETA_CTRL_ON }, { "lctrl", AMETA_CTRL_LEFT_ON }, { "rctrl", AMETA_CTRL_RIGHT_ON }, { "meta", AMETA_META_ON }, { "lmeta", AMETA_META_LEFT_ON }, { "rmeta", AMETA_META_RIGHT_ON }, { "sym", AMETA_SYM_ON }, { "fn", AMETA_FUNCTION_ON }, { "capslock", AMETA_CAPS_LOCK_ON }, { "numlock", AMETA_NUM_LOCK_ON }, { "scrolllock", AMETA_SCROLL_LOCK_ON }, }; #if DEBUG_MAPPING static String8 toString(const char16_t* chars, size_t numChars) { String8 result; for (size_t i = 0; i < numChars; i++) { result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); } return result; } #endif // --- KeyCharacterMap --- sp KeyCharacterMap::sEmpty = new KeyCharacterMap(); KeyCharacterMap::KeyCharacterMap() : mType(KEYBOARD_TYPE_UNKNOWN) { } KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), mKeysByUsageCode(other.mKeysByUsageCode) { for (size_t i = 0; i < other.mKeys.size(); i++) { mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); } } KeyCharacterMap::~KeyCharacterMap() { for (size_t i = 0; i < mKeys.size(); i++) { Key* key = mKeys.editValueAt(i); delete key; } } status_t KeyCharacterMap::load(const std::string& filename, Format format, sp* outMap) { outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { ALOGE("Error %d opening key character map file %s.", status, filename.c_str()); } else { status = load(tokenizer, format, outMap); delete tokenizer; } return status; } status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents, Format format, sp* outMap) { outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); if (status) { ALOGE("Error %d opening key character map.", status); } else { status = load(tokenizer, format, outMap); delete tokenizer; } return status; } status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format, sp* outMap) { status_t status = OK; sp map = new KeyCharacterMap(); if (!map.get()) { ALOGE("Error allocating key character map."); status = NO_MEMORY; } else { #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif Parser parser(map.get(), tokenizer, format); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif if (!status) { *outMap = map; } } return status; } sp KeyCharacterMap::combine(const sp& base, const sp& overlay) { if (overlay == nullptr) { return base; } if (base == nullptr) { return overlay; } sp map = new KeyCharacterMap(*base.get()); for (size_t i = 0; i < overlay->mKeys.size(); i++) { int32_t keyCode = overlay->mKeys.keyAt(i); Key* key = overlay->mKeys.valueAt(i); ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); if (oldIndex >= 0) { delete map->mKeys.valueAt(oldIndex); map->mKeys.editValueAt(oldIndex) = new Key(*key); } else { map->mKeys.add(keyCode, new Key(*key)); } } for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), overlay->mKeysByScanCode.valueAt(i)); } for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), overlay->mKeysByUsageCode.valueAt(i)); } return map; } sp KeyCharacterMap::empty() { return sEmpty; } int32_t KeyCharacterMap::getKeyboardType() const { return mType; } char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { char16_t result = 0; const Key* key; if (getKey(keyCode, &key)) { result = key->label; } #if DEBUG_MAPPING ALOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); #endif return result; } char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { char16_t result = 0; const Key* key; if (getKey(keyCode, &key)) { result = key->number; } #if DEBUG_MAPPING ALOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); #endif return result; } char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { char16_t result = 0; const Key* key; const Behavior* behavior; if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { result = behavior->character; } #if DEBUG_MAPPING ALOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); #endif return result; } bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, FallbackAction* outFallbackAction) const { outFallbackAction->keyCode = 0; outFallbackAction->metaState = 0; bool result = false; const Key* key; const Behavior* behavior; if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { if (behavior->fallbackKeyCode) { outFallbackAction->keyCode = behavior->fallbackKeyCode; outFallbackAction->metaState = metaState & ~behavior->metaState; result = true; } } #if DEBUG_MAPPING ALOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, " "fallback keyCode=%d, fallback metaState=0x%08x.", keyCode, metaState, result ? "true" : "false", outFallbackAction->keyCode, outFallbackAction->metaState); #endif return result; } char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, int32_t metaState) const { char16_t result = 0; const Key* key; if (getKey(keyCode, &key)) { // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. // However, if we find a perfect meta state match for one behavior then use that one. for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { if (behavior->character) { for (size_t i = 0; i < numChars; i++) { if (behavior->character == chars[i]) { result = behavior->character; if ((behavior->metaState & metaState) == behavior->metaState) { goto ExactMatch; } break; } } } } ExactMatch: ; } #if DEBUG_MAPPING ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", keyCode, toString(chars, numChars).string(), metaState, result); #endif return result; } bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector& outEvents) const { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); for (size_t i = 0; i < numChars; i++) { int32_t keyCode, metaState; char16_t ch = chars[i]; if (!findKey(ch, &keyCode, &metaState)) { #if DEBUG_MAPPING ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", deviceId, toString(chars, numChars).string(), ch); #endif return false; } int32_t currentMetaState = 0; addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); } #if DEBUG_MAPPING ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); for (size_t i = 0; i < outEvents.size(); i++) { ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", outEvents[i].getKeyCode(), outEvents[i].getMetaState(), outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); } #endif return true; } status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { if (usageCode) { ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); if (index >= 0) { *outKeyCode = mKeysByUsageCode.valueAt(index); #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", scanCode, usageCode, *outKeyCode); #endif return OK; } } if (scanCode) { ssize_t index = mKeysByScanCode.indexOfKey(scanCode); if (index >= 0) { *outKeyCode = mKeysByScanCode.valueAt(index); #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", scanCode, usageCode, *outKeyCode); #endif return OK; } } #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); #endif *outKeyCode = AKEYCODE_UNKNOWN; return NAME_NOT_FOUND; } void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState, int32_t *outKeyCode, int32_t *outMetaState) const { *outKeyCode = keyCode; *outMetaState = metaState; const Key* key; const Behavior* behavior; if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { if (behavior->replacementKeyCode) { *outKeyCode = behavior->replacementKeyCode; int32_t newMetaState = metaState & ~behavior->metaState; // Reset dependent meta states. if (behavior->metaState & AMETA_ALT_ON) { newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); } if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { newMetaState &= ~AMETA_ALT_ON; } if (behavior->metaState & AMETA_CTRL_ON) { newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); } if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { newMetaState &= ~AMETA_CTRL_ON; } if (behavior->metaState & AMETA_SHIFT_ON) { newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON); } if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { newMetaState &= ~AMETA_SHIFT_ON; } // ... and put universal bits back if needed *outMetaState = normalizeMetaState(newMetaState); } } #if DEBUG_MAPPING ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ " "replacement keyCode=%d, replacement metaState=0x%08x.", keyCode, metaState, *outKeyCode, *outMetaState); #endif } bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { ssize_t index = mKeys.indexOfKey(keyCode); if (index >= 0) { *outKey = mKeys.valueAt(index); return true; } return false; } bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, const Key** outKey, const Behavior** outBehavior) const { const Key* key; if (getKey(keyCode, &key)) { const Behavior* behavior = key->firstBehavior; while (behavior) { if (matchesMetaState(metaState, behavior->metaState)) { *outKey = key; *outBehavior = behavior; return true; } behavior = behavior->next; } } return false; } bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) { // Behavior must have at least the set of meta states specified. // And if the key event has CTRL, ALT or META then the behavior must exactly // match those, taking into account that a behavior can specify that it handles // one, both or either of a left/right modifier pair. if ((eventMetaState & behaviorMetaState) == behaviorMetaState) { const int32_t EXACT_META_STATES = AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON; int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES; if (behaviorMetaState & AMETA_CTRL_ON) { unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { unmatchedMetaState &= ~AMETA_CTRL_ON; } if (behaviorMetaState & AMETA_ALT_ON) { unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { unmatchedMetaState &= ~AMETA_ALT_ON; } if (behaviorMetaState & AMETA_META_ON) { unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { unmatchedMetaState &= ~AMETA_META_ON; } return !unmatchedMetaState; } return false; } bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { if (!ch) { return false; } for (size_t i = 0; i < mKeys.size(); i++) { const Key* key = mKeys.valueAt(i); // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. const Behavior* found = nullptr; for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { if (behavior->character == ch) { found = behavior; } } if (found) { *outKeyCode = mKeys.keyAt(i); *outMetaState = found->metaState; return true; } } return false; } void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, int32_t deviceId, int32_t metaState, bool down, nsecs_t time, int32_t* currentMetaState) { // Add and remove meta keys symmetrically. if (down) { addLockedMetaKey(outEvents, deviceId, metaState, time, AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); addLockedMetaKey(outEvents, deviceId, metaState, time, AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); addLockedMetaKey(outEvents, deviceId, metaState, time, AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, AMETA_SHIFT_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, AMETA_ALT_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, AMETA_CTRL_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, AMETA_META_ON, currentMetaState); addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); } else { addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, AMETA_META_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, AMETA_CTRL_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, AMETA_ALT_ON, currentMetaState); addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, AMETA_SHIFT_ON, currentMetaState); addLockedMetaKey(outEvents, deviceId, metaState, time, AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); addLockedMetaKey(outEvents, deviceId, metaState, time, AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); addLockedMetaKey(outEvents, deviceId, metaState, time, AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); } } bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector& outEvents, int32_t deviceId, int32_t metaState, bool down, nsecs_t time, int32_t keyCode, int32_t keyMetaState, int32_t* currentMetaState) { if ((metaState & keyMetaState) == keyMetaState) { *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); return true; } return false; } void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector& outEvents, int32_t deviceId, int32_t metaState, bool down, nsecs_t time, int32_t leftKeyCode, int32_t leftKeyMetaState, int32_t rightKeyCode, int32_t rightKeyMetaState, int32_t eitherKeyMetaState, int32_t* currentMetaState) { bool specific = false; specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, leftKeyCode, leftKeyMetaState, currentMetaState); specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, rightKeyCode, rightKeyMetaState, currentMetaState); if (!specific) { addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, leftKeyCode, eitherKeyMetaState, currentMetaState); } } void KeyCharacterMap::addLockedMetaKey(Vector& outEvents, int32_t deviceId, int32_t metaState, nsecs_t time, int32_t keyCode, int32_t keyMetaState, int32_t* currentMetaState) { if ((metaState & keyMetaState) == keyMetaState) { *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); } } #ifdef __ANDROID__ sp KeyCharacterMap::readFromParcel(Parcel* parcel) { sp map = new KeyCharacterMap(); map->mType = parcel->readInt32(); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; } if (numKeys > MAX_KEYS) { ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS); return nullptr; } for (size_t i = 0; i < numKeys; i++) { int32_t keyCode = parcel->readInt32(); char16_t label = parcel->readInt32(); char16_t number = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; } Key* key = new Key(); key->label = label; key->number = number; map->mKeys.add(keyCode, key); Behavior* lastBehavior = nullptr; while (parcel->readInt32()) { int32_t metaState = parcel->readInt32(); char16_t character = parcel->readInt32(); int32_t fallbackKeyCode = parcel->readInt32(); int32_t replacementKeyCode = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; } Behavior* behavior = new Behavior(); behavior->metaState = metaState; behavior->character = character; behavior->fallbackKeyCode = fallbackKeyCode; behavior->replacementKeyCode = replacementKeyCode; if (lastBehavior) { lastBehavior->next = behavior; } else { key->firstBehavior = behavior; } lastBehavior = behavior; } if (parcel->errorCheck()) { return nullptr; } } return map; } void KeyCharacterMap::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mType); size_t numKeys = mKeys.size(); parcel->writeInt32(numKeys); for (size_t i = 0; i < numKeys; i++) { int32_t keyCode = mKeys.keyAt(i); const Key* key = mKeys.valueAt(i); parcel->writeInt32(keyCode); parcel->writeInt32(key->label); parcel->writeInt32(key->number); for (const Behavior* behavior = key->firstBehavior; behavior != nullptr; behavior = behavior->next) { parcel->writeInt32(1); parcel->writeInt32(behavior->metaState); parcel->writeInt32(behavior->character); parcel->writeInt32(behavior->fallbackKeyCode); parcel->writeInt32(behavior->replacementKeyCode); } parcel->writeInt32(0); } } #endif // --- KeyCharacterMap::Key --- KeyCharacterMap::Key::Key() : label(0), number(0), firstBehavior(nullptr) { } KeyCharacterMap::Key::Key(const Key& other) : label(other.label), number(other.number), firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) { } KeyCharacterMap::Key::~Key() { Behavior* behavior = firstBehavior; while (behavior) { Behavior* next = behavior->next; delete behavior; behavior = next; } } // --- KeyCharacterMap::Behavior --- KeyCharacterMap::Behavior::Behavior() : next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { } KeyCharacterMap::Behavior::Behavior(const Behavior& other) : next(other.next ? new Behavior(*other.next) : nullptr), metaState(other.metaState), character(other.character), fallbackKeyCode(other.fallbackKeyCode), replacementKeyCode(other.replacementKeyCode) { } // --- KeyCharacterMap::Parser --- KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) : mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) { } KeyCharacterMap::Parser::~Parser() { } status_t KeyCharacterMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); #endif mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { switch (mState) { case STATE_TOP: { String8 keywordToken = mTokenizer->nextToken(WHITESPACE); if (keywordToken == "type") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseType(); if (status) return status; } else if (keywordToken == "map") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseMap(); if (status) return status; } else if (keywordToken == "key") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseKey(); if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); return BAD_VALUE; } break; } case STATE_KEY: { status_t status = parseKeyProperty(); if (status) return status; break; } } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { ALOGE("%s: Expected end of line or trailing comment, got '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); return BAD_VALUE; } } mTokenizer->nextLine(); } if (mState != STATE_TOP) { ALOGE("%s: Unterminated key description at end of file.", mTokenizer->getLocation().string()); return BAD_VALUE; } if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; } if (mFormat == FORMAT_BASE) { if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", mTokenizer->getLocation().string()); return BAD_VALUE; } } else if (mFormat == FORMAT_OVERLAY) { if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { ALOGE("%s: Overlay keyboard layout missing required keyboard " "'type OVERLAY' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; } } return NO_ERROR; } status_t KeyCharacterMap::Parser::parseType() { if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; } KeyboardType type; String8 typeToken = mTokenizer->nextToken(WHITESPACE); if (typeToken == "NUMERIC") { type = KEYBOARD_TYPE_NUMERIC; } else if (typeToken == "PREDICTIVE") { type = KEYBOARD_TYPE_PREDICTIVE; } else if (typeToken == "ALPHA") { type = KEYBOARD_TYPE_ALPHA; } else if (typeToken == "FULL") { type = KEYBOARD_TYPE_FULL; } else if (typeToken == "SPECIAL_FUNCTION") { ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set " "the property 'keyboard.specialFunction' to '1' there instead."); // TODO: return BAD_VALUE here in Q type = KEYBOARD_TYPE_SPECIAL_FUNCTION; } else if (typeToken == "OVERLAY") { type = KEYBOARD_TYPE_OVERLAY; } else { ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), typeToken.string()); return BAD_VALUE; } #if DEBUG_PARSER ALOGD("Parsed type: type=%d.", type); #endif mMap->mType = type; return NO_ERROR; } status_t KeyCharacterMap::Parser::parseMap() { String8 keywordToken = mTokenizer->nextToken(WHITESPACE); if (keywordToken == "key") { mTokenizer->skipDelimiters(WHITESPACE); return parseMapKey(); } ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); return BAD_VALUE; } status_t KeyCharacterMap::Parser::parseMapKey() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); bool mapUsage = false; if (codeToken == "usage") { mapUsage = true; mTokenizer->skipDelimiters(WHITESPACE); codeToken = mTokenizer->nextToken(WHITESPACE); } char* end; int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); if (*end) { ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } KeyedVector& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; if (map.indexOfKey(code) >= 0) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); return BAD_VALUE; } #if DEBUG_PARSER ALOGD("Parsed map key %s: code=%d, keyCode=%d.", mapUsage ? "usage" : "scan code", code, keyCode); #endif map.add(code, keyCode); return NO_ERROR; } status_t KeyCharacterMap::Parser::parseKey() { String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); return BAD_VALUE; } if (mMap->mKeys.indexOfKey(keyCode) >= 0) { ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); if (openBraceToken != "{") { ALOGE("%s: Expected '{' after key code label, got '%s'.", mTokenizer->getLocation().string(), openBraceToken.string()); return BAD_VALUE; } #if DEBUG_PARSER ALOGD("Parsed beginning of key: keyCode=%d.", keyCode); #endif mKeyCode = keyCode; mMap->mKeys.add(keyCode, new Key()); mState = STATE_KEY; return NO_ERROR; } status_t KeyCharacterMap::Parser::parseKeyProperty() { Key* key = mMap->mKeys.valueFor(mKeyCode); String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); if (token == "}") { mState = STATE_TOP; return finishKey(key); } Vector properties; // Parse all comma-delimited property names up to the first colon. for (;;) { if (token == "label") { properties.add(Property(PROPERTY_LABEL)); } else if (token == "number") { properties.add(Property(PROPERTY_NUMBER)); } else { int32_t metaState; status_t status = parseModifier(token.string(), &metaState); if (status) { ALOGE("%s: Expected a property name or modifier, got '%s'.", mTokenizer->getLocation().string(), token.string()); return status; } properties.add(Property(PROPERTY_META, metaState)); } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol()) { char ch = mTokenizer->nextChar(); if (ch == ':') { break; } else if (ch == ',') { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); continue; } } ALOGE("%s: Expected ',' or ':' after property name.", mTokenizer->getLocation().string()); return BAD_VALUE; } // Parse behavior after the colon. mTokenizer->skipDelimiters(WHITESPACE); Behavior behavior; bool haveCharacter = false; bool haveFallback = false; bool haveReplacement = false; do { char ch = mTokenizer->peekChar(); if (ch == '\'') { char16_t character; status_t status = parseCharacterLiteral(&character); if (status || !character) { ALOGE("%s: Invalid character literal for key.", mTokenizer->getLocation().string()); return BAD_VALUE; } if (haveCharacter) { ALOGE("%s: Cannot combine multiple character literals or 'none'.", mTokenizer->getLocation().string()); return BAD_VALUE; } if (haveReplacement) { ALOGE("%s: Cannot combine character literal with replace action.", mTokenizer->getLocation().string()); return BAD_VALUE; } behavior.character = character; haveCharacter = true; } else { token = mTokenizer->nextToken(WHITESPACE); if (token == "none") { if (haveCharacter) { ALOGE("%s: Cannot combine multiple character literals or 'none'.", mTokenizer->getLocation().string()); return BAD_VALUE; } if (haveReplacement) { ALOGE("%s: Cannot combine 'none' with replace action.", mTokenizer->getLocation().string()); return BAD_VALUE; } haveCharacter = true; } else if (token == "fallback") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); int32_t keyCode = getKeyCodeByLabel(token.string()); if (!keyCode) { ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", mTokenizer->getLocation().string(), token.string()); return BAD_VALUE; } if (haveFallback || haveReplacement) { ALOGE("%s: Cannot combine multiple fallback/replacement key codes.", mTokenizer->getLocation().string()); return BAD_VALUE; } behavior.fallbackKeyCode = keyCode; haveFallback = true; } else if (token == "replace") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); int32_t keyCode = getKeyCodeByLabel(token.string()); if (!keyCode) { ALOGE("%s: Invalid key code label for replace, got '%s'.", mTokenizer->getLocation().string(), token.string()); return BAD_VALUE; } if (haveCharacter) { ALOGE("%s: Cannot combine character literal with replace action.", mTokenizer->getLocation().string()); return BAD_VALUE; } if (haveFallback || haveReplacement) { ALOGE("%s: Cannot combine multiple fallback/replacement key codes.", mTokenizer->getLocation().string()); return BAD_VALUE; } behavior.replacementKeyCode = keyCode; haveReplacement = true; } else { ALOGE("%s: Expected a key behavior after ':'.", mTokenizer->getLocation().string()); return BAD_VALUE; } } mTokenizer->skipDelimiters(WHITESPACE); } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#'); // Add the behavior. for (size_t i = 0; i < properties.size(); i++) { const Property& property = properties.itemAt(i); switch (property.property) { case PROPERTY_LABEL: if (key->label) { ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().string()); return BAD_VALUE; } key->label = behavior.character; #if DEBUG_PARSER ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); #endif break; case PROPERTY_NUMBER: if (key->number) { ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().string()); return BAD_VALUE; } key->number = behavior.character; #if DEBUG_PARSER ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); #endif break; case PROPERTY_META: { for (Behavior* b = key->firstBehavior; b; b = b->next) { if (b->metaState == property.metaState) { ALOGE("%s: Duplicate key behavior for modifier.", mTokenizer->getLocation().string()); return BAD_VALUE; } } Behavior* newBehavior = new Behavior(behavior); newBehavior->metaState = property.metaState; newBehavior->next = key->firstBehavior; key->firstBehavior = newBehavior; #if DEBUG_PARSER ALOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d replace=%d.", mKeyCode, newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode, newBehavior->replacementKeyCode); #endif break; } } } return NO_ERROR; } status_t KeyCharacterMap::Parser::finishKey(Key* key) { // Fill in default number property. if (!key->number) { char16_t digit = 0; char16_t symbol = 0; for (Behavior* b = key->firstBehavior; b; b = b->next) { char16_t ch = b->character; if (ch) { if (ch >= '0' && ch <= '9') { digit = ch; } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*' || ch == '-' || ch == '+' || ch == ',' || ch == '.' || ch == '\'' || ch == ':' || ch == ';' || ch == '/') { symbol = ch; } } } key->number = digit ? digit : symbol; } return NO_ERROR; } status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) { if (token == "base") { *outMetaState = 0; return NO_ERROR; } int32_t combinedMeta = 0; const char* str = token.c_str(); const char* start = str; for (const char* cur = str; ; cur++) { char ch = *cur; if (ch == '+' || ch == '\0') { size_t len = cur - start; int32_t metaState = 0; for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { if (strlen(modifiers[i].label) == len && strncmp(modifiers[i].label, start, len) == 0) { metaState = modifiers[i].metaState; break; } } if (!metaState) { return BAD_VALUE; } if (combinedMeta & metaState) { ALOGE("%s: Duplicate modifier combination '%s'.", mTokenizer->getLocation().string(), token.c_str()); return BAD_VALUE; } combinedMeta |= metaState; start = cur + 1; if (ch == '\0') { break; } } } *outMetaState = combinedMeta; return NO_ERROR; } status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { char ch = mTokenizer->nextChar(); if (ch != '\'') { goto Error; } ch = mTokenizer->nextChar(); if (ch == '\\') { // Escape sequence. ch = mTokenizer->nextChar(); if (ch == 'n') { *outCharacter = '\n'; } else if (ch == 't') { *outCharacter = '\t'; } else if (ch == '\\') { *outCharacter = '\\'; } else if (ch == '\'') { *outCharacter = '\''; } else if (ch == '"') { *outCharacter = '"'; } else if (ch == 'u') { *outCharacter = 0; for (int i = 0; i < 4; i++) { ch = mTokenizer->nextChar(); int digit; if (ch >= '0' && ch <= '9') { digit = ch - '0'; } else if (ch >= 'A' && ch <= 'F') { digit = ch - 'A' + 10; } else if (ch >= 'a' && ch <= 'f') { digit = ch - 'a' + 10; } else { goto Error; } *outCharacter = (*outCharacter << 4) | digit; } } else { goto Error; } } else if (ch >= 32 && ch <= 126 && ch != '\'') { // ASCII literal character. *outCharacter = ch; } else { goto Error; } ch = mTokenizer->nextChar(); if (ch != '\'') { goto Error; } // Ensure that we consumed the entire token. if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { return NO_ERROR; } Error: ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); return BAD_VALUE; } } // namespace android libs/input/KeyLayoutMap.cpp0100644 0000000 0000000 00000035647 13756501735 015020 0ustar000000000 0000000 /* * Copyright (C) 2008 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. */ #define LOG_TAG "KeyLayoutMap" #include #include #include #include #include #include #include #include #include // Enables debug output for the parser. #define DEBUG_PARSER 0 // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 // Enables debug output for mapping. #define DEBUG_MAPPING 0 namespace android { static const char* WHITESPACE = " \t\r"; // --- KeyLayoutMap --- KeyLayoutMap::KeyLayoutMap() { } KeyLayoutMap::~KeyLayoutMap() { } status_t KeyLayoutMap::load(const std::string& filename, sp* outMap) { outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); } else { sp map = new KeyLayoutMap(); if (!map.get()) { ALOGE("Error allocating key layout map."); status = NO_MEMORY; } else { #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif Parser parser(map.get(), tokenizer); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif if (!status) { *outMap = map; } } delete tokenizer; } return status; } status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const { const Key* key = getKey(scanCode, usageCode); if (!key) { #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); #endif *outKeyCode = AKEYCODE_UNKNOWN; *outFlags = 0; return NAME_NOT_FOUND; } *outKeyCode = key->keyCode; *outFlags = key->flags; #if DEBUG_MAPPING ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", scanCode, usageCode, *outKeyCode, *outFlags); #endif return NO_ERROR; } const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); if (index >= 0) { return &mKeysByUsageCode.valueAt(index); } } if (scanCode) { ssize_t index = mKeysByScanCode.indexOfKey(scanCode); if (index >= 0) { return &mKeysByScanCode.valueAt(index); } } return nullptr; } status_t KeyLayoutMap::findScanCodesForKey( int32_t keyCode, std::vector* outScanCodes) const { const size_t N = mKeysByScanCode.size(); for (size_t i=0; ipush_back(mKeysByScanCode.keyAt(i)); } } return NO_ERROR; } status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { ssize_t index = mAxes.indexOfKey(scanCode); if (index < 0) { #if DEBUG_MAPPING ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); #endif return NAME_NOT_FOUND; } *outAxisInfo = mAxes.valueAt(index); #if DEBUG_MAPPING ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " "splitValue=%d, flatOverride=%d.", scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, outAxisInfo->splitValue, outAxisInfo->flatOverride); #endif return NO_ERROR; } status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { const size_t N = mLedsByScanCode.size(); for (size_t i = 0; i < N; i++) { if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { *outScanCode = mLedsByScanCode.keyAt(i); #if DEBUG_MAPPING ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode); #endif return NO_ERROR; } } #if DEBUG_MAPPING ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); #endif return NAME_NOT_FOUND; } status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { const size_t N = mLedsByUsageCode.size(); for (size_t i = 0; i < N; i++) { if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { *outUsageCode = mLedsByUsageCode.keyAt(i); #if DEBUG_MAPPING ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode); #endif return NO_ERROR; } } #if DEBUG_MAPPING ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode); #endif return NAME_NOT_FOUND; } // --- KeyLayoutMap::Parser --- KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : mMap(map), mTokenizer(tokenizer) { } KeyLayoutMap::Parser::~Parser() { } status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); #endif mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { String8 keywordToken = mTokenizer->nextToken(WHITESPACE); if (keywordToken == "key") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseKey(); if (status) return status; } else if (keywordToken == "axis") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseAxis(); if (status) return status; } else if (keywordToken == "led") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseLed(); if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { ALOGE("%s: Expected end of line or trailing comment, got '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); return BAD_VALUE; } } mTokenizer->nextLine(); } return NO_ERROR; } status_t KeyLayoutMap::Parser::parseKey() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); bool mapUsage = false; if (codeToken == "usage") { mapUsage = true; mTokenizer->skipDelimiters(WHITESPACE); codeToken = mTokenizer->nextToken(WHITESPACE); } char* end; int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); if (*end) { ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } KeyedVector& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; if (map.indexOfKey(code) >= 0) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); return BAD_VALUE; } uint32_t flags = 0; for (;;) { mTokenizer->skipDelimiters(WHITESPACE); if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; String8 flagToken = mTokenizer->nextToken(WHITESPACE); uint32_t flag = getKeyFlagByLabel(flagToken.string()); if (!flag) { ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), flagToken.string()); return BAD_VALUE; } if (flags & flag) { ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), flagToken.string()); return BAD_VALUE; } flags |= flag; } #if DEBUG_PARSER ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", mapUsage ? "usage" : "scan code", code, keyCode, flags); #endif Key key; key.keyCode = keyCode; key.flags = flags; map.add(code, key); return NO_ERROR; } status_t KeyLayoutMap::Parser::parseAxis() { String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); char* end; int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); if (*end) { ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; } if (mMap->mAxes.indexOfKey(scanCode) >= 0) { ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; } AxisInfo axisInfo; mTokenizer->skipDelimiters(WHITESPACE); String8 token = mTokenizer->nextToken(WHITESPACE); if (token == "invert") { axisInfo.mode = AxisInfo::MODE_INVERT; mTokenizer->skipDelimiters(WHITESPACE); String8 axisToken = mTokenizer->nextToken(WHITESPACE); axisInfo.axis = getAxisByLabel(axisToken.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected inverted axis label, got '%s'.", mTokenizer->getLocation().string(), axisToken.string()); return BAD_VALUE; } } else if (token == "split") { axisInfo.mode = AxisInfo::MODE_SPLIT; mTokenizer->skipDelimiters(WHITESPACE); String8 splitToken = mTokenizer->nextToken(WHITESPACE); axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); if (*end) { ALOGE("%s: Expected split value, got '%s'.", mTokenizer->getLocation().string(), splitToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); axisInfo.axis = getAxisByLabel(lowAxisToken.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected low axis label, got '%s'.", mTokenizer->getLocation().string(), lowAxisToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); if (axisInfo.highAxis < 0) { ALOGE("%s: Expected high axis label, got '%s'.", mTokenizer->getLocation().string(), highAxisToken.string()); return BAD_VALUE; } } else { axisInfo.axis = getAxisByLabel(token.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", mTokenizer->getLocation().string(), token.string()); return BAD_VALUE; } } for (;;) { mTokenizer->skipDelimiters(WHITESPACE); if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { break; } String8 keywordToken = mTokenizer->nextToken(WHITESPACE); if (keywordToken == "flat") { mTokenizer->skipDelimiters(WHITESPACE); String8 flatToken = mTokenizer->nextToken(WHITESPACE); axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); if (*end) { ALOGE("%s: Expected flat value, got '%s'.", mTokenizer->getLocation().string(), flatToken.string()); return BAD_VALUE; } } else { ALOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); return BAD_VALUE; } } #if DEBUG_PARSER ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " "splitValue=%d, flatOverride=%d.", scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, axisInfo.flatOverride); #endif mMap->mAxes.add(scanCode, axisInfo); return NO_ERROR; } status_t KeyLayoutMap::Parser::parseLed() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); bool mapUsage = false; if (codeToken == "usage") { mapUsage = true; mTokenizer->skipDelimiters(WHITESPACE); codeToken = mTokenizer->nextToken(WHITESPACE); } char* end; int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); if (*end) { ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } KeyedVector& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; if (map.indexOfKey(code) >= 0) { ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE); int32_t ledCode = getLedByLabel(ledCodeToken.string()); if (ledCode < 0) { ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(), ledCodeToken.string()); return BAD_VALUE; } #if DEBUG_PARSER ALOGD("Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code", code, ledCode); #endif Led led; led.ledCode = ledCode; map.add(code, led); return NO_ERROR; } }; libs/input/Keyboard.cpp0100644 0000000 0000000 00000021336 13756501735 014162 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #define LOG_TAG "Keyboard" #include #include #include #include #include #include #include #include #include #include namespace android { // --- KeyMap --- KeyMap::KeyMap() { } KeyMap::~KeyMap() { } status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, const PropertyMap* deviceConfiguration) { // Use the configured key layout if available. if (deviceConfiguration) { String8 keyLayoutName; if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), keyLayoutName)) { status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " "it was not found.", deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } String8 keyCharacterMapName; if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), keyCharacterMapName)) { status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " "map '%s' but it was not found.", deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } if (isComplete()) { return OK; } } // Try searching by device identifier. if (probeKeyMap(deviceIdenfifier, "")) { return OK; } // Fall back on the Generic key map. // TODO Apply some additional heuristics here to figure out what kind of // generic key map to use (US English, etc.) for typical external keyboards. if (probeKeyMap(deviceIdenfifier, "Generic")) { return OK; } // Try the Virtual key map as a last resort. if (probeKeyMap(deviceIdenfifier, "Virtual")) { return OK; } // Give up! ALOGE("Could not determine key map for device '%s' and no default key maps were found!", deviceIdenfifier.name.c_str()); return NAME_NOT_FOUND; } bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& keyMapName) { if (!haveKeyLayout()) { loadKeyLayout(deviceIdentifier, keyMapName); } if (!haveKeyCharacterMap()) { loadKeyCharacterMap(deviceIdentifier, keyMapName); } return isComplete(); } status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name) { std::string path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); if (path.empty()) { return NAME_NOT_FOUND; } status_t status = KeyLayoutMap::load(path, &keyLayoutMap); if (status) { return status; } keyLayoutFile = path; return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name) { std::string path = getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP); if (path.empty()) { return NAME_NOT_FOUND; } status_t status = KeyCharacterMap::load(path, KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); if (status) { return status; } keyCharacterMapFile = path; return OK; } std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name, InputDeviceConfigurationFileType type) { return name.empty() ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) : getInputDeviceConfigurationFilePathByName(name, type); } // --- Global functions --- bool isKeyboardSpecialFunction(const PropertyMap* config) { if (config == nullptr) { return false; } bool isSpecialFunction = false; config->tryGetProperty(String8("keyboard.specialFunction"), isSpecialFunction); return isSpecialFunction; } bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { // TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) || keyMap->keyCharacterMap->getKeyboardType() == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { return false; } if (deviceConfiguration) { bool builtIn = false; if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) && builtIn) { return true; } } return strstr(deviceIdentifier.name.c_str(), "-keypad"); } static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { int32_t newMetaState; if (down) { newMetaState = oldMetaState | mask; } else { newMetaState = oldMetaState & ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON); } return normalizeMetaState(newMetaState); } int32_t normalizeMetaState(int32_t oldMetaState) { int32_t newMetaState = oldMetaState; if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { newMetaState |= AMETA_ALT_ON; } if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { newMetaState |= AMETA_SHIFT_ON; } if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { newMetaState |= AMETA_CTRL_ON; } if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { newMetaState |= AMETA_META_ON; } return newMetaState; } static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { if (down) { return oldMetaState; } else { return oldMetaState ^ mask; } } int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { switch (keyCode) { case AKEYCODE_ALT_LEFT: return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); case AKEYCODE_ALT_RIGHT: return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); case AKEYCODE_SHIFT_LEFT: return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); case AKEYCODE_SHIFT_RIGHT: return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); case AKEYCODE_SYM: return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); case AKEYCODE_FUNCTION: return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); case AKEYCODE_CTRL_LEFT: return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); case AKEYCODE_CTRL_RIGHT: return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); case AKEYCODE_META_LEFT: return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); case AKEYCODE_META_RIGHT: return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); case AKEYCODE_CAPS_LOCK: return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); case AKEYCODE_NUM_LOCK: return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); case AKEYCODE_SCROLL_LOCK: return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); default: return oldMetaState; } } bool isMetaKey(int32_t keyCode) { switch (keyCode) { case AKEYCODE_ALT_LEFT: case AKEYCODE_ALT_RIGHT: case AKEYCODE_SHIFT_LEFT: case AKEYCODE_SHIFT_RIGHT: case AKEYCODE_SYM: case AKEYCODE_FUNCTION: case AKEYCODE_CTRL_LEFT: case AKEYCODE_CTRL_RIGHT: case AKEYCODE_META_LEFT: case AKEYCODE_META_RIGHT: case AKEYCODE_CAPS_LOCK: case AKEYCODE_NUM_LOCK: case AKEYCODE_SCROLL_LOCK: return true; default: return false; } } } // namespace android libs/input/TouchVideoFrame.cpp0100644 0000000 0000000 00000006634 13756501735 015452 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #include namespace android { TouchVideoFrame::TouchVideoFrame(uint32_t height, uint32_t width, std::vector data, const struct timeval& timestamp) : mHeight(height), mWidth(width),mData(std::move(data)), mTimestamp(timestamp) { } bool TouchVideoFrame::operator==(const TouchVideoFrame& rhs) const { return mHeight == rhs.mHeight && mWidth == rhs.mWidth && mData == rhs.mData && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec; } uint32_t TouchVideoFrame::getHeight() const { return mHeight; } uint32_t TouchVideoFrame::getWidth() const { return mWidth; } const std::vector& TouchVideoFrame::getData() const { return mData; } const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; } void TouchVideoFrame::rotate(int32_t orientation) { switch (orientation) { case DISPLAY_ORIENTATION_90: rotateQuarterTurn(true /*clockwise*/); break; case DISPLAY_ORIENTATION_180: rotate180(); break; case DISPLAY_ORIENTATION_270: rotateQuarterTurn(false /*clockwise*/); break; } } /** * Rotate once clockwise by a quarter turn === rotate 90 degrees * Rotate once counterclockwise by a quarter turn === rotate 270 degrees * For a clockwise rotation: * An element at position (i, j) is rotated to (j, height - i - 1) * For a counterclockwise rotation: * An element at position (i, j) is rotated to (width - j - 1, i) */ void TouchVideoFrame::rotateQuarterTurn(bool clockwise) { std::vector rotated(mData.size()); for (size_t i = 0; i < mHeight; i++) { for (size_t j = 0; j < mWidth; j++) { size_t iRotated, jRotated; if (clockwise) { iRotated = j; jRotated = mHeight - i - 1; } else { iRotated = mWidth - j - 1; jRotated = i; } size_t indexRotated = iRotated * mHeight + jRotated; rotated[indexRotated] = mData[i * mWidth + j]; } } mData = std::move(rotated); std::swap(mHeight, mWidth); } /** * An element at position (i, j) is rotated to (height - i - 1, width - j - 1) * This is equivalent to moving element [i] to position [height * width - i - 1] * Since element at [height * width - i - 1] would move to position [i], * we can just swap elements [i] and [height * width - i - 1]. */ void TouchVideoFrame::rotate180() { if (mData.size() == 0) { return; } // Just need to swap elements i and (height * width - 1 - i) for (size_t i = 0; i < mData.size() / 2; i++) { std::swap(mData[i], mData[mHeight * mWidth - 1 - i]); } } } // namespace android libs/input/VelocityControl.cpp0100644 0000000 0000000 00000006715 13756501735 015565 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #define LOG_TAG "VelocityControl" //#define LOG_NDEBUG 0 // Log debug messages about acceleration. #define DEBUG_ACCELERATION 0 #include #include #include #include #include namespace android { // --- VelocityControl --- const nsecs_t VelocityControl::STOP_TIME; VelocityControl::VelocityControl() { reset(); } void VelocityControl::setParameters(const VelocityControlParameters& parameters) { mParameters = parameters; reset(); } void VelocityControl::reset() { mLastMovementTime = LLONG_MIN; mRawPosition.x = 0; mRawPosition.y = 0; mVelocityTracker.clear(); } void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if ((deltaX && *deltaX) || (deltaY && *deltaY)) { if (eventTime >= mLastMovementTime + STOP_TIME) { #if DEBUG_ACCELERATION ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", (eventTime - mLastMovementTime) * 0.000001f); #endif reset(); } mLastMovementTime = eventTime; if (deltaX) { mRawPosition.x += *deltaX; } if (deltaY) { mRawPosition.y += *deltaY; } mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); float vx, vy; float scale = mParameters.scale; if (mVelocityTracker.getVelocity(0, &vx, &vy)) { float speed = hypotf(vx, vy) * scale; if (speed >= mParameters.highThreshold) { // Apply full acceleration above the high speed threshold. scale *= mParameters.acceleration; } else if (speed > mParameters.lowThreshold) { // Linearly interpolate the acceleration to apply between the low and high // speed thresholds. scale *= 1 + (speed - mParameters.lowThreshold) / (mParameters.highThreshold - mParameters.lowThreshold) * (mParameters.acceleration - 1); } #if DEBUG_ACCELERATION ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, mParameters.acceleration, vx, vy, speed, scale / mParameters.scale); #endif } else { #if DEBUG_ACCELERATION ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, mParameters.acceleration); #endif } if (deltaX) { *deltaX *= scale; } if (deltaY) { *deltaY *= scale; } } } } // namespace android libs/input/VelocityTracker.cpp0100644 0000000 0000000 00000126562 13756501735 015543 0ustar000000000 0000000 /* * Copyright (C) 2012 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. */ #define LOG_TAG "VelocityTracker" //#define LOG_NDEBUG 0 // Log debug messages about velocity tracking. #define DEBUG_VELOCITY 0 // Log debug messages about the progress of the algorithm itself. #define DEBUG_STRATEGY 0 #include #include #include #include #include #include #include #include #include #include namespace android { // Nanoseconds per milliseconds. static const nsecs_t NANOS_PER_MS = 1000000; // Threshold for determining that a pointer has stopped moving. // Some input devices do not send ACTION_MOVE events in the case where a pointer has // stopped. We need to detect this case so that we can accurately predict the // velocity after the pointer starts moving again. static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS; static float vectorDot(const float* a, const float* b, uint32_t m) { float r = 0; for (size_t i = 0; i < m; i++) { r += *(a++) * *(b++); } return r; } static float vectorNorm(const float* a, uint32_t m) { float r = 0; for (size_t i = 0; i < m; i++) { float t = *(a++); r += t * t; } return sqrtf(r); } #if DEBUG_STRATEGY || DEBUG_VELOCITY static std::string vectorToString(const float* a, uint32_t m) { std::string str; str += "["; for (size_t i = 0; i < m; i++) { if (i) { str += ","; } str += android::base::StringPrintf(" %f", *(a++)); } str += " ]"; return str; } #endif #if DEBUG_STRATEGY static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { std::string str; str = "["; for (size_t i = 0; i < m; i++) { if (i) { str += ","; } str += " ["; for (size_t j = 0; j < n; j++) { if (j) { str += ","; } str += android::base::StringPrintf(" %f", a[rowMajor ? i * n + j : j * m + i]); } str += " ]"; } str += " ]"; return str; } #endif // --- VelocityTracker --- // The default velocity tracker strategy. // Although other strategies are available for testing and comparison purposes, // this is the strategy that applications will actually use. Be very careful // when adjusting the default strategy because it can dramatically affect // (often in a bad way) the user experience. const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; VelocityTracker::VelocityTracker(const char* strategy) : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { char value[PROPERTY_VALUE_MAX]; // Allow the default strategy to be overridden using a system property for debugging. if (!strategy) { int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); if (length > 0) { strategy = value; } else { strategy = DEFAULT_STRATEGY; } } // Configure the strategy. if (!configureStrategy(strategy)) { ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); if (!configureStrategy(DEFAULT_STRATEGY)) { LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", strategy); } } } VelocityTracker::~VelocityTracker() { delete mStrategy; } bool VelocityTracker::configureStrategy(const char* strategy) { mStrategy = createStrategy(strategy); return mStrategy != nullptr; } VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { if (!strcmp("impulse", strategy)) { // Physical model of pushing an object. Quality: VERY GOOD. // Works with duplicate coordinates, unclean finger liftoff. return new ImpulseVelocityTrackerStrategy(); } if (!strcmp("lsq1", strategy)) { // 1st order least squares. Quality: POOR. // Frequently underfits the touch data especially when the finger accelerates // or changes direction. Often underestimates velocity. The direction // is overly influenced by historical touch points. return new LeastSquaresVelocityTrackerStrategy(1); } if (!strcmp("lsq2", strategy)) { // 2nd order least squares. Quality: VERY GOOD. // Pretty much ideal, but can be confused by certain kinds of touch data, // particularly if the panel has a tendency to generate delayed, // duplicate or jittery touch coordinates when the finger is released. return new LeastSquaresVelocityTrackerStrategy(2); } if (!strcmp("lsq3", strategy)) { // 3rd order least squares. Quality: UNUSABLE. // Frequently overfits the touch data yielding wildly divergent estimates // of the velocity when the finger is released. return new LeastSquaresVelocityTrackerStrategy(3); } if (!strcmp("wlsq2-delta", strategy)) { // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL return new LeastSquaresVelocityTrackerStrategy(2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); } if (!strcmp("wlsq2-central", strategy)) { // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL return new LeastSquaresVelocityTrackerStrategy(2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); } if (!strcmp("wlsq2-recent", strategy)) { // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL return new LeastSquaresVelocityTrackerStrategy(2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); } if (!strcmp("int1", strategy)) { // 1st order integrating filter. Quality: GOOD. // Not as good as 'lsq2' because it cannot estimate acceleration but it is // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate // the velocity of a fling but this strategy tends to respond to changes in // direction more quickly and accurately. return new IntegratingVelocityTrackerStrategy(1); } if (!strcmp("int2", strategy)) { // 2nd order integrating filter. Quality: EXPERIMENTAL. // For comparison purposes only. Unlike 'int1' this strategy can compensate // for acceleration but it typically overestimates the effect. return new IntegratingVelocityTrackerStrategy(2); } if (!strcmp("legacy", strategy)) { // Legacy velocity tracker algorithm. Quality: POOR. // For comparison purposes only. This algorithm is strongly influenced by // old data points, consistently underestimates velocity and takes a very long // time to adjust to changes in direction. return new LegacyVelocityTrackerStrategy(); } return nullptr; } void VelocityTracker::clear() { mCurrentPointerIdBits.clear(); mActivePointerId = -1; mStrategy->clear(); } void VelocityTracker::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); mCurrentPointerIdBits = remainingIdBits; if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; } mStrategy->clearPointers(idBits); } void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { while (idBits.count() > MAX_POINTERS) { idBits.clearLastMarkedBit(); } if ((mCurrentPointerIdBits.value & idBits.value) && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) { #if DEBUG_VELOCITY ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.", (eventTime - mLastEventTime) * 0.000001f); #endif // We have not received any movements for too long. Assume that all pointers // have stopped. mStrategy->clear(); } mLastEventTime = eventTime; mCurrentPointerIdBits = idBits; if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); } mStrategy->addMovement(eventTime, idBits, positions); #if DEBUG_VELOCITY ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d", eventTime, idBits.value, mActivePointerId); for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) { uint32_t id = iterBits.firstMarkedBit(); uint32_t index = idBits.getIndexOfBit(id); iterBits.clearBit(id); Estimator estimator; getEstimator(id, &estimator); ALOGD(" %d: position (%0.3f, %0.3f), " "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", id, positions[index].x, positions[index].y, int(estimator.degree), vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(), vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(), estimator.confidence); } #endif } void VelocityTracker::addMovement(const MotionEvent* event) { int32_t actionMasked = event->getActionMasked(); switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_HOVER_ENTER: // Clear all pointers on down before adding the new movement. clear(); break; case AMOTION_EVENT_ACTION_POINTER_DOWN: { // Start a new movement trace for a pointer that just went down. // We do this on down instead of on up because the client may want to query the // final velocity for a pointer that just went up. BitSet32 downIdBits; downIdBits.markBit(event->getPointerId(event->getActionIndex())); clearPointers(downIdBits); break; } case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_HOVER_MOVE: break; default: // Ignore all other actions because they do not convey any new information about // pointer movement. We also want to preserve the last known velocity of the pointers. // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position // of the pointers that went up. ACTION_POINTER_UP does include the new position of // pointers that remained down but we will also receive an ACTION_MOVE with this // information if any of them actually moved. Since we don't know how many pointers // will be going up at once it makes sense to just wait for the following ACTION_MOVE // before adding the movement. return; } size_t pointerCount = event->getPointerCount(); if (pointerCount > MAX_POINTERS) { pointerCount = MAX_POINTERS; } BitSet32 idBits; for (size_t i = 0; i < pointerCount; i++) { idBits.markBit(event->getPointerId(i)); } uint32_t pointerIndex[MAX_POINTERS]; for (size_t i = 0; i < pointerCount; i++) { pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); } nsecs_t eventTime; Position positions[pointerCount]; size_t historySize = event->getHistorySize(); for (size_t h = 0; h < historySize; h++) { eventTime = event->getHistoricalEventTime(h); for (size_t i = 0; i < pointerCount; i++) { uint32_t index = pointerIndex[i]; positions[index].x = event->getHistoricalX(i, h); positions[index].y = event->getHistoricalY(i, h); } addMovement(eventTime, idBits, positions); } eventTime = event->getEventTime(); for (size_t i = 0; i < pointerCount; i++) { uint32_t index = pointerIndex[i]; positions[index].x = event->getX(i); positions[index].y = event->getY(i); } addMovement(eventTime, idBits, positions); } bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { Estimator estimator; if (getEstimator(id, &estimator) && estimator.degree >= 1) { *outVx = estimator.xCoeff[1]; *outVy = estimator.yCoeff[1]; return true; } *outVx = 0; *outVy = 0; return false; } bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { return mStrategy->getEstimator(id, outEstimator); } // --- LeastSquaresVelocityTrackerStrategy --- LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( uint32_t degree, Weighting weighting) : mDegree(degree), mWeighting(weighting) { clear(); } LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { } void LeastSquaresVelocityTrackerStrategy::clear() { mIndex = 0; mMovements[0].idBits.clear(); } void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); mMovements[mIndex].idBits = remainingIdBits; } void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. mIndex++; } if (mIndex == HISTORY_SIZE) { mIndex = 0; } Movement& movement = mMovements[mIndex]; movement.eventTime = eventTime; movement.idBits = idBits; uint32_t count = idBits.count(); for (uint32_t i = 0; i < count; i++) { movement.positions[i] = positions[i]; } } /** * Solves a linear least squares problem to obtain a N degree polynomial that fits * the specified input data as nearly as possible. * * Returns true if a solution is found, false otherwise. * * The input consists of two vectors of data points X and Y with indices 0..m-1 * along with a weight vector W of the same size. * * The output is a vector B with indices 0..n that describes a polynomial * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i] * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized. * * Accordingly, the weight vector W should be initialized by the caller with the * reciprocal square root of the variance of the error in each input data point. * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]). * The weights express the relative importance of each data point. If the weights are * all 1, then the data points are considered to be of equal importance when fitting * the polynomial. It is a good idea to choose weights that diminish the importance * of data points that may have higher than usual error margins. * * Errors among data points are assumed to be independent. W is represented here * as a vector although in the literature it is typically taken to be a diagonal matrix. * * That is to say, the function that generated the input data can be approximated * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n. * * The coefficient of determination (R^2) is also returned to describe the goodness * of fit of the model for the given data. It is a value between 0 and 1, where 1 * indicates perfect correspondence. * * This function first expands the X vector to a m by n matrix A such that * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then * multiplies it by w[i]./ * * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q * and an m by n upper triangular matrix R. Because R is upper triangular (lower * part is all zeroes), we can simplify the decomposition into an m by n matrix * Q1 and a n by n matrix R1 such that A = Q1 R1. * * Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y) * to find B. * * For efficiency, we lay out A and Q column-wise in memory because we frequently * operate on the column vectors. Conversely, we lay out R row-wise. * * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares * http://en.wikipedia.org/wiki/Gram-Schmidt */ static bool solveLeastSquares(const float* x, const float* y, const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { #if DEBUG_STRATEGY ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), vectorToString(x, m).c_str(), vectorToString(y, m).c_str(), vectorToString(w, m).c_str()); #endif // Expand the X vector to a matrix A, pre-multiplied by the weights. float a[n][m]; // column-major order for (uint32_t h = 0; h < m; h++) { a[0][h] = w[h]; for (uint32_t i = 1; i < n; i++) { a[i][h] = a[i - 1][h] * x[h]; } } #if DEBUG_STRATEGY ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str()); #endif // Apply the Gram-Schmidt process to A to obtain its QR decomposition. float q[n][m]; // orthonormal basis, column-major order float r[n][n]; // upper triangular matrix, row-major order for (uint32_t j = 0; j < n; j++) { for (uint32_t h = 0; h < m; h++) { q[j][h] = a[j][h]; } for (uint32_t i = 0; i < j; i++) { float dot = vectorDot(&q[j][0], &q[i][0], m); for (uint32_t h = 0; h < m; h++) { q[j][h] -= dot * q[i][h]; } } float norm = vectorNorm(&q[j][0], m); if (norm < 0.000001f) { // vectors are linearly dependent or zero so no solution #if DEBUG_STRATEGY ALOGD(" - no solution, norm=%f", norm); #endif return false; } float invNorm = 1.0f / norm; for (uint32_t h = 0; h < m; h++) { q[j][h] *= invNorm; } for (uint32_t i = 0; i < n; i++) { r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m); } } #if DEBUG_STRATEGY ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str()); ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str()); // calculate QR, if we factored A correctly then QR should equal A float qr[n][m]; for (uint32_t h = 0; h < m; h++) { for (uint32_t i = 0; i < n; i++) { qr[i][h] = 0; for (uint32_t j = 0; j < n; j++) { qr[i][h] += q[j][h] * r[j][i]; } } } ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str()); #endif // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. // We just work from bottom-right to top-left calculating B's coefficients. float wy[m]; for (uint32_t h = 0; h < m; h++) { wy[h] = y[h] * w[h]; } for (uint32_t i = n; i != 0; ) { i--; outB[i] = vectorDot(&q[i][0], wy, m); for (uint32_t j = n - 1; j > i; j--) { outB[i] -= r[i][j] * outB[j]; } outB[i] /= r[i][i]; } #if DEBUG_STRATEGY ALOGD(" - b=%s", vectorToString(outB, n).c_str()); #endif // Calculate the coefficient of determination as 1 - (SSerr / SStot) where // SSerr is the residual sum of squares (variance of the error), // and SStot is the total sum of squares (variance of the data) where each // has been weighted. float ymean = 0; for (uint32_t h = 0; h < m; h++) { ymean += y[h]; } ymean /= m; float sserr = 0; float sstot = 0; for (uint32_t h = 0; h < m; h++) { float err = y[h] - outB[0]; float term = 1; for (uint32_t i = 1; i < n; i++) { term *= x[h]; err -= term * outB[i]; } sserr += w[h] * w[h] * err * err; float var = y[h] - ymean; sstot += w[h] * w[h] * var * var; } *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; #if DEBUG_STRATEGY ALOGD(" - sserr=%f", sserr); ALOGD(" - sstot=%f", sstot); ALOGD(" - det=%f", *outDet); #endif return true; } /* * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to * the default implementation */ static std::optional> solveUnweightedLeastSquaresDeg2( const float* x, const float* y, size_t count) { // Solving y = a*x^2 + b*x + c float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; for (size_t i = 0; i < count; i++) { float xi = x[i]; float yi = y[i]; float xi2 = xi*xi; float xi3 = xi2*xi; float xi4 = xi3*xi; float xiyi = xi*yi; float xi2yi = xi2*yi; sxi += xi; sxi2 += xi2; sxiyi += xiyi; sxi2yi += xi2yi; syi += yi; sxi3 += xi3; sxi4 += xi4; } float Sxx = sxi2 - sxi*sxi / count; float Sxy = sxiyi - sxi*syi / count; float Sxx2 = sxi3 - sxi*sxi2 / count; float Sx2y = sxi2yi - sxi2*syi / count; float Sx2x2 = sxi4 - sxi2*sxi2 / count; float denominator = Sxx*Sx2x2 - Sxx2*Sxx2; if (denominator == 0) { ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2); return std::nullopt; } // Compute a float numerator = Sx2y*Sxx - Sxy*Sxx2; float a = numerator / denominator; // Compute b numerator = Sxy*Sx2x2 - Sx2y*Sxx2; float b = numerator / denominator; // Compute c float c = syi/count - b * sxi/count - a * sxi2/count; return std::make_optional(std::array({c, b, a})); } bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const { outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. float x[HISTORY_SIZE]; float y[HISTORY_SIZE]; float w[HISTORY_SIZE]; float time[HISTORY_SIZE]; uint32_t m = 0; uint32_t index = mIndex; const Movement& newestMovement = mMovements[mIndex]; do { const Movement& movement = mMovements[index]; if (!movement.idBits.hasBit(id)) { break; } nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } const VelocityTracker::Position& position = movement.getPosition(id); x[m] = position.x; y[m] = position.y; w[m] = chooseWeight(index); time[m] = -age * 0.000000001f; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); if (m == 0) { return false; // no data } // Calculate a least squares polynomial fit. uint32_t degree = mDegree; if (degree > m - 1) { degree = m - 1; } if (degree == 2 && mWeighting == WEIGHTING_NONE) { // Optimize unweighted, quadratic polynomial fit std::optional> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m); std::optional> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m); if (xCoeff && yCoeff) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; outEstimator->confidence = 1; for (size_t i = 0; i <= outEstimator->degree; i++) { outEstimator->xCoeff[i] = (*xCoeff)[i]; outEstimator->yCoeff[i] = (*yCoeff)[i]; } return true; } } else if (degree >= 1) { // General case for an Nth degree polynomial fit float xdet, ydet; uint32_t n = degree + 1; if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = degree; outEstimator->confidence = xdet * ydet; #if DEBUG_STRATEGY ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(), vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence); #endif return true; } } // No velocity data available for this pointer, but we do have its current position. outEstimator->xCoeff[0] = x[0]; outEstimator->yCoeff[0] = y[0]; outEstimator->time = newestMovement.eventTime; outEstimator->degree = 0; outEstimator->confidence = 1; return true; } float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { switch (mWeighting) { case WEIGHTING_DELTA: { // Weight points based on how much time elapsed between them and the next // point so that points that "cover" a shorter time span are weighed less. // delta 0ms: 0.5 // delta 10ms: 1.0 if (index == mIndex) { return 1.0f; } uint32_t nextIndex = (index + 1) % HISTORY_SIZE; float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime) * 0.000001f; if (deltaMillis < 0) { return 0.5f; } if (deltaMillis < 10) { return 0.5f + deltaMillis * 0.05; } return 1.0f; } case WEIGHTING_CENTRAL: { // Weight points based on their age, weighing very recent and very old points less. // age 0ms: 0.5 // age 10ms: 1.0 // age 50ms: 1.0 // age 60ms: 0.5 float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) * 0.000001f; if (ageMillis < 0) { return 0.5f; } if (ageMillis < 10) { return 0.5f + ageMillis * 0.05; } if (ageMillis < 50) { return 1.0f; } if (ageMillis < 60) { return 0.5f + (60 - ageMillis) * 0.05; } return 0.5f; } case WEIGHTING_RECENT: { // Weight points based on their age, weighing older points less. // age 0ms: 1.0 // age 50ms: 1.0 // age 100ms: 0.5 float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) * 0.000001f; if (ageMillis < 50) { return 1.0f; } if (ageMillis < 100) { return 0.5f + (100 - ageMillis) * 0.01f; } return 0.5f; } case WEIGHTING_NONE: default: return 1.0f; } } // --- IntegratingVelocityTrackerStrategy --- IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : mDegree(degree) { } IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { } void IntegratingVelocityTrackerStrategy::clear() { mPointerIdBits.clear(); } void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mPointerIdBits.value &= ~idBits.value; } void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) { uint32_t index = 0; for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { uint32_t id = iterIdBits.clearFirstMarkedBit(); State& state = mPointerState[id]; const VelocityTracker::Position& position = positions[index++]; if (mPointerIdBits.hasBit(id)) { updateState(state, eventTime, position.x, position.y); } else { initState(state, eventTime, position.x, position.y); } } mPointerIdBits = idBits; } bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const { outEstimator->clear(); if (mPointerIdBits.hasBit(id)) { const State& state = mPointerState[id]; populateEstimator(state, outEstimator); return true; } return false; } void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime, float xpos, float ypos) const { state.updateTime = eventTime; state.degree = 0; state.xpos = xpos; state.xvel = 0; state.xaccel = 0; state.ypos = ypos; state.yvel = 0; state.yaccel = 0; } void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const { const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS; const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds if (eventTime <= state.updateTime + MIN_TIME_DELTA) { return; } float dt = (eventTime - state.updateTime) * 0.000000001f; state.updateTime = eventTime; float xvel = (xpos - state.xpos) / dt; float yvel = (ypos - state.ypos) / dt; if (state.degree == 0) { state.xvel = xvel; state.yvel = yvel; state.degree = 1; } else { float alpha = dt / (FILTER_TIME_CONSTANT + dt); if (mDegree == 1) { state.xvel += (xvel - state.xvel) * alpha; state.yvel += (yvel - state.yvel) * alpha; } else { float xaccel = (xvel - state.xvel) / dt; float yaccel = (yvel - state.yvel) / dt; if (state.degree == 1) { state.xaccel = xaccel; state.yaccel = yaccel; state.degree = 2; } else { state.xaccel += (xaccel - state.xaccel) * alpha; state.yaccel += (yaccel - state.yaccel) * alpha; } state.xvel += (state.xaccel * dt) * alpha; state.yvel += (state.yaccel * dt) * alpha; } } state.xpos = xpos; state.ypos = ypos; } void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const { outEstimator->time = state.updateTime; outEstimator->confidence = 1.0f; outEstimator->degree = state.degree; outEstimator->xCoeff[0] = state.xpos; outEstimator->xCoeff[1] = state.xvel; outEstimator->xCoeff[2] = state.xaccel / 2; outEstimator->yCoeff[0] = state.ypos; outEstimator->yCoeff[1] = state.yvel; outEstimator->yCoeff[2] = state.yaccel / 2; } // --- LegacyVelocityTrackerStrategy --- LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { clear(); } LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { } void LegacyVelocityTrackerStrategy::clear() { mIndex = 0; mMovements[0].idBits.clear(); } void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); mMovements[mIndex].idBits = remainingIdBits; } void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) { if (++mIndex == HISTORY_SIZE) { mIndex = 0; } Movement& movement = mMovements[mIndex]; movement.eventTime = eventTime; movement.idBits = idBits; uint32_t count = idBits.count(); for (uint32_t i = 0; i < count; i++) { movement.positions[i] = positions[i]; } } bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const { outEstimator->clear(); const Movement& newestMovement = mMovements[mIndex]; if (!newestMovement.idBits.hasBit(id)) { return false; // no data } // Find the oldest sample that contains the pointer and that is not older than HORIZON. nsecs_t minTime = newestMovement.eventTime - HORIZON; uint32_t oldestIndex = mIndex; uint32_t numTouches = 1; do { uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; const Movement& nextOldestMovement = mMovements[nextOldestIndex]; if (!nextOldestMovement.idBits.hasBit(id) || nextOldestMovement.eventTime < minTime) { break; } oldestIndex = nextOldestIndex; } while (++numTouches < HISTORY_SIZE); // Calculate an exponentially weighted moving average of the velocity estimate // at different points in time measured relative to the oldest sample. // This is essentially an IIR filter. Newer samples are weighted more heavily // than older samples. Samples at equal time points are weighted more or less // equally. // // One tricky problem is that the sample data may be poorly conditioned. // Sometimes samples arrive very close together in time which can cause us to // overestimate the velocity at that time point. Most samples might be measured // 16ms apart but some consecutive samples could be only 0.5sm apart because // the hardware or driver reports them irregularly or in bursts. float accumVx = 0; float accumVy = 0; uint32_t index = oldestIndex; uint32_t samplesUsed = 0; const Movement& oldestMovement = mMovements[oldestIndex]; const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); nsecs_t lastDuration = 0; while (numTouches-- > 1) { if (++index == HISTORY_SIZE) { index = 0; } const Movement& movement = mMovements[index]; nsecs_t duration = movement.eventTime - oldestMovement.eventTime; // If the duration between samples is small, we may significantly overestimate // the velocity. Consequently, we impose a minimum duration constraint on the // samples that we include in the calculation. if (duration >= MIN_DURATION) { const VelocityTracker::Position& position = movement.getPosition(id); float scale = 1000000000.0f / duration; // one over time delta in seconds float vx = (position.x - oldestPosition.x) * scale; float vy = (position.y - oldestPosition.y) * scale; accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); lastDuration = duration; samplesUsed += 1; } } // Report velocity. const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); outEstimator->time = newestMovement.eventTime; outEstimator->confidence = 1; outEstimator->xCoeff[0] = newestPosition.x; outEstimator->yCoeff[0] = newestPosition.y; if (samplesUsed) { outEstimator->xCoeff[1] = accumVx; outEstimator->yCoeff[1] = accumVy; outEstimator->degree = 1; } else { outEstimator->degree = 0; } return true; } // --- ImpulseVelocityTrackerStrategy --- ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() { clear(); } ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { } void ImpulseVelocityTrackerStrategy::clear() { mIndex = 0; mMovements[0].idBits.clear(); } void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); mMovements[mIndex].idBits = remainingIdBits; } void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include // the new pointer. If the eventtimes for both events are identical, just update the data // for this time. // We only compare against the last value, as it is likely that addMovement is called // in chronological order as events occur. mIndex++; } if (mIndex == HISTORY_SIZE) { mIndex = 0; } Movement& movement = mMovements[mIndex]; movement.eventTime = eventTime; movement.idBits = idBits; uint32_t count = idBits.count(); for (uint32_t i = 0; i < count; i++) { movement.positions[i] = positions[i]; } } /** * Calculate the total impulse provided to the screen and the resulting velocity. * * The touchscreen is modeled as a physical object. * Initial condition is discussed below, but for now suppose that v(t=0) = 0 * * The kinetic energy of the object at the release is E=0.5*m*v^2 * Then vfinal = sqrt(2E/m). The goal is to calculate E. * * The kinetic energy at the release is equal to the total work done on the object by the finger. * The total work W is the sum of all dW along the path. * * dW = F*dx, where dx is the piece of path traveled. * Force is change of momentum over time, F = dp/dt = m dv/dt. * Then substituting: * dW = m (dv/dt) * dx = m * v * dv * * Summing along the path, we get: * W = sum(dW) = sum(m * v * dv) = m * sum(v * dv) * Since the mass stays constant, the equation for final velocity is: * vfinal = sqrt(2*sum(v * dv)) * * Here, * dv : change of velocity = (v[i+1]-v[i]) * dx : change of distance = (x[i+1]-x[i]) * dt : change of time = (t[i+1]-t[i]) * v : instantaneous velocity = dx/dt * * The final formula is: * vfinal = sqrt(2) * sqrt(sum((v[i]-v[i-1])*|v[i]|)) for all i * The absolute value is needed to properly account for the sign. If the velocity over a * particular segment descreases, then this indicates braking, which means that negative * work was done. So for two positive, but decreasing, velocities, this contribution would be * negative and will cause a smaller final velocity. * * Initial condition * There are two ways to deal with initial condition: * 1) Assume that v(0) = 0, which would mean that the screen is initially at rest. * This is not entirely accurate. We are only taking the past X ms of touch data, where X is * currently equal to 100. However, a touch event that created a fling probably lasted for longer * than that, which would mean that the user has already been interacting with the touchscreen * and it has probably already been moving. * 2) Assume that the touchscreen has already been moving at a certain velocity, calculate this * initial velocity and the equivalent energy, and start with this initial energy. * Consider an example where we have the following data, consisting of 3 points: * time: t0, t1, t2 * x : x0, x1, x2 * v : 0 , v1, v2 * Here is what will happen in each of these scenarios: * 1) By directly applying the formula above with the v(0) = 0 boundary condition, we will get * vfinal = sqrt(2*(|v1|*(v1-v0) + |v2|*(v2-v1))). This can be simplified since v0=0 * vfinal = sqrt(2*(|v1|*v1 + |v2|*(v2-v1))) = sqrt(2*(v1^2 + |v2|*(v2 - v1))) * since velocity is a real number * 2) If we treat the screen as already moving, then it must already have an energy (per mass) * equal to 1/2*v1^2. Then the initial energy should be 1/2*v1*2, and only the second segment * will contribute to the total kinetic energy (since we can effectively consider that v0=v1). * This will give the following expression for the final velocity: * vfinal = sqrt(2*(1/2*v1^2 + |v2|*(v2-v1))) * This analysis can be generalized to an arbitrary number of samples. * * * Comparing the two equations above, we see that the only mathematical difference * is the factor of 1/2 in front of the first velocity term. * This boundary condition would allow for the "proper" calculation of the case when all of the * samples are equally spaced in time and distance, which should suggest a constant velocity. * * Note that approach 2) is sensitive to the proper ordering of the data in time, since * the boundary condition must be applied to the oldest sample to be accurate. */ static float kineticEnergyToVelocity(float work) { static constexpr float sqrt2 = 1.41421356237; return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2; } static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) { // The input should be in reversed time order (most recent sample at index i=0) // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function static constexpr float SECONDS_PER_NANO = 1E-9; if (count < 2) { return 0; // if 0 or 1 points, velocity is zero } if (t[1] > t[0]) { // Algorithm will still work, but not perfectly ALOGE("Samples provided to calculateImpulseVelocity in the wrong order"); } if (count == 2) { // if 2 points, basic linear calculation if (t[1] == t[0]) { ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]); return 0; } return (x[1] - x[0]) / (SECONDS_PER_NANO * (t[1] - t[0])); } // Guaranteed to have at least 3 points here float work = 0; for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time if (t[i] == t[i-1]) { ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]); continue; } float vprev = kineticEnergyToVelocity(work); // v[i-1] float vcurr = (x[i] - x[i-1]) / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i] work += (vcurr - vprev) * fabsf(vcurr); if (i == count - 1) { work *= 0.5; // initial condition, case 2) above } } return kineticEnergyToVelocity(work); } bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const { outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. float x[HISTORY_SIZE]; float y[HISTORY_SIZE]; nsecs_t time[HISTORY_SIZE]; size_t m = 0; // number of points that will be used for fitting size_t index = mIndex; const Movement& newestMovement = mMovements[mIndex]; do { const Movement& movement = mMovements[index]; if (!movement.idBits.hasBit(id)) { break; } nsecs_t age = newestMovement.eventTime - movement.eventTime; if (age > HORIZON) { break; } const VelocityTracker::Position& position = movement.getPosition(id); x[m] = position.x; y[m] = position.y; time[m] = movement.eventTime; index = (index == 0 ? HISTORY_SIZE : index) - 1; } while (++m < HISTORY_SIZE); if (m == 0) { return false; // no data } outEstimator->xCoeff[0] = 0; outEstimator->yCoeff[0] = 0; outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m); outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m); outEstimator->xCoeff[2] = 0; outEstimator->yCoeff[2] = 0; outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; // similar results to 2nd degree fit outEstimator->confidence = 1; #if DEBUG_STRATEGY ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]); #endif return true; } } // namespace android libs/input/VirtualKeyMap.cpp0100644 0000000 0000000 00000011420 13756501735 015150 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #define LOG_TAG "VirtualKeyMap" #include #include #include #include #include #include #include // Enables debug output for the parser. #define DEBUG_PARSER 0 namespace android { static const char* WHITESPACE = " \t\r"; static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:"; // --- VirtualKeyMap --- VirtualKeyMap::VirtualKeyMap() { } VirtualKeyMap::~VirtualKeyMap() { } std::unique_ptr VirtualKeyMap::load(const std::string& filename) { Tokenizer* t; status_t status = Tokenizer::open(String8(filename.c_str()), &t); if (status != OK) { ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str()); return nullptr; } std::unique_ptr tokenizer(t); // Using 'new' to access a non-public constructor std::unique_ptr map(new VirtualKeyMap()); if (!map) { ALOGE("Error allocating virtual key map."); return nullptr; } Parser parser(map.get(), tokenizer.get()); status = parser.parse(); if (status != OK) { return nullptr; } return map; } // --- VirtualKeyMap::Parser --- VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) : mMap(map), mTokenizer(tokenizer) { } VirtualKeyMap::Parser::~Parser() { } status_t VirtualKeyMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); #endif mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { // Multiple keys can appear on one line or they can be broken up across multiple lines. do { String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); if (token != "0x01") { ALOGE("%s: Unknown virtual key type, expected 0x01.", mTokenizer->getLocation().string()); return BAD_VALUE; } VirtualKeyDefinition defn; bool success = parseNextIntField(&defn.scanCode) && parseNextIntField(&defn.centerX) && parseNextIntField(&defn.centerY) && parseNextIntField(&defn.width) && parseNextIntField(&defn.height); if (!success) { ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", mTokenizer->getLocation().string()); return BAD_VALUE; } #if DEBUG_PARSER ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, " "width=%d, height=%d", defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); #endif mMap->mVirtualKeys.push_back(defn); } while (consumeFieldDelimiterAndSkipWhitespace()); if (!mTokenizer->isEol()) { ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); return BAD_VALUE; } } mTokenizer->nextLine(); } return NO_ERROR; } bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() { mTokenizer->skipDelimiters(WHITESPACE); if (mTokenizer->peekChar() == ':') { mTokenizer->nextChar(); mTokenizer->skipDelimiters(WHITESPACE); return true; } return false; } bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { if (!consumeFieldDelimiterAndSkipWhitespace()) { return false; } String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); char* end; *outValue = strtol(token.string(), &end, 0); if (token.isEmpty() || *end != '\0') { ALOGE("Expected an integer, got '%s'.", token.string()); return false; } return true; } } // namespace android libs/input/tests/0040755 0000000 0000000 00000000000 13756501735 013056 5ustar000000000 0000000 libs/input/tests/Android.bp0100644 0000000 0000000 00000002052 13756501735 014755 0ustar000000000 0000000 // Build the unit tests. cc_test { name: "libinput_tests", srcs: [ "InputChannel_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "InputWindow_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", ], cflags: [ "-Wall", "-Wextra", "-Werror", "-Wno-unused-variable", ], shared_libs: [ "libinput", "libcutils", "libutils", "libbinder", "libui", "libbase", ] } // NOTE: This is a compile time test, and does not need to be // run. All assertions are static_asserts and will fail during // buildtime if something's wrong. cc_library_static { name: "StructLayout_test", srcs: ["StructLayout_test.cpp"], cflags: [ "-O0", "-Wall", "-Werror", "-Wextra", ], shared_libs: [ "libinput", "libcutils", "libutils", "libbinder", "libui", "libbase", ] } libs/input/tests/InputChannel_test.cpp0100644 0000000 0000000 00000016611 13756501735 017213 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #include #include "TestHelpers.h" #include #include #include #include #include #include #include #include namespace android { class InputChannelTest : public testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { // Our purpose here is to verify that the input channel destructor closes the // file descriptor provided to it. One easy way is to provide it with one end // of a pipe and to check for EPIPE on the other end after the channel is destroyed. Pipe pipe; sp inputChannel = new InputChannel("channel name", pipe.sendFd); EXPECT_STREQ("channel name", inputChannel->getName().c_str()) << "channel should have provided name"; EXPECT_EQ(pipe.sendFd, inputChannel->getFd()) << "channel should have provided fd"; inputChannel.clear(); // destroys input channel EXPECT_EQ(-EPIPE, pipe.readSignal()) << "channel should have closed fd when destroyed"; // clean up fds of Pipe endpoints that were closed so we don't try to close them again pipe.sendFd = -1; } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { sp serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; // Name EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str()) << "server channel should have suffixed name"; EXPECT_STREQ("channel name (client)", clientChannel->getName().c_str()) << "client channel should have suffixed name"; // Server->Client communication InputMessage serverMsg; memset(&serverMsg, 0, sizeof(InputMessage)); serverMsg.header.type = InputMessage::TYPE_KEY; serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) << "server channel should be able to send message to client channel"; InputMessage clientMsg; EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) << "client channel should be able to receive message from server channel"; EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) << "client channel should receive the correct message from server channel"; EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) << "client channel should receive the correct message from server channel"; // Client->Server communication InputMessage clientReply; memset(&clientReply, 0, sizeof(InputMessage)); clientReply.header.type = InputMessage::TYPE_FINISHED; clientReply.body.finished.seq = 0x11223344; clientReply.body.finished.handled = true; EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) << "client channel should be able to send message to server channel"; InputMessage serverReply; EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply)) << "server channel should be able to receive message from client channel"; EXPECT_EQ(clientReply.header.type, serverReply.header.type) << "server channel should receive the correct message from client channel"; EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) << "server channel should receive the correct message from client channel"; EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) << "server channel should receive the correct message from client channel"; } TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { sp serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; InputMessage msg; EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg)) << "receiveMessage should have returned WOULD_BLOCK"; } TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { sp serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; serverChannel.clear(); // close server channel InputMessage msg; EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) << "receiveMessage should have returned DEAD_OBJECT"; } TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { sp serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; serverChannel.clear(); // close server channel InputMessage msg; msg.header.type = InputMessage::TYPE_KEY; EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg)) << "sendMessage should have returned DEAD_OBJECT"; } TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { sp serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; std::array classifications = { MotionClassification::NONE, MotionClassification::AMBIGUOUS_GESTURE, MotionClassification::DEEP_PRESS, }; InputMessage serverMsg = {}, clientMsg; serverMsg.header.type = InputMessage::TYPE_MOTION; serverMsg.body.motion.seq = 1; serverMsg.body.motion.pointerCount = 1; for (MotionClassification classification : classifications) { // Send and receive a message with classification serverMsg.body.motion.classification = classification; EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) << "server channel should be able to send message to client channel"; EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) << "client channel should be able to receive message from server channel"; EXPECT_EQ(serverMsg.header.type, clientMsg.header.type); EXPECT_EQ(classification, clientMsg.body.motion.classification) << "Expected to receive " << motionClassificationToString(classification); } } } // namespace android libs/input/tests/InputDevice_test.cpp0100644 0000000 0000000 00000002120 13756501735 017030 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #include #include namespace android { // --- InputDeviceIdentifierTest --- TEST(InputDeviceIdentifierTest, getCanonicalName) { InputDeviceIdentifier identifier; identifier.name = "test device"; ASSERT_EQ(std::string("test_device"), identifier.getCanonicalName()); identifier.name = "deviceName-123 version_C!"; ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName()); } } // namespace androidlibs/input/tests/InputEvent_test.cpp0100644 0000000 0000000 00000062244 13756501735 016727 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #include #include #include #include #include namespace android { // Default display id. static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; class BaseTest : public testing::Test { }; // --- PointerCoordsTest --- class PointerCoordsTest : public BaseTest { }; TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { PointerCoords coords; coords.clear(); ASSERT_EQ(0ULL, coords.bits); } TEST_F(PointerCoordsTest, AxisValues) { float* valuePtr; PointerCoords coords; coords.clear(); // Check invariants when no axes are present. ASSERT_EQ(0, coords.getAxisValue(0)) << "getAxisValue should return zero because axis is not present"; ASSERT_EQ(0, coords.getAxisValue(1)) << "getAxisValue should return zero because axis is not present"; // Set first axis. ASSERT_EQ(OK, coords.setAxisValue(1, 5)); ASSERT_EQ(5, coords.values[0]); ASSERT_EQ(0x4000000000000000ULL, coords.bits); ASSERT_EQ(0, coords.getAxisValue(0)) << "getAxisValue should return zero because axis is not present"; ASSERT_EQ(5, coords.getAxisValue(1)) << "getAxisValue should return value of axis"; // Set an axis with a higher id than all others. (appending value at the end) ASSERT_EQ(OK, coords.setAxisValue(3, 2)); ASSERT_EQ(0x5000000000000000ULL, coords.bits); ASSERT_EQ(5, coords.values[0]); ASSERT_EQ(2, coords.values[1]); ASSERT_EQ(0, coords.getAxisValue(0)) << "getAxisValue should return zero because axis is not present"; ASSERT_EQ(5, coords.getAxisValue(1)) << "getAxisValue should return value of axis"; ASSERT_EQ(0, coords.getAxisValue(2)) << "getAxisValue should return zero because axis is not present"; ASSERT_EQ(2, coords.getAxisValue(3)) << "getAxisValue should return value of axis"; // Set an axis with an id lower than all others. (prepending value at beginning) ASSERT_EQ(OK, coords.setAxisValue(0, 4)); ASSERT_EQ(0xd000000000000000ULL, coords.bits); ASSERT_EQ(4, coords.values[0]); ASSERT_EQ(5, coords.values[1]); ASSERT_EQ(2, coords.values[2]); ASSERT_EQ(4, coords.getAxisValue(0)) << "getAxisValue should return value of axis"; ASSERT_EQ(5, coords.getAxisValue(1)) << "getAxisValue should return value of axis"; ASSERT_EQ(0, coords.getAxisValue(2)) << "getAxisValue should return zero because axis is not present"; ASSERT_EQ(2, coords.getAxisValue(3)) << "getAxisValue should return value of axis"; // Set an axis with an id between the others. (inserting value in the middle) ASSERT_EQ(OK, coords.setAxisValue(2, 1)); ASSERT_EQ(0xf000000000000000ULL, coords.bits); ASSERT_EQ(4, coords.values[0]); ASSERT_EQ(5, coords.values[1]); ASSERT_EQ(1, coords.values[2]); ASSERT_EQ(2, coords.values[3]); ASSERT_EQ(4, coords.getAxisValue(0)) << "getAxisValue should return value of axis"; ASSERT_EQ(5, coords.getAxisValue(1)) << "getAxisValue should return value of axis"; ASSERT_EQ(1, coords.getAxisValue(2)) << "getAxisValue should return value of axis"; ASSERT_EQ(2, coords.getAxisValue(3)) << "getAxisValue should return value of axis"; // Set an existing axis value in place. ASSERT_EQ(OK, coords.setAxisValue(1, 6)); ASSERT_EQ(0xf000000000000000ULL, coords.bits); ASSERT_EQ(4, coords.values[0]); ASSERT_EQ(6, coords.values[1]); ASSERT_EQ(1, coords.values[2]); ASSERT_EQ(2, coords.values[3]); ASSERT_EQ(4, coords.getAxisValue(0)) << "getAxisValue should return value of axis"; ASSERT_EQ(6, coords.getAxisValue(1)) << "getAxisValue should return value of axis"; ASSERT_EQ(1, coords.getAxisValue(2)) << "getAxisValue should return value of axis"; ASSERT_EQ(2, coords.getAxisValue(3)) << "getAxisValue should return value of axis"; // Set maximum number of axes. for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) { ASSERT_EQ(OK, coords.setAxisValue(axis, axis)); } ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); // Try to set one more axis beyond maximum number. // Ensure bits are unchanged. ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100)); ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); } TEST_F(PointerCoordsTest, Parcel) { Parcel parcel; PointerCoords inCoords; inCoords.clear(); PointerCoords outCoords; // Round trip with empty coords. inCoords.writeToParcel(&parcel); parcel.setDataPosition(0); outCoords.readFromParcel(&parcel); ASSERT_EQ(0ULL, outCoords.bits); // Round trip with some values. parcel.freeData(); inCoords.setAxisValue(2, 5); inCoords.setAxisValue(5, 8); inCoords.writeToParcel(&parcel); parcel.setDataPosition(0); outCoords.readFromParcel(&parcel); ASSERT_EQ(outCoords.bits, inCoords.bits); ASSERT_EQ(outCoords.values[0], inCoords.values[0]); ASSERT_EQ(outCoords.values[1], inCoords.values[1]); } // --- KeyEventTest --- class KeyEventTest : public BaseTest { }; TEST_F(KeyEventTest, Properties) { KeyEvent event; // Initialize and get properties. const nsecs_t ARBITRARY_DOWN_TIME = 1; const nsecs_t ARBITRARY_EVENT_TIME = 2; event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_GAMEPAD), event.getSource()); ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); ASSERT_EQ(121, event.getScanCode()); ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); ASSERT_EQ(1, event.getRepeatCount()); ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime()); // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast(AINPUT_SOURCE_JOYSTICK), event.getSource()); // Set display id. constexpr int32_t newDisplayId = 2; event.setDisplayId(newDisplayId); ASSERT_EQ(newDisplayId, event.getDisplayId()); } // --- MotionEventTest --- class MotionEventTest : public BaseTest { protected: static const nsecs_t ARBITRARY_DOWN_TIME; static const nsecs_t ARBITRARY_EVENT_TIME; static const float X_OFFSET; static const float Y_OFFSET; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; const float MotionEventTest::X_OFFSET = 1.0f; const float MotionEventTest::Y_OFFSET = 1.1f; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { PointerProperties pointerProperties[2]; pointerProperties[0].clear(); pointerProperties[0].id = 1; pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerProperties[1].clear(); pointerProperties[1].id = 2; pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; PointerCoords pointerCoords[2]; pointerCoords[0].clear(); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); pointerCoords[1].clear(); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); } void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { // Check properties. ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); ASSERT_EQ(2.1f, event->getYPrecision()); ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime()); ASSERT_EQ(2U, event->getPointerCount()); ASSERT_EQ(1, event->getPointerId(0)); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, event->getToolType(0)); ASSERT_EQ(2, event->getPointerId(1)); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, event->getToolType(1)); ASSERT_EQ(2U, event->getHistorySize()); // Check data. ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0)); ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)-> getAxisValue(AMOTION_EVENT_AXIS_Y)); ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)-> getAxisValue(AMOTION_EVENT_AXIS_Y)); ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)-> getAxisValue(AMOTION_EVENT_AXIS_Y)); ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)-> getAxisValue(AMOTION_EVENT_AXIS_Y)); ASSERT_EQ(211, event->getRawPointerCoords(0)-> getAxisValue(AMOTION_EVENT_AXIS_Y)); ASSERT_EQ(221, event->getRawPointerCoords(1)-> getAxisValue(AMOTION_EVENT_AXIS_Y)); ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); ASSERT_EQ(10, event->getHistoricalRawX(0, 0)); ASSERT_EQ(20, event->getHistoricalRawX(1, 0)); ASSERT_EQ(110, event->getHistoricalRawX(0, 1)); ASSERT_EQ(120, event->getHistoricalRawX(1, 1)); ASSERT_EQ(210, event->getRawX(0)); ASSERT_EQ(220, event->getRawX(1)); ASSERT_EQ(11, event->getHistoricalRawY(0, 0)); ASSERT_EQ(21, event->getHistoricalRawY(1, 0)); ASSERT_EQ(111, event->getHistoricalRawY(0, 1)); ASSERT_EQ(121, event->getHistoricalRawY(1, 1)); ASSERT_EQ(211, event->getRawY(0)); ASSERT_EQ(221, event->getRawY(1)); ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0)); ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0)); ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1)); ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1)); ASSERT_EQ(X_OFFSET + 210, event->getX(0)); ASSERT_EQ(X_OFFSET + 220, event->getX(1)); ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0)); ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0)); ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1)); ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1)); ASSERT_EQ(Y_OFFSET + 211, event->getY(0)); ASSERT_EQ(Y_OFFSET + 221, event->getY(1)); ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); ASSERT_EQ(112, event->getHistoricalPressure(0, 1)); ASSERT_EQ(122, event->getHistoricalPressure(1, 1)); ASSERT_EQ(212, event->getPressure(0)); ASSERT_EQ(222, event->getPressure(1)); ASSERT_EQ(13, event->getHistoricalSize(0, 0)); ASSERT_EQ(23, event->getHistoricalSize(1, 0)); ASSERT_EQ(113, event->getHistoricalSize(0, 1)); ASSERT_EQ(123, event->getHistoricalSize(1, 1)); ASSERT_EQ(213, event->getSize(0)); ASSERT_EQ(223, event->getSize(1)); ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0)); ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0)); ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1)); ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1)); ASSERT_EQ(214, event->getTouchMajor(0)); ASSERT_EQ(224, event->getTouchMajor(1)); ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0)); ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0)); ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1)); ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1)); ASSERT_EQ(215, event->getTouchMinor(0)); ASSERT_EQ(225, event->getTouchMinor(1)); ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0)); ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0)); ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1)); ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1)); ASSERT_EQ(216, event->getToolMajor(0)); ASSERT_EQ(226, event->getToolMajor(1)); ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0)); ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0)); ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1)); ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1)); ASSERT_EQ(217, event->getToolMinor(0)); ASSERT_EQ(227, event->getToolMinor(1)); ASSERT_EQ(18, event->getHistoricalOrientation(0, 0)); ASSERT_EQ(28, event->getHistoricalOrientation(1, 0)); ASSERT_EQ(118, event->getHistoricalOrientation(0, 1)); ASSERT_EQ(128, event->getHistoricalOrientation(1, 1)); ASSERT_EQ(218, event->getOrientation(0)); ASSERT_EQ(228, event->getOrientation(1)); } TEST_F(MotionEventTest, Properties) { MotionEvent event; // Initialize, add samples and check properties. initializeEventWithHistory(&event); ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast(AINPUT_SOURCE_JOYSTICK), event.getSource()); // Set displayId. constexpr int32_t newDisplayId = 2; event.setDisplayId(newDisplayId); ASSERT_EQ(newDisplayId, event.getDisplayId()); // Set action. event.setAction(AMOTION_EVENT_ACTION_CANCEL); ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); // Set meta state. event.setMetaState(AMETA_CTRL_ON); ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState()); } TEST_F(MotionEventTest, CopyFrom_KeepHistory) { MotionEvent event; initializeEventWithHistory(&event); MotionEvent copy; copy.copyFrom(&event, true /*keepHistory*/); ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); } TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { MotionEvent event; initializeEventWithHistory(&event); MotionEvent copy; copy.copyFrom(&event, false /*keepHistory*/); ASSERT_EQ(event.getPointerCount(), copy.getPointerCount()); ASSERT_EQ(0U, copy.getHistorySize()); ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0)); ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1)); ASSERT_EQ(event.getEventTime(), copy.getEventTime()); ASSERT_EQ(event.getX(0), copy.getX(0)); } TEST_F(MotionEventTest, OffsetLocation) { MotionEvent event; initializeEventWithHistory(&event); event.offsetLocation(5.0f, -2.0f); ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset()); ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset()); } TEST_F(MotionEventTest, Scale) { MotionEvent event; initializeEventWithHistory(&event); event.scale(2.0f); ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); ASSERT_EQ(210 * 2, event.getRawX(0)); ASSERT_EQ(211 * 2, event.getRawY(0)); ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0)); ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0)); ASSERT_EQ(212, event.getPressure(0)); ASSERT_EQ(213, event.getSize(0)); ASSERT_EQ(214 * 2, event.getTouchMajor(0)); ASSERT_EQ(215 * 2, event.getTouchMinor(0)); ASSERT_EQ(216 * 2, event.getToolMajor(0)); ASSERT_EQ(217 * 2, event.getToolMinor(0)); ASSERT_EQ(218, event.getOrientation(0)); } TEST_F(MotionEventTest, Parcel) { Parcel parcel; MotionEvent inEvent; initializeEventWithHistory(&inEvent); MotionEvent outEvent; // Round trip. inEvent.writeToParcel(&parcel); parcel.setDataPosition(0); outEvent.readFromParcel(&parcel); ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent)); } static void setRotationMatrix(float matrix[9], float angle) { float sin = sinf(angle); float cos = cosf(angle); matrix[0] = cos; matrix[1] = -sin; matrix[2] = 0; matrix[3] = sin; matrix[4] = cos; matrix[5] = 0; matrix[6] = 0; matrix[7] = 0; matrix[8] = 1.0f; } TEST_F(MotionEventTest, Transform) { // Generate some points on a circle. // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle // of ARC * i degrees clockwise relative to the Y axis. // The geometrical representation is irrelevant to the test, it's just easy to generate // and check rotation. We set the orientation to the same angle. // Coordinate system: down is increasing Y, right is increasing X. const float PI_180 = float(M_PI / 180); const float RADIUS = 10; const float ARC = 36; const float ROTATION = ARC * 2; const size_t pointerCount = 11; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { float angle = float(i * ARC * PI_180); pointerProperties[i].clear(); pointerProperties[i].id = i; pointerCoords[i].clear(); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; // Check original raw X and Y assumption. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); // Now translate the motion event so the circle's origin is at (0,0). event.offsetLocation(-3, -2); // Offsetting the location should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); // Apply a rotation about the origin by ROTATION degrees clockwise. float matrix[9]; setRotationMatrix(matrix, ROTATION * PI_180); event.transform(matrix); // Check the points. for (size_t i = 0; i < pointerCount; i++) { float angle = float((i * ARC + ROTATION) * PI_180); ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001); ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001); ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } TEST_F(MotionEventTest, Initialize_SetsClassification) { std::array classifications = { MotionClassification::NONE, MotionClassification::AMBIGUOUS_GESTURE, MotionClassification::DEEP_PRESS, }; MotionEvent event; constexpr size_t pointerCount = 1; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { pointerProperties[i].clear(); pointerProperties[i].id = i; pointerCoords[i].clear(); } for (MotionClassification classification : classifications) { event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } } } // namespace android libs/input/tests/InputPublisherAndConsumer_test.cpp0100644 0000000 0000000 00000031564 13756501735 021743 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #include "TestHelpers.h" #include #include #include #include #include #include #include #include namespace android { class InputPublisherAndConsumerTest : public testing::Test { protected: sp serverChannel, clientChannel; InputPublisher* mPublisher; InputConsumer* mConsumer; PreallocatedInputEventFactory mEventFactory; virtual void SetUp() { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); mPublisher = new InputPublisher(serverChannel); mConsumer = new InputConsumer(clientChannel); } virtual void TearDown() { if (mPublisher) { delete mPublisher; mPublisher = nullptr; } if (mConsumer) { delete mConsumer; mConsumer = nullptr; } serverChannel.clear(); clientChannel.clear(); } void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); } void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; constexpr uint32_t seq = 15; constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; constexpr int32_t keyCode = AKEYCODE_ENTER; constexpr int32_t scanCode = 13; constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; constexpr int32_t repeatCount = 1; constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; uint32_t consumeSeq; InputEvent* event; status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) << "consumer should have returned a key event"; KeyEvent* keyEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(displayId, keyEvent->getDisplayId()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); EXPECT_EQ(scanCode, keyEvent->getScanCode()); EXPECT_EQ(metaState, keyEvent->getMetaState()); EXPECT_EQ(repeatCount, keyEvent->getRepeatCount()); EXPECT_EQ(downTime, keyEvent->getDownTime()); EXPECT_EQ(eventTime, keyEvent->getEventTime()); status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; uint32_t finishedSeq = 0; bool handled = false; status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; constexpr uint32_t seq = 15; constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; constexpr int32_t actionButton = 0; constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; constexpr float xOffset = -10; constexpr float yOffset = -20; constexpr float xPrecision = 0.25; constexpr float yPrecision = 0.5; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { pointerProperties[i].clear(); pointerProperties[i].id = (i + 2) % pointerCount; pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerCoords[i].clear(); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; uint32_t consumeSeq; InputEvent* event; status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << "consumer should have returned a motion event"; MotionEvent* motionEvent = static_cast(event); EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(displayId, motionEvent->getDisplayId()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(classification, motionEvent->getClassification()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); EXPECT_EQ(0U, motionEvent->getHistorySize()); for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), motionEvent->getRawX(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getRawY(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, motionEvent->getX(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, motionEvent->getY(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i)); } status = mConsumer->sendFinishedSignal(seq, false); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; uint32_t finishedSeq = 0; bool handled = true; status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_FALSE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); } TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { pointerProperties[i].clear(); pointerCoords[i].clear(); } status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { status_t status; const size_t pointerCount = 0; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { status_t status; const size_t pointerCount = MAX_POINTERS + 1; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { pointerProperties[i].clear(); pointerCoords[i].clear(); } status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } } // namespace android libs/input/tests/InputWindow_test.cpp0100644 0000000 0000000 00000006333 13756501735 017112 0ustar000000000 0000000 /* * Copyright 2018 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. */ #include #include #include #include #include namespace android { namespace test { TEST(InputWindowInfo, ParcellingWithoutToken) { InputWindowInfo i; i.token = nullptr; Parcel p; ASSERT_EQ(OK, i.write(p)); p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_TRUE(i2.token == nullptr); } TEST(InputWindowInfo, Parcelling) { sp touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); i.name = "Foobar"; i.layoutParamsFlags = 7; i.layoutParamsType = 39; i.dispatchingTimeout = 12; i.frameLeft = 93; i.frameTop = 34; i.frameRight = 16; i.frameBottom = 19; i.surfaceInset = 17; i.globalScaleFactor = 0.3; i.windowXScale = 0.4; i.windowYScale = 0.5; i.visible = false; i.canReceiveKeys = false; i.hasFocus = false; i.hasWallpaper = false; i.paused = false; i.layer = 7; i.ownerPid = 19; i.ownerUid = 24; i.inputFeatures = 29; i.displayId = 34; i.portalToDisplayId = 2; i.replaceTouchableRegionWithCrop = true; i.touchableRegionCropHandle = touchableRegionCropHandle; Parcel p; i.write(p); p.setDataPosition(0); InputWindowInfo i2 = InputWindowInfo::read(p); ASSERT_EQ(i.token, i2.token); ASSERT_EQ(i.name, i2.name); ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); ASSERT_EQ(i.frameLeft, i2.frameLeft); ASSERT_EQ(i.frameTop, i2.frameTop); ASSERT_EQ(i.frameRight, i2.frameRight); ASSERT_EQ(i.frameBottom, i2.frameBottom); ASSERT_EQ(i.surfaceInset, i2.surfaceInset); ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); ASSERT_EQ(i.windowXScale, i2.windowXScale); ASSERT_EQ(i.windowYScale, i2.windowYScale); ASSERT_EQ(i.visible, i2.visible); ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys); ASSERT_EQ(i.hasFocus, i2.hasFocus); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); ASSERT_EQ(i.paused, i2.paused); ASSERT_EQ(i.layer, i2.layer); ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.displayId, i2.displayId); ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); } } // namespace test } // namespace android libs/input/tests/StructLayout_test.cpp0100644 0000000 0000000 00000005563 13756501735 017311 0ustar000000000 0000000 /* * 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. */ #include #include namespace android { #define CHECK_OFFSET(type, member, expected_offset) \ static_assert((offsetof(type, member) == (expected_offset)), "") struct Foo { uint32_t dummy; PointerCoords coords; }; void TestPointerCoordsAlignment() { CHECK_OFFSET(Foo, coords, 8); } void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage, body, 8); CHECK_OFFSET(InputMessage::Body::Key, seq, 0); CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); CHECK_OFFSET(InputMessage::Body::Key, displayId, 24); CHECK_OFFSET(InputMessage::Body::Key, action, 28); CHECK_OFFSET(InputMessage::Body::Key, flags, 32); CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36); CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40); CHECK_OFFSET(InputMessage::Body::Key, metaState, 44); CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48); CHECK_OFFSET(InputMessage::Body::Key, downTime, 56); CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24); CHECK_OFFSET(InputMessage::Body::Motion, action, 28); CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32); CHECK_OFFSET(InputMessage::Body::Motion, flags, 36); CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40); CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44); CHECK_OFFSET(InputMessage::Body::Motion, classification, 48); CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52); CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56); CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64); CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88); CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } } // namespace android libs/input/tests/TestHelpers.h0100644 0000000 0000000 00000003335 13756501735 015472 0ustar000000000 0000000 /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TESTHELPERS_H #define TESTHELPERS_H #include #include namespace android { class Pipe { public: int sendFd; int receiveFd; Pipe() { int fds[2]; ::pipe(fds); receiveFd = fds[0]; sendFd = fds[1]; } ~Pipe() { if (sendFd != -1) { ::close(sendFd); } if (receiveFd != -1) { ::close(receiveFd); } } status_t writeSignal() { ssize_t nWritten = ::write(sendFd, "*", 1); return nWritten == 1 ? 0 : -errno; } status_t readSignal() { char buf[1]; ssize_t nRead = ::read(receiveFd, buf, 1); return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno; } }; class DelayedTask : public Thread { int mDelayMillis; public: explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } protected: virtual ~DelayedTask() { } virtual void doTask() = 0; virtual bool threadLoop() { usleep(mDelayMillis * 1000); doTask(); return false; } }; } // namespace android #endif // TESTHELPERS_H libs/input/tests/TouchVideoFrame_test.cpp0100644 0000000 0000000 00000015263 13756501735 017651 0ustar000000000 0000000 /* * Copyright 2019 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. */ #include #include namespace android { namespace test { static const struct timeval TIMESTAMP = {1, 2}; TEST(TouchVideoFrame, Constructor) { const std::vector data = {1, 2, 3, 4, 5, 6}; constexpr uint32_t height = 3; constexpr uint32_t width = 2; TouchVideoFrame frame(height, width, data, TIMESTAMP); ASSERT_EQ(data, frame.getData()); ASSERT_EQ(height, frame.getHeight()); ASSERT_EQ(width, frame.getWidth()); ASSERT_EQ(TIMESTAMP.tv_sec, frame.getTimestamp().tv_sec); ASSERT_EQ(TIMESTAMP.tv_usec, frame.getTimestamp().tv_usec); } TEST(TouchVideoFrame, Equality) { const std::vector data = {1, 2, 3, 4, 5, 6}; constexpr uint32_t height = 3; constexpr uint32_t width = 2; TouchVideoFrame frame(height, width, data, TIMESTAMP); TouchVideoFrame identicalFrame(height, width, data, TIMESTAMP); ASSERT_EQ(frame, identicalFrame); // The two cases below create an invalid frame, but it is OK for comparison purposes. // There aren't any checks currently enforced on the frame dimensions and data // Change height TouchVideoFrame changedHeightFrame(height + 1, width, data, TIMESTAMP); ASSERT_FALSE(frame == changedHeightFrame); // Change width TouchVideoFrame changedWidthFrame(height, width + 1, data, TIMESTAMP); ASSERT_FALSE(frame == changedWidthFrame); // Change data const std::vector differentData = {1, 2, 3, 3, 5, 6}; TouchVideoFrame changedDataFrame(height, width, differentData, TIMESTAMP); ASSERT_FALSE(frame == changedDataFrame); // Change timestamp const struct timeval differentTimestamp = {TIMESTAMP.tv_sec + 1, TIMESTAMP.tv_usec + 1}; TouchVideoFrame changedTimestampFrame(height, width, data, differentTimestamp); ASSERT_FALSE(frame == changedTimestampFrame); } // --- Rotate 90 degrees --- TEST(TouchVideoFrame, Rotate90_0x0) { TouchVideoFrame frame(0, 0, {}, TIMESTAMP); TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_1x1) { TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_3x2) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_3x2_4times) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); frame.rotate(DISPLAY_ORIENTATION_90); frame.rotate(DISPLAY_ORIENTATION_90); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameOriginal); } // --- Rotate 180 degrees --- TEST(TouchVideoFrame, Rotate180_0x0) { TouchVideoFrame frame(0, 0, {}, TIMESTAMP); TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_180); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate180_1x1) { TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_180); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate180_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_180); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate180_3x2) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_180); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate180_3x2_2times) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_180); frame.rotate(DISPLAY_ORIENTATION_180); ASSERT_EQ(frame, frameOriginal); } TEST(TouchVideoFrame, Rotate180_3x3) { TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP); TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_180); ASSERT_EQ(frame, frameRotated); } // --- Rotate 270 degrees --- TEST(TouchVideoFrame, Rotate270_0x0) { TouchVideoFrame frame(0, 0, {}, TIMESTAMP); TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_1x1) { TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_3x2) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_3x2_4times) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); frame.rotate(DISPLAY_ORIENTATION_270); frame.rotate(DISPLAY_ORIENTATION_270); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameOriginal); } } // namespace test } // namespace android libs/input/tests/VelocityTracker_test.cpp0100644 0000000 0000000 00000114123 13756501735 017732 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ #define LOG_TAG "VelocityTracker_test" #include #include #include #include #include #include using namespace std::chrono_literals; using android::base::StringPrintf; namespace android { constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests // velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV // here EV = expected value, tol = VELOCITY_TOLERANCE constexpr float VELOCITY_TOLERANCE = 0.2; // estimate coefficients must be within 0.001% of the target value constexpr float COEFFICIENT_TOLERANCE = 0.00001; // --- VelocityTrackerTest --- class VelocityTrackerTest : public testing::Test { }; /* * Similar to EXPECT_NEAR, but ensures that the difference between the two float values * is at most a certain fraction of the target value. * If fraction is zero, require exact match. */ static void EXPECT_NEAR_BY_FRACTION(float actual, float target, float fraction) { float tolerance = fabsf(target * fraction); if (target == 0 && fraction != 0) { // If target is zero, this would force actual == target, which is too harsh. // Relax this requirement a little. The value is determined empirically from the // coefficients computed by the quadratic least squares algorithms. tolerance = 1E-6; } EXPECT_NEAR(actual, target, tolerance); } static void checkVelocity(float Vactual, float Vtarget) { EXPECT_NEAR_BY_FRACTION(Vactual, Vtarget, VELOCITY_TOLERANCE); } static void checkCoefficient(float actual, float target) { EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE); } struct Position { float x; float y; /** * If both values are NAN, then this is considered to be an empty entry (no pointer data). * If only one of the values is NAN, this is still a valid entry, * because we may only care about a single axis. */ bool isValid() const { return !(isnan(x) && isnan(y)); } }; struct MotionEventEntry { std::chrono::nanoseconds eventTime; std::vector positions; }; static BitSet32 getValidPointers(const std::vector& positions) { BitSet32 pointers; for (size_t i = 0; i < positions.size(); i++) { if (positions[i].isValid()) { pointers.markBit(i); } } return pointers; } static uint32_t getChangingPointerId(BitSet32 pointers, BitSet32 otherPointers) { BitSet32 difference(pointers.value ^ otherPointers.value); uint32_t pointerId = difference.clearFirstMarkedBit(); EXPECT_EQ(0U, difference.value) << "Only 1 pointer can enter or leave at a time"; return pointerId; } static int32_t resolveAction(const std::vector& lastPositions, const std::vector& currentPositions, const std::vector& nextPositions) { BitSet32 pointers = getValidPointers(currentPositions); const uint32_t pointerCount = pointers.count(); BitSet32 lastPointers = getValidPointers(lastPositions); const uint32_t lastPointerCount = lastPointers.count(); if (lastPointerCount < pointerCount) { // A new pointer is down uint32_t pointerId = getChangingPointerId(pointers, lastPointers); return AMOTION_EVENT_ACTION_POINTER_DOWN | (pointerId << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); } BitSet32 nextPointers = getValidPointers(nextPositions); const uint32_t nextPointerCount = nextPointers.count(); if (pointerCount > nextPointerCount) { // An existing pointer is leaving uint32_t pointerId = getChangingPointerId(pointers, nextPointers); return AMOTION_EVENT_ACTION_POINTER_UP | (pointerId << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); } return AMOTION_EVENT_ACTION_MOVE; } static std::vector createMotionEventStream( const std::vector& motions) { if (motions.empty()) { ADD_FAILURE() << "Need at least 1 sample to create a MotionEvent. Received empty vector."; } std::vector events; for (size_t i = 0; i < motions.size(); i++) { const MotionEventEntry& entry = motions[i]; BitSet32 pointers = getValidPointers(entry.positions); const uint32_t pointerCount = pointers.count(); int32_t action; if (i == 0) { action = AMOTION_EVENT_ACTION_DOWN; EXPECT_EQ(1U, pointerCount) << "First event should only have 1 pointer"; } else if (i == motions.size() - 1) { EXPECT_EQ(1U, pointerCount) << "Last event should only have 1 pointer"; action = AMOTION_EVENT_ACTION_UP; } else { const MotionEventEntry& previousEntry = motions[i-1]; const MotionEventEntry& nextEntry = motions[i+1]; action = resolveAction(previousEntry.positions, entry.positions, nextEntry.positions); } PointerCoords coords[pointerCount]; PointerProperties properties[pointerCount]; uint32_t pointerIndex = 0; while(!pointers.isEmpty()) { uint32_t pointerId = pointers.clearFirstMarkedBit(); coords[pointerIndex].clear(); // We are treating column positions as pointerId EXPECT_TRUE(entry.positions[pointerId].isValid()) << "The entry at pointerId must be valid"; coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_X, entry.positions[pointerId].x); coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_Y, entry.positions[pointerId].y); properties[pointerIndex].id = pointerId; properties[pointerIndex].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerIndex++; } EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); } return events; } static void computeAndCheckVelocity(const char* strategy, const std::vector& motions, int32_t axis, float targetVelocity) { VelocityTracker vt(strategy); float Vx, Vy; std::vector events = createMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy); switch (axis) { case AMOTION_EVENT_AXIS_X: checkVelocity(Vx, targetVelocity); break; case AMOTION_EVENT_AXIS_Y: checkVelocity(Vy, targetVelocity); break; default: FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y"; } } static void computeAndCheckQuadraticEstimate(const std::vector& motions, const std::array& coefficients) { VelocityTracker vt("lsq2"); std::vector events = createMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } VelocityTracker::Estimator estimator; EXPECT_TRUE(vt.getEstimator(0, &estimator)); for (size_t i = 0; i< coefficients.size(); i++) { checkCoefficient(estimator.xCoeff[i], coefficients[i]); checkCoefficient(estimator.yCoeff[i], coefficients[i]); } } /* * ================== VelocityTracker tests generated manually ===================================== */ TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { // Same coordinate is reported 2 times in a row // It is difficult to determine the correct answer here, but at least the direction // of the reported velocity should be positive. std::vector motions = { {0ms, {{ 273, NAN}}}, {12585us, {{293, NAN}}}, {14730us, {{293, NAN}}}, {14730us, {{293, NAN}}}, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600); } TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { // Same coordinate is reported 3 times in a row std::vector motions = { { 0ms, {{293, NAN}} }, { 6132us, {{293, NAN}} }, { 11283us, {{293, NAN}} }, { 11283us, {{293, NAN}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0); } TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // Fixed velocity at 5 points per 10 milliseconds std::vector motions = { { 0ms, {{0, NAN}} }, { 10ms, {{5, NAN}} }, { 20ms, {{10, NAN}} }, { 20ms, {{10, NAN}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500); } /** * ================== VelocityTracker tests generated by recording real events ===================== * * To add a test, record the input coordinates and event times to all calls * to void VelocityTracker::addMovement(const MotionEvent* event). * Also record all calls to VelocityTracker::clear(). * Finally, record the output of VelocityTracker::getVelocity(...) * This will give you the necessary data to create a new test. * * Another good way to generate this data is to use 'dumpsys input' just after the event has * occurred. */ // --------------- Recorded by hand on swordfish --------------------------------------------------- TEST_F(VelocityTrackerTest, SwordfishFlingDown) { // Recording of a fling on Swordfish that could cause a fling in the wrong direction std::vector motions = { { 0ms, {{271, 96}} }, { 16071042ns, {{269.786346, 106.922775}} }, { 35648403ns, {{267.983063, 156.660034}} }, { 52313925ns, {{262.638397, 220.339081}} }, { 68976522ns, {{266.138824, 331.581116}} }, { 85639375ns, {{274.79245, 428.113159}} }, { 96948871ns, {{274.79245, 428.113159}} }, { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309); } // --------------- Recorded by hand on sailfish, generated by a script ----------------------------- // For some of these tests, the X-direction velocity checking has been removed, because the lsq2 // and the impulse VelocityTrackerStrategies did not agree within 20%. // Since the flings were recorded in the Y-direction, the intentional user action should only // be relevant for the Y axis. // There have been also cases where lsq2 and impulse disagreed more than 20% in the Y-direction. // Those recordings have been discarded because we didn't feel one strategy's interpretation was // more correct than another's but didn't want to increase the tolerance for the entire test suite. // // There are 18 tests total below: 9 in the positive Y direction and 9 in the opposite. // The recordings were loosely binned into 3 categories - slow, faster, and fast, which roughly // characterizes the velocity of the finger motion. // These can be treated approximately as: // slow - less than 1 page gets scrolled // faster - more than 1 page gets scrolled, but less than 3 // fast - entire list is scrolled (fling is done as hard as possible) TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { // Sailfish - fling up - slow - 1 std::vector motions = { { 235089067457000ns, {{528.00, 983.00}} }, { 235089084684000ns, {{527.00, 981.00}} }, { 235089093349000ns, {{527.00, 977.00}} }, { 235089095677625ns, {{527.00, 975.93}} }, { 235089101859000ns, {{527.00, 970.00}} }, { 235089110378000ns, {{528.00, 960.00}} }, { 235089112497111ns, {{528.25, 957.51}} }, { 235089118760000ns, {{531.00, 946.00}} }, { 235089126686000ns, {{535.00, 931.00}} }, { 235089129316820ns, {{536.33, 926.02}} }, { 235089135199000ns, {{540.00, 914.00}} }, { 235089144297000ns, {{546.00, 896.00}} }, { 235089146136443ns, {{547.21, 892.36}} }, { 235089152923000ns, {{553.00, 877.00}} }, { 235089160784000ns, {{559.00, 851.00}} }, { 235089162955851ns, {{560.66, 843.82}} }, { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181); computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064); } TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { // Sailfish - fling up - slow - 2 std::vector motions = { { 235110560704000ns, {{522.00, 1107.00}} }, { 235110575764000ns, {{522.00, 1107.00}} }, { 235110584385000ns, {{522.00, 1107.00}} }, { 235110588421179ns, {{521.52, 1106.52}} }, { 235110592830000ns, {{521.00, 1106.00}} }, { 235110601385000ns, {{520.00, 1104.00}} }, { 235110605088160ns, {{519.14, 1102.27}} }, { 235110609952000ns, {{518.00, 1100.00}} }, { 235110618353000ns, {{517.00, 1093.00}} }, { 235110621755146ns, {{516.60, 1090.17}} }, { 235110627010000ns, {{517.00, 1081.00}} }, { 235110634785000ns, {{518.00, 1063.00}} }, { 235110638422450ns, {{518.87, 1052.58}} }, { 235110643161000ns, {{520.00, 1039.00}} }, { 235110651767000ns, {{524.00, 1011.00}} }, { 235110655089581ns, {{525.54, 1000.19}} }, { 235110660368000ns, {{530.00, 980.00}} }, { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238); } TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { // Sailfish - fling up - slow - 3 std::vector motions = { { 792536237000ns, {{580.00, 1317.00}} }, { 792541538987ns, {{580.63, 1311.94}} }, { 792544613000ns, {{581.00, 1309.00}} }, { 792552301000ns, {{583.00, 1295.00}} }, { 792558362309ns, {{585.13, 1282.92}} }, { 792560828000ns, {{586.00, 1278.00}} }, { 792569446000ns, {{589.00, 1256.00}} }, { 792575185095ns, {{591.54, 1241.41}} }, { 792578491000ns, {{593.00, 1233.00}} }, { 792587044000ns, {{597.00, 1211.00}} }, { 792592008172ns, {{600.28, 1195.92}} }, { 792594616000ns, {{602.00, 1188.00}} }, { 792603129000ns, {{607.00, 1167.00}} }, { 792608831290ns, {{609.48, 1155.83}} }, { 792612321000ns, {{611.00, 1149.00}} }, { 792620768000ns, {{615.00, 1131.00}} }, { 792625653873ns, {{617.32, 1121.73}} }, { 792629200000ns, {{619.00, 1115.00}} }, { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664); } TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { // Sailfish - fling up - faster - 1 std::vector motions = { { 235160420675000ns, {{610.00, 1042.00}} }, { 235160428220000ns, {{609.00, 1026.00}} }, { 235160436544000ns, {{609.00, 1024.00}} }, { 235160441852394ns, {{609.64, 1020.82}} }, { 235160444878000ns, {{610.00, 1019.00}} }, { 235160452673000ns, {{613.00, 1006.00}} }, { 235160458519743ns, {{617.18, 992.06}} }, { 235160461061000ns, {{619.00, 986.00}} }, { 235160469798000ns, {{627.00, 960.00}} }, { 235160475186713ns, {{632.22, 943.02}} }, { 235160478051000ns, {{635.00, 934.00}} }, { 235160486489000ns, {{644.00, 906.00}} }, { 235160491853697ns, {{649.56, 890.56}} }, { 235160495177000ns, {{653.00, 881.00}} }, { 235160504148000ns, {{662.00, 858.00}} }, { 235160509231495ns, {{666.81, 845.37}} }, { 235160512603000ns, {{670.00, 837.00}} }, { 235160520366000ns, {{679.00, 814.00}} }, { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619); } TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { // Sailfish - fling up - faster - 2 std::vector motions = { { 847153808000ns, {{576.00, 1264.00}} }, { 847171174000ns, {{576.00, 1262.00}} }, { 847179640000ns, {{576.00, 1257.00}} }, { 847185187540ns, {{577.41, 1249.22}} }, { 847187487000ns, {{578.00, 1246.00}} }, { 847195710000ns, {{581.00, 1227.00}} }, { 847202027059ns, {{583.93, 1209.40}} }, { 847204324000ns, {{585.00, 1203.00}} }, { 847212672000ns, {{590.00, 1176.00}} }, { 847218861395ns, {{594.36, 1157.11}} }, { 847221190000ns, {{596.00, 1150.00}} }, { 847230484000ns, {{602.00, 1124.00}} }, { 847235701400ns, {{607.56, 1103.83}} }, { 847237986000ns, {{610.00, 1095.00}} }, { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395); } TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { // Sailfish - fling up - faster - 3 std::vector motions = { { 235200532789000ns, {{507.00, 1084.00}} }, { 235200549221000ns, {{507.00, 1083.00}} }, { 235200557841000ns, {{507.00, 1081.00}} }, { 235200558051189ns, {{507.00, 1080.95}} }, { 235200566314000ns, {{507.00, 1078.00}} }, { 235200574876586ns, {{508.97, 1070.12}} }, { 235200575006000ns, {{509.00, 1070.00}} }, { 235200582900000ns, {{514.00, 1054.00}} }, { 235200591276000ns, {{525.00, 1023.00}} }, { 235200591701829ns, {{525.56, 1021.42}} }, { 235200600064000ns, {{542.00, 976.00}} }, { 235200608519000ns, {{563.00, 911.00}} }, { 235200608527086ns, {{563.02, 910.94}} }, { 235200616933000ns, {{590.00, 844.00}} }, { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367); } TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { // Sailfish - fling up - fast - 1 std::vector motions = { { 920922149000ns, {{561.00, 1412.00}} }, { 920930185000ns, {{559.00, 1377.00}} }, { 920930262463ns, {{558.98, 1376.66}} }, { 920938547000ns, {{559.00, 1371.00}} }, { 920947096857ns, {{562.91, 1342.68}} }, { 920947302000ns, {{563.00, 1342.00}} }, { 920955502000ns, {{577.00, 1272.00}} }, { 920963931021ns, {{596.87, 1190.54}} }, { 920963987000ns, {{597.00, 1190.00}} }, { 920972530000ns, {{631.00, 1093.00}} }, { 920980765511ns, {{671.31, 994.68}} }, { 920980906000ns, {{672.00, 993.00}} }, { 920989261000ns, {{715.00, 903.00}} }, { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117); } TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { // Sailfish - fling up - fast - 2 std::vector motions = { { 235247153233000ns, {{518.00, 1168.00}} }, { 235247170452000ns, {{517.00, 1167.00}} }, { 235247178908000ns, {{515.00, 1159.00}} }, { 235247179556213ns, {{514.85, 1158.39}} }, { 235247186821000ns, {{515.00, 1125.00}} }, { 235247195265000ns, {{521.00, 1051.00}} }, { 235247196389476ns, {{521.80, 1041.15}} }, { 235247203649000ns, {{538.00, 932.00}} }, { 235247212253000ns, {{571.00, 794.00}} }, { 235247213222491ns, {{574.72, 778.45}} }, { 235247220736000ns, {{620.00, 641.00}} }, { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891); } TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { // Sailfish - fling up - fast - 3 std::vector motions = { { 235302568736000ns, {{529.00, 1167.00}} }, { 235302576644000ns, {{523.00, 1140.00}} }, { 235302579395063ns, {{520.91, 1130.61}} }, { 235302585140000ns, {{522.00, 1130.00}} }, { 235302593615000ns, {{527.00, 1065.00}} }, { 235302596207444ns, {{528.53, 1045.12}} }, { 235302602102000ns, {{559.00, 872.00}} }, { 235302610545000ns, {{652.00, 605.00}} }, { 235302613019881ns, {{679.26, 526.73}} }, { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875); } TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { // Sailfish - fling down - slow - 1 std::vector motions = { { 235655749552755ns, {{582.00, 432.49}} }, { 235655750638000ns, {{582.00, 433.00}} }, { 235655758865000ns, {{582.00, 440.00}} }, { 235655766221523ns, {{581.16, 448.43}} }, { 235655767594000ns, {{581.00, 450.00}} }, { 235655776044000ns, {{580.00, 462.00}} }, { 235655782890696ns, {{579.18, 474.35}} }, { 235655784360000ns, {{579.00, 477.00}} }, { 235655792795000ns, {{578.00, 496.00}} }, { 235655799559531ns, {{576.27, 515.04}} }, { 235655800612000ns, {{576.00, 518.00}} }, { 235655809535000ns, {{574.00, 542.00}} }, { 235655816988015ns, {{572.17, 564.86}} }, { 235655817685000ns, {{572.00, 567.00}} }, { 235655825981000ns, {{569.00, 595.00}} }, { 235655833808653ns, {{566.26, 620.60}} }, { 235655834541000ns, {{566.00, 623.00}} }, { 235655842893000ns, {{563.00, 649.00}} }, { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854); } TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { // Sailfish - fling down - slow - 2 std::vector motions = { { 235671152083370ns, {{485.24, 558.28}} }, { 235671154126000ns, {{485.00, 559.00}} }, { 235671162497000ns, {{484.00, 566.00}} }, { 235671168750511ns, {{483.27, 573.29}} }, { 235671171071000ns, {{483.00, 576.00}} }, { 235671179390000ns, {{482.00, 588.00}} }, { 235671185417210ns, {{481.31, 598.98}} }, { 235671188173000ns, {{481.00, 604.00}} }, { 235671196371000ns, {{480.00, 624.00}} }, { 235671202084196ns, {{479.27, 639.98}} }, { 235671204235000ns, {{479.00, 646.00}} }, { 235671212554000ns, {{478.00, 673.00}} }, { 235671219471011ns, {{476.39, 697.12}} }, { 235671221159000ns, {{476.00, 703.00}} }, { 235671229592000ns, {{474.00, 734.00}} }, { 235671236281462ns, {{472.43, 758.38}} }, { 235671238098000ns, {{472.00, 765.00}} }, { 235671246532000ns, {{470.00, 799.00}} }, { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816); } TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { // Sailfish - fling down - slow - 3 std::vector motions = { { 170983201000ns, {{557.00, 533.00}} }, { 171000668000ns, {{556.00, 534.00}} }, { 171007359750ns, {{554.73, 535.27}} }, { 171011197000ns, {{554.00, 536.00}} }, { 171017660000ns, {{552.00, 540.00}} }, { 171024201831ns, {{549.97, 544.73}} }, { 171027333000ns, {{549.00, 547.00}} }, { 171034603000ns, {{545.00, 557.00}} }, { 171041043371ns, {{541.98, 567.55}} }, { 171043147000ns, {{541.00, 571.00}} }, { 171051052000ns, {{536.00, 586.00}} }, { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456); } TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { // Sailfish - fling down - faster - 1 std::vector motions = { { 235695280333000ns, {{558.00, 451.00}} }, { 235695283971237ns, {{558.43, 454.45}} }, { 235695289038000ns, {{559.00, 462.00}} }, { 235695297388000ns, {{561.00, 478.00}} }, { 235695300638465ns, {{561.83, 486.25}} }, { 235695305265000ns, {{563.00, 498.00}} }, { 235695313591000ns, {{564.00, 521.00}} }, { 235695317305492ns, {{564.43, 532.68}} }, { 235695322181000ns, {{565.00, 548.00}} }, { 235695330709000ns, {{565.00, 577.00}} }, { 235695333972227ns, {{565.00, 588.10}} }, { 235695339250000ns, {{565.00, 609.00}} }, { 235695347839000ns, {{565.00, 642.00}} }, { 235695351313257ns, {{565.00, 656.18}} }, { 235695356412000ns, {{565.00, 677.00}} }, { 235695364899000ns, {{563.00, 710.00}} }, { 235695368118682ns, {{562.24, 722.52}} }, { 235695373403000ns, {{564.00, 744.00}} }, { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039); } TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { // Sailfish - fling down - faster - 2 std::vector motions = { { 235709624766000ns, {{535.00, 579.00}} }, { 235709642256000ns, {{534.00, 580.00}} }, { 235709643350278ns, {{533.94, 580.06}} }, { 235709650760000ns, {{532.00, 584.00}} }, { 235709658615000ns, {{530.00, 593.00}} }, { 235709660170495ns, {{529.60, 594.78}} }, { 235709667095000ns, {{527.00, 606.00}} }, { 235709675616000ns, {{524.00, 628.00}} }, { 235709676983261ns, {{523.52, 631.53}} }, { 235709684289000ns, {{521.00, 652.00}} }, { 235709692763000ns, {{518.00, 682.00}} }, { 235709693804993ns, {{517.63, 685.69}} }, { 235709701438000ns, {{515.00, 709.00}} }, { 235709709830000ns, {{512.00, 739.00}} }, { 235709710626776ns, {{511.72, 741.85}} }, { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273); } TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { // Sailfish - fling down - faster - 3 std::vector motions = { { 235727628927000ns, {{540.00, 440.00}} }, { 235727636810000ns, {{537.00, 454.00}} }, { 235727646176000ns, {{536.00, 454.00}} }, { 235727653586628ns, {{535.12, 456.65}} }, { 235727654557000ns, {{535.00, 457.00}} }, { 235727663024000ns, {{534.00, 465.00}} }, { 235727670410103ns, {{533.04, 479.45}} }, { 235727670691000ns, {{533.00, 480.00}} }, { 235727679255000ns, {{531.00, 501.00}} }, { 235727687233704ns, {{529.09, 526.73}} }, { 235727687628000ns, {{529.00, 528.00}} }, { 235727696113000ns, {{526.00, 558.00}} }, { 235727704057546ns, {{523.18, 588.98}} }, { 235727704576000ns, {{523.00, 591.00}} }, { 235727713099000ns, {{520.00, 626.00}} }, { 235727720880776ns, {{516.33, 655.36}} }, { 235727721580000ns, {{516.00, 658.00}} }, { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627); } TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { // Sailfish - fling down - fast - 1 std::vector motions = { { 235762352849000ns, {{467.00, 286.00}} }, { 235762360250000ns, {{443.00, 344.00}} }, { 235762362787412ns, {{434.77, 363.89}} }, { 235762368807000ns, {{438.00, 359.00}} }, { 235762377220000ns, {{425.00, 423.00}} }, { 235762379608561ns, {{421.31, 441.17}} }, { 235762385698000ns, {{412.00, 528.00}} }, { 235762394133000ns, {{406.00, 648.00}} }, { 235762396429369ns, {{404.37, 680.67}} }, { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547); } TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { // Sailfish - fling down - fast - 2 std::vector motions = { { 235772487188000ns, {{576.00, 204.00}} }, { 235772495159000ns, {{553.00, 236.00}} }, { 235772503568000ns, {{551.00, 240.00}} }, { 235772508192247ns, {{545.55, 254.17}} }, { 235772512051000ns, {{541.00, 266.00}} }, { 235772520794000ns, {{520.00, 337.00}} }, { 235772525015263ns, {{508.92, 394.43}} }, { 235772529174000ns, {{498.00, 451.00}} }, { 235772537635000ns, {{484.00, 589.00}} }, { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453); } TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { // Sailfish - fling down - fast - 3 std::vector motions = { { 507650295000ns, {{628.00, 233.00}} }, { 507658234000ns, {{605.00, 269.00}} }, { 507666784000ns, {{601.00, 274.00}} }, { 507669660483ns, {{599.65, 275.68}} }, { 507675427000ns, {{582.00, 308.00}} }, { 507683740000ns, {{541.00, 404.00}} }, { 507686506238ns, {{527.36, 435.95}} }, { 507692220000ns, {{487.00, 581.00}} }, { 507700707000ns, {{454.00, 792.00}} }, { 507703352649ns, {{443.71, 857.77}} }, { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP }; computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875); } /** * ================== Multiple pointers ============================================================ * * Three fingers quickly tap the screen. Since this is a tap, the velocities should be zero. * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be * part of the fitted data), this can cause large velocity values to be reported instead. */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) { std::vector motions = { { 0us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, { 10800us, {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN { 10800us, {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_DOWN { 267300us, {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_UP { 267300us, {{1063, 1128}, {NAN, NAN}, {397, 1747}} }, // POINTER_UP { 272700us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, }; // Velocity should actually be zero, but we expect 0.016 here instead. // This is close enough to zero, and is likely caused by division by a very small number. computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016); computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0); } /** * ================== Tests for least squares fitting ============================================== * * Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy * getEstimator function. In particular: * - inside the function, time gets converted from nanoseconds to seconds * before being used in the fit. * - any values that are older than 100 ms are being discarded. * - the newest time gets subtracted from all of the other times before being used in the fit. * So these tests have to be designed with those limitations in mind. * * General approach for the tests below: * We only used timestamps in milliseconds, 0 ms, 1 ms, and 2 ms, to be sure that * we are well within the HORIZON range. * When specifying the expected values of the coefficients, we treat the x values as if * they were in ms. Then, to adjust for the time units, the coefficients get progressively * multiplied by powers of 1E3. * For example: * data: t(ms), x * 1 ms, 1 * 2 ms, 4 * 3 ms, 9 * The coefficients are (0, 0, 1). * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2). */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) { std::vector motions = { { 0ms, {{1, 1}} }, // 0 s { 1ms, {{1, 1}} }, // 0.001 s { 2ms, {{1, 1}} }, // 0.002 s { 2ms, {{1, 1}} }, // ACTION_UP }; // The data used for the fit will be as follows: // time(s), position // -0.002, 1 // -0.001, 1 // -0.ms, 1 computeAndCheckQuadraticEstimate(motions, std::array({1, 0, 0})); } /* * Straight line y = x :: the constant and quadratic coefficients are zero. */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) { std::vector motions = { { 0ms, {{-2, -2}} }, { 1ms, {{-1, -1}} }, { 2ms, {{-0, -0}} }, { 2ms, {{-0, -0}} }, // ACTION_UP }; // The data used for the fit will be as follows: // time(s), position // -0.002, -2 // -0.001, -1 // -0.000, 0 computeAndCheckQuadraticEstimate(motions, std::array({0, 1E3, 0})); } /* * Parabola */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) { std::vector motions = { { 0ms, {{1, 1}} }, { 1ms, {{4, 4}} }, { 2ms, {{8, 8}} }, { 2ms, {{8, 8}} }, // ACTION_UP }; // The data used for the fit will be as follows: // time(s), position // -0.002, 1 // -0.001, 4 // -0.000, 8 computeAndCheckQuadraticEstimate(motions, std::array({8, 4.5E3, 0.5E6})); } /* * Parabola */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) { std::vector motions = { { 0ms, {{1, 1}} }, { 1ms, {{4, 4}} }, { 2ms, {{9, 9}} }, { 2ms, {{9, 9}} }, // ACTION_UP }; // The data used for the fit will be as follows: // time(s), position // -0.002, 1 // -0.001, 4 // -0.000, 9 computeAndCheckQuadraticEstimate(motions, std::array({9, 6E3, 1E6})); } /* * Parabola :: y = x^2 :: the constant and linear coefficients are zero. */ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) { std::vector motions = { { 0ms, {{4, 4}} }, { 1ms, {{1, 1}} }, { 2ms, {{0, 0}} }, { 2ms, {{0, 0}} }, // ACTION_UP }; // The data used for the fit will be as follows: // time(s), position // -0.002, 4 // -0.001, 1 // -0.000, 0 computeAndCheckQuadraticEstimate(motions, std::array({0, 0E3, 1E6})); } } // namespace android libs/math/0040755 0000000 0000000 00000000000 13756501735 011506 5ustar000000000 0000000 libs/math/Android.bp0100644 0000000 0000000 00000001402 13756501735 013403 0ustar000000000 0000000 // Copyright (C) 2017 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. cc_library_static { name: "libmath", host_supported: true, vendor_available: true, export_include_dirs: ["include"], } subdirs = ["tests"] libs/math/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756501735 014626 0ustar000000000 0000000 libs/math/NOTICE0100644 0000000 0000000 00000024707 13756501735 012421 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS libs/math/OWNERS0100644 0000000 0000000 00000000162 13756501735 012442 0ustar000000000 0000000 jaesoo@google.com jiyong@google.com mathias@google.com pawin@google.com randolphs@google.com romainguy@google.com libs/math/include/0040755 0000000 0000000 00000000000 13756501735 013131 5ustar000000000 0000000 libs/math/include/math/0040755 0000000 0000000 00000000000 13756501735 014062 5ustar000000000 0000000 libs/math/include/math/TMatHelpers.h0100644 0000000 0000000 00000046744 13756501735 016437 0ustar000000000 0000000 /* * Copyright 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include #ifndef LIKELY #define LIKELY_DEFINED_LOCAL #ifdef __cplusplus # define LIKELY( exp ) (__builtin_expect( !!(exp), true )) # define UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) #else # define LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) # define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) #endif #endif #define PURE __attribute__((pure)) #if __cplusplus >= 201402L #define CONSTEXPR constexpr #else #define CONSTEXPR #endif namespace android { namespace details { // ------------------------------------------------------------------------------------- /* * No user serviceable parts here. * * Don't use this file directly, instead include ui/mat*.h */ /* * Matrix utilities */ namespace matrix { inline constexpr int transpose(int v) { return v; } inline constexpr float transpose(float v) { return v; } inline constexpr double transpose(double v) { return v; } inline constexpr int trace(int v) { return v; } inline constexpr float trace(float v) { return v; } inline constexpr double trace(double v) { return v; } /* * Matrix inversion */ template MATRIX PURE gaussJordanInverse(const MATRIX& src) { typedef typename MATRIX::value_type T; static constexpr unsigned int N = MATRIX::NUM_ROWS; MATRIX tmp(src); MATRIX inverted(1); for (size_t i = 0; i < N; ++i) { // look for largest element in i'th column size_t swap = i; T t = std::abs(tmp[i][i]); for (size_t j = i + 1; j < N; ++j) { const T t2 = std::abs(tmp[j][i]); if (t2 > t) { swap = j; t = t2; } } if (swap != i) { // swap columns. std::swap(tmp[i], tmp[swap]); std::swap(inverted[i], inverted[swap]); } const T denom(tmp[i][i]); for (size_t k = 0; k < N; ++k) { tmp[i][k] /= denom; inverted[i][k] /= denom; } // Factor out the lower triangle for (size_t j = 0; j < N; ++j) { if (j != i) { const T d = tmp[j][i]; for (size_t k = 0; k < N; ++k) { tmp[j][k] -= tmp[i][k] * d; inverted[j][k] -= inverted[i][k] * d; } } } } return inverted; } //------------------------------------------------------------------------------ // 2x2 matrix inverse is easy. template CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) { typedef typename MATRIX::value_type T; // Assuming the input matrix is: // | a b | // | c d | // // The analytic inverse is // | d -b | // | -c a | / (a d - b c) // // Importantly, our matrices are column-major! MATRIX inverted(MATRIX::NO_INIT); const T a = x[0][0]; const T c = x[0][1]; const T b = x[1][0]; const T d = x[1][1]; const T det((a * d) - (b * c)); inverted[0][0] = d / det; inverted[0][1] = -c / det; inverted[1][0] = -b / det; inverted[1][1] = a / det; return inverted; } //------------------------------------------------------------------------------ // From the Wikipedia article on matrix inversion's section on fast 3x3 // matrix inversion: // http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices template CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) { typedef typename MATRIX::value_type T; // Assuming the input matrix is: // | a b c | // | d e f | // | g h i | // // The analytic inverse is // | A B C |^T // | D E F | // | G H I | / determinant // // Which is // | A D G | // | B E H | // | C F I | / determinant // // Where: // A = (ei - fh), B = (fg - di), C = (dh - eg) // D = (ch - bi), E = (ai - cg), F = (bg - ah) // G = (bf - ce), H = (cd - af), I = (ae - bd) // // and the determinant is a*A + b*B + c*C (The rule of Sarrus) // // Importantly, our matrices are column-major! MATRIX inverted(MATRIX::NO_INIT); const T a = x[0][0]; const T b = x[1][0]; const T c = x[2][0]; const T d = x[0][1]; const T e = x[1][1]; const T f = x[2][1]; const T g = x[0][2]; const T h = x[1][2]; const T i = x[2][2]; // Do the full analytic inverse const T A = e * i - f * h; const T B = f * g - d * i; const T C = d * h - e * g; inverted[0][0] = A; // A inverted[0][1] = B; // B inverted[0][2] = C; // C inverted[1][0] = c * h - b * i; // D inverted[1][1] = a * i - c * g; // E inverted[1][2] = b * g - a * h; // F inverted[2][0] = b * f - c * e; // G inverted[2][1] = c * d - a * f; // H inverted[2][2] = a * e - b * d; // I const T det(a * A + b * B + c * C); for (size_t col = 0; col < 3; ++col) { for (size_t row = 0; row < 3; ++row) { inverted[col][row] /= det; } } return inverted; } /** * Inversion function which switches on the matrix size. * @warning This function assumes the matrix is invertible. The result is * undefined if it is not. It is the responsibility of the caller to * make sure the matrix is not singular. */ template inline constexpr MATRIX PURE inverse(const MATRIX& matrix) { static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted"); return (MATRIX::NUM_ROWS == 2) ? fastInverse2(matrix) : ((MATRIX::NUM_ROWS == 3) ? fastInverse3(matrix) : gaussJordanInverse(matrix)); } template CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) { // pre-requisite: // lhs : D columns, R rows // rhs : C columns, D rows // res : C columns, R rows static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS, "matrices can't be multiplied. invalid dimensions."); static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS, "invalid dimension of matrix multiply result."); static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS, "invalid dimension of matrix multiply result."); MATRIX_R res(MATRIX_R::NO_INIT); for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) { res[col] = lhs * rhs[col]; } return res; } // transpose. this handles matrices of matrices template CONSTEXPR MATRIX PURE transpose(const MATRIX& m) { // for now we only handle square matrix transpose static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices"); MATRIX result(MATRIX::NO_INIT); for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) { result[col][row] = transpose(m[row][col]); } } return result; } // trace. this handles matrices of matrices template CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) { static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices"); typename MATRIX::value_type result(0); for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { result += trace(m[col][col]); } return result; } // diag. this handles matrices of matrices template CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) { static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices"); typename MATRIX::col_type result(MATRIX::col_type::NO_INIT); for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { result[col] = m[col][col]; } return result; } //------------------------------------------------------------------------------ // This is taken from the Imath MatrixAlgo code, and is identical to Eigen. template TQuaternion extractQuat(const MATRIX& mat) { typedef typename MATRIX::value_type T; TQuaternion quat(TQuaternion::NO_INIT); // Compute the trace to see if it is positive or not. const T trace = mat[0][0] + mat[1][1] + mat[2][2]; // check the sign of the trace if (LIKELY(trace > 0)) { // trace is positive T s = std::sqrt(trace + 1); quat.w = T(0.5) * s; s = T(0.5) / s; quat.x = (mat[1][2] - mat[2][1]) * s; quat.y = (mat[2][0] - mat[0][2]) * s; quat.z = (mat[0][1] - mat[1][0]) * s; } else { // trace is negative // Find the index of the greatest diagonal size_t i = 0; if (mat[1][1] > mat[0][0]) { i = 1; } if (mat[2][2] > mat[i][i]) { i = 2; } // Get the next indices: (n+1)%3 static constexpr size_t next_ijk[3] = { 1, 2, 0 }; size_t j = next_ijk[i]; size_t k = next_ijk[j]; T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1); quat[i] = T(0.5) * s; if (s != 0) { s = T(0.5) / s; } quat.w = (mat[j][k] - mat[k][j]) * s; quat[j] = (mat[i][j] + mat[j][i]) * s; quat[k] = (mat[i][k] + mat[k][i]) * s; } return quat; } template String8 asString(const MATRIX& m) { String8 s; for (size_t c = 0; c < MATRIX::COL_SIZE; c++) { s.append("| "); for (size_t r = 0; r < MATRIX::ROW_SIZE; r++) { s.appendFormat("%7.2f ", m[r][c]); } s.append("|\n"); } return s; } } // namespace matrix // ------------------------------------------------------------------------------------- /* * TMatProductOperators implements basic arithmetic and basic compound assignments * operators on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TMatProductOperators BASE will automatically * get all the functionality here. */ template class BASE, typename T> class TMatProductOperators { public: // multiply by a scalar BASE& operator *= (T v) { BASE& lhs(static_cast< BASE& >(*this)); for (size_t col = 0; col < BASE::NUM_COLS; ++col) { lhs[col] *= v; } return lhs; } // matrix *= matrix template const BASE& operator *= (const BASE& rhs) { BASE& lhs(static_cast< BASE& >(*this)); lhs = matrix::multiply >(lhs, rhs); return lhs; } // divide by a scalar BASE& operator /= (T v) { BASE& lhs(static_cast< BASE& >(*this)); for (size_t col = 0; col < BASE::NUM_COLS; ++col) { lhs[col] /= v; } return lhs; } // matrix * matrix, result is a matrix of the same type than the lhs matrix template friend CONSTEXPR BASE PURE operator *(const BASE& lhs, const BASE& rhs) { return matrix::multiply >(lhs, rhs); } }; /* * TMatSquareFunctions implements functions on a matrix of type BASE. * * BASE only needs to implement: * - operator[] * - col_type * - row_type * - COL_SIZE * - ROW_SIZE * * By simply inheriting from TMatSquareFunctions BASE will automatically * get all the functionality here. */ template class BASE, typename T> class TMatSquareFunctions { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ friend inline CONSTEXPR BASE PURE inverse(const BASE& matrix) { return matrix::inverse(matrix); } friend inline constexpr BASE PURE transpose(const BASE& m) { return matrix::transpose(m); } friend inline constexpr T PURE trace(const BASE& m) { return matrix::trace(m); } }; template class BASE, typename T> class TMatHelpers { public: constexpr inline size_t getColumnSize() const { return BASE::COL_SIZE; } constexpr inline size_t getRowSize() const { return BASE::ROW_SIZE; } constexpr inline size_t getColumnCount() const { return BASE::NUM_COLS; } constexpr inline size_t getRowCount() const { return BASE::NUM_ROWS; } constexpr inline size_t size() const { return BASE::ROW_SIZE; } // for TVec*<> // array access constexpr T const* asArray() const { return &static_cast const &>(*this)[0][0]; } // element access inline constexpr T const& operator()(size_t row, size_t col) const { return static_cast const &>(*this)[col][row]; } inline T& operator()(size_t row, size_t col) { return static_cast&>(*this)[col][row]; } template static CONSTEXPR BASE translate(const VEC& t) { BASE r; r[BASE::NUM_COLS-1] = t; return r; } template static constexpr BASE scale(const VEC& s) { return BASE(s); } friend inline CONSTEXPR BASE PURE abs(BASE m) { for (size_t col = 0; col < BASE::NUM_COLS; ++col) { m[col] = abs(m[col]); } return m; } }; // functions for 3x3 and 4x4 matrices template class BASE, typename T> class TMatTransform { public: inline constexpr TMatTransform() { static_assert(BASE::NUM_ROWS == 3 || BASE::NUM_ROWS == 4, "3x3 or 4x4 matrices only"); } template static CONSTEXPR BASE rotate(A radian, const VEC& about) { BASE r; T c = std::cos(radian); T s = std::sin(radian); if (about.x == 1 && about.y == 0 && about.z == 0) { r[1][1] = c; r[2][2] = c; r[1][2] = s; r[2][1] = -s; } else if (about.x == 0 && about.y == 1 && about.z == 0) { r[0][0] = c; r[2][2] = c; r[2][0] = s; r[0][2] = -s; } else if (about.x == 0 && about.y == 0 && about.z == 1) { r[0][0] = c; r[1][1] = c; r[0][1] = s; r[1][0] = -s; } else { VEC nabout = normalize(about); typename VEC::value_type x = nabout.x; typename VEC::value_type y = nabout.y; typename VEC::value_type z = nabout.z; T nc = 1 - c; T xy = x * y; T yz = y * z; T zx = z * x; T xs = x * s; T ys = y * s; T zs = z * s; r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys; r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs; r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c; // Clamp results to -1, 1. for (size_t col = 0; col < 3; ++col) { for (size_t row = 0; row < 3; ++row) { r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1)); } } } return r; } /** * Create a matrix from euler angles using YPR around YXZ respectively * @param yaw about Y axis * @param pitch about X axis * @param roll about Z axis */ template < typename Y, typename P, typename R, typename = typename std::enable_if::value >::type, typename = typename std::enable_if::value >::type, typename = typename std::enable_if::value >::type > static CONSTEXPR BASE eulerYXZ(Y yaw, P pitch, R roll) { return eulerZYX(roll, pitch, yaw); } /** * Create a matrix from euler angles using YPR around ZYX respectively * @param roll about X axis * @param pitch about Y axis * @param yaw about Z axis * * The euler angles are applied in ZYX order. i.e: a vector is first rotated * about X (roll) then Y (pitch) and then Z (yaw). */ template < typename Y, typename P, typename R, typename = typename std::enable_if::value >::type, typename = typename std::enable_if::value >::type, typename = typename std::enable_if::value >::type > static CONSTEXPR BASE eulerZYX(Y yaw, P pitch, R roll) { BASE r; T cy = std::cos(yaw); T sy = std::sin(yaw); T cp = std::cos(pitch); T sp = std::sin(pitch); T cr = std::cos(roll); T sr = std::sin(roll); T cc = cr * cy; T cs = cr * sy; T sc = sr * cy; T ss = sr * sy; r[0][0] = cp * cy; r[0][1] = cp * sy; r[0][2] = -sp; r[1][0] = sp * sc - cs; r[1][1] = sp * ss + cc; r[1][2] = cp * sr; r[2][0] = sp * cc + ss; r[2][1] = sp * cs - sc; r[2][2] = cp * cr; // Clamp results to -1, 1. for (size_t col = 0; col < 3; ++col) { for (size_t row = 0; row < 3; ++row) { r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1)); } } return r; } TQuaternion toQuaternion() const { return matrix::extractQuat(static_cast&>(*this)); } }; template class BASE, typename T> class TMatDebug { public: friend std::ostream& operator<<(std::ostream& stream, const BASE& m) { for (size_t row = 0; row < BASE::NUM_ROWS; ++row) { if (row != 0) { stream << std::endl; } if (row == 0) { stream << "/ "; } else if (row == BASE::NUM_ROWS-1) { stream << "\\ "; } else { stream << "| "; } for (size_t col = 0; col < BASE::NUM_COLS; ++col) { stream << std::setw(10) << std::to_string(m[col][row]); } if (row == 0) { stream << " \\"; } else if (row == BASE::NUM_ROWS-1) { stream << " /"; } else { stream << " |"; } } return stream; } String8 asString() const { return matrix::asString(static_cast&>(*this)); } }; // ------------------------------------------------------------------------------------- } // namespace details } // namespace android #ifdef LIKELY_DEFINED_LOCAL #undef LIKELY_DEFINED_LOCAL #undef LIKELY #undef UNLIKELY #endif //LIKELY_DEFINED_LOCAL #undef PURE #undef CONSTEXPR libs/math/include/math/TQuatHelpers.h0100644 0000000 0000000 00000023323 13756501735 016614 0ustar000000000 0000000 /* * Copyright 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. */ #pragma once #include #include #include #include #include #define PURE __attribute__((pure)) namespace android { namespace details { // ------------------------------------------------------------------------------------- /* * No user serviceable parts here. * * Don't use this file directly, instead include ui/quat.h */ /* * TQuatProductOperators implements basic arithmetic and basic compound assignment * operators on a quaternion of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TQuatProductOperators BASE will automatically * get all the functionality here. */ template class QUATERNION, typename T> class TQuatProductOperators { public: /* compound assignment from a another quaternion of the same size but different * element type. */ template QUATERNION& operator *= (const QUATERNION& r) { QUATERNION& q = static_cast&>(*this); q = q * r; return q; } /* compound assignment products by a scalar */ QUATERNION& operator *= (T v) { QUATERNION& lhs = static_cast&>(*this); for (size_t i = 0; i < QUATERNION::size(); i++) { lhs[i] *= v; } return lhs; } QUATERNION& operator /= (T v) { QUATERNION& lhs = static_cast&>(*this); for (size_t i = 0; i < QUATERNION::size(); i++) { lhs[i] /= v; } return lhs; } /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ /* The operators below handle operation between quaternion of the same size * but of a different element type. */ template friend inline constexpr QUATERNION PURE operator *(const QUATERNION& q, const QUATERNION& r) { // could be written as: // return QUATERNION( // q.w*r.w - dot(q.xyz, r.xyz), // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz)); return QUATERNION( q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z, q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y, q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x, q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); } template friend inline constexpr TVec3 PURE operator *(const QUATERNION& q, const TVec3& v) { // note: if q is known to be a unit quaternion, then this simplifies to: // TVec3 t = 2 * cross(q.xyz, v) // return v + (q.w * t) + cross(q.xyz, t) return imaginary(q * QUATERNION(v, 0) * inverse(q)); } /* For quaternions, we use explicit "by a scalar" products because it's much faster * than going (implicitly) through the quaternion multiplication. * For reference: we could use the code below instead, but it would be a lot slower. * friend inline * constexpr BASE PURE operator *(const BASE& q, const BASE& r) { * return BASE( * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z, * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y, * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x, * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); * */ friend inline constexpr QUATERNION PURE operator *(QUATERNION q, T scalar) { // don't pass q by reference because we need a copy anyways return q *= scalar; } friend inline constexpr QUATERNION PURE operator *(T scalar, QUATERNION q) { // don't pass q by reference because we need a copy anyways return q *= scalar; } friend inline constexpr QUATERNION PURE operator /(QUATERNION q, T scalar) { // don't pass q by reference because we need a copy anyways return q /= scalar; } }; /* * TQuatFunctions implements functions on a quaternion of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TQuatFunctions BASE will automatically * get all the functionality here. */ template class QUATERNION, typename T> class TQuatFunctions { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ template friend inline constexpr T PURE dot(const QUATERNION& p, const QUATERNION& q) { return p.x * q.x + p.y * q.y + p.z * q.z + p.w * q.w; } friend inline constexpr T PURE norm(const QUATERNION& q) { return std::sqrt( dot(q, q) ); } friend inline constexpr T PURE length(const QUATERNION& q) { return norm(q); } friend inline constexpr T PURE length2(const QUATERNION& q) { return dot(q, q); } friend inline constexpr QUATERNION PURE normalize(const QUATERNION& q) { return length(q) ? q / length(q) : QUATERNION(1); } friend inline constexpr QUATERNION PURE conj(const QUATERNION& q) { return QUATERNION(q.w, -q.x, -q.y, -q.z); } friend inline constexpr QUATERNION PURE inverse(const QUATERNION& q) { return conj(q) * (1 / dot(q, q)); } friend inline constexpr T PURE real(const QUATERNION& q) { return q.w; } friend inline constexpr TVec3 PURE imaginary(const QUATERNION& q) { return q.xyz; } friend inline constexpr QUATERNION PURE unreal(const QUATERNION& q) { return QUATERNION(q.xyz, 0); } friend inline constexpr QUATERNION PURE cross(const QUATERNION& p, const QUATERNION& q) { return unreal(p*q); } friend inline QUATERNION PURE exp(const QUATERNION& q) { const T nq(norm(q.xyz)); return std::exp(q.w)*QUATERNION((sin(nq)/nq)*q.xyz, cos(nq)); } friend inline QUATERNION PURE log(const QUATERNION& q) { const T nq(norm(q)); return QUATERNION((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq)); } friend inline QUATERNION PURE pow(const QUATERNION& q, T a) { // could also be computed as: exp(a*log(q)); const T nq(norm(q)); const T theta(a*std::acos(q.w / nq)); return std::pow(nq, a) * QUATERNION(normalize(q.xyz) * std::sin(theta), std::cos(theta)); } friend inline QUATERNION PURE slerp(const QUATERNION& p, const QUATERNION& q, T t) { // could also be computed as: pow(q * inverse(p), t) * p; const T d = dot(p, q); const T npq = sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| const T a = std::acos(std::abs(d) / npq); const T a0 = a * (1 - t); const T a1 = a * t; const T isina = 1 / sin(a); const T s0 = std::sin(a0) * isina; const T s1 = std::sin(a1) * isina; // ensure we're taking the "short" side return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q); } friend inline constexpr QUATERNION PURE lerp(const QUATERNION& p, const QUATERNION& q, T t) { return ((1 - t) * p) + (t * q); } friend inline constexpr QUATERNION PURE nlerp(const QUATERNION& p, const QUATERNION& q, T t) { return normalize(lerp(p, q, t)); } friend inline constexpr QUATERNION PURE positive(const QUATERNION& q) { return q.w < 0 ? -q : q; } }; /* * TQuatDebug implements functions on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TQuatDebug BASE will automatically * get all the functionality here. */ template class QUATERNION, typename T> class TQuatDebug { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ friend std::ostream& operator<< (std::ostream& stream, const QUATERNION& q) { return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >"; } }; #undef PURE // ------------------------------------------------------------------------------------- } // namespace details } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/include/math/TVecHelpers.h����������������������������������������������������������������0100644 0000000 0000000 00000045571 13756501735 016430� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #pragma once #include #include #include #include #include #include #define PURE __attribute__((pure)) #if __cplusplus >= 201402L #define CONSTEXPR constexpr #else #define CONSTEXPR #endif namespace android { namespace details { // ------------------------------------------------------------------------------------- /* * No user serviceable parts here. * * Don't use this file directly, instead include ui/vec{2|3|4}.h */ /* * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments * operators on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TVec{Add|Product}Operators BASE will automatically * get all the functionality here. */ template class VECTOR, typename T> class TVecAddOperators { public: /* compound assignment from a another vector of the same size but different * element type. */ template VECTOR& operator +=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] += v[i]; } return lhs; } template VECTOR& operator -=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] -= v[i]; } return lhs; } /* compound assignment from a another vector of the same type. * These operators can be used for implicit conversion and handle operations * like "vector *= scalar" by letting the compiler implicitly convert a scalar * to a vector (assuming the BASE allows it). */ VECTOR& operator +=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] += v[i]; } return lhs; } VECTOR& operator -=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] -= v[i]; } return lhs; } /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ /* The operators below handle operation between vectors of the same size * but of a different element type. */ template friend inline constexpr VECTOR PURE operator +(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv += rv; } template friend inline constexpr VECTOR PURE operator -(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv -= rv; } /* The operators below (which are not templates once this class is instanced, * i.e.: BASE is known) can be used for implicit conversion on both sides. * These handle operations like "vector + scalar" and "scalar + vector" by * letting the compiler implicitly convert a scalar to a vector (assuming * the BASE allows it). */ friend inline constexpr VECTOR PURE operator +(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv += rv; } friend inline constexpr VECTOR PURE operator -(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv -= rv; } }; template class VECTOR, typename T> class TVecProductOperators { public: /* compound assignment from a another vector of the same size but different * element type. */ template VECTOR& operator *=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] *= v[i]; } return lhs; } template VECTOR& operator /=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] /= v[i]; } return lhs; } /* compound assignment from a another vector of the same type. * These operators can be used for implicit conversion and handle operations * like "vector *= scalar" by letting the compiler implicitly convert a scalar * to a vector (assuming the BASE allows it). */ VECTOR& operator *=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] *= v[i]; } return lhs; } VECTOR& operator /=(const VECTOR& v) { VECTOR& lhs = static_cast&>(*this); for (size_t i = 0; i < lhs.size(); i++) { lhs[i] /= v[i]; } return lhs; } /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ /* The operators below handle operation between vectors of the same size * but of a different element type. */ template friend inline constexpr VECTOR PURE operator *(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv *= rv; } template friend inline constexpr VECTOR PURE operator /(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv /= rv; } /* The operators below (which are not templates once this class is instanced, * i.e.: BASE is known) can be used for implicit conversion on both sides. * These handle operations like "vector * scalar" and "scalar * vector" by * letting the compiler implicitly convert a scalar to a vector (assuming * the BASE allows it). */ friend inline constexpr VECTOR PURE operator *(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv *= rv; } friend inline constexpr VECTOR PURE operator /(VECTOR lv, const VECTOR& rv) { // don't pass lv by reference because we need a copy anyways return lv /= rv; } }; /* * TVecUnaryOperators implements unary operators on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TVecUnaryOperators BASE will automatically * get all the functionality here. * * These operators are implemented as friend functions of TVecUnaryOperators */ template class VECTOR, typename T> class TVecUnaryOperators { public: VECTOR& operator ++() { VECTOR& rhs = static_cast&>(*this); for (size_t i = 0; i < rhs.size(); i++) { ++rhs[i]; } return rhs; } VECTOR& operator --() { VECTOR& rhs = static_cast&>(*this); for (size_t i = 0; i < rhs.size(); i++) { --rhs[i]; } return rhs; } CONSTEXPR VECTOR operator -() const { VECTOR r(VECTOR::NO_INIT); VECTOR const& rv(static_cast const&>(*this)); for (size_t i = 0; i < r.size(); i++) { r[i] = -rv[i]; } return r; } }; /* * TVecComparisonOperators implements relational/comparison operators * on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TVecComparisonOperators BASE will automatically * get all the functionality here. */ template class VECTOR, typename T> class TVecComparisonOperators { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ template friend inline bool PURE operator ==(const VECTOR& lv, const VECTOR& rv) { for (size_t i = 0; i < lv.size(); i++) if (lv[i] != rv[i]) return false; return true; } template friend inline bool PURE operator !=(const VECTOR& lv, const VECTOR& rv) { return !operator ==(lv, rv); } template friend inline bool PURE operator >(const VECTOR& lv, const VECTOR& rv) { for (size_t i = 0; i < lv.size(); i++) { if (lv[i] == rv[i]) { continue; } return lv[i] > rv[i]; } return false; } template friend inline constexpr bool PURE operator <=(const VECTOR& lv, const VECTOR& rv) { return !(lv > rv); } template friend inline bool PURE operator <(const VECTOR& lv, const VECTOR& rv) { for (size_t i = 0; i < lv.size(); i++) { if (lv[i] == rv[i]) { continue; } return lv[i] < rv[i]; } return false; } template friend inline constexpr bool PURE operator >=(const VECTOR& lv, const VECTOR& rv) { return !(lv < rv); } template friend inline CONSTEXPR VECTOR PURE equal(const VECTOR& lv, const VECTOR& rv) { VECTOR r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] == rv[i]; } return r; } template friend inline CONSTEXPR VECTOR PURE notEqual(const VECTOR& lv, const VECTOR& rv) { VECTOR r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] != rv[i]; } return r; } template friend inline CONSTEXPR VECTOR PURE lessThan(const VECTOR& lv, const VECTOR& rv) { VECTOR r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] < rv[i]; } return r; } template friend inline CONSTEXPR VECTOR PURE lessThanEqual(const VECTOR& lv, const VECTOR& rv) { VECTOR r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] <= rv[i]; } return r; } template friend inline CONSTEXPR VECTOR PURE greaterThan(const VECTOR& lv, const VECTOR& rv) { VECTOR r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] > rv[i]; } return r; } template friend inline CONSTEXPR VECTOR PURE greaterThanEqual(const VECTOR& lv, const VECTOR& rv) { VECTOR r; for (size_t i = 0; i < lv.size(); i++) { r[i] = lv[i] >= rv[i]; } return r; } }; /* * TVecFunctions implements functions on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TVecFunctions BASE will automatically * get all the functionality here. */ template class VECTOR, typename T> class TVecFunctions { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ template friend inline CONSTEXPR T PURE dot(const VECTOR& lv, const VECTOR& rv) { T r(0); for (size_t i = 0; i < lv.size(); i++) { //r = std::fma(lv[i], rv[i], r); r += lv[i] * rv[i]; } return r; } friend inline constexpr T PURE norm(const VECTOR& lv) { return std::sqrt(dot(lv, lv)); } friend inline constexpr T PURE length(const VECTOR& lv) { return norm(lv); } friend inline constexpr T PURE norm2(const VECTOR& lv) { return dot(lv, lv); } friend inline constexpr T PURE length2(const VECTOR& lv) { return norm2(lv); } template friend inline constexpr T PURE distance(const VECTOR& lv, const VECTOR& rv) { return length(rv - lv); } template friend inline constexpr T PURE distance2(const VECTOR& lv, const VECTOR& rv) { return length2(rv - lv); } friend inline constexpr VECTOR PURE normalize(const VECTOR& lv) { return lv * (T(1) / length(lv)); } friend inline constexpr VECTOR PURE rcp(VECTOR v) { return T(1) / v; } friend inline CONSTEXPR VECTOR PURE abs(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::abs(v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE floor(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::floor(v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE ceil(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::ceil(v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE round(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::round(v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE inversesqrt(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = T(1) / std::sqrt(v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE sqrt(VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::sqrt(v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE pow(VECTOR v, T p) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::pow(v[i], p); } return v; } friend inline CONSTEXPR VECTOR PURE saturate(const VECTOR& lv) { return clamp(lv, T(0), T(1)); } friend inline CONSTEXPR VECTOR PURE clamp(VECTOR v, T min, T max) { for (size_t i = 0; i< v.size(); i++) { v[i] = std::min(max, std::max(min, v[i])); } return v; } friend inline CONSTEXPR VECTOR PURE fma(const VECTOR& lv, const VECTOR& rv, VECTOR a) { for (size_t i = 0; i PURE min(const VECTOR& u, VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::min(u[i], v[i]); } return v; } friend inline CONSTEXPR VECTOR PURE max(const VECTOR& u, VECTOR v) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::max(u[i], v[i]); } return v; } friend inline CONSTEXPR T PURE max(const VECTOR& v) { T r(std::numeric_limits::lowest()); for (size_t i = 0; i < v.size(); i++) { r = std::max(r, v[i]); } return r; } friend inline CONSTEXPR T PURE min(const VECTOR& v) { T r(std::numeric_limits::max()); for (size_t i = 0; i < v.size(); i++) { r = std::min(r, v[i]); } return r; } friend inline CONSTEXPR VECTOR PURE apply(VECTOR v, const std::function& f) { for (size_t i = 0; i < v.size(); i++) { v[i] = f(v[i]); } return v; } friend inline CONSTEXPR bool PURE any(const VECTOR& v) { for (size_t i = 0; i < v.size(); i++) { if (v[i] != T(0)) return true; } return false; } friend inline CONSTEXPR bool PURE all(const VECTOR& v) { bool result = true; for (size_t i = 0; i < v.size(); i++) { result &= (v[i] != T(0)); } return result; } template friend inline CONSTEXPR VECTOR PURE map(VECTOR v, const std::function& f) { VECTOR result; for (size_t i = 0; i < v.size(); i++) { result[i] = f(v[i]); } return result; } }; /* * TVecDebug implements functions on a vector of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TVecDebug BASE will automatically * get all the functionality here. */ template class VECTOR, typename T> class TVecDebug { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ friend std::ostream& operator<<(std::ostream& stream, const VECTOR& v) { stream << "< "; for (size_t i = 0; i < v.size() - 1; i++) { stream << T(v[i]) << ", "; } stream << T(v[v.size() - 1]) << " >"; return stream; } }; #undef CONSTEXPR #undef PURE // ------------------------------------------------------------------------------------- } // namespace details } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������libs/math/include/math/half.h�����������������������������������������������������������������������0100644 0000000 0000000 00000017154 13756501735 015152� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #pragma once #include #include #include #include #ifndef LIKELY #define LIKELY_DEFINED_LOCAL #ifdef __cplusplus # define LIKELY( exp ) (__builtin_expect( !!(exp), true )) # define UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) #else # define LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) # define UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) #endif #endif #if __cplusplus >= 201402L #define CONSTEXPR constexpr #else #define CONSTEXPR #endif namespace android { /* * half-float * * 1 5 10 * +-+------+------------+ * |s|eee.ee|mm.mmmm.mmmm| * +-+------+------------+ * * minimum (denormal) value: 2^-24 = 5.96e-8 * minimum (normal) value: 2^-14 = 6.10e-5 * maximum value: 2-2^-10 = 65504 * * Integers between 0 and 2048 can be represented exactly */ class half { struct fp16 { uint16_t bits; explicit constexpr fp16() noexcept : bits(0) { } explicit constexpr fp16(uint16_t b) noexcept : bits(b) { } void setS(unsigned int s) noexcept { bits = uint16_t((bits & 0x7FFF) | (s<<15)); } void setE(unsigned int s) noexcept { bits = uint16_t((bits & 0xE3FF) | (s<<10)); } void setM(unsigned int s) noexcept { bits = uint16_t((bits & 0xFC00) | (s<< 0)); } constexpr unsigned int getS() const noexcept { return bits >> 15u; } constexpr unsigned int getE() const noexcept { return (bits >> 10u) & 0x1Fu; } constexpr unsigned int getM() const noexcept { return bits & 0x3FFu; } }; struct fp32 { union { uint32_t bits; float fp; }; explicit constexpr fp32() noexcept : bits(0) { } explicit constexpr fp32(float f) noexcept : fp(f) { } void setS(unsigned int s) noexcept { bits = uint32_t((bits & 0x7FFFFFFF) | (s<<31)); } void setE(unsigned int s) noexcept { bits = uint32_t((bits & 0x807FFFFF) | (s<<23)); } void setM(unsigned int s) noexcept { bits = uint32_t((bits & 0xFF800000) | (s<< 0)); } constexpr unsigned int getS() const noexcept { return bits >> 31u; } constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; } constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; } }; public: CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { } CONSTEXPR operator float() const noexcept { return htof(mBits); } uint16_t getBits() const noexcept { return mBits.bits; } unsigned int getExponent() const noexcept { return mBits.getE(); } unsigned int getMantissa() const noexcept { return mBits.getM(); } private: friend class std::numeric_limits; friend CONSTEXPR half operator"" _hf(long double v); enum Binary { binary }; explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { } static CONSTEXPR fp16 ftoh(float v) noexcept; static CONSTEXPR float htof(fp16 v) noexcept; fp16 mBits; }; inline CONSTEXPR half::fp16 half::ftoh(float v) noexcept { fp16 out; fp32 in(v); if (UNLIKELY(in.getE() == 0xFF)) { // inf or nan out.setE(0x1F); out.setM(in.getM() ? 0x200 : 0); } else { int e = static_cast(in.getE()) - 127 + 15; if (e >= 0x1F) { // overflow out.setE(0x31); // +/- inf } else if (e <= 0) { // underflow // flush to +/- 0 } else { unsigned int m = in.getM(); out.setE(uint16_t(e)); out.setM(m >> 13); if (m & 0x1000) { // rounding out.bits++; } } } out.setS(in.getS()); return out; } inline CONSTEXPR float half::htof(half::fp16 in) noexcept { fp32 out; if (UNLIKELY(in.getE() == 0x1F)) { // inf or nan out.setE(0xFF); out.setM(in.getM() ? 0x400000 : 0); } else { if (in.getE() == 0) { if (in.getM()) { // TODO: denormal half float, treat as zero for now // (it's stupid because they can be represented as regular float) } } else { int e = static_cast(in.getE()) - 15 + 127; unsigned int m = in.getM(); out.setE(uint32_t(e)); out.setM(m << 13); } } out.setS(in.getS()); return out.fp; } inline CONSTEXPR android::half operator"" _hf(long double v) { return android::half(android::half::binary, android::half::ftoh(static_cast(v)).bits); } } // namespace android namespace std { template<> struct is_floating_point : public std::true_type {}; template<> class numeric_limits { public: typedef android::half type; static constexpr const bool is_specialized = true; static constexpr const bool is_signed = true; static constexpr const bool is_integer = false; static constexpr const bool is_exact = false; static constexpr const bool has_infinity = true; static constexpr const bool has_quiet_NaN = true; static constexpr const bool has_signaling_NaN = false; static constexpr const float_denorm_style has_denorm = denorm_absent; static constexpr const bool has_denorm_loss = true; static constexpr const bool is_iec559 = false; static constexpr const bool is_bounded = true; static constexpr const bool is_modulo = false; static constexpr const bool traps = false; static constexpr const bool tinyness_before = false; static constexpr const float_round_style round_style = round_indeterminate; static constexpr const int digits = 11; static constexpr const int digits10 = 3; static constexpr const int max_digits10 = 5; static constexpr const int radix = 2; static constexpr const int min_exponent = -13; static constexpr const int min_exponent10 = -4; static constexpr const int max_exponent = 16; static constexpr const int max_exponent10 = 4; inline static constexpr type round_error() noexcept { return android::half(android::half::binary, 0x3800); } inline static constexpr type min() noexcept { return android::half(android::half::binary, 0x0400); } inline static constexpr type max() noexcept { return android::half(android::half::binary, 0x7bff); } inline static constexpr type lowest() noexcept { return android::half(android::half::binary, 0xfbff); } inline static constexpr type epsilon() noexcept { return android::half(android::half::binary, 0x1400); } inline static constexpr type infinity() noexcept { return android::half(android::half::binary, 0x7c00); } inline static constexpr type quiet_NaN() noexcept { return android::half(android::half::binary, 0x7fff); } inline static constexpr type denorm_min() noexcept { return android::half(android::half::binary, 0x0001); } inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); } }; } // namespace std #ifdef LIKELY_DEFINED_LOCAL #undef LIKELY_DEFINED_LOCAL #undef LIKELY #undef UNLIKELY #endif // LIKELY_DEFINED_LOCAL #undef CONSTEXPR ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/include/math/mat2.h�����������������������������������������������������������������������0100644 0000000 0000000 00000024401 13756501735 015074� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #pragma once #include #include #include #include #define PURE __attribute__((pure)) #if __cplusplus >= 201402L #define CONSTEXPR constexpr #else #define CONSTEXPR #endif namespace android { // ------------------------------------------------------------------------------------- namespace details { /** * A 2x2 column-major matrix class. * * Conceptually a 2x2 matrix is a an array of 2 column vec2: * * mat2 m = * \f$ * \left( * \begin{array}{cc} * m[0] & m[1] \\ * \end{array} * \right) * \f$ * = * \f$ * \left( * \begin{array}{cc} * m[0][0] & m[1][0] \\ * m[0][1] & m[1][1] \\ * \end{array} * \right) * \f$ * = * \f$ * \left( * \begin{array}{cc} * m(0,0) & m(0,1) \\ * m(1,0) & m(1,1) \\ * \end{array} * \right) * \f$ * * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2. * */ template class TMat22 : public TVecUnaryOperators, public TVecComparisonOperators, public TVecAddOperators, public TMatProductOperators, public TMatSquareFunctions, public TMatHelpers, public TMatDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; typedef TVec2 col_type; typedef TVec2 row_type; static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) static constexpr size_t NUM_ROWS = COL_SIZE; static constexpr size_t NUM_COLS = ROW_SIZE; private: /* * <-- N columns --> * * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ * a[0][1] a[1][1] a[2][1] ... a[N][1] | * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows * ... | * a[0][M] a[1][M] a[2][M] ... a[N][M] v * * COL_SIZE = M * ROW_SIZE = N * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] */ col_type m_value[NUM_COLS]; public: // array access inline constexpr col_type const& operator[](size_t column) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(column < NUM_COLS); #endif return m_value[column]; } inline col_type& operator[](size_t column) { assert(column < NUM_COLS); return m_value[column]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TMat22(const TMat22&) = default; ~TMat22() = default; TMat22& operator = (const TMat22&) = default; /** * constructors */ /** * leaves object uninitialized. use with caution. */ explicit constexpr TMat22(no_init) : m_value{ col_type(col_type::NO_INIT), col_type(col_type::NO_INIT) } {} /** * initialize to identity. * * \f$ * \left( * \begin{array}{cc} * 1 & 0 \\ * 0 & 1 \\ * \end{array} * \right) * \f$ */ CONSTEXPR TMat22(); /** * initialize to Identity*scalar. * * \f$ * \left( * \begin{array}{cc} * v & 0 \\ * 0 & v \\ * \end{array} * \right) * \f$ */ template explicit CONSTEXPR TMat22(U v); /** * sets the diagonal to a vector. * * \f$ * \left( * \begin{array}{cc} * v[0] & 0 \\ * 0 & v[1] \\ * \end{array} * \right) * \f$ */ template explicit CONSTEXPR TMat22(const TVec2& v); /** * construct from another matrix of the same size */ template explicit CONSTEXPR TMat22(const TMat22& rhs); /** * construct from 2 column vectors. * * \f$ * \left( * \begin{array}{cc} * v0 & v1 \\ * \end{array} * \right) * \f$ */ template CONSTEXPR TMat22(const TVec2& v0, const TVec2& v1); /** construct from 4 elements in column-major form. * * \f$ * \left( * \begin{array}{cc} * m[0][0] & m[1][0] \\ * m[0][1] & m[1][1] \\ * \end{array} * \right) * \f$ */ template < typename A, typename B, typename C, typename D> CONSTEXPR TMat22(A m00, B m01, C m10, D m11); /** * construct from a C array in column major form. */ template explicit CONSTEXPR TMat22(U const* rawArray); /** * Rotate by radians in the 2D plane */ static CONSTEXPR TMat22 rotate(T radian) { TMat22 r(TMat22::NO_INIT); T c = std::cos(radian); T s = std::sin(radian); r[0][0] = c; r[1][1] = c; r[0][1] = s; r[1][0] = -s; return r; } }; // ---------------------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------------------- // Since the matrix code could become pretty big quickly, we don't inline most // operations. template CONSTEXPR TMat22::TMat22() { m_value[0] = col_type(1, 0); m_value[1] = col_type(0, 1); } template template CONSTEXPR TMat22::TMat22(U v) { m_value[0] = col_type(v, 0); m_value[1] = col_type(0, v); } template template CONSTEXPR TMat22::TMat22(const TVec2& v) { m_value[0] = col_type(v.x, 0); m_value[1] = col_type(0, v.y); } // construct from 4 scalars. Note that the arrangement // of values in the constructor is the transpose of the matrix // notation. template template < typename A, typename B, typename C, typename D> CONSTEXPR TMat22::TMat22( A m00, B m01, C m10, D m11) { m_value[0] = col_type(m00, m01); m_value[1] = col_type(m10, m11); } template template CONSTEXPR TMat22::TMat22(const TMat22& rhs) { for (size_t col = 0; col < NUM_COLS; ++col) { m_value[col] = col_type(rhs[col]); } } // Construct from 2 column vectors. template template CONSTEXPR TMat22::TMat22(const TVec2& v0, const TVec2& v1) { m_value[0] = v0; m_value[1] = v1; } // Construct from raw array, in column-major form. template template CONSTEXPR TMat22::TMat22(U const* rawArray) { for (size_t col = 0; col < NUM_COLS; ++col) { for (size_t row = 0; row < NUM_ROWS; ++row) { m_value[col][row] = *rawArray++; } } } // ---------------------------------------------------------------------------------------- // Arithmetic operators outside of class // ---------------------------------------------------------------------------------------- /* We use non-friend functions here to prevent the compiler from using * implicit conversions, for instance of a scalar to a vector. The result would * not be what the caller expects. * * Also note that the order of the arguments in the inner loop is important since * it determines the output type (only relevant when T != U). */ // matrix * column-vector, result is a vector of the same type than the input vector template CONSTEXPR typename TMat22::col_type PURE operator *(const TMat22& lhs, const TVec2& rhs) { // Result is initialized to zero. typename TMat22::col_type result; for (size_t col = 0; col < TMat22::NUM_COLS; ++col) { result += lhs[col] * rhs[col]; } return result; } // row-vector * matrix, result is a vector of the same type than the input vector template CONSTEXPR typename TMat22::row_type PURE operator *(const TVec2& lhs, const TMat22& rhs) { typename TMat22::row_type result(TMat22::row_type::NO_INIT); for (size_t col = 0; col < TMat22::NUM_COLS; ++col) { result[col] = dot(lhs, rhs[col]); } return result; } // matrix * scalar, result is a matrix of the same type than the input matrix template constexpr typename std::enable_if::value, TMat22>::type PURE operator*(TMat22 lhs, U rhs) { return lhs *= rhs; } // scalar * matrix, result is a matrix of the same type than the input matrix template constexpr typename std::enable_if::value, TMat22>::type PURE operator*(U lhs, const TMat22& rhs) { return rhs * lhs; } // ---------------------------------------------------------------------------------------- /* FIXME: this should go into TMatSquareFunctions<> but for some reason * BASE::col_type is not accessible from there (???) */ template CONSTEXPR typename TMat22::col_type PURE diag(const TMat22& m) { return matrix::diag(m); } } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TMat22 mat2d; typedef details::TMat22 mat2; typedef details::TMat22 mat2f; // ---------------------------------------------------------------------------------------- } // namespace android #undef PURE #undef CONSTEXPR libs/math/include/math/mat3.h0100644 0000000 0000000 00000030572 13756501735 015103 0ustar000000000 0000000 /* * Copyright 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. */ #pragma once #include #include #include #include #include #define PURE __attribute__((pure)) #if __cplusplus >= 201402L #define CONSTEXPR constexpr #else #define CONSTEXPR #endif namespace android { // ------------------------------------------------------------------------------------- namespace details { template class TQuaternion; /** * A 3x3 column-major matrix class. * * Conceptually a 3x3 matrix is a an array of 3 column vec3: * * mat3 m = * \f$ * \left( * \begin{array}{ccc} * m[0] & m[1] & m[2] \\ * \end{array} * \right) * \f$ * = * \f$ * \left( * \begin{array}{ccc} * m[0][0] & m[1][0] & m[2][0] \\ * m[0][1] & m[1][1] & m[2][1] \\ * m[0][2] & m[1][2] & m[2][2] \\ * \end{array} * \right) * \f$ * = * \f$ * \left( * \begin{array}{ccc} * m(0,0) & m(0,1) & m(0,2) \\ * m(1,0) & m(1,1) & m(1,2) \\ * m(2,0) & m(2,1) & m(2,2) \\ * \end{array} * \right) * \f$ * * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3. * */ template class TMat33 : public TVecUnaryOperators, public TVecComparisonOperators, public TVecAddOperators, public TMatProductOperators, public TMatSquareFunctions, public TMatTransform, public TMatHelpers, public TMatDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; typedef TVec3 col_type; typedef TVec3 row_type; static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) static constexpr size_t NUM_ROWS = COL_SIZE; static constexpr size_t NUM_COLS = ROW_SIZE; private: /* * <-- N columns --> * * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ * a[0][1] a[1][1] a[2][1] ... a[N][1] | * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows * ... | * a[0][M] a[1][M] a[2][M] ... a[N][M] v * * COL_SIZE = M * ROW_SIZE = N * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] */ col_type m_value[NUM_COLS]; public: // array access inline constexpr col_type const& operator[](size_t column) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(column < NUM_COLS); #endif return m_value[column]; } inline col_type& operator[](size_t column) { assert(column < NUM_COLS); return m_value[column]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TMat33(const TMat33&) = default; ~TMat33() = default; TMat33& operator = (const TMat33&) = default; /** * constructors */ /** * leaves object uninitialized. use with caution. */ explicit constexpr TMat33(no_init) : m_value{ col_type(col_type::NO_INIT), col_type(col_type::NO_INIT), col_type(col_type::NO_INIT) } {} /** * initialize to identity. * * \f$ * \left( * \begin{array}{ccc} * 1 & 0 & 0 \\ * 0 & 1 & 0 \\ * 0 & 0 & 1 \\ * \end{array} * \right) * \f$ */ CONSTEXPR TMat33(); /** * initialize to Identity*scalar. * * \f$ * \left( * \begin{array}{ccc} * v & 0 & 0 \\ * 0 & v & 0 \\ * 0 & 0 & v \\ * \end{array} * \right) * \f$ */ template explicit CONSTEXPR TMat33(U v); /** * sets the diagonal to a vector. * * \f$ * \left( * \begin{array}{ccc} * v[0] & 0 & 0 \\ * 0 & v[1] & 0 \\ * 0 & 0 & v[2] \\ * \end{array} * \right) * \f$ */ template explicit CONSTEXPR TMat33(const TVec3& v); /** * construct from another matrix of the same size */ template explicit CONSTEXPR TMat33(const TMat33& rhs); /** * construct from 3 column vectors. * * \f$ * \left( * \begin{array}{ccc} * v0 & v1 & v2 \\ * \end{array} * \right) * \f$ */ template CONSTEXPR TMat33(const TVec3& v0, const TVec3& v1, const TVec3& v2); /** construct from 9 elements in column-major form. * * \f$ * \left( * \begin{array}{ccc} * m[0][0] & m[1][0] & m[2][0] \\ * m[0][1] & m[1][1] & m[2][1] \\ * m[0][2] & m[1][2] & m[2][2] \\ * \end{array} * \right) * \f$ */ template < typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I> CONSTEXPR TMat33( A m00, B m01, C m02, D m10, E m11, F m12, G m20, H m21, I m22); /** * construct from a quaternion */ template explicit CONSTEXPR TMat33(const TQuaternion& q); /** * construct from a C array in column major form. */ template explicit CONSTEXPR TMat33(U const* rawArray); /** * orthogonalize only works on matrices of size 3x3 */ friend inline CONSTEXPR TMat33 orthogonalize(const TMat33& m) { TMat33 ret(TMat33::NO_INIT); ret[0] = normalize(m[0]); ret[2] = normalize(cross(ret[0], m[1])); ret[1] = normalize(cross(ret[2], ret[0])); return ret; } }; // ---------------------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------------------- // Since the matrix code could become pretty big quickly, we don't inline most // operations. template CONSTEXPR TMat33::TMat33() { m_value[0] = col_type(1, 0, 0); m_value[1] = col_type(0, 1, 0); m_value[2] = col_type(0, 0, 1); } template template CONSTEXPR TMat33::TMat33(U v) { m_value[0] = col_type(v, 0, 0); m_value[1] = col_type(0, v, 0); m_value[2] = col_type(0, 0, v); } template template CONSTEXPR TMat33::TMat33(const TVec3& v) { m_value[0] = col_type(v.x, 0, 0); m_value[1] = col_type(0, v.y, 0); m_value[2] = col_type(0, 0, v.z); } // construct from 9 scalars. Note that the arrangement // of values in the constructor is the transpose of the matrix // notation. template template < typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I> CONSTEXPR TMat33::TMat33( A m00, B m01, C m02, D m10, E m11, F m12, G m20, H m21, I m22) { m_value[0] = col_type(m00, m01, m02); m_value[1] = col_type(m10, m11, m12); m_value[2] = col_type(m20, m21, m22); } template template CONSTEXPR TMat33::TMat33(const TMat33& rhs) { for (size_t col = 0; col < NUM_COLS; ++col) { m_value[col] = col_type(rhs[col]); } } // Construct from 3 column vectors. template template CONSTEXPR TMat33::TMat33(const TVec3& v0, const TVec3& v1, const TVec3& v2) { m_value[0] = v0; m_value[1] = v1; m_value[2] = v2; } // Construct from raw array, in column-major form. template template CONSTEXPR TMat33::TMat33(U const* rawArray) { for (size_t col = 0; col < NUM_COLS; ++col) { for (size_t row = 0; row < NUM_ROWS; ++row) { m_value[col][row] = *rawArray++; } } } template template CONSTEXPR TMat33::TMat33(const TQuaternion& q) { const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; const U s = n > 0 ? 2/n : 0; const U x = s*q.x; const U y = s*q.y; const U z = s*q.z; const U xx = x*q.x; const U xy = x*q.y; const U xz = x*q.z; const U xw = x*q.w; const U yy = y*q.y; const U yz = y*q.z; const U yw = y*q.w; const U zz = z*q.z; const U zw = z*q.w; m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw); // NOLINT m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw); // NOLINT m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy); // NOLINT } // ---------------------------------------------------------------------------------------- // Arithmetic operators outside of class // ---------------------------------------------------------------------------------------- /* We use non-friend functions here to prevent the compiler from using * implicit conversions, for instance of a scalar to a vector. The result would * not be what the caller expects. * * Also note that the order of the arguments in the inner loop is important since * it determines the output type (only relevant when T != U). */ // matrix * column-vector, result is a vector of the same type than the input vector template CONSTEXPR typename TMat33::col_type PURE operator *(const TMat33& lhs, const TVec3& rhs) { // Result is initialized to zero. typename TMat33::col_type result; for (size_t col = 0; col < TMat33::NUM_COLS; ++col) { result += lhs[col] * rhs[col]; } return result; } // row-vector * matrix, result is a vector of the same type than the input vector template CONSTEXPR typename TMat33::row_type PURE operator *(const TVec3& lhs, const TMat33& rhs) { typename TMat33::row_type result(TMat33::row_type::NO_INIT); for (size_t col = 0; col < TMat33::NUM_COLS; ++col) { result[col] = dot(lhs, rhs[col]); } return result; } // matrix * scalar, result is a matrix of the same type than the input matrix template constexpr typename std::enable_if::value, TMat33>::type PURE operator*(TMat33 lhs, U rhs) { return lhs *= rhs; } // scalar * matrix, result is a matrix of the same type than the input matrix template constexpr typename std::enable_if::value, TMat33>::type PURE operator*(U lhs, const TMat33& rhs) { return rhs * lhs; } //------------------------------------------------------------------------------ template CONSTEXPR TMat33 orthogonalize(const TMat33& m) { TMat33 ret(TMat33::NO_INIT); ret[0] = normalize(m[0]); ret[2] = normalize(cross(ret[0], m[1])); ret[1] = normalize(cross(ret[2], ret[0])); return ret; } // ---------------------------------------------------------------------------------------- /* FIXME: this should go into TMatSquareFunctions<> but for some reason * BASE::col_type is not accessible from there (???) */ template CONSTEXPR typename TMat33::col_type PURE diag(const TMat33& m) { return matrix::diag(m); } } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TMat33 mat3d; typedef details::TMat33 mat3; typedef details::TMat33 mat3f; // ---------------------------------------------------------------------------------------- } // namespace android #undef PURE #undef CONSTEXPR libs/math/include/math/mat4.h0100644 0000000 0000000 00000044144 13756501735 015104 0ustar000000000 0000000 /* * Copyright 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. */ #pragma once #include #include #include #include #include #include #include #include #define PURE __attribute__((pure)) #if __cplusplus >= 201402L #define CONSTEXPR constexpr #else #define CONSTEXPR #endif namespace android { // ------------------------------------------------------------------------------------- namespace details { template class TQuaternion; /** * A 4x4 column-major matrix class. * * Conceptually a 4x4 matrix is a an array of 4 column double4: * * mat4 m = * \f$ * \left( * \begin{array}{cccc} * m[0] & m[1] & m[2] & m[3] \\ * \end{array} * \right) * \f$ * = * \f$ * \left( * \begin{array}{cccc} * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\ * \end{array} * \right) * \f$ * = * \f$ * \left( * \begin{array}{cccc} * m(0,0) & m(0,1) & m(0,2) & m(0,3) \\ * m(1,0) & m(1,1) & m(1,2) & m(1,3) \\ * m(2,0) & m(2,1) & m(2,2) & m(2,3) \\ * m(3,0) & m(3,1) & m(3,2) & m(3,3) \\ * \end{array} * \right) * \f$ * * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4. * */ template class TMat44 : public TVecUnaryOperators, public TVecComparisonOperators, public TVecAddOperators, public TMatProductOperators, public TMatSquareFunctions, public TMatTransform, public TMatHelpers, public TMatDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; typedef TVec4 col_type; typedef TVec4 row_type; static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) static constexpr size_t NUM_ROWS = COL_SIZE; static constexpr size_t NUM_COLS = ROW_SIZE; private: /* * <-- N columns --> * * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ * a[0][1] a[1][1] a[2][1] ... a[N][1] | * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows * ... | * a[0][M] a[1][M] a[2][M] ... a[N][M] v * * COL_SIZE = M * ROW_SIZE = N * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] */ col_type m_value[NUM_COLS]; public: // array access inline constexpr col_type const& operator[](size_t column) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(column < NUM_COLS); #endif return m_value[column]; } inline col_type& operator[](size_t column) { assert(column < NUM_COLS); return m_value[column]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TMat44(const TMat44&) = default; ~TMat44() = default; TMat44& operator = (const TMat44&) = default; /* * constructors */ // leaves object uninitialized. use with caution. explicit constexpr TMat44(no_init) : m_value{ col_type(col_type::NO_INIT), col_type(col_type::NO_INIT), col_type(col_type::NO_INIT), col_type(col_type::NO_INIT) } {} /** initialize to identity. * * \f$ * \left( * \begin{array}{cccc} * 1 & 0 & 0 & 0 \\ * 0 & 1 & 0 & 0 \\ * 0 & 0 & 1 & 0 \\ * 0 & 0 & 0 & 1 \\ * \end{array} * \right) * \f$ */ CONSTEXPR TMat44(); /** initialize to Identity*scalar. * * \f$ * \left( * \begin{array}{cccc} * v & 0 & 0 & 0 \\ * 0 & v & 0 & 0 \\ * 0 & 0 & v & 0 \\ * 0 & 0 & 0 & v \\ * \end{array} * \right) * \f$ */ template explicit CONSTEXPR TMat44(U v); /** sets the diagonal to a vector. * * \f$ * \left( * \begin{array}{cccc} * v[0] & 0 & 0 & 0 \\ * 0 & v[1] & 0 & 0 \\ * 0 & 0 & v[2] & 0 \\ * 0 & 0 & 0 & v[3] \\ * \end{array} * \right) * \f$ */ template explicit CONSTEXPR TMat44(const TVec4& v); // construct from another matrix of the same size template explicit CONSTEXPR TMat44(const TMat44& rhs); /** construct from 4 column vectors. * * \f$ * \left( * \begin{array}{cccc} * v0 & v1 & v2 & v3 \\ * \end{array} * \right) * \f$ */ template CONSTEXPR TMat44(const TVec4& v0, const TVec4& v1, const TVec4& v2, const TVec4& v3); /** construct from 16 elements in column-major form. * * \f$ * \left( * \begin{array}{cccc} * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\ * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\ * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\ * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\ * \end{array} * \right) * \f$ */ template < typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L, typename M, typename N, typename O, typename P> CONSTEXPR TMat44( A m00, B m01, C m02, D m03, E m10, F m11, G m12, H m13, I m20, J m21, K m22, L m23, M m30, N m31, O m32, P m33); /** * construct from a quaternion */ template explicit CONSTEXPR TMat44(const TQuaternion& q); /** * construct from a C array in column major form. */ template explicit CONSTEXPR TMat44(U const* rawArray); /** * construct from a 3x3 matrix */ template explicit CONSTEXPR TMat44(const TMat33& matrix); /** * construct from a 3x3 matrix and 3d translation */ template CONSTEXPR TMat44(const TMat33& matrix, const TVec3& translation); /** * construct from a 3x3 matrix and 4d last column. */ template CONSTEXPR TMat44(const TMat33& matrix, const TVec4& column3); /* * helpers */ static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far); static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far); enum class Fov { HORIZONTAL, VERTICAL }; static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL); template static CONSTEXPR TMat44 lookAt(const TVec3& eye, const TVec3& center, const TVec3& up); template static CONSTEXPR TVec3 project(const TMat44& projectionMatrix, TVec3 vertice) { TVec4 r = projectionMatrix * TVec4{ vertice, 1 }; return r.xyz / r.w; } template static CONSTEXPR TVec4 project(const TMat44& projectionMatrix, TVec4 vertice) { vertice = projectionMatrix * vertice; return { vertice.xyz / vertice.w, 1 }; } /** * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix */ inline constexpr TMat33 upperLeft() const { return TMat33(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz); } }; // ---------------------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------------------- // Since the matrix code could become pretty big quickly, we don't inline most // operations. template CONSTEXPR TMat44::TMat44() { m_value[0] = col_type(1, 0, 0, 0); m_value[1] = col_type(0, 1, 0, 0); m_value[2] = col_type(0, 0, 1, 0); m_value[3] = col_type(0, 0, 0, 1); } template template CONSTEXPR TMat44::TMat44(U v) { m_value[0] = col_type(v, 0, 0, 0); m_value[1] = col_type(0, v, 0, 0); m_value[2] = col_type(0, 0, v, 0); m_value[3] = col_type(0, 0, 0, v); } template template CONSTEXPR TMat44::TMat44(const TVec4& v) { m_value[0] = col_type(v.x, 0, 0, 0); m_value[1] = col_type(0, v.y, 0, 0); m_value[2] = col_type(0, 0, v.z, 0); m_value[3] = col_type(0, 0, 0, v.w); } // construct from 16 scalars template template < typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L, typename M, typename N, typename O, typename P> CONSTEXPR TMat44::TMat44( A m00, B m01, C m02, D m03, E m10, F m11, G m12, H m13, I m20, J m21, K m22, L m23, M m30, N m31, O m32, P m33) { m_value[0] = col_type(m00, m01, m02, m03); m_value[1] = col_type(m10, m11, m12, m13); m_value[2] = col_type(m20, m21, m22, m23); m_value[3] = col_type(m30, m31, m32, m33); } template template CONSTEXPR TMat44::TMat44(const TMat44& rhs) { for (size_t col = 0; col < NUM_COLS; ++col) { m_value[col] = col_type(rhs[col]); } } // Construct from 4 column vectors. template template CONSTEXPR TMat44::TMat44( const TVec4& v0, const TVec4& v1, const TVec4& v2, const TVec4& v3) { m_value[0] = col_type(v0); m_value[1] = col_type(v1); m_value[2] = col_type(v2); m_value[3] = col_type(v3); } // Construct from raw array, in column-major form. template template CONSTEXPR TMat44::TMat44(U const* rawArray) { for (size_t col = 0; col < NUM_COLS; ++col) { for (size_t row = 0; row < NUM_ROWS; ++row) { m_value[col][row] = *rawArray++; } } } template template CONSTEXPR TMat44::TMat44(const TQuaternion& q) { const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; const U s = n > 0 ? 2/n : 0; const U x = s*q.x; const U y = s*q.y; const U z = s*q.z; const U xx = x*q.x; const U xy = x*q.y; const U xz = x*q.z; const U xw = x*q.w; const U yy = y*q.y; const U yz = y*q.z; const U yw = y*q.w; const U zz = z*q.z; const U zw = z*q.w; m_value[0] = col_type(1-yy-zz, xy+zw, xz-yw, 0); m_value[1] = col_type( xy-zw, 1-xx-zz, yz+xw, 0); // NOLINT m_value[2] = col_type( xz+yw, yz-xw, 1-xx-yy, 0); // NOLINT m_value[3] = col_type( 0, 0, 0, 1); // NOLINT } template template CONSTEXPR TMat44::TMat44(const TMat33& m) { m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); m_value[3] = col_type( 0, 0, 0, 1); // NOLINT } template template CONSTEXPR TMat44::TMat44(const TMat33& m, const TVec3& v) { m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); m_value[3] = col_type( v[0], v[1], v[2], 1); // NOLINT } template template CONSTEXPR TMat44::TMat44(const TMat33& m, const TVec4& v) { m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0); // NOLINT m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0); // NOLINT m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0); // NOLINT m_value[3] = col_type( v[0], v[1], v[2], v[3]); // NOLINT } // ---------------------------------------------------------------------------------------- // Helpers // ---------------------------------------------------------------------------------------- template CONSTEXPR TMat44 TMat44::ortho(T left, T right, T bottom, T top, T near, T far) { TMat44 m; m[0][0] = 2 / (right - left); m[1][1] = 2 / (top - bottom); m[2][2] = -2 / (far - near); m[3][0] = -(right + left) / (right - left); m[3][1] = -(top + bottom) / (top - bottom); m[3][2] = -(far + near) / (far - near); return m; } template CONSTEXPR TMat44 TMat44::frustum(T left, T right, T bottom, T top, T near, T far) { TMat44 m; m[0][0] = (2 * near) / (right - left); m[1][1] = (2 * near) / (top - bottom); m[2][0] = (right + left) / (right - left); m[2][1] = (top + bottom) / (top - bottom); m[2][2] = -(far + near) / (far - near); m[2][3] = -1; m[3][2] = -(2 * far * near) / (far - near); m[3][3] = 0; return m; } template CONSTEXPR TMat44 TMat44::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) { T h; T w; if (direction == TMat44::Fov::VERTICAL) { h = std::tan(fov * M_PI / 360.0f) * near; w = h * aspect; } else { w = std::tan(fov * M_PI / 360.0f) * near; h = w / aspect; } return frustum(-w, w, -h, h, near, far); } /* * Returns a matrix representing the pose of a virtual camera looking towards -Z in its * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its * looking at and "up" defines where the Y axis of the camera's local coordinate system is. */ template template CONSTEXPR TMat44 TMat44::lookAt(const TVec3& eye, const TVec3& center, const TVec3& up) { TVec3 z_axis(normalize(center - eye)); TVec3 norm_up(normalize(up)); if (std::abs(dot(z_axis, norm_up)) > 0.999) { // Fix up vector if we're degenerate (looking straight up, basically) norm_up = { norm_up.z, norm_up.x, norm_up.y }; } TVec3 x_axis(normalize(cross(z_axis, norm_up))); TVec3 y_axis(cross(x_axis, z_axis)); return TMat44( TVec4(x_axis, 0), TVec4(y_axis, 0), TVec4(-z_axis, 0), TVec4(eye, 1)); } // ---------------------------------------------------------------------------------------- // Arithmetic operators outside of class // ---------------------------------------------------------------------------------------- /* We use non-friend functions here to prevent the compiler from using * implicit conversions, for instance of a scalar to a vector. The result would * not be what the caller expects. * * Also note that the order of the arguments in the inner loop is important since * it determines the output type (only relevant when T != U). */ // matrix * column-vector, result is a vector of the same type than the input vector template CONSTEXPR typename TMat44::col_type PURE operator *(const TMat44& lhs, const TVec4& rhs) { // Result is initialized to zero. typename TMat44::col_type result; for (size_t col = 0; col < TMat44::NUM_COLS; ++col) { result += lhs[col] * rhs[col]; } return result; } // mat44 * vec3, result is vec3( mat44 * {vec3, 1} ) template CONSTEXPR typename TMat44::col_type PURE operator *(const TMat44& lhs, const TVec3& rhs) { return lhs * TVec4{ rhs, 1 }; } // row-vector * matrix, result is a vector of the same type than the input vector template CONSTEXPR typename TMat44::row_type PURE operator *(const TVec4& lhs, const TMat44& rhs) { typename TMat44::row_type result(TMat44::row_type::NO_INIT); for (size_t col = 0; col < TMat44::NUM_COLS; ++col) { result[col] = dot(lhs, rhs[col]); } return result; } // matrix * scalar, result is a matrix of the same type than the input matrix template constexpr typename std::enable_if::value, TMat44>::type PURE operator *(TMat44 lhs, U rhs) { return lhs *= rhs; } // scalar * matrix, result is a matrix of the same type than the input matrix template constexpr typename std::enable_if::value, TMat44>::type PURE operator *(U lhs, const TMat44& rhs) { return rhs * lhs; } // ---------------------------------------------------------------------------------------- /* FIXME: this should go into TMatSquareFunctions<> but for some reason * BASE::col_type is not accessible from there (???) */ template typename TMat44::col_type PURE diag(const TMat44& m) { return matrix::diag(m); } } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TMat44 mat4d; typedef details::TMat44 mat4; typedef details::TMat44 mat4f; // ---------------------------------------------------------------------------------------- } // namespace android #undef PURE #undef CONSTEXPR libs/math/include/math/quat.h0100644 0000000 0000000 00000013656 13756501735 015215 0ustar000000000 0000000 /* * Copyright 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. */ #pragma once #include #include #include #include #include #include #ifndef PURE #define PURE __attribute__((pure)) #endif #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" #pragma clang diagnostic ignored "-Wnested-anon-types" namespace android { // ------------------------------------------------------------------------------------- namespace details { template class TQuaternion : public TVecAddOperators, public TVecUnaryOperators, public TVecComparisonOperators, public TQuatProductOperators, public TQuatFunctions, public TQuatDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; /* * quaternion internals stored as: * * q = w + xi + yj + zk * * q[0] = x; * q[1] = y; * q[2] = z; * q[3] = w; * */ union { struct { T x, y, z, w; }; TVec4 xyzw; TVec3 xyz; TVec2 xy; }; enum { SIZE = 4 }; inline constexpr static size_type size() { return SIZE; } // array access inline constexpr T const& operator[](size_t i) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(i < SIZE); #endif return (&x)[i]; } inline T& operator[](size_t i) { assert(i < SIZE); return (&x)[i]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TQuaternion(const TQuaternion&) = default; ~TQuaternion() = default; TQuaternion& operator = (const TQuaternion&) = default; // constructors // leaves object uninitialized. use with caution. explicit constexpr TQuaternion(no_init) : xyzw(TVec4::NO_INIT) {} // default constructor. sets all values to zero. constexpr TQuaternion() : x(0), y(0), z(0), w(0) { } // handles implicit conversion to a tvec4. must not be explicit. template constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) { static_assert(std::is_arithmetic::value, "requires arithmetic type"); } // initialize from 4 values to w + xi + yj + zk template constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { } // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w template constexpr TQuaternion(const TVec3& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { } // initialize from a double4 template constexpr explicit TQuaternion(const TVec4& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } // initialize from a quaternion of a different type template constexpr explicit TQuaternion(const TQuaternion& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } // conjugate operator constexpr TQuaternion operator~() const { return conj(*this); } // constructs a quaternion from an axis and angle template constexpr static TQuaternion PURE fromAxisAngle(const TVec3& axis, B angle) { return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5)); } }; } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TQuaternion quatd; typedef details::TQuaternion quat; typedef details::TQuaternion quatf; typedef details::TQuaternion quath; constexpr inline quat operator"" _i(long double v) { return quat(0, static_cast(v), 0, 0); } constexpr inline quat operator"" _j(long double v) { return quat(0, 0, static_cast(v), 0); } constexpr inline quat operator"" _k(long double v) { return quat(0, 0, 0, static_cast(v)); } constexpr inline quat operator"" _i(unsigned long long v) { // NOLINT return quat(0, static_cast(v), 0, 0); } constexpr inline quat operator"" _j(unsigned long long v) { // NOLINT return quat(0, 0, static_cast(v), 0); } constexpr inline quat operator"" _k(unsigned long long v) { // NOLINT return quat(0, 0, 0, static_cast(v)); } constexpr inline quatd operator"" _id(long double v) { return quatd(0, static_cast(v), 0, 0); } constexpr inline quatd operator"" _jd(long double v) { return quatd(0, 0, static_cast(v), 0); } constexpr inline quatd operator"" _kd(long double v) { return quatd(0, 0, 0, static_cast(v)); } constexpr inline quatd operator"" _id(unsigned long long v) { // NOLINT return quatd(0, static_cast(v), 0, 0); } constexpr inline quatd operator"" _jd(unsigned long long v) { // NOLINT return quatd(0, 0, static_cast(v), 0); } constexpr inline quatd operator"" _kd(unsigned long long v) { // NOLINT return quatd(0, 0, 0, static_cast(v)); } // ---------------------------------------------------------------------------------------- } // namespace android #pragma clang diagnostic pop #undef PURE ����������������������������������������������������������������������������������libs/math/include/math/scalar.h���������������������������������������������������������������������0100644 0000000 0000000 00000002214 13756501735 015474� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #pragma once #include #include namespace android { template static constexpr T saturate(T v) noexcept { return T(std::min(T(1), std::max(T(0), v))); } template static constexpr T clamp(T v, T min, T max) noexcept { return T(std::min(max, std::max(min, v))); } template static constexpr T mix(T x, T y, T a) noexcept { return x * (T(1) - a) + y * a; } template static constexpr T lerp(T x, T y, T a) noexcept { return mix(x, y, a); } } // namespace std ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/include/math/vec2.h�����������������������������������������������������������������������0100644 0000000 0000000 00000007332 13756501735 015074� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #pragma once #include #include #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" #pragma clang diagnostic ignored "-Wnested-anon-types" namespace android { // ------------------------------------------------------------------------------------- namespace details { template class TVec2 : public TVecProductOperators, public TVecAddOperators, public TVecUnaryOperators, public TVecComparisonOperators, public TVecFunctions, public TVecDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; union { struct { T x, y; }; struct { T s, t; }; struct { T r, g; }; }; static constexpr size_t SIZE = 2; inline constexpr size_type size() const { return SIZE; } // array access inline constexpr T const& operator[](size_t i) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(i < SIZE); #endif return (&x)[i]; } inline T& operator[](size_t i) { assert(i < SIZE); return (&x)[i]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TVec2(const TVec2&) = default; ~TVec2() = default; TVec2& operator = (const TVec2&) = default; // constructors // leaves object uninitialized. use with caution. explicit constexpr TVec2(no_init) { } // default constructor constexpr TVec2() : x(0), y(0) { } // handles implicit conversion to a tvec4. must not be explicit. template::value >::type> constexpr TVec2(A v) : x(v), y(v) { } template constexpr TVec2(A x, B y) : x(x), y(y) { } template explicit constexpr TVec2(const TVec2& v) : x(v.x), y(v.y) { } // cross product works only on vectors of size 2 or 3 template friend inline constexpr value_type cross(const TVec2& u, const TVec2& v) { return value_type(u.x*v.y - u.y*v.x); } }; } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TVec2 double2; typedef details::TVec2 float2; typedef details::TVec2 vec2; typedef details::TVec2 half2; typedef details::TVec2 int2; typedef details::TVec2 uint2; typedef details::TVec2 short2; typedef details::TVec2 ushort2; typedef details::TVec2 byte2; typedef details::TVec2 ubyte2; typedef details::TVec2 bool2; // ---------------------------------------------------------------------------------------- } // namespace android #pragma clang diagnostic pop ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/include/math/vec3.h�����������������������������������������������������������������������0100644 0000000 0000000 00000007720 13756501735 015076� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #pragma once #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" #pragma clang diagnostic ignored "-Wnested-anon-types" namespace android { // ------------------------------------------------------------------------------------- namespace details { template class TVec3 : public TVecProductOperators, public TVecAddOperators, public TVecUnaryOperators, public TVecComparisonOperators, public TVecFunctions, public TVecDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; union { struct { T x, y, z; }; struct { T s, t, p; }; struct { T r, g, b; }; TVec2 xy; TVec2 st; TVec2 rg; }; static constexpr size_t SIZE = 3; inline constexpr size_type size() const { return SIZE; } // array access inline constexpr T const& operator[](size_t i) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(i < SIZE); #endif return (&x)[i]; } inline T& operator[](size_t i) { assert(i < SIZE); return (&x)[i]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TVec3(const TVec3&) = default; ~TVec3() = default; TVec3& operator = (const TVec3&) = default; // constructors // leaves object uninitialized. use with caution. explicit constexpr TVec3(no_init) { } // default constructor constexpr TVec3() : x(0), y(0), z(0) { } // handles implicit conversion to a tvec4. must not be explicit. template::value >::type> constexpr TVec3(A v) : x(v), y(v), z(v) { } template constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { } template constexpr TVec3(const TVec2& v, B z) : x(v.x), y(v.y), z(z) { } template explicit constexpr TVec3(const TVec3& v) : x(v.x), y(v.y), z(v.z) { } // cross product works only on vectors of size 3 template friend inline constexpr TVec3 cross(const TVec3& u, const TVec3& v) { return TVec3( u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x); } }; } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TVec3 double3; typedef details::TVec3 float3; typedef details::TVec3 vec3; typedef details::TVec3 half3; typedef details::TVec3 int3; typedef details::TVec3 uint3; typedef details::TVec3 short3; typedef details::TVec3 ushort3; typedef details::TVec3 byte3; typedef details::TVec3 ubyte3; typedef details::TVec3 bool3; // ---------------------------------------------------------------------------------------- } // namespace android #pragma clang diagnostic pop ������������������������������������������������libs/math/include/math/vec4.h�����������������������������������������������������������������������0100644 0000000 0000000 00000007652 13756501735 015103� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #pragma once #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" #pragma clang diagnostic ignored "-Wnested-anon-types" namespace android { // ------------------------------------------------------------------------------------- namespace details { template class TVec4 : public TVecProductOperators, public TVecAddOperators, public TVecUnaryOperators, public TVecComparisonOperators, public TVecFunctions, public TVecDebug { public: enum no_init { NO_INIT }; typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef size_t size_type; union { struct { T x, y, z, w; }; struct { T s, t, p, q; }; struct { T r, g, b, a; }; TVec2 xy; TVec2 st; TVec2 rg; TVec3 xyz; TVec3 stp; TVec3 rgb; }; static constexpr size_t SIZE = 4; inline constexpr size_type size() const { return SIZE; } // array access inline constexpr T const& operator[](size_t i) const { #if __cplusplus >= 201402L // only possible in C++0x14 with constexpr assert(i < SIZE); #endif return (&x)[i]; } inline T& operator[](size_t i) { assert(i < SIZE); return (&x)[i]; } // ----------------------------------------------------------------------- // we want the compiler generated versions for these... TVec4(const TVec4&) = default; ~TVec4() = default; TVec4& operator = (const TVec4&) = default; // constructors // leaves object uninitialized. use with caution. explicit constexpr TVec4(no_init) { } // default constructor constexpr TVec4() : x(0), y(0), z(0), w(0) { } // handles implicit conversion to a tvec4. must not be explicit. template::value >::type> constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { } template constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { } template constexpr TVec4(const TVec2& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { } template constexpr TVec4(const TVec3& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { } template explicit constexpr TVec4(const TVec4& v) : x(v.x), y(v.y), z(v.z), w(v.w) { } }; } // namespace details // ---------------------------------------------------------------------------------------- typedef details::TVec4 double4; typedef details::TVec4 float4; typedef details::TVec4 vec4; typedef details::TVec4 half4; typedef details::TVec4 int4; typedef details::TVec4 uint4; typedef details::TVec4 short4; typedef details::TVec4 ushort4; typedef details::TVec4 byte4; typedef details::TVec4 ubyte4; typedef details::TVec4 bool4; // ---------------------------------------------------------------------------------------- } // namespace android #pragma clang diagnostic pop ��������������������������������������������������������������������������������������libs/math/tests/������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012650� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/tests/Android.bp��������������������������������������������������������������������������0100644 0000000 0000000 00000002153 13756501735 014551� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// // 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. // cc_test { name: "vec_test", srcs: ["vec_test.cpp"], static_libs: ["libmath"], cflags: ["-Wall", "-Werror"], } cc_test { name: "mat_test", srcs: ["mat_test.cpp"], static_libs: ["libmath"], cflags: ["-Wall", "-Werror"], } cc_test { name: "half_test", srcs: ["half_test.cpp"], static_libs: ["libmath"], cflags: ["-Wall", "-Werror"], } cc_test { name: "quat_test", srcs: ["quat_test.cpp"], static_libs: ["libmath"], cflags: ["-Wall", "-Werror"], } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/tests/half_test.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000005366 13756501735 015334� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "HalfTest" #include #include #include #include #include namespace android { class HalfTest : public testing::Test { protected: }; TEST_F(HalfTest, Basics) { EXPECT_EQ(2UL, sizeof(half)); // test +/- zero EXPECT_EQ(0x0000, half( 0.0f).getBits()); EXPECT_EQ(0x8000, half(-0.0f).getBits()); // test nan EXPECT_EQ(0x7e00, half(NAN).getBits()); // test +/- infinity EXPECT_EQ(0x7C00, half( std::numeric_limits::infinity()).getBits()); EXPECT_EQ(0xFC00, half(-std::numeric_limits::infinity()).getBits()); // test a few known values EXPECT_EQ(0x3C01, half(1.0009765625).getBits()); EXPECT_EQ(0xC000, half(-2).getBits()); EXPECT_EQ(0x0400, half(6.10352e-5).getBits()); EXPECT_EQ(0x7BFF, half(65504).getBits()); EXPECT_EQ(0x3555, half(1.0f/3).getBits()); // numeric limits EXPECT_EQ(0x7C00, std::numeric_limits::infinity().getBits()); EXPECT_EQ(0x0400, std::numeric_limits::min().getBits()); EXPECT_EQ(0x7BFF, std::numeric_limits::max().getBits()); EXPECT_EQ(0xFBFF, std::numeric_limits::lowest().getBits()); // denormals (flushed to zero) EXPECT_EQ(0x0000, half( 6.09756e-5).getBits()); // if handled, should be: 0x03FF EXPECT_EQ(0x0000, half( 5.96046e-8).getBits()); // if handled, should be: 0x0001 EXPECT_EQ(0x8000, half(-6.09756e-5).getBits()); // if handled, should be: 0x83FF EXPECT_EQ(0x8000, half(-5.96046e-8).getBits()); // if handled, should be: 0x8001 // test all exactly representable integers for (int i=-2048 ; i<= 2048 ; ++i) { half h = i; EXPECT_EQ(i, float(h)); } } TEST_F(HalfTest, Literals) { half one = 1.0_hf; half pi = 3.1415926_hf; half minusTwo = -2.0_hf; EXPECT_EQ(half(1.0f), one); EXPECT_EQ(half(3.1415926), pi); EXPECT_EQ(half(-2.0f), minusTwo); } TEST_F(HalfTest, Vec) { float4 f4(1,2,3,4); half4 h4(f4); half3 h3(f4.xyz); half2 h2(f4.xy); EXPECT_EQ(f4, h4); EXPECT_EQ(f4.xyz, h3); EXPECT_EQ(f4.xy, h2); } }; // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/tests/mat_test.cpp������������������������������������������������������������������������0100644 0000000 0000000 00000047531 13756501735 015203� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "MatTest" #include #include #include #include #include #include #include namespace android { class MatTest : public testing::Test { protected: }; TEST_F(MatTest, Basics) { mat4 m0; EXPECT_EQ(sizeof(m0), sizeof(float)*16); } TEST_F(MatTest, ComparisonOps) { mat4 m0; mat4 m1(2); EXPECT_TRUE(m0 == m0); EXPECT_TRUE(m0 != m1); EXPECT_FALSE(m0 != m0); EXPECT_FALSE(m0 == m1); } TEST_F(MatTest, Constructors) { mat4 m0; ASSERT_EQ(m0[0].x, 1); ASSERT_EQ(m0[0].y, 0); ASSERT_EQ(m0[0].z, 0); ASSERT_EQ(m0[0].w, 0); ASSERT_EQ(m0[1].x, 0); ASSERT_EQ(m0[1].y, 1); ASSERT_EQ(m0[1].z, 0); ASSERT_EQ(m0[1].w, 0); ASSERT_EQ(m0[2].x, 0); ASSERT_EQ(m0[2].y, 0); ASSERT_EQ(m0[2].z, 1); ASSERT_EQ(m0[2].w, 0); ASSERT_EQ(m0[3].x, 0); ASSERT_EQ(m0[3].y, 0); ASSERT_EQ(m0[3].z, 0); ASSERT_EQ(m0[3].w, 1); mat4 m1(2); mat4 m2(vec4(2)); mat4 m3(m2); EXPECT_EQ(m1, m2); EXPECT_EQ(m2, m3); EXPECT_EQ(m3, m1); mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4)); EXPECT_NE(m4, m1); } TEST_F(MatTest, ArithmeticOps) { mat4 m0; mat4 m1(2); mat4 m2(vec4(2)); m1 += m2; EXPECT_EQ(mat4(4), m1); m2 -= m1; EXPECT_EQ(mat4(-2), m2); m1 *= 2; EXPECT_EQ(mat4(8), m1); m1 /= 2; EXPECT_EQ(mat4(4), m1); m0 = -m0; EXPECT_EQ(mat4(-1), m0); } TEST_F(MatTest, UnaryOps) { const mat4 identity; mat4 m0; m0 = -m0; EXPECT_EQ(mat4(vec4(-1, 0, 0, 0), vec4(0, -1, 0, 0), vec4(0, 0, -1, 0), vec4(0, 0, 0, -1)), m0); m0 = -m0; EXPECT_EQ(identity, m0); } TEST_F(MatTest, MiscOps) { const mat4 identity; mat4 m0; EXPECT_EQ(4, trace(m0)); mat4 m1(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16)); mat4 m2(vec4(1, 5, 9, 13), vec4(2, 6, 10, 14), vec4(3, 7, 11, 15), vec4(4, 8, 12, 16)); EXPECT_EQ(m1, transpose(m2)); EXPECT_EQ(m2, transpose(m1)); EXPECT_EQ(vec4(1, 6, 11, 16), diag(m1)); EXPECT_EQ(identity, inverse(identity)); mat4 m3(vec4(4, 3, 0, 0), vec4(3, 2, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1)); mat4 m3i(inverse(m3)); EXPECT_FLOAT_EQ(-2, m3i[0][0]); EXPECT_FLOAT_EQ(3, m3i[0][1]); EXPECT_FLOAT_EQ(3, m3i[1][0]); EXPECT_FLOAT_EQ(-4, m3i[1][1]); mat4 m3ii(inverse(m3i)); EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]); EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]); EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]); EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]); EXPECT_EQ(m1, m1*identity); for (size_t c=0 ; c<4 ; c++) { for (size_t r=0 ; r<4 ; r++) { EXPECT_FLOAT_EQ(m1[c][r], m1(r, c)); } } } TEST_F(MatTest, ElementAccess) { mat4 m(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16)); for (size_t c=0 ; c<4 ; c++) { for (size_t r=0 ; r<4 ; r++) { EXPECT_FLOAT_EQ(m[c][r], m(r, c)); } } m(3,2) = 100; EXPECT_FLOAT_EQ(m[2][3], 100); EXPECT_FLOAT_EQ(m(3, 2), 100); } //------------------------------------------------------------------------------ // MAT 3 //------------------------------------------------------------------------------ class Mat3Test : public testing::Test { protected: }; TEST_F(Mat3Test, Basics) { mat3 m0; EXPECT_EQ(sizeof(m0), sizeof(float)*9); } TEST_F(Mat3Test, ComparisonOps) { mat3 m0; mat3 m1(2); EXPECT_TRUE(m0 == m0); EXPECT_TRUE(m0 != m1); EXPECT_FALSE(m0 != m0); EXPECT_FALSE(m0 == m1); } TEST_F(Mat3Test, Constructors) { mat3 m0; ASSERT_EQ(m0[0].x, 1); ASSERT_EQ(m0[0].y, 0); ASSERT_EQ(m0[0].z, 0); ASSERT_EQ(m0[1].x, 0); ASSERT_EQ(m0[1].y, 1); ASSERT_EQ(m0[1].z, 0); ASSERT_EQ(m0[2].x, 0); ASSERT_EQ(m0[2].y, 0); ASSERT_EQ(m0[2].z, 1); mat3 m1(2); mat3 m2(vec3(2)); mat3 m3(m2); EXPECT_EQ(m1, m2); EXPECT_EQ(m2, m3); EXPECT_EQ(m3, m1); } TEST_F(Mat3Test, ArithmeticOps) { mat3 m0; mat3 m1(2); mat3 m2(vec3(2)); m1 += m2; EXPECT_EQ(mat3(4), m1); m2 -= m1; EXPECT_EQ(mat3(-2), m2); m1 *= 2; EXPECT_EQ(mat3(8), m1); m1 /= 2; EXPECT_EQ(mat3(4), m1); m0 = -m0; EXPECT_EQ(mat3(-1), m0); } TEST_F(Mat3Test, UnaryOps) { const mat3 identity; mat3 m0; m0 = -m0; EXPECT_EQ(mat3(vec3(-1, 0, 0), vec3(0, -1, 0), vec3(0, 0, -1)), m0); m0 = -m0; EXPECT_EQ(identity, m0); } TEST_F(Mat3Test, MiscOps) { const mat3 identity; mat3 m0; EXPECT_EQ(3, trace(m0)); mat3 m1(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 8, 9)); mat3 m2(vec3(1, 4, 7), vec3(2, 5, 8), vec3(3, 6, 9)); EXPECT_EQ(m1, transpose(m2)); EXPECT_EQ(m2, transpose(m1)); EXPECT_EQ(vec3(1, 5, 9), diag(m1)); EXPECT_EQ(identity, inverse(identity)); mat3 m3(vec3(4, 3, 0), vec3(3, 2, 0), vec3(0, 0, 1)); mat3 m3i(inverse(m3)); EXPECT_FLOAT_EQ(-2, m3i[0][0]); EXPECT_FLOAT_EQ(3, m3i[0][1]); EXPECT_FLOAT_EQ(3, m3i[1][0]); EXPECT_FLOAT_EQ(-4, m3i[1][1]); mat3 m3ii(inverse(m3i)); EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]); EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]); EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]); EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]); EXPECT_EQ(m1, m1*identity); } //------------------------------------------------------------------------------ // MAT 2 //------------------------------------------------------------------------------ class Mat2Test : public testing::Test { protected: }; TEST_F(Mat2Test, Basics) { mat2 m0; EXPECT_EQ(sizeof(m0), sizeof(float)*4); } TEST_F(Mat2Test, ComparisonOps) { mat2 m0; mat2 m1(2); EXPECT_TRUE(m0 == m0); EXPECT_TRUE(m0 != m1); EXPECT_FALSE(m0 != m0); EXPECT_FALSE(m0 == m1); } TEST_F(Mat2Test, Constructors) { mat2 m0; ASSERT_EQ(m0[0].x, 1); ASSERT_EQ(m0[0].y, 0); ASSERT_EQ(m0[1].x, 0); ASSERT_EQ(m0[1].y, 1); mat2 m1(2); mat2 m2(vec2(2)); mat2 m3(m2); EXPECT_EQ(m1, m2); EXPECT_EQ(m2, m3); EXPECT_EQ(m3, m1); } TEST_F(Mat2Test, ArithmeticOps) { mat2 m0; mat2 m1(2); mat2 m2(vec2(2)); m1 += m2; EXPECT_EQ(mat2(4), m1); m2 -= m1; EXPECT_EQ(mat2(-2), m2); m1 *= 2; EXPECT_EQ(mat2(8), m1); m1 /= 2; EXPECT_EQ(mat2(4), m1); m0 = -m0; EXPECT_EQ(mat2(-1), m0); } TEST_F(Mat2Test, UnaryOps) { const mat2 identity; mat2 m0; m0 = -m0; EXPECT_EQ(mat2(vec2(-1, 0), vec2(0, -1)), m0); m0 = -m0; EXPECT_EQ(identity, m0); } TEST_F(Mat2Test, MiscOps) { const mat2 identity; mat2 m0; EXPECT_EQ(2, trace(m0)); mat2 m1(vec2(1, 2), vec2(3, 4)); mat2 m2(vec2(1, 3), vec2(2, 4)); EXPECT_EQ(m1, transpose(m2)); EXPECT_EQ(m2, transpose(m1)); EXPECT_EQ(vec2(1, 4), diag(m1)); EXPECT_EQ(identity, inverse(identity)); EXPECT_EQ(m1, m1*identity); } //------------------------------------------------------------------------------ // MORE MATRIX TESTS //------------------------------------------------------------------------------ template class MatTestT : public ::testing::Test { public: }; typedef ::testing::Types TestMatrixValueTypes; TYPED_TEST_CASE(MatTestT, TestMatrixValueTypes); #define TEST_MATRIX_INVERSE(MATRIX, EPSILON) \ { \ typedef decltype(MATRIX) MatrixType; \ MatrixType inv1 = inverse(MATRIX); \ MatrixType ident1 = MATRIX * inv1; \ static const MatrixType IDENTITY; \ for (size_t row = 0; row < MatrixType::ROW_SIZE; ++row) { \ for (size_t col = 0; col < MatrixType::COL_SIZE; ++col) { \ EXPECT_NEAR(ident1[row][col], IDENTITY[row][col], EPSILON); \ } \ } \ } TYPED_TEST(MatTestT, Inverse4) { typedef ::android::details::TMat44 M44T; M44T m1(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); M44T m2(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); M44T m3(1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0); M44T m4( 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00, -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00, 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00); M44T m5( 4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00, -8.749647e-01, 1.456563e-01, -4.617587e-01, 3.044795e+00, 1.229049e-01, 9.892561e-01, 7.916244e-02, -6.737138e+00, 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00); TEST_MATRIX_INVERSE(m1, 0); TEST_MATRIX_INVERSE(m2, 0); TEST_MATRIX_INVERSE(m3, 0); TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits::epsilon()); TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits::epsilon()); } //------------------------------------------------------------------------------ TYPED_TEST(MatTestT, Inverse3) { typedef ::android::details::TMat33 M33T; M33T m1(1, 0, 0, 0, 1, 0, 0, 0, 1); M33T m2(0, -1, 0, 1, 0, 0, 0, 0, 1); M33T m3(2, 0, 0, 0, 0, 1, 0, -1, 0); M33T m4( 4.683281e-01, 1.251189e-02, 0.000000e+00, -8.749647e-01, 1.456563e-01, 0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00); M33T m5( 4.683281e-01, 1.251189e-02, -8.834660e-01, -8.749647e-01, 1.456563e-01, -4.617587e-01, 1.229049e-01, 9.892561e-01, 7.916244e-02); TEST_MATRIX_INVERSE(m1, 0); TEST_MATRIX_INVERSE(m2, 0); TEST_MATRIX_INVERSE(m3, 0); TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits::epsilon()); TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits::epsilon()); } //------------------------------------------------------------------------------ TYPED_TEST(MatTestT, Inverse2) { typedef ::android::details::TMat22 M22T; M22T m1(1, 0, 0, 1); M22T m2(0, -1, 1, 0); M22T m3( 4.683281e-01, 1.251189e-02, -8.749647e-01, 1.456563e-01); M22T m4( 4.683281e-01, 1.251189e-02, -8.749647e-01, 1.456563e-01); TEST_MATRIX_INVERSE(m1, 0); TEST_MATRIX_INVERSE(m2, 0); TEST_MATRIX_INVERSE(m3, 20.0 * std::numeric_limits::epsilon()); TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits::epsilon()); } //------------------------------------------------------------------------------ // A macro to help with vector comparisons within floating point range. #define EXPECT_VEC_EQ(VEC1, VEC2) \ do { \ const decltype(VEC1) v1 = VEC1; \ const decltype(VEC2) v2 = VEC2; \ if (std::is_same::value) { \ for (size_t i = 0; i < v1.size(); ++i) { \ EXPECT_FLOAT_EQ(v1[i], v2[i]); \ } \ } else if (std::is_same::value) { \ for (size_t i = 0; i < v1.size(); ++i) { \ EXPECT_DOUBLE_EQ(v1[i], v2[i]); \ } \ } else { \ for (size_t i = 0; i < v1.size(); ++i) { \ EXPECT_EQ(v1[i], v2[i]); \ } \ } \ } while(0) //------------------------------------------------------------------------------ // A macro to help with type comparisons within floating point range. #define ASSERT_TYPE_EQ(T1, T2) \ do { \ const decltype(T1) t1 = T1; \ const decltype(T2) t2 = T2; \ if (std::is_same::value) { \ ASSERT_FLOAT_EQ(t1, t2); \ } else if (std::is_same::value) { \ ASSERT_DOUBLE_EQ(t1, t2); \ } else { \ ASSERT_EQ(t1, t2); \ } \ } while(0) //------------------------------------------------------------------------------ // Test some translation stuff. TYPED_TEST(MatTestT, Translation4) { typedef ::android::details::TMat44 M44T; typedef ::android::details::TVec4 V4T; V4T translateBy(-7.3, 1.1, 14.4, 0.0); V4T translation(translateBy[0], translateBy[1], translateBy[2], 1.0); M44T translation_matrix = M44T::translate(translation); V4T p1(9.9, 3.1, 41.1, 1.0); V4T p2(-18.0, 0.0, 1.77, 1.0); V4T p3(0, 0, 0, 1); V4T p4(-1000, -1000, 1000, 1.0); EXPECT_VEC_EQ(translation_matrix * p1, translateBy + p1); EXPECT_VEC_EQ(translation_matrix * p2, translateBy + p2); EXPECT_VEC_EQ(translation_matrix * p3, translateBy + p3); EXPECT_VEC_EQ(translation_matrix * p4, translateBy + p4); } //------------------------------------------------------------------------------ template static void verifyOrthonormal(const MATRIX& A) { typedef typename MATRIX::value_type T; static constexpr T value_eps = T(100) * std::numeric_limits::epsilon(); const MATRIX prod = A * transpose(A); for (size_t i = 0; i < MATRIX::NUM_COLS; ++i) { for (size_t j = 0; j < MATRIX::NUM_ROWS; ++j) { if (i == j) { ASSERT_NEAR(prod[i][j], T(1), value_eps); } else { ASSERT_NEAR(prod[i][j], T(0), value_eps); } } } } //------------------------------------------------------------------------------ // Test euler code. TYPED_TEST(MatTestT, EulerZYX_44) { typedef ::android::details::TMat44 M44T; std::default_random_engine generator(82828); std::uniform_real_distribution distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI); auto rand_gen = std::bind(distribution, generator); for (size_t i = 0; i < 100; ++i) { M44T m = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); verifyOrthonormal(m); } M44T m = M44T::eulerZYX(1, 2, 3); verifyOrthonormal(m); } //------------------------------------------------------------------------------ // Test euler code. TYPED_TEST(MatTestT, EulerZYX_33) { typedef ::android::details::TMat33 M33T; std::default_random_engine generator(112233); std::uniform_real_distribution distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI); auto rand_gen = std::bind(distribution, generator); for (size_t i = 0; i < 100; ++i) { M33T m = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); verifyOrthonormal(m); } M33T m = M33T::eulerZYX(1, 2, 3); verifyOrthonormal(m); } //------------------------------------------------------------------------------ // Test to quaternion with post translation. TYPED_TEST(MatTestT, ToQuaternionPostTranslation) { typedef ::android::details::TMat44 M44T; typedef ::android::details::TVec4 V4T; typedef ::android::details::TQuaternion QuatT; std::default_random_engine generator(112233); std::uniform_real_distribution distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI); auto rand_gen = std::bind(distribution, generator); for (size_t i = 0; i < 100; ++i) { M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); M44T t = M44T::translate(V4T(rand_gen(), rand_gen(), rand_gen(), 1)); QuatT qr = r.toQuaternion(); M44T tr = t * r; QuatT qtr = tr.toQuaternion(); ASSERT_TYPE_EQ(qr.x, qtr.x); ASSERT_TYPE_EQ(qr.y, qtr.y); ASSERT_TYPE_EQ(qr.z, qtr.z); ASSERT_TYPE_EQ(qr.w, qtr.w); } M44T r = M44T::eulerZYX(1, 2, 3); M44T t = M44T::translate(V4T(20, -15, 2, 1)); QuatT qr = r.toQuaternion(); M44T tr = t * r; QuatT qtr = tr.toQuaternion(); ASSERT_TYPE_EQ(qr.x, qtr.x); ASSERT_TYPE_EQ(qr.y, qtr.y); ASSERT_TYPE_EQ(qr.z, qtr.z); ASSERT_TYPE_EQ(qr.w, qtr.w); } //------------------------------------------------------------------------------ // Test to quaternion with post translation. TYPED_TEST(MatTestT, ToQuaternionPointTransformation33) { static constexpr TypeParam value_eps = TypeParam(1000) * std::numeric_limits::epsilon(); typedef ::android::details::TMat33 M33T; typedef ::android::details::TVec3 V3T; typedef ::android::details::TQuaternion QuatT; std::default_random_engine generator(112233); std::uniform_real_distribution distribution(-100.0, 100.0); auto rand_gen = std::bind(distribution, generator); for (size_t i = 0; i < 100; ++i) { M33T r = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); QuatT qr = r.toQuaternion(); V3T p(rand_gen(), rand_gen(), rand_gen()); V3T pr = r * p; V3T pq = qr * p; ASSERT_NEAR(pr.x, pq.x, value_eps); ASSERT_NEAR(pr.y, pq.y, value_eps); ASSERT_NEAR(pr.z, pq.z, value_eps); } } //------------------------------------------------------------------------------ // Test to quaternion with post translation. TYPED_TEST(MatTestT, ToQuaternionPointTransformation44) { static constexpr TypeParam value_eps = TypeParam(1000) * std::numeric_limits::epsilon(); typedef ::android::details::TMat44 M44T; typedef ::android::details::TVec4 V4T; typedef ::android::details::TVec3 V3T; typedef ::android::details::TQuaternion QuatT; std::default_random_engine generator(992626); std::uniform_real_distribution distribution(-100.0, 100.0); auto rand_gen = std::bind(distribution, generator); for (size_t i = 0; i < 100; ++i) { M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen()); QuatT qr = r.toQuaternion(); V3T p(rand_gen(), rand_gen(), rand_gen()); V4T pr = r * V4T(p.x, p.y, p.z, 1); pr.x /= pr.w; pr.y /= pr.w; pr.z /= pr.w; V3T pq = qr * p; ASSERT_NEAR(pr.x, pq.x, value_eps); ASSERT_NEAR(pr.y, pq.y, value_eps); ASSERT_NEAR(pr.z, pq.z, value_eps); } } #undef TEST_MATRIX_INVERSE }; // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/tests/quat_test.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000015765 13756501735 015400� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "QuatTest" #include #include #include #include #include #include #include #include #include namespace android { class QuatTest : public testing::Test { protected: }; TEST_F(QuatTest, Basics) { quatd q; double4& v(q.xyzw); EXPECT_EQ(sizeof(quatd), sizeof(double)*4); EXPECT_EQ(reinterpret_cast(&q), reinterpret_cast(&v)); } TEST_F(QuatTest, Constructors) { quatd q0; EXPECT_EQ(q0.x, 0); EXPECT_EQ(q0.y, 0); EXPECT_EQ(q0.z, 0); EXPECT_EQ(q0.w, 0); quatd q1(1); EXPECT_EQ(q1.x, 0); EXPECT_EQ(q1.y, 0); EXPECT_EQ(q1.z, 0); EXPECT_EQ(q1.w, 1); quatd q2(1, 2, 3, 4); EXPECT_EQ(q2.x, 2); EXPECT_EQ(q2.y, 3); EXPECT_EQ(q2.z, 4); EXPECT_EQ(q2.w, 1); quatd q3(q2); EXPECT_EQ(q3.x, 2); EXPECT_EQ(q3.y, 3); EXPECT_EQ(q3.z, 4); EXPECT_EQ(q3.w, 1); quatd q4(q3.xyz, 42); EXPECT_EQ(q4.x, 2); EXPECT_EQ(q4.y, 3); EXPECT_EQ(q4.z, 4); EXPECT_EQ(q4.w, 42); quatd q5(double3(q2.xy, 42), 24); EXPECT_EQ(q5.x, 2); EXPECT_EQ(q5.y, 3); EXPECT_EQ(q5.z, 42); EXPECT_EQ(q5.w, 24); quatd q6; q6 = 12; EXPECT_EQ(q6.x, 0); EXPECT_EQ(q6.y, 0); EXPECT_EQ(q6.z, 0); EXPECT_EQ(q6.w, 12); quatd q7 = 1 + 2_id + 3_jd + 4_kd; EXPECT_EQ(q7.x, 2); EXPECT_EQ(q7.y, 3); EXPECT_EQ(q7.z, 4); EXPECT_EQ(q7.w, 1); quatf qf(2); EXPECT_EQ(qf.x, 0); EXPECT_EQ(qf.y, 0); EXPECT_EQ(qf.z, 0); EXPECT_EQ(qf.w, 2); } TEST_F(QuatTest, Access) { quatd q0(1, 2, 3, 4); q0.x = 10; q0.y = 20; q0.z = 30; q0.w = 40; EXPECT_EQ(q0.x, 10); EXPECT_EQ(q0.y, 20); EXPECT_EQ(q0.z, 30); EXPECT_EQ(q0.w, 40); q0[0] = 100; q0[1] = 200; q0[2] = 300; q0[3] = 400; EXPECT_EQ(q0.x, 100); EXPECT_EQ(q0.y, 200); EXPECT_EQ(q0.z, 300); EXPECT_EQ(q0.w, 400); q0.xyz = double3(1, 2, 3); EXPECT_EQ(q0.x, 1); EXPECT_EQ(q0.y, 2); EXPECT_EQ(q0.z, 3); EXPECT_EQ(q0.w, 400); } TEST_F(QuatTest, UnaryOps) { quatd q0(1, 2, 3, 4); q0 += 1; EXPECT_EQ(q0.x, 2); EXPECT_EQ(q0.y, 3); EXPECT_EQ(q0.z, 4); EXPECT_EQ(q0.w, 2); q0 -= 1; EXPECT_EQ(q0.x, 2); EXPECT_EQ(q0.y, 3); EXPECT_EQ(q0.z, 4); EXPECT_EQ(q0.w, 1); q0 *= 2; EXPECT_EQ(q0.x, 4); EXPECT_EQ(q0.y, 6); EXPECT_EQ(q0.z, 8); EXPECT_EQ(q0.w, 2); q0 /= 2; EXPECT_EQ(q0.x, 2); EXPECT_EQ(q0.y, 3); EXPECT_EQ(q0.z, 4); EXPECT_EQ(q0.w, 1); quatd q1(10, 20, 30, 40); q0 += q1; EXPECT_EQ(q0.x, 22); EXPECT_EQ(q0.y, 33); EXPECT_EQ(q0.z, 44); EXPECT_EQ(q0.w, 11); q0 -= q1; EXPECT_EQ(q0.x, 2); EXPECT_EQ(q0.y, 3); EXPECT_EQ(q0.z, 4); EXPECT_EQ(q0.w, 1); q1 = -q1; EXPECT_EQ(q1.x, -20); EXPECT_EQ(q1.y, -30); EXPECT_EQ(q1.z, -40); EXPECT_EQ(q1.w, -10); // TODO(mathias): multiplies } TEST_F(QuatTest, ComparisonOps) { quatd q0(1, 2, 3, 4); quatd q1(10, 20, 30, 40); EXPECT_TRUE(q0 == q0); EXPECT_TRUE(q0 != q1); EXPECT_FALSE(q0 != q0); EXPECT_FALSE(q0 == q1); } TEST_F(QuatTest, ArithmeticOps) { quatd q0(1, 2, 3, 4); quatd q1(10, 20, 30, 40); quatd q2(q0 + q1); EXPECT_EQ(q2.x, 22); EXPECT_EQ(q2.y, 33); EXPECT_EQ(q2.z, 44); EXPECT_EQ(q2.w, 11); q0 = q1 * 2; EXPECT_EQ(q0.x, 40); EXPECT_EQ(q0.y, 60); EXPECT_EQ(q0.z, 80); EXPECT_EQ(q0.w, 20); q0 = 2 * q1; EXPECT_EQ(q0.x, 40); EXPECT_EQ(q0.y, 60); EXPECT_EQ(q0.z, 80); EXPECT_EQ(q0.w, 20); quatf qf(2); q0 = q1 * qf; EXPECT_EQ(q0.x, 40); EXPECT_EQ(q0.y, 60); EXPECT_EQ(q0.z, 80); EXPECT_EQ(q0.w, 20); EXPECT_EQ(1_id * 1_id, quat(-1)); EXPECT_EQ(1_jd * 1_jd, quat(-1)); EXPECT_EQ(1_kd * 1_kd, quat(-1)); EXPECT_EQ(1_id * 1_jd * 1_kd, quat(-1)); } TEST_F(QuatTest, ArithmeticFunc) { quatd q(1, 2, 3, 4); quatd qc(conj(q)); __attribute__((unused)) quatd qi(inverse(q)); quatd qn(normalize(q)); EXPECT_EQ(qc.x, -2); EXPECT_EQ(qc.y, -3); EXPECT_EQ(qc.z, -4); EXPECT_EQ(qc.w, 1); EXPECT_EQ(~q, qc); EXPECT_EQ(length(q), length(qc)); EXPECT_EQ(sqrt(30), length(q)); EXPECT_FLOAT_EQ(1, length(qn)); EXPECT_FLOAT_EQ(1, dot(qn, qn)); quatd qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2); EXPECT_EQ(mat4d(qr).toQuaternion(), qr); EXPECT_EQ(1_id, mat4d(1_id).toQuaternion()); EXPECT_EQ(1_jd, mat4d(1_jd).toQuaternion()); EXPECT_EQ(1_kd, mat4d(1_kd).toQuaternion()); EXPECT_EQ(qr, log(exp(qr))); quatd qq = qr * qr; quatd q2 = pow(qr, 2); EXPECT_NEAR(qq.x, q2.x, 1e-15); EXPECT_NEAR(qq.y, q2.y, 1e-15); EXPECT_NEAR(qq.z, q2.z, 1e-15); EXPECT_NEAR(qq.w, q2.w, 1e-15); quatd qa = quatd::fromAxisAngle(double3(0, 0, 1), 0); quatd qb = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2); quatd qs = slerp(qa, qb, 0.5); qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 4); EXPECT_FLOAT_EQ(qr.x, qs.x); EXPECT_FLOAT_EQ(qr.y, qs.y); EXPECT_FLOAT_EQ(qr.z, qs.z); EXPECT_FLOAT_EQ(qr.w, qs.w); qs = nlerp(qa, qb, 0.5); EXPECT_FLOAT_EQ(qr.x, qs.x); EXPECT_FLOAT_EQ(qr.y, qs.y); EXPECT_FLOAT_EQ(qr.z, qs.z); EXPECT_FLOAT_EQ(qr.w, qs.w); } TEST_F(QuatTest, MultiplicationExhaustive) { static constexpr double value_eps = double(1000) * std::numeric_limits::epsilon(); std::default_random_engine generator(171717); std::uniform_real_distribution distribution(-10.0, 10.0); auto rand_gen = std::bind(distribution, generator); for (size_t i = 0; i < (1024 * 1024); ++i) { double3 axis_a = normalize(double3(rand_gen(), rand_gen(), rand_gen())); double angle_a = rand_gen(); quatd a = quatd::fromAxisAngle(axis_a, angle_a); double3 axis_b = normalize(double3(rand_gen(), rand_gen(), rand_gen())); double angle_b = rand_gen(); quatd b = quatd::fromAxisAngle(axis_b, angle_b); quatd ab = a * b; quatd ab_other(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz), (a.w * b.w) - dot(a.xyz, b.xyz)); ASSERT_NEAR(ab.x, ab_other.x, value_eps); ASSERT_NEAR(ab.y, ab_other.y, value_eps); ASSERT_NEAR(ab.z, ab_other.z, value_eps); ASSERT_NEAR(ab.w, ab_other.w, value_eps); } } }; // namespace android �����������libs/math/tests/vec_test.cpp������������������������������������������������������������������������0100644 0000000 0000000 00000013445 13756501735 015174� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "VecTest" #include #include #include #include namespace android { class VecTest : public testing::Test { }; TEST_F(VecTest, Basics) { vec4 v4; vec3& v3(v4.xyz); EXPECT_EQ(sizeof(vec4), sizeof(float)*4); EXPECT_EQ(sizeof(vec3), sizeof(float)*3); EXPECT_EQ(sizeof(vec2), sizeof(float)*2); EXPECT_EQ(reinterpret_cast(&v3), reinterpret_cast(&v4)); } TEST_F(VecTest, Constructors) { vec4 v0; EXPECT_EQ(v0.x, 0); EXPECT_EQ(v0.y, 0); EXPECT_EQ(v0.z, 0); EXPECT_EQ(v0.w, 0); vec4 v1(1); EXPECT_EQ(v1.x, 1); EXPECT_EQ(v1.y, 1); EXPECT_EQ(v1.z, 1); EXPECT_EQ(v1.w, 1); vec4 v2(1, 2, 3, 4); EXPECT_EQ(v2.x, 1); EXPECT_EQ(v2.y, 2); EXPECT_EQ(v2.z, 3); EXPECT_EQ(v2.w, 4); vec4 v3(v2); EXPECT_EQ(v3.x, 1); EXPECT_EQ(v3.y, 2); EXPECT_EQ(v3.z, 3); EXPECT_EQ(v3.w, 4); vec4 v4(v3.xyz, 42); EXPECT_EQ(v4.x, 1); EXPECT_EQ(v4.y, 2); EXPECT_EQ(v4.z, 3); EXPECT_EQ(v4.w, 42); vec4 v5(vec3(v2.xy, 42), 24); EXPECT_EQ(v5.x, 1); EXPECT_EQ(v5.y, 2); EXPECT_EQ(v5.z, 42); EXPECT_EQ(v5.w, 24); float4 vf(2); EXPECT_EQ(vf.x, 2); EXPECT_EQ(vf.y, 2); EXPECT_EQ(vf.z, 2); EXPECT_EQ(vf.w, 2); } TEST_F(VecTest, Access) { vec4 v0(1, 2, 3, 4); v0.x = 10; v0.y = 20; v0.z = 30; v0.w = 40; EXPECT_EQ(v0.x, 10); EXPECT_EQ(v0.y, 20); EXPECT_EQ(v0.z, 30); EXPECT_EQ(v0.w, 40); v0[0] = 100; v0[1] = 200; v0[2] = 300; v0[3] = 400; EXPECT_EQ(v0.x, 100); EXPECT_EQ(v0.y, 200); EXPECT_EQ(v0.z, 300); EXPECT_EQ(v0.w, 400); v0.xyz = vec3(1, 2, 3); EXPECT_EQ(v0.x, 1); EXPECT_EQ(v0.y, 2); EXPECT_EQ(v0.z, 3); EXPECT_EQ(v0.w, 400); } TEST_F(VecTest, UnaryOps) { vec4 v0(1, 2, 3, 4); v0 += 1; EXPECT_EQ(v0.x, 2); EXPECT_EQ(v0.y, 3); EXPECT_EQ(v0.z, 4); EXPECT_EQ(v0.w, 5); v0 -= 1; EXPECT_EQ(v0.x, 1); EXPECT_EQ(v0.y, 2); EXPECT_EQ(v0.z, 3); EXPECT_EQ(v0.w, 4); v0 *= 2; EXPECT_EQ(v0.x, 2); EXPECT_EQ(v0.y, 4); EXPECT_EQ(v0.z, 6); EXPECT_EQ(v0.w, 8); v0 /= 2; EXPECT_EQ(v0.x, 1); EXPECT_EQ(v0.y, 2); EXPECT_EQ(v0.z, 3); EXPECT_EQ(v0.w, 4); vec4 v1(10, 20, 30, 40); v0 += v1; EXPECT_EQ(v0.x, 11); EXPECT_EQ(v0.y, 22); EXPECT_EQ(v0.z, 33); EXPECT_EQ(v0.w, 44); v0 -= v1; EXPECT_EQ(v0.x, 1); EXPECT_EQ(v0.y, 2); EXPECT_EQ(v0.z, 3); EXPECT_EQ(v0.w, 4); v0 *= v1; EXPECT_EQ(v0.x, 10); EXPECT_EQ(v0.y, 40); EXPECT_EQ(v0.z, 90); EXPECT_EQ(v0.w, 160); v0 /= v1; EXPECT_EQ(v0.x, 1); EXPECT_EQ(v0.y, 2); EXPECT_EQ(v0.z, 3); EXPECT_EQ(v0.w, 4); v1 = -v1; EXPECT_EQ(v1.x, -10); EXPECT_EQ(v1.y, -20); EXPECT_EQ(v1.z, -30); EXPECT_EQ(v1.w, -40); float4 fv(1, 2, 3, 4); v1 += fv; EXPECT_EQ(v1.x, -9); EXPECT_EQ(v1.y, -18); EXPECT_EQ(v1.z, -27); EXPECT_EQ(v1.w, -36); } TEST_F(VecTest, ComparisonOps) { vec4 v0(1, 2, 3, 4); vec4 v1(10, 20, 30, 40); EXPECT_TRUE(v0 == v0); EXPECT_TRUE(v0 != v1); EXPECT_FALSE(v0 != v0); EXPECT_FALSE(v0 == v1); } TEST_F(VecTest, ComparisonFunctions) { vec4 v0(1, 2, 3, 4); vec4 v1(10, 20, 30, 40); EXPECT_TRUE(all(equal(v0, v0))); EXPECT_TRUE(all(notEqual(v0, v1))); EXPECT_FALSE(any(notEqual(v0, v0))); EXPECT_FALSE(any(equal(v0, v1))); EXPECT_FALSE(all(lessThan(v0, v0))); EXPECT_TRUE(all(lessThanEqual(v0, v0))); EXPECT_FALSE(all(greaterThan(v0, v0))); EXPECT_TRUE(all(greaterThanEqual(v0, v0))); EXPECT_TRUE(all(lessThan(v0, v1))); EXPECT_TRUE(all(greaterThan(v1, v0))); } TEST_F(VecTest, ArithmeticOps) { vec4 v0(1, 2, 3, 4); vec4 v1(10, 20, 30, 40); vec4 v2(v0 + v1); EXPECT_EQ(v2.x, 11); EXPECT_EQ(v2.y, 22); EXPECT_EQ(v2.z, 33); EXPECT_EQ(v2.w, 44); v0 = v1 * 2; EXPECT_EQ(v0.x, 20); EXPECT_EQ(v0.y, 40); EXPECT_EQ(v0.z, 60); EXPECT_EQ(v0.w, 80); v0 = 2 * v1; EXPECT_EQ(v0.x, 20); EXPECT_EQ(v0.y, 40); EXPECT_EQ(v0.z, 60); EXPECT_EQ(v0.w, 80); float4 vf(2); v0 = v1 * vf; EXPECT_EQ(v0.x, 20); EXPECT_EQ(v0.y, 40); EXPECT_EQ(v0.z, 60); EXPECT_EQ(v0.w, 80); } TEST_F(VecTest, ArithmeticFunc) { vec3 east(1, 0, 0); vec3 north(0, 1, 0); vec3 up(cross(east, north)); EXPECT_EQ(up, vec3(0, 0, 1)); EXPECT_EQ(dot(east, north), 0); EXPECT_EQ(length(east), 1); EXPECT_EQ(distance(east, north), sqrtf(2)); vec3 v0(1, 2, 3); vec3 vn(normalize(v0)); EXPECT_FLOAT_EQ(1, length(vn)); EXPECT_FLOAT_EQ(length(v0), dot(v0, vn)); float3 vf(east); EXPECT_EQ(length(vf), 1); EXPECT_TRUE(any(vec3(0, 0, 1))); EXPECT_FALSE(any(vec3(0, 0, 0))); EXPECT_TRUE(all(vec3(1, 1, 1))); EXPECT_FALSE(all(vec3(0, 0, 1))); EXPECT_TRUE(any(bool3(false, false, true))); EXPECT_FALSE(any(bool3(false))); EXPECT_TRUE(all(bool3(true))); EXPECT_FALSE(all(bool3(false, false, true))); std::function p = [](auto v) -> bool { return v > 0.0f; }; EXPECT_TRUE(all(map(vec3(1, 2, 3), p))); } }; // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012676� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/Android.bp��������������������������������������������������������������������������0100644 0000000 0000000 00000001610 13756501735 014574� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2017 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. cc_library_headers { name: "libnativebase_headers", vendor_available: true, host_supported: true, export_include_dirs: ["include"], target: { linux_bionic: { enabled: true, }, windows: { enabled: true, }, } } ������������������������������������������������������������������������������������������������������������������������libs/nativebase/MODULE_LICENSE_APACHE2��������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 016016� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/NOTICE������������������������������������������������������������������������������0100644 0000000 0000000 00000024707 13756501735 013611� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ���������������������������������������������������������libs/nativebase/include/����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014321� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/include/nativebase/�����������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016442� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/include/nativebase/nativebase.h�����������������������������������������������������0100644 0000000 0000000 00000006063 13756501735 020736� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 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. */ #pragma once #include #include #include #include #include #include __BEGIN_DECLS #ifdef __cplusplus #define ANDROID_NATIVE_UNSIGNED_CAST(x) static_cast(x) #else #define ANDROID_NATIVE_UNSIGNED_CAST(x) ((unsigned int)(x)) #endif #define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \ ((ANDROID_NATIVE_UNSIGNED_CAST(a) << 24) | \ (ANDROID_NATIVE_UNSIGNED_CAST(b) << 16) | \ (ANDROID_NATIVE_UNSIGNED_CAST(c) << 8) | \ (ANDROID_NATIVE_UNSIGNED_CAST(d))) #define ANDROID_NATIVE_BUFFER_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r') typedef struct android_native_base_t { /* a magic value defined by the actual EGL native type */ int magic; /* the sizeof() of the actual EGL native type */ int version; void* reserved[4]; /* reference-counting interface */ void (*incRef)(struct android_native_base_t* base); void (*decRef)(struct android_native_base_t* base); } android_native_base_t; typedef struct android_native_rect_t { int32_t left; int32_t top; int32_t right; int32_t bottom; } android_native_rect_t; typedef struct ANativeWindowBuffer { #ifdef __cplusplus ANativeWindowBuffer() { common.magic = ANDROID_NATIVE_BUFFER_MAGIC; common.version = sizeof(ANativeWindowBuffer); memset(common.reserved, 0, sizeof(common.reserved)); } // Implement the methods that sp expects so that it // can be used to automatically refcount ANativeWindowBuffer's. void incStrong(const void* /*id*/) const { common.incRef(const_cast(&common)); } void decStrong(const void* /*id*/) const { common.decRef(const_cast(&common)); } #endif struct android_native_base_t common; int width; int height; int stride; int format; int usage_deprecated; uintptr_t layerCount; void* reserved[1]; const native_handle_t* handle; uint64_t usage; // we needed extra space for storing the 64-bits usage flags // the number of slots to use from reserved_proc depends on the // architecture. void* reserved_proc[8 - (sizeof(uint64_t) / sizeof(void*))]; } ANativeWindowBuffer_t; typedef struct ANativeWindowBuffer ANativeWindowBuffer; // Old typedef for backwards compatibility. typedef ANativeWindowBuffer_t android_native_buffer_t; __END_DECLS �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/����������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013273� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/AHardwareBuffer.cpp���������������������������������������������������������������0100644 0000000 0000000 00000072437 13756501735 017001� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 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. */ #define LOG_TAG "AHardwareBuffer" #include #include #include #include #include #include #include #include #include #include #include static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints using namespace android; // ---------------------------------------------------------------------------- // Public functions // ---------------------------------------------------------------------------- int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) { if (!outBuffer || !desc) return BAD_VALUE; if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE; int format = AHardwareBuffer_convertToPixelFormat(desc->format); uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); sp gbuffer(new GraphicBuffer( desc->width, desc->height, format, desc->layers, usage, std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]")); status_t err = gbuffer->initCheck(); if (err != 0 || gbuffer->handle == 0) { if (err == NO_MEMORY) { GraphicBuffer::dumpAllocationsToSystemLog(); } ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p", desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle); return err; } *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); // Ensure the buffer doesn't get destroyed when the sp<> goes away. AHardwareBuffer_acquire(*outBuffer); return NO_ERROR; } void AHardwareBuffer_acquire(AHardwareBuffer* buffer) { // incStrong/decStrong token must be the same, doesn't matter what it is AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong((void*)AHardwareBuffer_acquire); } void AHardwareBuffer_release(AHardwareBuffer* buffer) { // incStrong/decStrong token must be the same, doesn't matter what it is AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong((void*)AHardwareBuffer_acquire); } void AHardwareBuffer_describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc) { if (!buffer || !outDesc) return; const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); outDesc->width = gbuffer->getWidth(); outDesc->height = gbuffer->getHeight(); outDesc->layers = gbuffer->getLayerCount(); outDesc->format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gbuffer->getPixelFormat())); outDesc->usage = AHardwareBuffer_convertFromGrallocUsageBits(gbuffer->getUsage()); outDesc->stride = gbuffer->getStride(); outDesc->rfu0 = 0; outDesc->rfu1 = 0; } int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { if (outBytesPerPixel) *outBytesPerPixel = -1; if (outBytesPerStride) *outBytesPerStride = -1; if (!buffer) { return BAD_VALUE; } if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } usage = AHardwareBuffer_convertToGrallocUsageBits(usage); GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); //Mapper implementations before 3.0 will not return bytes per pixel or //bytes per stride information. if (gbuffer->getBufferMapperVersion() == GraphicBufferMapper::Version::GRALLOC_2) { ALOGE("Mapper versions before 3.0 cannot retrieve bytes per pixel and bytes per stride info"); return INVALID_OPERATION; } if (gbuffer->getLayerCount() > 1) { ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; " "only buffers with one layer are allowed"); return INVALID_OPERATION; } Rect bounds; if (!rect) { bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight())); } else { bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); } int32_t bytesPerPixel; int32_t bytesPerStride; int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride); // if hardware returns -1 for bytes per pixel or bytes per stride, we fail // and unlock the buffer if (bytesPerPixel == -1 || bytesPerStride == -1) { gbuffer->unlock(); return INVALID_OPERATION; } if (outBytesPerPixel) *outBytesPerPixel = bytesPerPixel; if (outBytesPerStride) *outBytesPerStride = bytesPerStride; return result; } int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress) { int32_t bytesPerPixel; int32_t bytesPerStride; if (!buffer) return BAD_VALUE; if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } usage = AHardwareBuffer_convertToGrallocUsageBits(usage); GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); if (gbuffer->getLayerCount() > 1) { ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; " "only buffers with one layer are allowed"); return INVALID_OPERATION; } Rect bounds; if (!rect) { bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight())); } else { bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); } return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride); } int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) { if (!buffer || !outPlanes) return BAD_VALUE; if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } usage = AHardwareBuffer_convertToGrallocUsageBits(usage); GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); Rect bounds; if (!rect) { bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight())); } else { bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); } int format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gBuffer->getPixelFormat())); memset(outPlanes->planes, 0, sizeof(outPlanes->planes)); if (AHardwareBuffer_formatIsYuv(format)) { android_ycbcr yuvData; int result = gBuffer->lockAsyncYCbCr(usage, bounds, &yuvData, fence); if (result == 0) { outPlanes->planeCount = 3; outPlanes->planes[0].data = yuvData.y; outPlanes->planes[0].pixelStride = 1; outPlanes->planes[0].rowStride = yuvData.ystride; outPlanes->planes[1].data = yuvData.cb; outPlanes->planes[1].pixelStride = yuvData.chroma_step; outPlanes->planes[1].rowStride = yuvData.cstride; outPlanes->planes[2].data = yuvData.cr; outPlanes->planes[2].pixelStride = yuvData.chroma_step; outPlanes->planes[2].rowStride = yuvData.cstride; } else { outPlanes->planeCount = 0; } return result; } else { const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format); outPlanes->planeCount = 1; outPlanes->planes[0].pixelStride = pixelStride; outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride; return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence); } } int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) { if (!buffer) return BAD_VALUE; GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); if (fence == nullptr) return gBuffer->unlock(); else return gBuffer->unlockAsync(fence); } int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) { if (!buffer) return BAD_VALUE; const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); size_t flattenedSize = gBuffer->getFlattenedSize(); size_t fdCount = gBuffer->getFdCount(); std::unique_ptr data(new uint8_t[flattenedSize]); std::unique_ptr fds(new int[fdCount]); // Make copies of needed items since flatten modifies them, and we don't // want to send anything if there's an error during flatten. size_t flattenedSizeCopy = flattenedSize; size_t fdCountCopy = fdCount; void* dataStart = data.get(); int* fdsStart = fds.get(); status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart, fdCountCopy); if (err != NO_ERROR) { return err; } struct iovec iov[1]; iov[0].iov_base = data.get(); iov[0].iov_len = flattenedSize; char buf[CMSG_SPACE(kFdBufferSize)]; struct msghdr msg = { .msg_control = buf, .msg_controllen = sizeof(buf), .msg_iov = &iov[0], .msg_iovlen = 1, }; struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount); int* fdData = reinterpret_cast(CMSG_DATA(cmsg)); memcpy(fdData, fds.get(), sizeof(int) * fdCount); msg.msg_controllen = cmsg->cmsg_len; int result; do { result = sendmsg(socketFd, &msg, 0); } while (result == -1 && errno == EINTR); if (result == -1) { result = errno; ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)", result, strerror(result)); return -result; } return NO_ERROR; } int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) { if (!outBuffer) return BAD_VALUE; static constexpr int kMessageBufferSize = 4096 * sizeof(int); std::unique_ptr dataBuf(new char[kMessageBufferSize]); char fdBuf[CMSG_SPACE(kFdBufferSize)]; struct iovec iov[1]; iov[0].iov_base = dataBuf.get(); iov[0].iov_len = kMessageBufferSize; struct msghdr msg = { .msg_control = fdBuf, .msg_controllen = sizeof(fdBuf), .msg_iov = &iov[0], .msg_iovlen = 1, }; int result; do { result = recvmsg(socketFd, &msg, 0); } while (result == -1 && errno == EINTR); if (result == -1) { result = errno; ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)", result, strerror(result)); return -result; } if (msg.msg_iovlen != 1) { ALOGE("Error reading AHardwareBuffer from socket: bad data length"); return INVALID_OPERATION; } if (msg.msg_controllen % sizeof(int) != 0) { ALOGE("Error reading AHardwareBuffer from socket: bad fd length"); return INVALID_OPERATION; } size_t dataLen = msg.msg_iov[0].iov_len; const void* data = static_cast(msg.msg_iov[0].iov_base); if (!data) { ALOGE("Error reading AHardwareBuffer from socket: no buffer data"); return INVALID_OPERATION; } struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); if (!cmsg) { ALOGE("Error reading AHardwareBuffer from socket: no fd header"); return INVALID_OPERATION; } size_t fdCount = msg.msg_controllen >> 2; const int* fdData = reinterpret_cast(CMSG_DATA(cmsg)); if (!fdData) { ALOGE("Error reading AHardwareBuffer from socket: no fd data"); return INVALID_OPERATION; } GraphicBuffer* gBuffer = new GraphicBuffer(); status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount); if (err != NO_ERROR) { return err; } *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer); // Ensure the buffer has a positive ref-count. AHardwareBuffer_acquire(*outBuffer); return NO_ERROR; } int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { if (!desc) return 0; if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0; bool supported = false; GraphicBuffer* gBuffer = new GraphicBuffer(); status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers, desc->usage, &supported); if (err == NO_ERROR) { return supported; } // function isSupported is not implemented on device or an error occurred during HAL // query. Make a trial allocation. AHardwareBuffer_Desc trialDesc = *desc; trialDesc.width = 4; trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4; if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) { trialDesc.layers = desc->layers == 6 ? 6 : 12; } else { trialDesc.layers = desc->layers == 1 ? 1 : 2; } AHardwareBuffer* trialBuffer = nullptr; int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer); if (result == NO_ERROR) { AHardwareBuffer_release(trialBuffer); return 1; } return 0; } // ---------------------------------------------------------------------------- // VNDK functions // ---------------------------------------------------------------------------- const native_handle_t* AHardwareBuffer_getNativeHandle( const AHardwareBuffer* buffer) { if (!buffer) return nullptr; const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); return gbuffer->handle; } int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, const native_handle_t* handle, int32_t method, AHardwareBuffer** outBuffer) { static_assert(static_cast(AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER) == static_cast(GraphicBuffer::TAKE_UNREGISTERED_HANDLE)); static_assert(static_cast(AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE) == static_cast(GraphicBuffer::CLONE_HANDLE)); if (!desc || !handle || !outBuffer) return BAD_VALUE; if (!(method == AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER || method == AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE)) return BAD_VALUE; if (desc->rfu0 != 0 || desc->rfu1 != 0) return BAD_VALUE; if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) return BAD_VALUE; const int format = AHardwareBuffer_convertToPixelFormat(desc->format); const uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); const auto wrapMethod = static_cast(method); sp gbuffer(new GraphicBuffer(handle, wrapMethod, desc->width, desc->height, format, desc->layers, usage, desc->stride)); status_t err = gbuffer->initCheck(); if (err != 0 || gbuffer->handle == 0) return err; *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); // Ensure the buffer doesn't get destroyed when the sp<> goes away. AHardwareBuffer_acquire(*outBuffer); return NO_ERROR; } // ---------------------------------------------------------------------------- // Helpers implementation // ---------------------------------------------------------------------------- namespace android { bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) { if (desc->width == 0 || desc->height == 0 || desc->layers == 0) { ALOGE_IF(log, "Width, height and layers must all be nonzero"); return false; } if (!AHardwareBuffer_isValidPixelFormat(desc->format)) { ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format); return false; } if (desc->rfu0 != 0 || desc->rfu1 != 0) { ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0"); return false; } if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) { if (desc->height != 1 || desc->layers != 1) { ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB"); return false; } const uint64_t blobInvalidGpuMask = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP; if (desc->usage & blobInvalidGpuMask) { ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; " "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed"); return false; } if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) { ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video"); return false; } } else if (AHardwareBuffer_formatIsYuv(desc->format)) { if (desc->layers != 1) { ALOGE_IF(log, "Layers must be 1 for YUV formats."); return false; } const uint64_t yuvInvalidGpuMask = AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP; if (desc->usage & yuvInvalidGpuMask) { ALOGE_IF(log, "Invalid usage flags specified for YUV format; " "mip-mapping and cube-mapping are not allowed."); return false; } } else { if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) { ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB"); return false; } if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) { ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB"); return false; } } if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) && (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) { ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER " "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER"); return false; } if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) { if (desc->width != desc->height) { ALOGE_IF(log, "Cube maps must be square"); return false; } if (desc->layers % 6 != 0) { ALOGE_IF(log, "Cube map layers must be a multiple of 6"); return false; } } return true; } bool AHardwareBuffer_isValidPixelFormat(uint32_t format) { static_assert(HAL_PIXEL_FORMAT_RGBA_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RGBX_8888 == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RGB_565 == AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RGB_888 == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RGBA_FP16 == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RGBA_1010102 == AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_BLOB == AHARDWAREBUFFER_FORMAT_BLOB, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_DEPTH_16 == AHARDWAREBUFFER_FORMAT_D16_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_DEPTH_24 == AHARDWAREBUFFER_FORMAT_D24_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_DEPTH_32F == AHARDWAREBUFFER_FORMAT_D32_FLOAT, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 == AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_STENCIL_8 == AHARDWAREBUFFER_FORMAT_S8_UINT, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_BGRA_8888 == AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YV12 == AHARDWAREBUFFER_FORMAT_YV12, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_Y8 == AHARDWAREBUFFER_FORMAT_Y8, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_Y16 == AHARDWAREBUFFER_FORMAT_Y16, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RAW16 == AHARDWAREBUFFER_FORMAT_RAW16, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RAW10 == AHARDWAREBUFFER_FORMAT_RAW10, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RAW12 == AHARDWAREBUFFER_FORMAT_RAW12, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_RAW_OPAQUE == AHARDWAREBUFFER_FORMAT_RAW_OPAQUE, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED == AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YCBCR_420_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YCBCR_422_SP == AHARDWAREBUFFER_FORMAT_YCbCr_422_SP, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YCRCB_420_SP == AHARDWAREBUFFER_FORMAT_YCrCb_420_SP, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I, "HAL and AHardwareBuffer pixel format don't match"); switch (format) { case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: case AHARDWAREBUFFER_FORMAT_BLOB: case AHARDWAREBUFFER_FORMAT_D16_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT: case AHARDWAREBUFFER_FORMAT_D32_FLOAT: case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT: case AHARDWAREBUFFER_FORMAT_S8_UINT: case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420: // VNDK formats only -- unfortunately we can't differentiate from where we're called case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM: case AHARDWAREBUFFER_FORMAT_YV12: case AHARDWAREBUFFER_FORMAT_Y8: case AHARDWAREBUFFER_FORMAT_Y16: case AHARDWAREBUFFER_FORMAT_RAW16: case AHARDWAREBUFFER_FORMAT_RAW10: case AHARDWAREBUFFER_FORMAT_RAW12: case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE: case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED: case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP: case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP: case AHARDWAREBUFFER_FORMAT_YCbCr_422_I: return true; default: return false; } } bool AHardwareBuffer_formatIsYuv(uint32_t format) { switch (format) { case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420: case AHARDWAREBUFFER_FORMAT_YV12: case AHARDWAREBUFFER_FORMAT_Y8: case AHARDWAREBUFFER_FORMAT_Y16: case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP: case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP: case AHARDWAREBUFFER_FORMAT_YCbCr_422_I: return true; default: return false; } } uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) { switch (format) { case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: case AHARDWAREBUFFER_FORMAT_D16_UNORM: return 2; case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM: return 3; case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: case AHARDWAREBUFFER_FORMAT_D32_FLOAT: case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT: return 4; default: return 0; } } uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) { return hal_format; } uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t ahardwarebuffer_format) { return ahardwarebuffer_format; } uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { using android::hardware::graphics::common::V1_1::BufferUsage; static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_RARELY == (uint64_t)BufferUsage::CPU_READ_RARELY, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN == (uint64_t)BufferUsage::CPU_READ_OFTEN, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER == (uint64_t)BufferUsage::CPU_WRITE_NEVER, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY == (uint64_t)BufferUsage::CPU_WRITE_RARELY, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN == (uint64_t)BufferUsage::CPU_WRITE_OFTEN, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_VIDEO_ENCODE == (uint64_t)BufferUsage::VIDEO_ENCODER, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER == (uint64_t)BufferUsage::GPU_DATA_BUFFER, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA == (uint64_t)BufferUsage::SENSOR_DIRECT_DATA, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP == (uint64_t)BufferUsage::GPU_CUBE_MAP, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE == (uint64_t)BufferUsage::GPU_MIPMAP_COMPLETE, "gralloc and AHardwareBuffer flags don't match"); return usage; } uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage) { return usage; } const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) { return GraphicBuffer::fromAHardwareBuffer(buffer); } GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) { return GraphicBuffer::fromAHardwareBuffer(buffer); } const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) { return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer(); } ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer) { return AHardwareBuffer_to_GraphicBuffer(buffer)->getNativeBuffer(); } AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) { return buffer->toAHardwareBuffer(); } } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/ANativeWindow.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000023114 13756501735 016514� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #define LOG_TAG "ANativeWindow" #include // from nativewindow/includes/system/window.h // (not to be confused with the compatibility-only window.h from system/core/includes) #include #include #include using namespace android; static int32_t query(ANativeWindow* window, int what) { int value; int res = window->query(window, what, &value); return res < 0 ? res : value; } static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) { bool supported = false; switch (dataSpace) { case HAL_DATASPACE_UNKNOWN: case HAL_DATASPACE_V0_SRGB: return true; // These data space need wide gamut support. case HAL_DATASPACE_V0_SCRGB_LINEAR: case HAL_DATASPACE_V0_SCRGB: case HAL_DATASPACE_DISPLAY_P3: native_window_get_wide_color_support(window, &supported); return supported; // These data space need HDR support. case HAL_DATASPACE_BT2020_PQ: native_window_get_hdr_support(window, &supported); return supported; default: return false; } } /************************************************************************************************** * NDK **************************************************************************************************/ void ANativeWindow_acquire(ANativeWindow* window) { // incStrong/decStrong token must be the same, doesn't matter what it is window->incStrong((void*)ANativeWindow_acquire); } void ANativeWindow_release(ANativeWindow* window) { // incStrong/decStrong token must be the same, doesn't matter what it is window->decStrong((void*)ANativeWindow_acquire); } int32_t ANativeWindow_getWidth(ANativeWindow* window) { return query(window, NATIVE_WINDOW_WIDTH); } int32_t ANativeWindow_getHeight(ANativeWindow* window) { return query(window, NATIVE_WINDOW_HEIGHT); } int32_t ANativeWindow_getFormat(ANativeWindow* window) { return query(window, NATIVE_WINDOW_FORMAT); } int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format) { int32_t err = native_window_set_buffers_format(window, format); if (!err) { err = native_window_set_buffers_user_dimensions(window, width, height); if (!err) { int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE; if (width && height) { mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; } err = native_window_set_scaling_mode(window, mode); } } return err; } int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds); } int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) { return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST); } int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) { static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL == NATIVE_WINDOW_TRANSFORM_FLIP_H); static_assert(ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL == NATIVE_WINDOW_TRANSFORM_FLIP_V); static_assert(ANATIVEWINDOW_TRANSFORM_ROTATE_90 == NATIVE_WINDOW_TRANSFORM_ROT_90); constexpr int32_t kAllTransformBits = ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL | ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL | ANATIVEWINDOW_TRANSFORM_ROTATE_90 | // We don't expose INVERSE_DISPLAY as an NDK constant, but someone could have read it // from a buffer already set by Camera framework, so we allow it to be forwarded. NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) return -EINVAL; if ((transform & ~kAllTransformBits) != 0) return -EINVAL; return native_window_set_buffers_transform(window, transform); } int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { static_assert(static_cast(ADATASPACE_UNKNOWN) == static_cast(HAL_DATASPACE_UNKNOWN)); static_assert(static_cast(ADATASPACE_SCRGB_LINEAR) == static_cast(HAL_DATASPACE_V0_SCRGB_LINEAR)); static_assert(static_cast(ADATASPACE_SRGB) == static_cast(HAL_DATASPACE_V0_SRGB)); static_assert(static_cast(ADATASPACE_SCRGB) == static_cast(HAL_DATASPACE_V0_SCRGB)); static_assert(static_cast(ADATASPACE_DISPLAY_P3) == static_cast(HAL_DATASPACE_DISPLAY_P3)); static_assert(static_cast(ADATASPACE_BT2020_PQ) == static_cast(HAL_DATASPACE_BT2020_PQ)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || !isDataSpaceValid(window, dataSpace)) { return -EINVAL; } return native_window_set_buffers_data_space(window, static_cast(dataSpace)); } int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) { if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) return -EINVAL; return query(window, NATIVE_WINDOW_DATASPACE); } /************************************************************************************************** * vndk-stable **************************************************************************************************/ AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb) { return AHardwareBuffer_from_GraphicBuffer(static_cast(anwb)); } int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value) { if (slot < 4) { window->oem[slot] = value; return 0; } return -EINVAL; } int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value) { if (slot >= 4) { *value = window->oem[slot]; return 0; } return -EINVAL; } int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval) { return window->setSwapInterval(window, interval); } int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery what, int* value) { switch (what) { case ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS: case ANATIVEWINDOW_QUERY_DEFAULT_WIDTH: case ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT: case ANATIVEWINDOW_QUERY_TRANSFORM_HINT: // these are part of the VNDK API break; case ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL: *value = window->minSwapInterval; return 0; case ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL: *value = window->maxSwapInterval; return 0; case ANATIVEWINDOW_QUERY_XDPI: *value = (int)window->xdpi; return 0; case ANATIVEWINDOW_QUERY_YDPI: *value = (int)window->ydpi; return 0; default: // asked for an invalid query(), one that isn't part of the VNDK return -EINVAL; } return window->query(window, int(what), value); } int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery what, float* value) { switch (what) { case ANATIVEWINDOW_QUERY_XDPI: *value = window->xdpi; return 0; case ANATIVEWINDOW_QUERY_YDPI: *value = window->ydpi; return 0; default: break; } int i; int e = ANativeWindow_query(window, what, &i); if (e == 0) { *value = (float)i; } return e; } int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) { return window->dequeueBuffer(window, buffer, fenceFd); } int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) { return window->queueBuffer(window, buffer, fenceFd); } int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) { return window->cancelBuffer(window, buffer, fenceFd); } int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage) { return native_window_set_usage(window, usage); } int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount) { return native_window_set_buffer_count(window, bufferCount); } int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h) { return native_window_set_buffers_dimensions(window, (int)w, (int)h); } int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format) { return native_window_set_buffers_format(window, format); } int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp) { return native_window_set_buffers_timestamp(window, timestamp); } int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode) { return native_window_set_shared_buffer_mode(window, sharedBufferMode); } int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) { return native_window_set_auto_refresh(window, autoRefresh); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/Android.bp������������������������������������������������������������������������0100644 0000000 0000000 00000004172 13756501735 015177� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2017 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. ndk_headers { name: "libnativewindow_ndk_headers", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } // TODO(b/118715870): cleanup header files cc_library_headers { name: "libnativewindow_headers", export_include_dirs: ["include"], vendor_available: true, } ndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", // Android O first_version: "26", } cc_library { name: "libnativewindow", export_include_dirs: [ "include", "include-private", ], clang: true, cflags: [ "-Wall", "-Werror", "-Wno-enum-compare", "-Wno-unused-function", ], version_script: "libnativewindow.map.txt", srcs: [ "AHardwareBuffer.cpp", "ANativeWindow.cpp", ], shared_libs: [ "libhardware", "libcutils", "liblog", "libutils", "libui", "android.hardware.graphics.common@1.1", ], static_libs: [ "libarect", "libgrallocusage", ], header_libs: [ "libnativebase_headers", "libnativewindow_headers", ], // headers we include in our public headers export_static_lib_headers: [ "libarect", ], export_header_lib_headers: [ "libnativebase_headers", ], } llndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", unversioned: true, export_include_dirs: ["include"], } subdirs = ["tests"] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/MODULE_LICENSE_APACHE2������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 016413� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/NOTICE����������������������������������������������������������������������������0100644 0000000 0000000 00000024707 13756501735 014206� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ���������������������������������������������������������libs/nativewindow/include-private/������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016366� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include-private/private/����������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020040� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include-private/private/android/��������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 021460� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h��������������������������0100644 0000000 0000000 00000005353 13756501735 026147� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H #define ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H /* * This file contains utility functions related to AHardwareBuffer, mostly to * convert to/from HAL formats. * * These are PRIVATE methods, so this file can NEVER appear in a public NDK * header. They are used by higher level libraries such as core/jni. */ #include struct AHardwareBuffer; struct AHardwareBuffer_Desc; struct ANativeWindowBuffer; namespace android { // Validates whether the passed description does not have conflicting // parameters. Note: this does not verify any platform-specific contraints. bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log); // whether this AHardwareBuffer format is valid bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format); // whether this is a YUV type format bool AHardwareBuffer_formatIsYuv(uint32_t format); // number of bytes per pixel or 0 if unknown or multi-planar uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format); // convert AHardwareBuffer format to HAL format (note: this is a no-op) uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format); // convert HAL format to AHardwareBuffer format (note: this is a no-op) uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format); // convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage); // convert HAL usage bits to AHardwareBuffer usage bits (note: this is a no-op) uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage); class GraphicBuffer; const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer); GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer); const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer); ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buffer); AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer); } // namespace android #endif // ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/��������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014716� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016336� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/data_space.h������������������������������������������������������0100644 0000000 0000000 00000006647 13756501735 020605� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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 data_space.h */ #ifndef ANDROID_DATA_SPACE_H #define ANDROID_DATA_SPACE_H #include #include __BEGIN_DECLS /** * ADataSpace. */ enum ADataSpace { /** * Default-assumption data space, when not explicitly specified. * * It is safest to assume the buffer is an image with sRGB primaries and * encoding ranges, but the consumer and/or the producer of the data may * simply be using defaults. No automatic gamma transform should be * expected, except for a possible display gamma transform when drawn to a * screen. */ ADATASPACE_UNKNOWN = 0, /** * scRGB linear encoding: * * The red, green, and blue components are stored in extended sRGB space, * but are linear, not gamma-encoded. * The RGB primaries and the white point are the same as BT.709. * * The values are floating point. * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits. * Values beyond the range [0.0 - 1.0] would correspond to other colors * spaces and/or HDR content. */ ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED /** * sRGB gamma encoding: * * The red, green and blue components are stored in sRGB space, and * converted to linear space when read, using the SRGB transfer function * for each of the R, G and B components. When written, the inverse * transformation is performed. * * The alpha component, if present, is always stored in linear space and * is left unmodified when read or written. * * Use full range and BT.709 standard. */ ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL /** * scRGB: * * The red, green, and blue components are stored in extended sRGB space, * and gamma-encoded using the SRGB transfer function. * The RGB primaries and the white point are the same as BT.709. * * The values are floating point. * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits. * Values beyond the range [0.0 - 1.0] would correspond to other colors * spaces and/or HDR content. */ ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED /** * Display P3 * * Use same primaries and white-point as DCI-P3 * but sRGB transfer function. */ ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL /** * ITU-R Recommendation 2020 (BT.2020) * * Ultra High-definition television * * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard */ ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL }; __END_DECLS #endif // ANDROID_DATA_SPACE_H �����������������������������������������������������������������������������������������libs/nativewindow/include/android/hardware_buffer.h�������������������������������������������������0100644 0000000 0000000 00000051334 13756501735 021640� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 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 hardware_buffer.h * @brief API for native hardware buffers. */ /** * @defgroup AHardwareBuffer Native Hardware Buffer * * AHardwareBuffer objects represent chunks of memory that can be * accessed by various hardware components in the system. It can be * easily converted to the Java counterpart * android.hardware.HardwareBuffer and passed between processes using * Binder. All operations involving AHardwareBuffer and HardwareBuffer * are zero-copy, i.e., passing AHardwareBuffer to another process * creates a shared view of the same region of memory. * * AHardwareBuffers can be bound to EGL/OpenGL and Vulkan primitives. * For EGL, use the extension function eglGetNativeClientBufferANDROID * to obtain an EGLClientBuffer and pass it directly to * eglCreateImageKHR. Refer to the EGL extensions * EGL_ANDROID_get_native_client_buffer and * EGL_ANDROID_image_native_buffer for more information. In Vulkan, * the contents of the AHardwareBuffer can be accessed as external * memory. See the VK_ANDROID_external_memory_android_hardware_buffer * extension for details. * * @{ */ #ifndef ANDROID_HARDWARE_BUFFER_H #define ANDROID_HARDWARE_BUFFER_H #include #include #include __BEGIN_DECLS /** * Buffer pixel formats. */ enum AHardwareBuffer_Format { /** * Corresponding formats: * Vulkan: VK_FORMAT_R8G8B8A8_UNORM * OpenGL ES: GL_RGBA8 */ AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1, /** * 32 bits per pixel, 8 bits per channel format where alpha values are * ignored (always opaque). * Corresponding formats: * Vulkan: VK_FORMAT_R8G8B8A8_UNORM * OpenGL ES: GL_RGB8 */ AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2, /** * Corresponding formats: * Vulkan: VK_FORMAT_R8G8B8_UNORM * OpenGL ES: GL_RGB8 */ AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3, /** * Corresponding formats: * Vulkan: VK_FORMAT_R5G6B5_UNORM_PACK16 * OpenGL ES: GL_RGB565 */ AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4, /** * Corresponding formats: * Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT * OpenGL ES: GL_RGBA16F */ AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 0x16, /** * Corresponding formats: * Vulkan: VK_FORMAT_A2B10G10R10_UNORM_PACK32 * OpenGL ES: GL_RGB10_A2 */ AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b, /** * Opaque binary blob format. * Must have height 1 and one layer, with width equal to the buffer * size in bytes. Corresponds to Vulkan buffers and OpenGL buffer * objects. Can be bound to the latter using GL_EXT_external_buffer. */ AHARDWAREBUFFER_FORMAT_BLOB = 0x21, /** * Corresponding formats: * Vulkan: VK_FORMAT_D16_UNORM * OpenGL ES: GL_DEPTH_COMPONENT16 */ AHARDWAREBUFFER_FORMAT_D16_UNORM = 0x30, /** * Corresponding formats: * Vulkan: VK_FORMAT_X8_D24_UNORM_PACK32 * OpenGL ES: GL_DEPTH_COMPONENT24 */ AHARDWAREBUFFER_FORMAT_D24_UNORM = 0x31, /** * Corresponding formats: * Vulkan: VK_FORMAT_D24_UNORM_S8_UINT * OpenGL ES: GL_DEPTH24_STENCIL8 */ AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT = 0x32, /** * Corresponding formats: * Vulkan: VK_FORMAT_D32_SFLOAT * OpenGL ES: GL_DEPTH_COMPONENT32F */ AHARDWAREBUFFER_FORMAT_D32_FLOAT = 0x33, /** * Corresponding formats: * Vulkan: VK_FORMAT_D32_SFLOAT_S8_UINT * OpenGL ES: GL_DEPTH32F_STENCIL8 */ AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT = 0x34, /** * Corresponding formats: * Vulkan: VK_FORMAT_S8_UINT * OpenGL ES: GL_STENCIL_INDEX8 */ AHARDWAREBUFFER_FORMAT_S8_UINT = 0x35, /** * YUV 420 888 format. * Must have an even width and height. Can be accessed in OpenGL * shaders through an external sampler. Does not support mip-maps * cube-maps or multi-layered textures. */ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23, }; /** * Buffer usage flags, specifying how the buffer will be accessed. */ enum AHardwareBuffer_UsageFlags { /// The buffer will never be locked for direct CPU reads using the /// AHardwareBuffer_lock() function. Note that reading the buffer /// using OpenGL or Vulkan functions or memory mappings is still /// allowed. AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL, /// The buffer will sometimes be locked for direct CPU reads using /// the AHardwareBuffer_lock() function. Note that reading the /// buffer using OpenGL or Vulkan functions or memory mappings /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL, /// The buffer will often be locked for direct CPU reads using /// the AHardwareBuffer_lock() function. Note that reading the /// buffer using OpenGL or Vulkan functions or memory mappings /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL, /// CPU read value mask. AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL, /// The buffer will never be locked for direct CPU writes using the /// AHardwareBuffer_lock() function. Note that writing the buffer /// using OpenGL or Vulkan functions or memory mappings is still /// allowed. AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4, /// The buffer will sometimes be locked for direct CPU writes using /// the AHardwareBuffer_lock() function. Note that writing the /// buffer using OpenGL or Vulkan functions or memory mappings /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4, /// The buffer will often be locked for direct CPU writes using /// the AHardwareBuffer_lock() function. Note that writing the /// buffer using OpenGL or Vulkan functions or memory mappings /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4, /// CPU write value mask. AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4, /// The buffer will be read from by the GPU as a texture. AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, /// The buffer will be written to by the GPU as a framebuffer attachment. AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9, /** * The buffer will be written to by the GPU as a framebuffer * attachment. * * Note that the name of this flag is somewhat misleading: it does * not imply that the buffer contains a color format. A buffer with * depth or stencil format that will be used as a framebuffer * attachment should also have this flag. Use the equivalent flag * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion. */ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, /** * The buffer will be used as a composer HAL overlay layer. * * This flag is currently only needed when using ASurfaceTransaction_setBuffer * to set a buffer. In all other cases, the framework adds this flag * internally to buffers that could be presented in a composer overlay. * ASurfaceTransaction_setBuffer is special because it uses buffers allocated * directly through AHardwareBuffer_allocate instead of buffers allocated * by the framework. */ AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11, /** * The buffer is protected from direct CPU access or being read by * non-secure hardware, such as video encoders. * * This flag is incompatible with CPU read and write flags. It is * mainly used when handling DRM video. Refer to the EGL extension * EGL_EXT_protected_content and GL extension * GL_EXT_protected_textures for more information on how these * buffers are expected to behave. */ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, /// The buffer will be read by a hardware video encoder. AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, /** * The buffer will be used for direct writes from sensors. * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB. */ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, /** * The buffer will be used as a shader storage or uniform buffer object. * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB. */ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, /** * The buffer will be used as a cube map texture. * When this flag is present, the buffer must have a layer count * that is a multiple of 6. Note that buffers with this flag must be * bound to OpenGL textures using the extension * GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image. */ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25, /** * The buffer contains a complete mipmap hierarchy. * Note that buffers with this flag must be bound to OpenGL textures using * the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image. */ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26, AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28, AHARDWAREBUFFER_USAGE_VENDOR_1 = 1ULL << 29, AHARDWAREBUFFER_USAGE_VENDOR_2 = 1ULL << 30, AHARDWAREBUFFER_USAGE_VENDOR_3 = 1ULL << 31, AHARDWAREBUFFER_USAGE_VENDOR_4 = 1ULL << 48, AHARDWAREBUFFER_USAGE_VENDOR_5 = 1ULL << 49, AHARDWAREBUFFER_USAGE_VENDOR_6 = 1ULL << 50, AHARDWAREBUFFER_USAGE_VENDOR_7 = 1ULL << 51, AHARDWAREBUFFER_USAGE_VENDOR_8 = 1ULL << 52, AHARDWAREBUFFER_USAGE_VENDOR_9 = 1ULL << 53, AHARDWAREBUFFER_USAGE_VENDOR_10 = 1ULL << 54, AHARDWAREBUFFER_USAGE_VENDOR_11 = 1ULL << 55, AHARDWAREBUFFER_USAGE_VENDOR_12 = 1ULL << 56, AHARDWAREBUFFER_USAGE_VENDOR_13 = 1ULL << 57, AHARDWAREBUFFER_USAGE_VENDOR_14 = 1ULL << 58, AHARDWAREBUFFER_USAGE_VENDOR_15 = 1ULL << 59, AHARDWAREBUFFER_USAGE_VENDOR_16 = 1ULL << 60, AHARDWAREBUFFER_USAGE_VENDOR_17 = 1ULL << 61, AHARDWAREBUFFER_USAGE_VENDOR_18 = 1ULL << 62, AHARDWAREBUFFER_USAGE_VENDOR_19 = 1ULL << 63, }; /** * Buffer description. Used for allocating new buffers and querying * parameters of existing ones. */ typedef struct AHardwareBuffer_Desc { uint32_t width; ///< Width in pixels. uint32_t height; ///< Height in pixels. /** * Number of images in an image array. AHardwareBuffers with one * layer correspond to regular 2D textures. AHardwareBuffers with * more than layer correspond to texture arrays. If the layer count * is a multiple of 6 and the usage flag * AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is * a cube map or a cube map array. */ uint32_t layers; uint32_t format; ///< One of AHardwareBuffer_Format. uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags. uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate() uint32_t rfu0; ///< Initialize to zero, reserved for future use. uint64_t rfu1; ///< Initialize to zero, reserved for future use. } AHardwareBuffer_Desc; /** * Holds data for a single image plane. */ typedef struct AHardwareBuffer_Plane { void* data; ///< Points to first byte in plane uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to /// the first value of the next row. } AHardwareBuffer_Plane; /** * Holds all image planes that contain the pixel data. */ typedef struct AHardwareBuffer_Planes { uint32_t planeCount; ///< Number of distinct planes AHardwareBuffer_Plane planes[4]; ///< Array of image planes } AHardwareBuffer_Planes; /** * Opaque handle for a native hardware buffer. */ typedef struct AHardwareBuffer AHardwareBuffer; #if __ANDROID_API__ >= 26 /** * Allocates a buffer that matches the passed AHardwareBuffer_Desc. * * If allocation succeeds, the buffer can be used according to the * usage flags specified in its description. If a buffer is used in ways * not compatible with its usage flags, the results are undefined and * may include program termination. * * \return 0 on success, or an error number of the allocation fails for * any reason. The returned buffer has a reference count of 1. */ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); /** * Acquire a reference on the given AHardwareBuffer object. * * This prevents the object from being deleted until the last reference * is removed. */ void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Remove a reference that was previously acquired with * AHardwareBuffer_acquire() or AHardwareBuffer_allocate(). */ void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Return a description of the AHardwareBuffer in the passed * AHardwareBuffer_Desc struct. */ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26); /** * Lock the AHardwareBuffer for direct CPU access. * * This function can lock the buffer for either reading or writing. * It may block if the hardware needs to finish rendering, if CPU caches * need to be synchronized, or possibly for other implementation- * specific reasons. * * The passed AHardwareBuffer must have one layer, otherwise the call * will fail. * * If \a fence is not negative, it specifies a fence file descriptor on * which to wait before locking the buffer. If it's negative, the caller * is responsible for ensuring that writes to the buffer have completed * before calling this function. Using this parameter is more efficient * than waiting on the fence and then calling this function. * * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*. * If set, then outVirtualAddress is filled with the address of the * buffer in virtual memory. The flags must also be compatible with * usage flags specified at buffer creation: if a read flag is passed, * the buffer must have been created with * AHARDWAREBUFFER_USAGE_CPU_READ_RARELY or * AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN. If a write flag is passed, it * must have been created with AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY or * AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN. * * If \a rect is not NULL, the caller promises to modify only data in * the area specified by rect. If rect is NULL, the caller may modify * the contents of the entire buffer. The content of the buffer outside * of the specified rect is NOT modified by this call. * * It is legal for several different threads to lock a buffer for read * access; none of the threads are blocked. * * Locking a buffer simultaneously for write or read/write is undefined, * but will neither terminate the process nor block the caller. * AHardwareBuffer_lock may return an error or leave the buffer's * content in an indeterminate state. * * If the buffer has AHARDWAREBUFFER_FORMAT_BLOB, it is legal lock it * for reading and writing in multiple threads and/or processes * simultaneously, and the contents of the buffer behave like shared * memory. * * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer * has more than one layer. Error number if the lock fails for any other * reason. */ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26); /** * Lock a potentially multi-planar AHardwareBuffer for direct CPU access. * * This function is similar to AHardwareBuffer_lock, but can lock multi-planar * formats. The locked planes are returned in the \a outPlanes argument. Note, * that multi-planar should not be confused with multi-layer images, which this * locking function does not support. * * YUV formats are always represented by three separate planes of data, one for * each color plane. The order of planes in the array is guaranteed such that * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V * (Cr). All other formats are represented by a single plane. * * Additional information always accompanies the buffers, describing the row * stride and the pixel stride for each plane. * * In case the buffer cannot be locked, \a outPlanes will contain zero planes. * * See the AHardwareBuffer_lock documentation for all other locking semantics. * * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer * has more than one layer. Error number if the lock fails for any other * reason. */ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29); /** * Unlock the AHardwareBuffer from direct CPU access. * * Must be called after all changes to the buffer are completed by the * caller. If \a fence is NULL, the function will block until all work * is completed. Otherwise, \a fence will be set either to a valid file * descriptor or to -1. The file descriptor will become signaled once * the unlocking is complete and buffer contents are updated. * The caller is responsible for closing the file descriptor once it's * no longer needed. The value -1 indicates that unlocking has already * completed before the function returned and no further operations are * necessary. * * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if * the unlock fails for any reason. */ int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26); /** * Send the AHardwareBuffer to an AF_UNIX socket. * * \return 0 on success, -EINVAL if \a buffer is NULL, or an error * number if the operation fails for any reason. */ int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26); /** * Receive an AHardwareBuffer from an AF_UNIX socket. * * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error * number if the operation fails for any reason. */ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); #endif // __ANDROID_API__ >= 26 #if __ANDROID_API__ >= 29 /** * Test whether the given format and usage flag combination is * allocatable. * * If this function returns true, it means that a buffer with the given * description can be allocated on this implementation, unless resource * exhaustion occurs. If this function returns false, it means that the * allocation of the given description will never succeed. * * The return value of this function may depend on all fields in the * description, except stride, which is always ignored. For example, * some implementations have implementation-defined limits on texture * size and layer count. * * \return 1 if the format and usage flag combination is allocatable, * 0 otherwise. */ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29); /** * Lock an AHardwareBuffer for direct CPU access. * * This function is the same as the above lock function, but passes back * additional information about the bytes per pixel and the bytes per stride * of the locked buffer. If the bytes per pixel or bytes per stride are unknown * or variable, or if the underlying mapper implementation does not support returning * additional information, then this call will fail with INVALID_OPERATION */ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) __INTRODUCED_IN(29); #endif // __ANDROID_API__ >= 29 __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_H /** @} */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/hdr_metadata.h����������������������������������������������������0100644 0000000 0000000 00000003317 13756501735 021125� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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 hdr_metadata.h */ #ifndef ANDROID_HDR_METADATA_H #define ANDROID_HDR_METADATA_H #include #include __BEGIN_DECLS /** * These structures are used to define the display's capabilities for HDR content. * They can be used to better tone map content to user's display. */ /** * HDR metadata standards that are supported by Android. */ enum AHdrMetadataType : uint32_t { HDR10_SMPTE2086 = 1, HDR10_CTA861_3 = 2, HDR10PLUS_SEI = 3, }; /** * Color is defined in CIE XYZ coordinates. */ struct AColor_xy { float x; float y; }; /** * SMPTE ST 2086 "Mastering Display Color Volume" static metadata */ struct AHdrMetadata_smpte2086 { struct AColor_xy displayPrimaryRed; struct AColor_xy displayPrimaryGreen; struct AColor_xy displayPrimaryBlue; struct AColor_xy whitePoint; float maxLuminance; float minLuminance; }; /** * CTA 861.3 "HDR Static Metadata Extension" static metadata */ struct AHdrMetadata_cta861_3 { float maxContentLightLevel; float maxFrameAverageLightLevel; }; __END_DECLS #endif // ANDROID_HDR_METADATA_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/native_window.h���������������������������������������������������0100644 0000000 0000000 00000017235 13756501735 021371� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ /** * @defgroup ANativeWindow Native Window * * ANativeWindow represents the producer end of an image queue. * It is the C counterpart of the android.view.Surface object in Java, * and can be converted both ways. Depending on the consumer, images * submitted to ANativeWindow can be shown on the display or sent to * other consumers, such as video encoders. * @{ */ /** * @file native_window.h * @brief API for accessing a native window. */ #ifndef ANDROID_NATIVE_WINDOW_H #define ANDROID_NATIVE_WINDOW_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** * Legacy window pixel format names, kept for backwards compatibility. * New code and APIs should use AHARDWAREBUFFER_FORMAT_*. */ enum ANativeWindow_LegacyFormat { // NOTE: these values must match the values from graphics/common/x.x/types.hal /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/ WINDOW_FORMAT_RGBA_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/ WINDOW_FORMAT_RGBX_8888 = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/ WINDOW_FORMAT_RGB_565 = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM, }; /** * Transforms that can be applied to buffers as they are displayed to a window. * * Supported transforms are any combination of horizontal mirror, vertical * mirror, and clockwise 90 degree rotation, in that order. Rotations of 180 * and 270 degrees are made up of those basic transforms. */ enum ANativeWindowTransform { ANATIVEWINDOW_TRANSFORM_IDENTITY = 0x00, ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL = 0x01, ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL = 0x02, ANATIVEWINDOW_TRANSFORM_ROTATE_90 = 0x04, ANATIVEWINDOW_TRANSFORM_ROTATE_180 = ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL | ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL, ANATIVEWINDOW_TRANSFORM_ROTATE_270 = ANATIVEWINDOW_TRANSFORM_ROTATE_180 | ANATIVEWINDOW_TRANSFORM_ROTATE_90, }; struct ANativeWindow; /** * Opaque type that provides access to a native window. * * A pointer can be obtained using {@link ANativeWindow_fromSurface()}. */ typedef struct ANativeWindow ANativeWindow; /** * Struct that represents a windows buffer. * * A pointer can be obtained using {@link ANativeWindow_lock()}. */ typedef struct ANativeWindow_Buffer { /// The number of pixels that are shown horizontally. int32_t width; /// The number of pixels that are shown vertically. int32_t height; /// The number of *pixels* that a line in the buffer takes in /// memory. This may be >= width. int32_t stride; /// The format of the buffer. One of AHardwareBuffer_Format. int32_t format; /// The actual bits. void* bits; /// Do not touch. uint32_t reserved[6]; } ANativeWindow_Buffer; /** * Acquire a reference on the given {@link ANativeWindow} object. This prevents the object * from being deleted until the reference is removed. */ void ANativeWindow_acquire(ANativeWindow* window); /** * Remove a reference that was previously acquired with {@link ANativeWindow_acquire()}. */ void ANativeWindow_release(ANativeWindow* window); /** * Return the current width in pixels of the window surface. * * \return negative value on error. */ int32_t ANativeWindow_getWidth(ANativeWindow* window); /** * Return the current height in pixels of the window surface. * * \return a negative value on error. */ int32_t ANativeWindow_getHeight(ANativeWindow* window); /** * Return the current pixel format (AHARDWAREBUFFER_FORMAT_*) of the window surface. * * \return a negative value on error. */ int32_t ANativeWindow_getFormat(ANativeWindow* window); /** * Change the format and size of the window buffers. * * The width and height control the number of pixels in the buffers, not the * dimensions of the window on screen. If these are different than the * window's physical size, then its buffer will be scaled to match that size * when compositing it to the screen. The width and height must be either both zero * or both non-zero. * * For all of these parameters, if 0 is supplied then the window's base * value will come back in force. * * \param width width of the buffers in pixels. * \param height height of the buffers in pixels. * \param format one of the AHardwareBuffer_Format constants. * \return 0 for success, or a negative value on error. */ int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format); /** * Lock the window's next drawing surface for writing. * inOutDirtyBounds is used as an in/out parameter, upon entering the * function, it contains the dirty region, that is, the region the caller * intends to redraw. When the function returns, inOutDirtyBounds is updated * with the actual area the caller needs to redraw -- this region is often * extended by {@link ANativeWindow_lock}. * * \return 0 for success, or a negative value on error. */ int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); /** * Unlock the window's drawing surface after previously locking it, * posting the new buffer to the display. * * \return 0 for success, or a negative value on error. */ int32_t ANativeWindow_unlockAndPost(ANativeWindow* window); #if __ANDROID_API__ >= 26 /** * Set a transform that will be applied to future buffers posted to the window. * * \param transform combination of {@link ANativeWindowTransform} flags * \return 0 for success, or -EINVAL if \p transform is invalid */ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) __INTRODUCED_IN(26); #endif // __ANDROID_API__ >= 26 #if __ANDROID_API__ >= 28 /** * All buffers queued after this call will be associated with the dataSpace * parameter specified. * * dataSpace specifies additional information about the buffer. * For example, it can be used to convey the color space of the image data in * the buffer, or it can be used to indicate that the buffers contain depth * measurement data instead of color images. The default dataSpace is 0, * ADATASPACE_UNKNOWN, unless it has been overridden by the producer. * * \param dataSpace data space of all buffers queued after this call. * \return 0 for success, -EINVAL if window is invalid or the dataspace is not * supported. */ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) __INTRODUCED_IN(28); /** * Get the dataspace of the buffers in window. * \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if * dataspace is unknown, or -EINVAL if window is invalid. */ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN(28); #endif // __ANDROID_API__ >= 28 #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_WINDOW_H /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/system/�������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016242� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/system/window.h�����������������������������������������������������������0100644 0000000 0000000 00000113731 13756501735 017725� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2011 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. */ /************************************************************************************************* * * IMPORTANT: * * There is an old copy of this file in system/core/include/system/window.h, which exists only * for backward source compatibility. * But there are binaries out there as well, so this version of window.h must stay binary * backward compatible with the one found in system/core. * * * Source compatibility is also required for now, because this is how we're handling the * transition from system/core/include (global include path) to nativewindow/include. * *************************************************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include // system/window.h is a superset of the vndk #include #ifndef __UNUSED #define __UNUSED __attribute__((__unused__)) #endif #ifndef __deprecated #define __deprecated __attribute__((__deprecated__)) #endif __BEGIN_DECLS /*****************************************************************************/ #define ANDROID_NATIVE_WINDOW_MAGIC ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d') // --------------------------------------------------------------------------- /* attributes queriable with query() */ enum { NATIVE_WINDOW_WIDTH = 0, NATIVE_WINDOW_HEIGHT = 1, NATIVE_WINDOW_FORMAT = 2, /* see ANativeWindowQuery in vndk/window.h */ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS, /* Check whether queueBuffer operations on the ANativeWindow send the buffer * to the window compositor. The query sets the returned 'value' argument * to 1 if the ANativeWindow DOES send queued buffers directly to the window * compositor and 0 if the buffers do not go directly to the window * compositor. * * This can be used to determine whether protected buffer content should be * sent to the ANativeWindow. Note, however, that a result of 1 does NOT * indicate that queued buffers will be protected from applications or users * capturing their contents. If that behavior is desired then some other * mechanism (e.g. the GRALLOC_USAGE_PROTECTED flag) should be used in * conjunction with this query. */ NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER = 4, /* Get the concrete type of a ANativeWindow. See below for the list of * possible return values. * * This query should not be used outside the Android framework and will * likely be removed in the near future. */ NATIVE_WINDOW_CONCRETE_TYPE = 5, /* * Default width and height of ANativeWindow buffers, these are the * dimensions of the window buffers irrespective of the * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS. */ NATIVE_WINDOW_DEFAULT_WIDTH = ANATIVEWINDOW_QUERY_DEFAULT_WIDTH, NATIVE_WINDOW_DEFAULT_HEIGHT = ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT, /* see ANativeWindowQuery in vndk/window.h */ NATIVE_WINDOW_TRANSFORM_HINT = ANATIVEWINDOW_QUERY_TRANSFORM_HINT, /* * Boolean that indicates whether the consumer is running more than * one buffer behind the producer. */ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9, /* * The consumer gralloc usage bits currently set by the consumer. * The values are defined in hardware/libhardware/include/gralloc.h. */ NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10, /* deprecated */ /** * Transformation that will by applied to buffers by the hwcomposer. * This must not be set or checked by producer endpoints, and will * disable the transform hint set in SurfaceFlinger (see * NATIVE_WINDOW_TRANSFORM_HINT). * * INTENDED USE: * Temporary - Please do not use this. This is intended only to be used * by the camera's LEGACY mode. * * In situations where a SurfaceFlinger client wishes to set a transform * that is not visible to the producer, and will always be applied in the * hardware composer, the client can set this flag with * native_window_set_buffers_sticky_transform. This can be used to rotate * and flip buffers consumed by hardware composer without actually changing * the aspect ratio of the buffers produced. */ NATIVE_WINDOW_STICKY_TRANSFORM = 11, /** * The default data space for the buffers as set by the consumer. * The values are defined in graphics.h. */ NATIVE_WINDOW_DEFAULT_DATASPACE = 12, /* see ANativeWindowQuery in vndk/window.h */ NATIVE_WINDOW_BUFFER_AGE = ANATIVEWINDOW_QUERY_BUFFER_AGE, /* * Returns the duration of the last dequeueBuffer call in microseconds */ NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14, /* * Returns the duration of the last queueBuffer call in microseconds */ NATIVE_WINDOW_LAST_QUEUE_DURATION = 15, /* * Returns the number of image layers that the ANativeWindow buffer * contains. By default this is 1, unless a buffer is explicitly allocated * to contain multiple layers. */ NATIVE_WINDOW_LAYER_COUNT = 16, /* * Returns 1 if the native window is valid, 0 otherwise. native window is valid * if it is safe (i.e. no crash will occur) to call any method on it. */ NATIVE_WINDOW_IS_VALID = 17, /* * Returns 1 if NATIVE_WINDOW_GET_FRAME_TIMESTAMPS will return display * present info, 0 if it won't. */ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18, /* * The consumer end is capable of handling protected buffers, i.e. buffer * with GRALLOC_USAGE_PROTECTED usage bits on. */ NATIVE_WINDOW_CONSUMER_IS_PROTECTED = 19, /* * Returns data space for the buffers. */ NATIVE_WINDOW_DATASPACE = 20, /* * Returns maxBufferCount set by BufferQueueConsumer */ NATIVE_WINDOW_MAX_BUFFER_COUNT = 21, }; /* Valid operations for the (*perform)() hook. * * Values marked as 'deprecated' are supported, but have been superceded by * other functionality. * * Values marked as 'private' should be considered private to the framework. * HAL implementation code with access to an ANativeWindow should not use these, * as it may not interact properly with the framework's use of the * ANativeWindow. */ enum { // clang-format off NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */ NATIVE_WINDOW_CONNECT = 1, /* deprecated */ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ NATIVE_WINDOW_SET_CROP = 3, /* private */ NATIVE_WINDOW_SET_BUFFER_COUNT = 4, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */ NATIVE_WINDOW_LOCK = 11, /* private */ NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ NATIVE_WINDOW_API_CONNECT = 13, /* private */ NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */ NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* deprecated, unimplemented */ NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17, /* private */ NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18, NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19, NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */ NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21, NATIVE_WINDOW_SET_AUTO_REFRESH = 22, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION = 23, NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25, NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, NATIVE_WINDOW_GET_HDR_SUPPORT = 29, NATIVE_WINDOW_SET_USAGE64 = 30, NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32, NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34, // clang-format on }; /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ enum { /* Buffers will be queued by EGL via eglSwapBuffers after being filled using * OpenGL ES. */ NATIVE_WINDOW_API_EGL = 1, /* Buffers will be queued after being filled using the CPU */ NATIVE_WINDOW_API_CPU = 2, /* Buffers will be queued by Stagefright after being filled by a video * decoder. The video decoder can either be a software or hardware decoder. */ NATIVE_WINDOW_API_MEDIA = 3, /* Buffers will be queued by the the camera HAL. */ NATIVE_WINDOW_API_CAMERA = 4, }; /* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */ enum { /* flip source image horizontally */ NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , /* flip source image vertically */ NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */ NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, /* rotate source image 180 degrees */ NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, /* rotate source image 270 degrees clock-wise */ NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, /* transforms source by the inverse transform of the screen it is displayed onto. This * transform is applied last */ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08 }; /* parameter for NATIVE_WINDOW_SET_SCALING_MODE * keep in sync with Surface.java in frameworks/base */ enum { /* the window content is not updated (frozen) until a buffer of * the window size is received (enqueued) */ NATIVE_WINDOW_SCALING_MODE_FREEZE = 0, /* the buffer is scaled in both dimensions to match the window size */ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1, /* the buffer is scaled uniformly such that the smaller dimension * of the buffer matches the window size (cropping in the process) */ NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2, /* the window is clipped to the size of the buffer's crop rectangle; pixels * outside the crop rectangle are treated as if they are completely * transparent. */ NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3, }; /* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */ enum { NATIVE_WINDOW_FRAMEBUFFER = 0, /* FramebufferNativeWindow */ NATIVE_WINDOW_SURFACE = 1, /* Surface */ }; /* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP * * Special timestamp value to indicate that timestamps should be auto-generated * by the native window when queueBuffer is called. This is equal to INT64_MIN, * defined directly to avoid problems with C99/C++ inclusion of stdint.h. */ static const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1); /* parameter for NATIVE_WINDOW_GET_FRAME_TIMESTAMPS * * Special timestamp value to indicate the timestamps aren't yet known or * that they are invalid. */ static const int64_t NATIVE_WINDOW_TIMESTAMP_PENDING = -2; static const int64_t NATIVE_WINDOW_TIMESTAMP_INVALID = -1; struct ANativeWindow { #ifdef __cplusplus ANativeWindow() : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) { common.magic = ANDROID_NATIVE_WINDOW_MAGIC; common.version = sizeof(ANativeWindow); memset(common.reserved, 0, sizeof(common.reserved)); } /* Implement the methods that sp expects so that it can be used to automatically refcount ANativeWindow's. */ void incStrong(const void* /*id*/) const { common.incRef(const_cast(&common)); } void decStrong(const void* /*id*/) const { common.decRef(const_cast(&common)); } #endif struct android_native_base_t common; /* flags describing some attributes of this surface or its updater */ const uint32_t flags; /* min swap interval supported by this updated */ const int minSwapInterval; /* max swap interval supported by this updated */ const int maxSwapInterval; /* horizontal and vertical resolution in DPI */ const float xdpi; const float ydpi; /* Some storage reserved for the OEM's driver. */ intptr_t oem[4]; /* * Set the swap interval for this surface. * * Returns 0 on success or -errno on error. */ int (*setSwapInterval)(struct ANativeWindow* window, int interval); /* * Hook called by EGL to acquire a buffer. After this call, the buffer * is not locked, so its content cannot be modified. This call may block if * no buffers are available. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * Returns 0 on success or -errno on error. * * XXX: This function is deprecated. It will continue to work for some * time for binary compatibility, but the new dequeueBuffer function that * outputs a fence file descriptor should be used in its place. */ int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer); /* * hook called by EGL to lock a buffer. This MUST be called before modifying * the content of a buffer. The buffer must have been acquired with * dequeueBuffer first. * * Returns 0 on success or -errno on error. * * XXX: This function is deprecated. It will continue to work for some * time for binary compatibility, but it is essentially a no-op, and calls * to it should be removed. */ int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); /* * Hook called by EGL when modifications to the render buffer are done. * This unlocks and post the buffer. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * Buffers MUST be queued in the same order than they were dequeued. * * Returns 0 on success or -errno on error. * * XXX: This function is deprecated. It will continue to work for some * time for binary compatibility, but the new queueBuffer function that * takes a fence file descriptor should be used in its place (pass a value * of -1 for the fence file descriptor if there is no valid one to pass). */ int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); /* * hook used to retrieve information about the native window. * * Returns 0 on success or -errno on error. */ int (*query)(const struct ANativeWindow* window, int what, int* value); /* * hook used to perform various operations on the surface. * (*perform)() is a generic mechanism to add functionality to * ANativeWindow while keeping backward binary compatibility. * * DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions * defined below. * * (*perform)() returns -ENOENT if the 'what' parameter is not supported * by the surface's implementation. * * See above for a list of valid operations, such as * NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT */ int (*perform)(struct ANativeWindow* window, int operation, ... ); /* * Hook used to cancel a buffer that has been dequeued. * No synchronization is performed between dequeue() and cancel(), so * either external synchronization is needed, or these functions must be * called from the same thread. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * XXX: This function is deprecated. It will continue to work for some * time for binary compatibility, but the new cancelBuffer function that * takes a fence file descriptor should be used in its place (pass a value * of -1 for the fence file descriptor if there is no valid one to pass). */ int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); /* * Hook called by EGL to acquire a buffer. This call may block if no * buffers are available. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * The libsync fence file descriptor returned in the int pointed to by the * fenceFd argument will refer to the fence that must signal before the * dequeued buffer may be written to. A value of -1 indicates that the * caller may access the buffer immediately without waiting on a fence. If * a valid file descriptor is returned (i.e. any value except -1) then the * caller is responsible for closing the file descriptor. * * Returns 0 on success or -errno on error. */ int (*dequeueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer, int* fenceFd); /* * Hook called by EGL when modifications to the render buffer are done. * This unlocks and post the buffer. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * The fenceFd argument specifies a libsync fence file descriptor for a * fence that must signal before the buffer can be accessed. If the buffer * can be accessed immediately then a value of -1 should be used. The * caller must not use the file descriptor after it is passed to * queueBuffer, and the ANativeWindow implementation is responsible for * closing it. * * Returns 0 on success or -errno on error. */ int (*queueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer, int fenceFd); /* * Hook used to cancel a buffer that has been dequeued. * No synchronization is performed between dequeue() and cancel(), so * either external synchronization is needed, or these functions must be * called from the same thread. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * The fenceFd argument specifies a libsync fence file decsriptor for a * fence that must signal before the buffer can be accessed. If the buffer * can be accessed immediately then a value of -1 should be used. * * Note that if the client has not waited on the fence that was returned * from dequeueBuffer, that same fence should be passed to cancelBuffer to * ensure that future uses of the buffer are preceded by a wait on that * fence. The caller must not use the file descriptor after it is passed * to cancelBuffer, and the ANativeWindow implementation is responsible for * closing it. * * Returns 0 on success or -errno on error. */ int (*cancelBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer, int fenceFd); }; /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). * android_native_window_t is deprecated. */ typedef struct ANativeWindow android_native_window_t __deprecated; /* * native_window_set_usage64(..., usage) * Sets the intended usage flags for the next buffers * acquired with (*lockBuffer)() and on. * * Valid usage flags are defined in android/hardware_buffer.h * All AHARDWAREBUFFER_USAGE_* flags can be specified as needed. * * Calling this function will usually cause following buffers to be * reallocated. */ static inline int native_window_set_usage(struct ANativeWindow* window, uint64_t usage) { return window->perform(window, NATIVE_WINDOW_SET_USAGE64, usage); } /* deprecated. Always returns 0. Don't call. */ static inline int native_window_connect( struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated; static inline int native_window_connect( struct ANativeWindow* window __UNUSED, int api __UNUSED) { return 0; } /* deprecated. Always returns 0. Don't call. */ static inline int native_window_disconnect( struct ANativeWindow* window __UNUSED, int api __UNUSED) __deprecated; static inline int native_window_disconnect( struct ANativeWindow* window __UNUSED, int api __UNUSED) { return 0; } /* * native_window_set_crop(..., crop) * Sets which region of the next queued buffers needs to be considered. * Depending on the scaling mode, a buffer's crop region is scaled and/or * cropped to match the surface's size. This function sets the crop in * pre-transformed buffer pixel coordinates. * * The specified crop region applies to all buffers queued after it is called. * * If 'crop' is NULL, subsequently queued buffers won't be cropped. * * An error is returned if for instance the crop region is invalid, out of the * buffer's bound or if the window is invalid. */ static inline int native_window_set_crop( struct ANativeWindow* window, android_native_rect_t const * crop) { return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); } /* * native_window_set_buffer_count(..., count) * Sets the number of buffers associated with this native window. */ static inline int native_window_set_buffer_count( struct ANativeWindow* window, size_t bufferCount) { return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); } /* * native_window_set_buffers_geometry(..., int w, int h, int format) * All buffers dequeued after this call will have the dimensions and format * specified. A successful call to this function has the same effect as calling * native_window_set_buffers_size and native_window_set_buffers_format. * * XXX: This function is deprecated. The native_window_set_buffers_dimensions * and native_window_set_buffers_format functions should be used instead. */ static inline int native_window_set_buffers_geometry( struct ANativeWindow* window, int w, int h, int format) __deprecated; static inline int native_window_set_buffers_geometry( struct ANativeWindow* window, int w, int h, int format) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, w, h, format); } /* * native_window_set_buffers_dimensions(..., int w, int h) * All buffers dequeued after this call will have the dimensions specified. * In particular, all buffers will have a fixed-size, independent from the * native-window size. They will be scaled according to the scaling mode * (see native_window_set_scaling_mode) upon window composition. * * If w and h are 0, the normal behavior is restored. That is, dequeued buffers * following this call will be sized to match the window's size. * * Calling this function will reset the window crop to a NULL value, which * disables cropping of the buffers. */ static inline int native_window_set_buffers_dimensions( struct ANativeWindow* window, int w, int h) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS, w, h); } /* * native_window_set_buffers_user_dimensions(..., int w, int h) * * Sets the user buffer size for the window, which overrides the * window's size. All buffers dequeued after this call will have the * dimensions specified unless overridden by * native_window_set_buffers_dimensions. All buffers will have a * fixed-size, independent from the native-window size. They will be * scaled according to the scaling mode (see * native_window_set_scaling_mode) upon window composition. * * If w and h are 0, the normal behavior is restored. That is, the * default buffer size will match the windows's size. * * Calling this function will reset the window crop to a NULL value, which * disables cropping of the buffers. */ static inline int native_window_set_buffers_user_dimensions( struct ANativeWindow* window, int w, int h) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS, w, h); } /* * native_window_set_buffers_format(..., int format) * All buffers dequeued after this call will have the format specified. * * If the specified format is 0, the default buffer format will be used. */ static inline int native_window_set_buffers_format( struct ANativeWindow* window, int format) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_FORMAT, format); } /* * native_window_set_buffers_data_space(..., int dataSpace) * All buffers queued after this call will be associated with the dataSpace * parameter specified. * * dataSpace specifies additional information about the buffer that's dependent * on the buffer format and the endpoints. For example, it can be used to convey * the color space of the image data in the buffer, or it can be used to * indicate that the buffers contain depth measurement data instead of color * images. The default dataSpace is 0, HAL_DATASPACE_UNKNOWN, unless it has been * overridden by the consumer. */ static inline int native_window_set_buffers_data_space( struct ANativeWindow* window, android_dataspace_t dataSpace) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_DATASPACE, dataSpace); } /* * native_window_set_buffers_smpte2086_metadata(..., metadata) * All buffers queued after this call will be associated with the SMPTE * ST.2086 metadata specified. * * metadata specifies additional information about the contents of the buffer * that may affect how it's displayed. When it is nullptr, it means no such * information is available. No SMPTE ST.2086 metadata is associated with the * buffers by default. */ static inline int native_window_set_buffers_smpte2086_metadata( struct ANativeWindow* window, const struct android_smpte2086_metadata* metadata) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA, metadata); } /* * native_window_set_buffers_cta861_3_metadata(..., metadata) * All buffers queued after this call will be associated with the CTA-861.3 * metadata specified. * * metadata specifies additional information about the contents of the buffer * that may affect how it's displayed. When it is nullptr, it means no such * information is available. No CTA-861.3 metadata is associated with the * buffers by default. */ static inline int native_window_set_buffers_cta861_3_metadata( struct ANativeWindow* window, const struct android_cta861_3_metadata* metadata) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA, metadata); } /* * native_window_set_buffers_hdr10_plus_metadata(..., metadata) * All buffers queued after this call will be associated with the * HDR10+ dynamic metadata specified. * * metadata specifies additional dynamic information about the * contents of the buffer that may affect how it is displayed. When * it is nullptr, it means no such information is available. No * HDR10+ dynamic emtadata is associated with the buffers by default. * * Parameter "size" refers to the length of the metadata blob pointed to * by parameter "data". The metadata blob will adhere to the HDR10+ SEI * message standard. */ static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window, const size_t size, const uint8_t* metadata) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size, metadata); } /* * native_window_set_buffers_transform(..., int transform) * All buffers queued after this call will be displayed transformed according * to the transform parameter specified. */ static inline int native_window_set_buffers_transform( struct ANativeWindow* window, int transform) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, transform); } /* * native_window_set_buffers_sticky_transform(..., int transform) * All buffers queued after this call will be displayed transformed according * to the transform parameter specified applied on top of the regular buffer * transform. Setting this transform will disable the transform hint. * * Temporary - This is only intended to be used by the LEGACY camera mode, do * not use this for anything else. */ static inline int native_window_set_buffers_sticky_transform( struct ANativeWindow* window, int transform) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM, transform); } /* * native_window_set_buffers_timestamp(..., int64_t timestamp) * All buffers queued after this call will be associated with the timestamp * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO * (the default), timestamps will be generated automatically when queueBuffer is * called. The timestamp is measured in nanoseconds, and is normally monotonically * increasing. The timestamp should be unaffected by time-of-day adjustments, * and for a camera should be strictly monotonic but for a media player may be * reset when the position is set. */ static inline int native_window_set_buffers_timestamp( struct ANativeWindow* window, int64_t timestamp) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP, timestamp); } /* * native_window_set_scaling_mode(..., int mode) * All buffers queued after this call will be associated with the scaling mode * specified. */ static inline int native_window_set_scaling_mode( struct ANativeWindow* window, int mode) { return window->perform(window, NATIVE_WINDOW_SET_SCALING_MODE, mode); } /* * native_window_api_connect(..., int api) * connects an API to this window. only one API can be connected at a time. * Returns -EINVAL if for some reason the window cannot be connected, which * can happen if it's connected to some other API. */ static inline int native_window_api_connect( struct ANativeWindow* window, int api) { return window->perform(window, NATIVE_WINDOW_API_CONNECT, api); } /* * native_window_api_disconnect(..., int api) * disconnect the API from this window. * An error is returned if for instance the window wasn't connected in the * first place. */ static inline int native_window_api_disconnect( struct ANativeWindow* window, int api) { return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); } /* * native_window_dequeue_buffer_and_wait(...) * Dequeue a buffer and wait on the fence associated with that buffer. The * buffer may safely be accessed immediately upon this function returning. An * error is returned if either of the dequeue or the wait operations fail. */ static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw, struct ANativeWindowBuffer** anb) { return anw->dequeueBuffer_DEPRECATED(anw, anb); } /* * native_window_set_sideband_stream(..., native_handle_t*) * Attach a sideband buffer stream to a native window. */ static inline int native_window_set_sideband_stream( struct ANativeWindow* window, native_handle_t* sidebandHandle) { return window->perform(window, NATIVE_WINDOW_SET_SIDEBAND_STREAM, sidebandHandle); } /* * native_window_set_surface_damage(..., android_native_rect_t* rects, int numRects) * Set the surface damage (i.e., the region of the surface that has changed * since the previous frame). The damage set by this call will be reset (to the * default of full-surface damage) after calling queue, so this must be called * prior to every frame with damage that does not cover the whole surface if the * caller desires downstream consumers to use this optimization. * * The damage region is specified as an array of rectangles, with the important * caveat that the origin of the surface is considered to be the bottom-left * corner, as in OpenGL ES. * * If numRects is set to 0, rects may be NULL, and the surface damage will be * set to the full surface (the same as if this function had not been called for * this frame). */ static inline int native_window_set_surface_damage( struct ANativeWindow* window, const android_native_rect_t* rects, size_t numRects) { return window->perform(window, NATIVE_WINDOW_SET_SURFACE_DAMAGE, rects, numRects); } /* * native_window_set_shared_buffer_mode(..., bool sharedBufferMode) * Enable/disable shared buffer mode */ static inline int native_window_set_shared_buffer_mode( struct ANativeWindow* window, bool sharedBufferMode) { return window->perform(window, NATIVE_WINDOW_SET_SHARED_BUFFER_MODE, sharedBufferMode); } /* * native_window_set_auto_refresh(..., autoRefresh) * Enable/disable auto refresh when in shared buffer mode */ static inline int native_window_set_auto_refresh( struct ANativeWindow* window, bool autoRefresh) { return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh); } static inline int native_window_get_refresh_cycle_duration( struct ANativeWindow* window, int64_t* outRefreshDuration) { return window->perform(window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, outRefreshDuration); } static inline int native_window_get_next_frame_id( struct ANativeWindow* window, uint64_t* frameId) { return window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, frameId); } static inline int native_window_enable_frame_timestamps( struct ANativeWindow* window, bool enable) { return window->perform(window, NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS, enable); } static inline int native_window_get_compositor_timing( struct ANativeWindow* window, int64_t* compositeDeadline, int64_t* compositeInterval, int64_t* compositeToPresentLatency) { return window->perform(window, NATIVE_WINDOW_GET_COMPOSITOR_TIMING, compositeDeadline, compositeInterval, compositeToPresentLatency); } static inline int native_window_get_frame_timestamps( struct ANativeWindow* window, uint64_t frameId, int64_t* outRequestedPresentTime, int64_t* outAcquireTime, int64_t* outLatchTime, int64_t* outFirstRefreshStartTime, int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime, int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime, int64_t* outReleaseTime) { return window->perform(window, NATIVE_WINDOW_GET_FRAME_TIMESTAMPS, frameId, outRequestedPresentTime, outAcquireTime, outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, outGpuCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime); } static inline int native_window_get_wide_color_support( struct ANativeWindow* window, bool* outSupport) { return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT, outSupport); } static inline int native_window_get_hdr_support(struct ANativeWindow* window, bool* outSupport) { return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport); } static inline int native_window_get_consumer_usage(struct ANativeWindow* window, uint64_t* outUsage) { return window->perform(window, NATIVE_WINDOW_GET_CONSUMER_USAGE64, outUsage); } __END_DECLS ���������������������������������������libs/nativewindow/include/vndk/���������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015660� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/vndk/hardware_buffer.h����������������������������������������������������0100644 0000000 0000000 00000006730 13756501735 021162� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H #define ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H // vndk is a superset of the NDK #include #include __BEGIN_DECLS const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer); enum CreateFromHandleMethod { // enum values chosen to match internal GraphicBuffer::HandleWrapMethod AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER = 2, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE = 3, }; /** * Create a AHardwareBuffer from a native handle. * * This function wraps a native handle in a AHardwareBuffer suitable for use by applications or * other parts of the system. The contents of desc will be returned by AHardwareBuffer_describe(). * * If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER, the handle is assumed to be * unregistered, and it will be registered/imported before being wrapped in the AHardwareBuffer. * If successful, the AHardwareBuffer will own the handle. * * If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, the handle will be cloned and the * clone registered. The AHardwareBuffer will own the cloned handle but not the original. */ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, const native_handle_t* handle, int32_t method, AHardwareBuffer** outBuffer); /** * Buffer pixel formats. */ enum { /* for future proofing, keep these in sync with system/graphics-base.h */ /* same as HAL_PIXEL_FORMAT_BGRA_8888 */ AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM = 5, /* same as HAL_PIXEL_FORMAT_YV12 */ AHARDWAREBUFFER_FORMAT_YV12 = 0x32315659, /* same as HAL_PIXEL_FORMAT_Y8 */ AHARDWAREBUFFER_FORMAT_Y8 = 0x20203859, /* same as HAL_PIXEL_FORMAT_Y16 */ AHARDWAREBUFFER_FORMAT_Y16 = 0x20363159, /* same as HAL_PIXEL_FORMAT_RAW16 */ AHARDWAREBUFFER_FORMAT_RAW16 = 0x20, /* same as HAL_PIXEL_FORMAT_RAW10 */ AHARDWAREBUFFER_FORMAT_RAW10 = 0x25, /* same as HAL_PIXEL_FORMAT_RAW12 */ AHARDWAREBUFFER_FORMAT_RAW12 = 0x26, /* same as HAL_PIXEL_FORMAT_RAW_OPAQUE */ AHARDWAREBUFFER_FORMAT_RAW_OPAQUE = 0x24, /* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */ AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED = 0x22, /* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */ AHARDWAREBUFFER_FORMAT_YCbCr_422_SP = 0x10, /* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */ AHARDWAREBUFFER_FORMAT_YCrCb_420_SP = 0x11, /* same as HAL_PIXEL_FORMAT_YCBCR_422_I */ AHARDWAREBUFFER_FORMAT_YCbCr_422_I = 0x14, }; __END_DECLS #endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */ ����������������������������������������libs/nativewindow/include/vndk/window.h�������������������������������������������������������������0100644 0000000 0000000 00000030375 13756501735 017345� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H #define ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H #include // vndk is a superset of the NDK #include __BEGIN_DECLS /* * Convert this ANativeWindowBuffer into a AHardwareBuffer */ AHardwareBuffer* ANativeWindowBuffer_getHardwareBuffer(ANativeWindowBuffer* anwb); /*****************************************************************************/ /* * Stores a value into one of the 4 available slots * Retrieve the value with ANativeWindow_OemStorageGet() * * slot: 0 to 3 * * Returns 0 on success or -errno on error. */ int ANativeWindow_OemStorageSet(ANativeWindow* window, uint32_t slot, intptr_t value); /* * Retrieves a value from one of the 4 available slots * By default the returned value is 0 if it wasn't set by ANativeWindow_OemStorageSet() * * slot: 0 to 3 * * Returns 0 on success or -errno on error. */ int ANativeWindow_OemStorageGet(ANativeWindow* window, uint32_t slot, intptr_t* value); /* * Set the swap interval for this surface. * * Returns 0 on success or -errno on error. */ int ANativeWindow_setSwapInterval(ANativeWindow* window, int interval); /* * queries that can be used with ANativeWindow_query() and ANativeWindow_queryf() */ enum ANativeWindowQuery { /* The minimum number of buffers that must remain un-dequeued after a buffer * has been queued. This value applies only if set_buffer_count was used to * override the number of buffers and if a buffer has since been queued. * Users of the set_buffer_count ANativeWindow method should query this * value before calling set_buffer_count. If it is necessary to have N * buffers simultaneously dequeued as part of the steady-state operation, * and this query returns M then N+M buffers should be requested via * native_window_set_buffer_count. * * Note that this value does NOT apply until a single buffer has been * queued. In particular this means that it is possible to: * * 1. Query M = min undequeued buffers * 2. Set the buffer count to N + M * 3. Dequeue all N + M buffers * 4. Cancel M buffers * 5. Queue, dequeue, queue, dequeue, ad infinitum */ ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS = 3, /* * Default width of ANativeWindow buffers, these are the * dimensions of the window buffers irrespective of the * ANativeWindow_setBuffersDimensions() call and match the native window * size. */ ANATIVEWINDOW_QUERY_DEFAULT_WIDTH = 6, ANATIVEWINDOW_QUERY_DEFAULT_HEIGHT = 7, /* * transformation that will most-likely be applied to buffers. This is only * a hint, the actual transformation applied might be different. * * INTENDED USE: * * The transform hint can be used by a producer, for instance the GLES * driver, to pre-rotate the rendering such that the final transformation * in the composer is identity. This can be very useful when used in * conjunction with the h/w composer HAL, in situations where it * cannot handle arbitrary rotations. * * 1. Before dequeuing a buffer, the GL driver (or any other ANW client) * queries the ANW for NATIVE_WINDOW_TRANSFORM_HINT. * * 2. The GL driver overrides the width and height of the ANW to * account for NATIVE_WINDOW_TRANSFORM_HINT. This is done by querying * NATIVE_WINDOW_DEFAULT_{WIDTH | HEIGHT}, swapping the dimensions * according to NATIVE_WINDOW_TRANSFORM_HINT and calling * native_window_set_buffers_dimensions(). * * 3. The GL driver dequeues a buffer of the new pre-rotated size. * * 4. The GL driver renders to the buffer such that the image is * already transformed, that is applying NATIVE_WINDOW_TRANSFORM_HINT * to the rendering. * * 5. The GL driver calls native_window_set_transform to apply * inverse transformation to the buffer it just rendered. * In order to do this, the GL driver needs * to calculate the inverse of NATIVE_WINDOW_TRANSFORM_HINT, this is * done easily: * * int hintTransform, inverseTransform; * query(..., NATIVE_WINDOW_TRANSFORM_HINT, &hintTransform); * inverseTransform = hintTransform; * if (hintTransform & HAL_TRANSFORM_ROT_90) * inverseTransform ^= HAL_TRANSFORM_ROT_180; * * * 6. The GL driver queues the pre-transformed buffer. * * 7. The composer combines the buffer transform with the display * transform. If the buffer transform happens to cancel out the * display transform then no rotation is needed. * */ ANATIVEWINDOW_QUERY_TRANSFORM_HINT = 8, /* * Returns the age of the contents of the most recently dequeued buffer as * the number of frames that have elapsed since it was last queued. For * example, if the window is double-buffered, the age of any given buffer in * steady state will be 2. If the dequeued buffer has never been queued, its * age will be 0. */ ANATIVEWINDOW_QUERY_BUFFER_AGE = 13, /* min swap interval supported by this compositor */ ANATIVEWINDOW_QUERY_MIN_SWAP_INTERVAL = 0x10000, /* max swap interval supported by this compositor */ ANATIVEWINDOW_QUERY_MAX_SWAP_INTERVAL = 0x10001, /* horizontal resolution in DPI. value is float, use queryf() */ ANATIVEWINDOW_QUERY_XDPI = 0x10002, /* vertical resolution in DPI. value is float, use queryf() */ ANATIVEWINDOW_QUERY_YDPI = 0x10003, }; typedef enum ANativeWindowQuery ANativeWindowQuery; /* * hook used to retrieve information about the native window. * * Returns 0 on success or -errno on error. */ int ANativeWindow_query(const ANativeWindow* window, ANativeWindowQuery query, int* value); int ANativeWindow_queryf(const ANativeWindow* window, ANativeWindowQuery query, float* value); /* * Hook called by EGL to acquire a buffer. This call may block if no * buffers are available. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * The libsync fence file descriptor returned in the int pointed to by the * fenceFd argument will refer to the fence that must signal before the * dequeued buffer may be written to. A value of -1 indicates that the * caller may access the buffer immediately without waiting on a fence. If * a valid file descriptor is returned (i.e. any value except -1) then the * caller is responsible for closing the file descriptor. * * Returns 0 on success or -errno on error. */ int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd); /* * Hook called by EGL when modifications to the render buffer are done. * This unlocks and post the buffer. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * The fenceFd argument specifies a libsync fence file descriptor for a * fence that must signal before the buffer can be accessed. If the buffer * can be accessed immediately then a value of -1 should be used. The * caller must not use the file descriptor after it is passed to * queueBuffer, and the ANativeWindow implementation is responsible for * closing it. * * Returns 0 on success or -errno on error. */ int ANativeWindow_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); /* * Hook used to cancel a buffer that has been dequeued. * No synchronization is performed between dequeue() and cancel(), so * either external synchronization is needed, or these functions must be * called from the same thread. * * The window holds a reference to the buffer between dequeueBuffer and * either queueBuffer or cancelBuffer, so clients only need their own * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. * * The fenceFd argument specifies a libsync fence file decsriptor for a * fence that must signal before the buffer can be accessed. If the buffer * can be accessed immediately then a value of -1 should be used. * * Note that if the client has not waited on the fence that was returned * from dequeueBuffer, that same fence should be passed to cancelBuffer to * ensure that future uses of the buffer are preceded by a wait on that * fence. The caller must not use the file descriptor after it is passed * to cancelBuffer, and the ANativeWindow implementation is responsible for * closing it. * * Returns 0 on success or -errno on error. */ int ANativeWindow_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); /* * Sets the intended usage flags for the next buffers. * * usage: one of AHARDWAREBUFFER_USAGE_* constant * * By default (if this function is never called), a usage of * AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT * is assumed. * * Calling this function will usually cause following buffers to be * reallocated. */ int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage); /* * Sets the number of buffers associated with this native window. */ int ANativeWindow_setBufferCount(ANativeWindow* window, size_t bufferCount); /* * All buffers dequeued after this call will have the dimensions specified. * In particular, all buffers will have a fixed-size, independent from the * native-window size. They will be scaled according to the scaling mode * (see native_window_set_scaling_mode) upon window composition. * * If w and h are 0, the normal behavior is restored. That is, dequeued buffers * following this call will be sized to match the window's size. * * Calling this function will reset the window crop to a NULL value, which * disables cropping of the buffers. */ int ANativeWindow_setBuffersDimensions(ANativeWindow* window, uint32_t w, uint32_t h); /* * All buffers dequeued after this call will have the format specified. * format: one of AHARDWAREBUFFER_FORMAT_* constant * * If the specified format is 0, the default buffer format will be used. */ int ANativeWindow_setBuffersFormat(ANativeWindow* window, int format); /* * All buffers queued after this call will be associated with the timestamp in nanosecond * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO * (the default), timestamps will be generated automatically when queueBuffer is * called. The timestamp is measured in nanoseconds, and is normally monotonically * increasing. The timestamp should be unaffected by time-of-day adjustments, * and for a camera should be strictly monotonic but for a media player may be * reset when the position is set. */ int ANativeWindow_setBuffersTimestamp(ANativeWindow* window, int64_t timestamp); /* * Enable/disable shared buffer mode */ int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMode); /* * Enable/disable auto refresh when in shared buffer mode */ int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh); /*****************************************************************************/ __END_DECLS #endif /* ANDROID_VNDK_NATIVEWINDOW_ANATIVEWINDOW_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/libnativewindow.map.txt�����������������������������������������������������������0100644 0000000 0000000 00000004073 13756501735 020016� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; AHardwareBuffer_createFromHandle; # vndk AHardwareBuffer_describe; AHardwareBuffer_getNativeHandle; # vndk AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 AHardwareBuffer_lockPlanes; # introduced=29 AHardwareBuffer_recvHandleFromUnixSocket; AHardwareBuffer_release; AHardwareBuffer_sendHandleToUnixSocket; AHardwareBuffer_unlock; ANativeWindowBuffer_getHardwareBuffer; # vndk ANativeWindow_OemStorageGet; # vndk ANativeWindow_OemStorageSet; # vndk ANativeWindow_acquire; ANativeWindow_cancelBuffer; # vndk ANativeWindow_dequeueBuffer; # vndk ANativeWindow_getBuffersDataSpace; # introduced=28 ANativeWindow_getFormat; ANativeWindow_getHeight; ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # vndk ANativeWindow_queryf; # vndk ANativeWindow_queueBuffer; # vndk ANativeWindow_release; ANativeWindow_setAutoRefresh; # vndk ANativeWindow_setBufferCount; # vndk ANativeWindow_setBuffersDataSpace; # introduced=28 ANativeWindow_setBuffersDimensions; # vndk ANativeWindow_setBuffersFormat; # vndk ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # vndk ANativeWindow_setBuffersTransform; ANativeWindow_setSharedBufferMode; # vndk ANativeWindow_setSwapInterval; # vndk ANativeWindow_setUsage; # vndk ANativeWindow_unlockAndPost; local: *; }; LIBNATIVEWINDOW_PLATFORM { global: extern "C++" { android::AHardwareBuffer_isValidPixelFormat*; android::AHardwareBuffer_convertFromPixelFormat*; android::AHardwareBuffer_convertToPixelFormat*; android::AHardwareBuffer_convertFromGrallocUsageBits*; android::AHardwareBuffer_convertToGrallocUsageBits*; android::AHardwareBuffer_to_GraphicBuffer*; android::AHardwareBuffer_to_ANativeWindowBuffer*; android::AHardwareBuffer_from_GraphicBuffer*; }; } LIBNATIVEWINDOW; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/tests/����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014435� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/tests/AHardwareBufferTest.cpp�����������������������������������������������������0100644 0000000 0000000 00000013033 13756501735 020766� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 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. */ #define LOG_TAG "AHardwareBuffer_test" //#define LOG_NDEBUG 0 #include #include #include #include using namespace android; using android::hardware::graphics::common::V1_0::BufferUsage; static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected, uint64_t actual, const char* type) { std::ostringstream ss; ss << type << " 0x" << std::hex << actual << " does not match expected " << type << " 0x" << std::hex << expected; return ::testing::AssertionFailure() << ss.str(); } static ::testing::AssertionResult TestUsageConversion( uint64_t grallocUsage, uint64_t hardwareBufferUsage) { uint64_t convertedGrallocUsage = AHardwareBuffer_convertToGrallocUsageBits(hardwareBufferUsage); if (convertedGrallocUsage != grallocUsage) return BuildHexFailureMessage(grallocUsage, convertedGrallocUsage, "converToGralloc"); uint64_t convertedHArdwareBufferUsage = AHardwareBuffer_convertFromGrallocUsageBits(grallocUsage); if (convertedHArdwareBufferUsage != grallocUsage) return BuildHexFailureMessage(grallocUsage, convertedHArdwareBufferUsage, "convertFromGralloc"); return testing::AssertionSuccess(); } // This is a unit test rather than going through AHardwareBuffer because not // all flags may be supported by the host device. TEST(AHardwareBufferTest, ConvertToAndFromGrallocBits) { EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_RARELY, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_READ_OFTEN, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_RARELY, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::CPU_WRITE_OFTEN, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_TEXTURE, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_RENDER_TARGET, AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::GPU_DATA_BUFFER, AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::PROTECTED, AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::SENSOR_DIRECT_DATA, AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA)); EXPECT_TRUE(TestUsageConversion((uint64_t)BufferUsage::VIDEO_ENCODER, AHARDWAREBUFFER_USAGE_VIDEO_ENCODE)); EXPECT_TRUE(TestUsageConversion(1ull<<28, AHARDWAREBUFFER_USAGE_VENDOR_0)); EXPECT_TRUE(TestUsageConversion(1ull<<29, AHARDWAREBUFFER_USAGE_VENDOR_1)); EXPECT_TRUE(TestUsageConversion(1ull<<30, AHARDWAREBUFFER_USAGE_VENDOR_2)); EXPECT_TRUE(TestUsageConversion(1ull<<31, AHARDWAREBUFFER_USAGE_VENDOR_3)); EXPECT_TRUE(TestUsageConversion(1ull<<48, AHARDWAREBUFFER_USAGE_VENDOR_4)); EXPECT_TRUE(TestUsageConversion(1ull<<49, AHARDWAREBUFFER_USAGE_VENDOR_5)); EXPECT_TRUE(TestUsageConversion(1ull<<50, AHARDWAREBUFFER_USAGE_VENDOR_6)); EXPECT_TRUE(TestUsageConversion(1ull<<51, AHARDWAREBUFFER_USAGE_VENDOR_7)); EXPECT_TRUE(TestUsageConversion(1ull<<52, AHARDWAREBUFFER_USAGE_VENDOR_8)); EXPECT_TRUE(TestUsageConversion(1ull<<53, AHARDWAREBUFFER_USAGE_VENDOR_9)); EXPECT_TRUE(TestUsageConversion(1ull<<54, AHARDWAREBUFFER_USAGE_VENDOR_10)); EXPECT_TRUE(TestUsageConversion(1ull<<55, AHARDWAREBUFFER_USAGE_VENDOR_11)); EXPECT_TRUE(TestUsageConversion(1ull<<56, AHARDWAREBUFFER_USAGE_VENDOR_12)); EXPECT_TRUE(TestUsageConversion(1ull<<57, AHARDWAREBUFFER_USAGE_VENDOR_13)); EXPECT_TRUE(TestUsageConversion(1ull<<58, AHARDWAREBUFFER_USAGE_VENDOR_14)); EXPECT_TRUE(TestUsageConversion(1ull<<59, AHARDWAREBUFFER_USAGE_VENDOR_15)); EXPECT_TRUE(TestUsageConversion(1ull<<60, AHARDWAREBUFFER_USAGE_VENDOR_16)); EXPECT_TRUE(TestUsageConversion(1ull<<61, AHARDWAREBUFFER_USAGE_VENDOR_17)); EXPECT_TRUE(TestUsageConversion(1ull<<62, AHARDWAREBUFFER_USAGE_VENDOR_18)); EXPECT_TRUE(TestUsageConversion(1ull<<63, AHARDWAREBUFFER_USAGE_VENDOR_19)); // Test some more complex flag combinations. EXPECT_TRUE(TestUsageConversion( (uint64_t)BufferUsage::CPU_READ_RARELY | (uint64_t)BufferUsage::CPU_WRITE_RARELY, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY)); EXPECT_TRUE(TestUsageConversion( (uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE | 1ull << 29 | 1ull << 57, AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13)); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/tests/Android.bp������������������������������������������������������������������0100644 0000000 0000000 00000001555 13756501735 016343� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// // Copyright (C) 2017 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. // cc_test { name: "AHardwareBufferTest", shared_libs: [ "libnativewindow", "android.hardware.graphics.common@1.0", ], srcs: [ "AHardwareBufferTest.cpp", "c_compatibility.c"], cflags: ["-Wall", "-Werror"], } ���������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/tests/c_compatibility.c�����������������������������������������������������������0100644 0000000 0000000 00000001450 13756501735 017751� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2017 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. */ #include #include #include #include // this checks that all these headers are C-compatible ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/����������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013222� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/Android.bp������������������������������������������������������������������������0100644 0000000 0000000 00000004020 13756501735 015116� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������cc_defaults { name: "renderengine_defaults", cflags: [ "-DLOG_TAG=\"RenderEngine\"", "-Wall", "-Werror", "-Wthread-safety", "-Wunused", "-Wunreachable-code", ], } cc_defaults { name: "librenderengine_defaults", defaults: ["renderengine_defaults"], cflags: [ "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", ], shared_libs: [ "libbase", "libcutils", "libEGL", "libGLESv1_CM", "libGLESv2", "libgui", "liblog", "libnativewindow", "libprocessgroup", "libsync", "libui", "libutils", ], local_include_dirs: ["include"], export_include_dirs: ["include"], } filegroup { name: "librenderengine_sources", srcs: [ "Description.cpp", "Mesh.cpp", "RenderEngine.cpp", "Texture.cpp", ], } filegroup { name: "librenderengine_gl_sources", srcs: [ "gl/GLESRenderEngine.cpp", "gl/GLExtensions.cpp", "gl/GLFramebuffer.cpp", "gl/GLImage.cpp", "gl/ImageManager.cpp", "gl/Program.cpp", "gl/ProgramCache.cpp", ], } cc_library_static { name: "librenderengine", defaults: ["librenderengine_defaults"], vendor_available: true, vndk: { enabled: true, }, double_loadable: true, clang: true, cflags: [ "-fvisibility=hidden", "-Werror=format", ], cppflags: [ "-fwhole-program-vtables", // requires ThinLTO ], srcs: [ ":librenderengine_sources", ":librenderengine_gl_sources", ], lto: { thin: true, }, } cc_library_static { name: "librenderengine_mocks", defaults: ["librenderengine_defaults"], srcs: [ "mock/Framebuffer.cpp", "mock/Image.cpp", "mock/RenderEngine.cpp", ], static_libs: [ "libgtest", "libgmock", ], local_include_dirs: ["include"], export_include_dirs: ["include"], } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/Description.cpp�������������������������������������������������������������������0100644 0000000 0000000 00000003363 13756501735 016213� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include #include #include namespace android { namespace renderengine { Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) { ui::Dataspace transfer = static_cast(dataSpace & ui::Dataspace::TRANSFER_MASK); switch (transfer) { case ui::Dataspace::TRANSFER_ST2084: return Description::TransferFunction::ST2084; case ui::Dataspace::TRANSFER_HLG: return Description::TransferFunction::HLG; case ui::Dataspace::TRANSFER_LINEAR: return Description::TransferFunction::LINEAR; default: return Description::TransferFunction::SRGB; } } bool Description::hasInputTransformMatrix() const { const mat4 identity; return inputTransformMatrix != identity; } bool Description::hasOutputTransformMatrix() const { const mat4 identity; return outputTransformMatrix != identity; } bool Description::hasColorMatrix() const { const mat4 identity; return colorMatrix != identity; } } // namespace renderengine } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/Mesh.cpp��������������������������������������������������������������������������0100644 0000000 0000000 00000005464 13756501735 014630� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include #include namespace android { namespace renderengine { Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize) : mVertexCount(vertexCount), mVertexSize(vertexSize), mTexCoordsSize(texCoordSize), mPrimitive(primitive) { if (vertexCount == 0) { mVertices.resize(1); mVertices[0] = 0.0f; mStride = 0; return; } const size_t CROP_COORD_SIZE = 2; size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE; size_t remainder = (stride * vertexCount) / vertexCount; // Since all of the input parameters are unsigned, if stride is less than // either vertexSize or texCoordSize, it must have overflowed. remainder // will be equal to stride as long as stride * vertexCount doesn't overflow. if ((stride < vertexSize) || (remainder != stride)) { ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize, CROP_COORD_SIZE); mVertices.resize(1); mVertices[0] = 0.0f; mVertexCount = 0; mVertexSize = 0; mTexCoordsSize = 0; mStride = 0; return; } mVertices.resize(stride * vertexCount); mStride = stride; } Mesh::Primitive Mesh::getPrimitive() const { return mPrimitive; } float const* Mesh::getPositions() const { return mVertices.data(); } float* Mesh::getPositions() { return mVertices.data(); } float const* Mesh::getTexCoords() const { return mVertices.data() + mVertexSize; } float* Mesh::getTexCoords() { return mVertices.data() + mVertexSize; } float const* Mesh::getCropCoords() const { return mVertices.data() + mVertexSize + mTexCoordsSize; } float* Mesh::getCropCoords() { return mVertices.data() + mVertexSize + mTexCoordsSize; } size_t Mesh::getVertexCount() const { return mVertexCount; } size_t Mesh::getVertexSize() const { return mVertexSize; } size_t Mesh::getTexCoordsSize() const { return mTexCoordsSize; } size_t Mesh::getByteStride() const { return mStride * sizeof(float); } size_t Mesh::getStride() const { return mStride; } } // namespace renderengine } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/OWNERS����������������������������������������������������������������������������0100644 0000000 0000000 00000000040 13756501735 014151� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������lpy@google.com stoza@google.com ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/RenderEngine.cpp������������������������������������������������������������������0100644 0000000 0000000 00000003616 13756501735 016276� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include #include #include #include #include "gl/GLESRenderEngine.h" namespace android { namespace renderengine { std::unique_ptr RenderEngine::create(int hwcFormat, uint32_t featureFlags, uint32_t imageCacheSize) { char prop[PROPERTY_VALUE_MAX]; property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); if (strcmp(prop, "gles") == 0) { ALOGD("RenderEngine GLES Backend"); return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize); } ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize); } RenderEngine::~RenderEngine() = default; namespace impl { RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {} RenderEngine::~RenderEngine() = default; bool RenderEngine::useNativeFenceSync() const { return SyncFeatures::getInstance().useNativeFenceSync(); } bool RenderEngine::useWaitSync() const { return SyncFeatures::getInstance().useWaitSync(); } } // namespace impl } // namespace renderengine } // namespace android ������������������������������������������������������������������������������������������������������������������libs/renderengine/TEST_MAPPING����������������������������������������������������������������������0100644 0000000 0000000 00000000112 13756501735 015066� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "presubmit": [ { "name": "librenderengine_test" } ] } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/Texture.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000003537 13756501735 015373� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include namespace android { namespace renderengine { Texture::Texture() : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {} Texture::Texture(Target textureTarget, uint32_t textureName) : mTextureName(textureName), mTextureTarget(textureTarget), mWidth(0), mHeight(0), mFiltering(false) {} void Texture::init(Target textureTarget, uint32_t textureName) { mTextureName = textureName; mTextureTarget = textureTarget; } Texture::~Texture() {} void Texture::setMatrix(float const* matrix) { mTextureMatrix = mat4(matrix); } void Texture::setFiltering(bool enabled) { mFiltering = enabled; } void Texture::setDimensions(size_t width, size_t height) { mWidth = width; mHeight = height; } uint32_t Texture::getTextureName() const { return mTextureName; } uint32_t Texture::getTextureTarget() const { return mTextureTarget; } const mat4& Texture::getMatrix() const { return mTextureMatrix; } bool Texture::getFiltering() const { return mFiltering; } size_t Texture::getWidth() const { return mWidth; } size_t Texture::getHeight() const { return mHeight; } } // namespace renderengine } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013624� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLESRenderEngine.cpp�����������������������������������������������������������0100644 0000000 0000000 00000172060 13756501735 017353� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ //#define LOG_NDEBUG 0 #undef LOG_TAG #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GLESRenderEngine.h" #include "GLExtensions.h" #include "GLFramebuffer.h" #include "GLImage.h" #include "Program.h" #include "ProgramCache.h" extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); bool checkGlError(const char* op, int lineNumber) { bool errorFound = false; GLint error = glGetError(); while (error != GL_NO_ERROR) { errorFound = true; error = glGetError(); ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); } return errorFound; } static constexpr bool outputDebugPPMs = false; void writePPM(const char* basename, GLuint width, GLuint height) { ALOGV("writePPM #%s: %d x %d", basename, width, height); std::vector pixels(width * height * 4); std::vector outBuffer(width * height * 3); // TODO(courtneygo): We can now have float formats, need // to remove this code or update to support. // Make returned pixels fit in uint32_t, one byte per component glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); if (checkGlError(__FUNCTION__, __LINE__)) { return; } std::string filename(basename); filename.append(".ppm"); std::ofstream file(filename.c_str(), std::ios::binary); if (!file.is_open()) { ALOGE("Unable to open file: %s", filename.c_str()); ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " "surfaceflinger to write debug images"); return; } file << "P6\n"; file << width << "\n"; file << height << "\n"; file << 255 << "\n"; auto ptr = reinterpret_cast(pixels.data()); auto outPtr = reinterpret_cast(outBuffer.data()); for (int y = height - 1; y >= 0; y--) { char* data = ptr + y * width * sizeof(uint32_t); for (GLuint x = 0; x < width; x++) { // Only copy R, G and B components outPtr[0] = data[0]; outPtr[1] = data[1]; outPtr[2] = data[2]; data += sizeof(uint32_t); outPtr += 3; } } file.write(reinterpret_cast(outBuffer.data()), outBuffer.size()); } namespace android { namespace renderengine { namespace gl { using base::StringAppendF; using ui::Dataspace; static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, EGLint wanted, EGLConfig* outConfig) { EGLint numConfigs = -1, n = 0; eglGetConfigs(dpy, nullptr, 0, &numConfigs); std::vector configs(numConfigs, EGL_NO_CONFIG_KHR); eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); configs.resize(n); if (!configs.empty()) { if (attribute != EGL_NONE) { for (EGLConfig config : configs) { EGLint value = 0; eglGetConfigAttrib(dpy, config, attribute, &value); if (wanted == value) { *outConfig = config; return NO_ERROR; } } } else { // just pick the first one *outConfig = configs[0]; return NO_ERROR; } } return NAME_NOT_FOUND; } class EGLAttributeVector { struct Attribute; class Adder; friend class Adder; KeyedVector mList; struct Attribute { Attribute() : v(0){}; explicit Attribute(EGLint v) : v(v) {} EGLint v; bool operator<(const Attribute& other) const { // this places EGL_NONE at the end EGLint lhs(v); EGLint rhs(other.v); if (lhs == EGL_NONE) lhs = 0x7FFFFFFF; if (rhs == EGL_NONE) rhs = 0x7FFFFFFF; return lhs < rhs; } }; class Adder { friend class EGLAttributeVector; EGLAttributeVector& v; EGLint attribute; Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {} public: void operator=(EGLint value) { if (attribute != EGL_NONE) { v.mList.add(Attribute(attribute), value); } } operator EGLint() const { return v.mList[attribute]; } }; public: EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); } void remove(EGLint attribute) { if (attribute != EGL_NONE) { mList.removeItem(Attribute(attribute)); } } Adder operator[](EGLint attribute) { return Adder(*this, attribute); } EGLint operator[](EGLint attribute) const { return mList[attribute]; } // cast-operator to (EGLint const*) operator EGLint const*() const { return &mList.keyAt(0).v; } }; static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, EGLConfig* config) { // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if // it is to be used with WIFI displays status_t err; EGLint wantedAttribute; EGLint wantedAttributeValue; EGLAttributeVector attribs; if (renderableType) { attribs[EGL_RENDERABLE_TYPE] = renderableType; attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; attribs[EGL_RED_SIZE] = 8; attribs[EGL_GREEN_SIZE] = 8; attribs[EGL_BLUE_SIZE] = 8; attribs[EGL_ALPHA_SIZE] = 8; wantedAttribute = EGL_NONE; wantedAttributeValue = EGL_NONE; } else { // if no renderable type specified, fallback to a simplified query wantedAttribute = EGL_NATIVE_VISUAL_ID; wantedAttributeValue = format; } err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config); if (err == NO_ERROR) { EGLint caveat; if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); } return err; } std::unique_ptr GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags, uint32_t imageCacheSize) { // initialize EGL for the default display EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (!eglInitialize(display, nullptr, nullptr)) { LOG_ALWAYS_FATAL("failed to initialize EGL"); } GLExtensions& extensions = GLExtensions::getInstance(); extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION), eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS)); // The code assumes that ES2 or later is available if this extension is // supported. EGLConfig config = EGL_NO_CONFIG; if (!extensions.hasNoConfigContext()) { config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); } bool useContextPriority = extensions.hasContextPriority() && (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT); EGLContext protectedContext = EGL_NO_CONTEXT; if ((featureFlags & RenderEngine::ENABLE_PROTECTED_CONTEXT) && extensions.hasProtectedContent()) { protectedContext = createEglContext(display, config, nullptr, useContextPriority, Protection::PROTECTED); ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); } EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority, Protection::UNPROTECTED); // if can't create a GL context, we can only abort. LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); EGLSurface dummy = EGL_NO_SURFACE; if (!extensions.hasSurfacelessContext()) { dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED); LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); } EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); EGLSurface protectedDummy = EGL_NO_SURFACE; if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { protectedDummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED); ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer"); } // now figure out what version of GL did we actually get GlesVersion version = parseGlesVersion(extensions.getVersion()); // initialize the renderer while GL is current std::unique_ptr engine; switch (version) { case GLES_VERSION_1_0: case GLES_VERSION_1_1: LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); break; case GLES_VERSION_2_0: case GLES_VERSION_3_0: engine = std::make_unique(featureFlags, display, config, ctxt, dummy, protectedContext, protectedDummy, imageCacheSize); break; } ALOGI("OpenGL ES informations:"); ALOGI("vendor : %s", extensions.getVendor()); ALOGI("renderer : %s", extensions.getRenderer()); ALOGI("version : %s", extensions.getVersion()); ALOGI("extensions: %s", extensions.getExtensions()); ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); return engine; } EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { status_t err; EGLConfig config; // First try to get an ES3 config err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config); if (err != NO_ERROR) { // If ES3 fails, try to get an ES2 config err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); if (err != NO_ERROR) { // If ES2 still doesn't work, probably because we're on the emulator. // try a simplified query ALOGW("no suitable EGLConfig found, trying a simpler query"); err = selectEGLConfig(display, format, 0, &config); if (err != NO_ERROR) { // this EGL is too lame for android LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); } } } if (logConfig) { // print some debugging info EGLint r, g, b, a; eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); ALOGI("EGL information:"); ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); } return config; } GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext, EGLSurface protectedDummy, uint32_t imageCacheSize) : renderengine::impl::RenderEngine(featureFlags), mEGLDisplay(display), mEGLConfig(config), mEGLContext(ctxt), mDummySurface(dummy), mProtectedEGLContext(protectedContext), mProtectedDummySurface(protectedDummy), mVpWidth(0), mVpHeight(0), mFramebufferImageCacheSize(imageCacheSize), mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glPixelStorei(GL_PACK_ALIGNMENT, 4); // Initialize protected EGL Context. if (mProtectedEGLContext != EGL_NO_CONTEXT) { EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface, mProtectedEGLContext); ALOGE_IF(!success, "can't make protected context current"); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glPixelStorei(GL_PACK_ALIGNMENT, 4); success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext); LOG_ALWAYS_FATAL_IF(!success, "can't make default context current"); } const uint16_t protTexData[] = {0}; glGenTextures(1, &mProtectedTexName); glBindTexture(GL_TEXTURE_2D, mProtectedTexName); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); // mColorBlindnessCorrection = M; if (mUseColorManagement) { const ColorSpace srgb(ColorSpace::sRGB()); const ColorSpace displayP3(ColorSpace::DisplayP3()); const ColorSpace bt2020(ColorSpace::BT2020()); // no chromatic adaptation needed since all color spaces use D65 for their white points. mSrgbToXyz = mat4(srgb.getRGBtoXYZ()); mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ()); mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ()); mXyzToSrgb = mat4(srgb.getXYZtoRGB()); mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); // Compute sRGB to Display P3 and BT2020 transform matrix. // NOTE: For now, we are limiting output wide color space support to // Display-P3 and BT2020 only. mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz; mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz; // Compute Display P3 to sRGB and BT2020 transform matrix. mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz; mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz; // Compute BT2020 to sRGB and Display P3 transform matrix mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz; mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz; } char value[PROPERTY_VALUE_MAX]; property_get("debug.egl.traceGpuCompletion", value, "0"); if (atoi(value)) { mTraceGpuCompletion = true; mFlushTracer = std::make_unique(this); } mImageManager = std::make_unique(this); mDrawingBuffer = createFramebuffer(); } GLESRenderEngine::~GLESRenderEngine() { // Destroy the image manager first. mImageManager = nullptr; std::lock_guard lock(mRenderingMutex); unbindFrameBuffer(mDrawingBuffer.get()); mDrawingBuffer = nullptr; while (!mFramebufferImageCache.empty()) { EGLImageKHR expired = mFramebufferImageCache.front().second; mFramebufferImageCache.pop_front(); eglDestroyImageKHR(mEGLDisplay, expired); DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEGLDisplay); } std::unique_ptr GLESRenderEngine::createFramebuffer() { return std::make_unique(*this); } std::unique_ptr GLESRenderEngine::createImage() { return std::make_unique(*this); } Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { return mDrawingBuffer.get(); } void GLESRenderEngine::primeCache() const { ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, mFeatureFlags & USE_COLOR_MANAGEMENT); } bool GLESRenderEngine::isCurrent() const { return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext(); } base::unique_fd GLESRenderEngine::flush() { ATRACE_CALL(); if (!GLExtensions::getInstance().hasNativeFenceSync()) { return base::unique_fd(); } EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); return base::unique_fd(); } // native fence fd will not be populated until flush() is done. glFlush(); // get the fence fd base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); eglDestroySyncKHR(mEGLDisplay, sync); if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); } // Only trace if we have a valid fence, as current usage falls back to // calling finish() if the fence fd is invalid. if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) { mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr)); } return fenceFd; } bool GLESRenderEngine::finish() { ATRACE_CALL(); if (!GLExtensions::getInstance().hasFenceSync()) { ALOGW("no synchronization support"); return false; } EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr); if (sync == EGL_NO_SYNC_KHR) { ALOGW("failed to create EGL fence sync: %#x", eglGetError()); return false; } if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) { mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr)); } return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR); } bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) { EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/); EGLint error = eglGetError(); eglDestroySyncKHR(mEGLDisplay, sync); if (result != EGL_CONDITION_SATISFIED_KHR) { if (result == EGL_TIMEOUT_EXPIRED_KHR) { ALOGW("fence wait timed out"); } else { ALOGW("error waiting on EGL fence: %#x", error); } return false; } return true; } bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) { if (!GLExtensions::getInstance().hasNativeFenceSync() || !GLExtensions::getInstance().hasWaitSync()) { return false; } // release the fd and transfer the ownership to EGLSync EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE}; EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (sync == EGL_NO_SYNC_KHR) { ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); return false; } // XXX: The spec draft is inconsistent as to whether this should return an // EGLint or void. Ignore the return value for now, as it's not strictly // needed. eglWaitSyncKHR(mEGLDisplay, sync, 0); EGLint error = eglGetError(); eglDestroySyncKHR(mEGLDisplay, sync); if (error != EGL_SUCCESS) { ALOGE("failed to wait for EGL native fence sync: %#x", error); return false; } return true; } void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) { ATRACE_CALL(); glDisable(GL_BLEND); glClearColor(red, green, blue, alpha); glClear(GL_COLOR_BUFFER_BIT); } void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha) { size_t c; Rect const* r = region.getArray(&c); Mesh mesh(Mesh::TRIANGLES, c * 6, 2); Mesh::VertexArray position(mesh.getPositionArray()); for (size_t i = 0; i < c; i++, r++) { position[i * 6 + 0].x = r->left; position[i * 6 + 0].y = r->top; position[i * 6 + 1].x = r->left; position[i * 6 + 1].y = r->bottom; position[i * 6 + 2].x = r->right; position[i * 6 + 2].y = r->bottom; position[i * 6 + 3].x = r->left; position[i * 6 + 3].y = r->top; position[i * 6 + 4].x = r->right; position[i * 6 + 4].y = r->bottom; position[i * 6 + 5].x = r->right; position[i * 6 + 5].y = r->top; } setupFillWithColor(red, green, blue, alpha); drawMesh(mesh); } void GLESRenderEngine::setScissor(const Rect& region) { glScissor(region.left, region.top, region.getWidth(), region.getHeight()); glEnable(GL_SCISSOR_TEST); } void GLESRenderEngine::disableScissor() { glDisable(GL_SCISSOR_TEST); } void GLESRenderEngine::genTextures(size_t count, uint32_t* names) { glGenTextures(count, names); } void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) { glDeleteTextures(count, names); } void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) { ATRACE_CALL(); const GLImage& glImage = static_cast(image); const GLenum target = GL_TEXTURE_EXTERNAL_OES; glBindTexture(target, texName); if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) { glEGLImageTargetTexture2DOES(target, static_cast(glImage.getEGLImage())); } } status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp& buffer, const sp& bufferFence) { if (buffer == nullptr) { return BAD_VALUE; } ATRACE_CALL(); bool found = false; { std::lock_guard lock(mRenderingMutex); auto cachedImage = mImageCache.find(buffer->getId()); found = (cachedImage != mImageCache.end()); } // If we couldn't find the image in the cache at this time, then either // SurfaceFlinger messed up registering the buffer ahead of time or we got // backed up creating other EGLImages. if (!found) { status_t cacheResult = mImageManager->cache(buffer); if (cacheResult != NO_ERROR) { return cacheResult; } } // Whether or not we needed to cache, re-check mImageCache to make sure that // there's an EGLImage. The current threading model guarantees that we don't // destroy a cached image until it's really not needed anymore (i.e. this // function should not be called), so the only possibility is that something // terrible went wrong and we should just bind something and move on. { std::lock_guard lock(mRenderingMutex); auto cachedImage = mImageCache.find(buffer->getId()); if (cachedImage == mImageCache.end()) { // We failed creating the image if we got here, so bail out. ALOGE("Failed to create an EGLImage when rendering"); bindExternalTextureImage(texName, *createImage()); return NO_INIT; } bindExternalTextureImage(texName, *cachedImage->second); } // Wait for the new buffer to be ready. if (bufferFence != nullptr && bufferFence->isValid()) { if (GLExtensions::getInstance().hasWaitSync()) { base::unique_fd fenceFd(bufferFence->dup()); if (fenceFd == -1) { ALOGE("error dup'ing fence fd: %d", errno); return -errno; } if (!waitFence(std::move(fenceFd))) { ALOGE("failed to wait on fence fd"); return UNKNOWN_ERROR; } } else { status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer"); if (err != NO_ERROR) { ALOGE("error waiting for fence: %d", err); return err; } } } return NO_ERROR; } void GLESRenderEngine::cacheExternalTextureBuffer(const sp& buffer) { mImageManager->cacheAsync(buffer, nullptr); } std::shared_ptr GLESRenderEngine::cacheExternalTextureBufferForTesting( const sp& buffer) { auto barrier = std::make_shared(); mImageManager->cacheAsync(buffer, barrier); return barrier; } status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp& buffer) { if (buffer == nullptr) { return BAD_VALUE; } { std::lock_guard lock(mRenderingMutex); if (mImageCache.count(buffer->getId()) > 0) { // If there's already an image then fail fast here. return NO_ERROR; } } ATRACE_CALL(); // Create the image without holding a lock so that we don't block anything. std::unique_ptr newImage = createImage(); bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), buffer->getUsage() & GRALLOC_USAGE_PROTECTED); if (!created) { ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), buffer->getPixelFormat()); return NO_INIT; } { std::lock_guard lock(mRenderingMutex); if (mImageCache.count(buffer->getId()) > 0) { // In theory it's possible for another thread to recache the image, // so bail out if another thread won. return NO_ERROR; } mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); } return NO_ERROR; } void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { mImageManager->releaseAsync(bufferId, nullptr); } std::shared_ptr GLESRenderEngine::unbindExternalTextureBufferForTesting( uint64_t bufferId) { auto barrier = std::make_shared(); mImageManager->releaseAsync(bufferId, barrier); return barrier; } void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) { std::unique_ptr image; { std::lock_guard lock(mRenderingMutex); const auto& cachedImage = mImageCache.find(bufferId); if (cachedImage != mImageCache.end()) { ALOGV("Destroying image for buffer: %" PRIu64, bufferId); // Move the buffer out of cache first, so that we can destroy // without holding the cache's lock. image = std::move(cachedImage->second); mImageCache.erase(bufferId); return; } } ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); } FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) { // Translate win by the rounded corners rect coordinates, to have all values in // layer coordinate space. FloatRect cropWin = layer.geometry.boundaries; const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop; cropWin.left -= roundedCornersCrop.left; cropWin.right -= roundedCornersCrop.left; cropWin.top -= roundedCornersCrop.top; cropWin.bottom -= roundedCornersCrop.top; Mesh::VertexArray cropCoords(mesh.getCropCoordArray()); cropCoords[0] = vec2(cropWin.left, cropWin.top); cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight()); cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight()); cropCoords[3] = vec2(cropWin.right, cropWin.top); setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight()); return cropWin; } void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer, const Mesh& mesh) { // We separate the layer into 3 parts essentially, such that we only turn on blending for the // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle. FloatRect bounds = layer.geometry.roundedCornersCrop; // Firstly, we need to convert the coordination from layer native coordination space to // device coordination space. const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform; const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0); const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0); const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate; const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate; bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]); // Secondly, if the display is rotated, we need to undo the rotation on coordination and // align the (left, top) and (right, bottom) coordination with the device coordination // space. switch (display.orientation) { case ui::Transform::ROT_90: std::swap(bounds.left, bounds.right); break; case ui::Transform::ROT_180: std::swap(bounds.left, bounds.right); std::swap(bounds.top, bounds.bottom); break; case ui::Transform::ROT_270: std::swap(bounds.top, bounds.bottom); break; default: break; } // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners // and the middle part without rounded corners. const int32_t radius = ceil(layer.geometry.roundedCornersRadius); const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius); setScissor(topRect); drawMesh(mesh); const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom); setScissor(bottomRect); drawMesh(mesh); // The middle part of the layer can turn off blending. const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius); setScissor(middleRect); mState.cornerRadius = 0.0; disableBlending(); drawMesh(mesh); disableScissor(); } status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { ATRACE_CALL(); GLFramebuffer* glFramebuffer = static_cast(framebuffer); EGLImageKHR eglImage = glFramebuffer->getEGLImage(); uint32_t textureName = glFramebuffer->getTextureName(); uint32_t framebufferName = glFramebuffer->getFramebufferName(); // Bind the texture and turn our EGLImage into a texture glBindTexture(GL_TEXTURE_2D, textureName); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage); // Bind the Framebuffer to render into glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", glStatus); return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; } void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) { ATRACE_CALL(); // back to main framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); } void GLESRenderEngine::checkErrors() const { do { // there could be more than one error flag GLenum error = glGetError(); if (error == GL_NO_ERROR) break; ALOGE("GL error 0x%04x", int(error)); } while (true); } bool GLESRenderEngine::supportsProtectedContent() const { return mProtectedEGLContext != EGL_NO_CONTEXT; } bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContext == mInProtectedContext) { return true; } if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) { return false; } const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface; const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; if (success) { mInProtectedContext = useProtectedContext; } return success; } EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, bool useFramebufferCache) { sp graphicBuffer = GraphicBuffer::from(nativeBuffer); if (useFramebufferCache) { std::lock_guard lock(mFramebufferImageCacheMutex); for (const auto& image : mFramebufferImageCache) { if (image.first == graphicBuffer->getId()) { return image.second; } } } EGLint attributes[] = { isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, isProtected ? EGL_TRUE : EGL_NONE, EGL_NONE, }; EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, nativeBuffer, attributes); if (useFramebufferCache) { if (image != EGL_NO_IMAGE_KHR) { std::lock_guard lock(mFramebufferImageCacheMutex); if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) { EGLImageKHR expired = mFramebufferImageCache.front().second; mFramebufferImageCache.pop_front(); eglDestroyImageKHR(mEGLDisplay, expired); DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mFramebufferImageCache.push_back({graphicBuffer->getId(), image}); } } if (image != EGL_NO_IMAGE_KHR) { DEBUG_EGL_IMAGE_TRACKER_CREATE(); } return image; } status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const std::vector& layers, ANativeWindowBuffer* const buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) { ATRACE_CALL(); if (layers.empty()) { ALOGV("Drawing empty layer stack"); return NO_ERROR; } if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) { ATRACE_NAME("Waiting before draw"); sync_wait(bufferFence.get(), -1); } if (buffer == nullptr) { ALOGE("No output buffer provided. Aborting GPU composition."); return BAD_VALUE; } BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); if (fbo.getStatus() != NO_ERROR) { ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->handle); checkErrors(); return fbo.getStatus(); } // clear the entire buffer, sometimes when we reuse buffers we'd persist // ghost images otherwise. // we also require a full transparent framebuffer for overlays. This is // probably not quite efficient on all GPUs, since we could filter out // opaque layers. clearWithColor(0.0, 0.0, 0.0, 0.0); setViewportAndProjection(display.physicalDisplay, display.clip); setOutputDataSpace(display.outputDataspace); setDisplayMaxLuminance(display.maxLuminance); mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; mState.projectionMatrix = projectionMatrix; if (!display.clearRegion.isEmpty()) { glDisable(GL_BLEND); fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); } Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); for (auto layer : layers) { mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; const FloatRect bounds = layer.geometry.boundaries; Mesh::VertexArray position(mesh.getPositionArray()); position[0] = vec2(bounds.left, bounds.top); position[1] = vec2(bounds.left, bounds.bottom); position[2] = vec2(bounds.right, bounds.bottom); position[3] = vec2(bounds.right, bounds.top); setupLayerCropping(layer, mesh); setColorTransform(display.colorTransform * layer.colorTransform); bool usePremultipliedAlpha = true; bool disableTexture = true; bool isOpaque = false; if (layer.source.buffer.buffer != nullptr) { disableTexture = false; isOpaque = layer.source.buffer.isOpaque; sp gBuf = layer.source.buffer.buffer; bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf, layer.source.buffer.fence); usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); mat4 texMatrix = layer.source.buffer.textureTransform; texture.setMatrix(texMatrix.asArray()); texture.setFiltering(layer.source.buffer.useTextureFiltering); texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); setSourceY410BT2020(layer.source.buffer.isY410BT2020); renderengine::Mesh::VertexArray texCoords(mesh.getTexCoordArray()); texCoords[0] = vec2(0.0, 0.0); texCoords[1] = vec2(0.0, 1.0); texCoords[2] = vec2(1.0, 1.0); texCoords[3] = vec2(1.0, 0.0); setupLayerTexturing(texture); } const half3 solidColor = layer.source.solidColor; const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); // Buffer sources will have a black solid color ignored in the shader, // so in that scenario the solid color passed here is arbitrary. setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, layer.geometry.roundedCornersRadius); if (layer.disableBlending) { glDisable(GL_BLEND); } setSourceDataSpace(layer.sourceDataspace); // We only want to do a special handling for rounded corners when having rounded corners // is the only reason it needs to turn on blending, otherwise, we handle it like the // usual way since it needs to turn on blending anyway. if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) { handleRoundedCorners(display, layer, mesh); } else { drawMesh(mesh); } // Cleanup if there's a buffer source if (layer.source.buffer.buffer != nullptr) { disableBlending(); setSourceY410BT2020(false); disableTexturing(); } } if (drawFence != nullptr) { *drawFence = flush(); } // If flush failed or we don't support native fences, we need to force the // gl command stream to be executed. if (drawFence == nullptr || drawFence->get() < 0) { bool success = finish(); if (!success) { ALOGE("Failed to flush RenderEngine commands"); checkErrors(); // Chances are, something illegal happened (either the caller passed // us bad parameters, or we messed up our shader generation). return INVALID_OPERATION; } } checkErrors(); return NO_ERROR; } void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, ui::Transform::orientation_flags rotation) { setViewportAndProjection(Rect(vpw, vph), sourceCrop); if (rotation == ui::Transform::ROT_0) { return; } // Apply custom rotation to the projection. float rot90InRadians = 2.0f * static_cast(M_PI) / 4.0f; mat4 m = mState.projectionMatrix; switch (rotation) { case ui::Transform::ROT_90: m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m; break; case ui::Transform::ROT_180: m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m; break; case ui::Transform::ROT_270: m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m; break; default: break; } mState.projectionMatrix = m; } void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { ATRACE_CALL(); mVpWidth = viewport.getWidth(); mVpHeight = viewport.getHeight(); // We pass the the top left corner instead of the bottom left corner, // because since we're rendering off-screen first. glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight); mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1); } void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, const half4& color, float cornerRadius) { mState.isPremultipliedAlpha = premultipliedAlpha; mState.isOpaque = opaque; mState.color = color; mState.cornerRadius = cornerRadius; if (disableTexture) { mState.textureEnabled = false; } if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) { glEnable(GL_BLEND); glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } } void GLESRenderEngine::setSourceY410BT2020(bool enable) { mState.isY410BT2020 = enable; } void GLESRenderEngine::setSourceDataSpace(Dataspace source) { mDataSpace = source; } void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) { mOutputDataSpace = dataspace; } void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) { mState.displayMaxLuminance = maxLuminance; } void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { GLuint target = texture.getTextureTarget(); glBindTexture(target, texture.getTextureName()); GLenum filter = GL_NEAREST; if (texture.getFiltering()) { filter = GL_LINEAR; } glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); mState.texture = texture; mState.textureEnabled = true; } void GLESRenderEngine::setupLayerBlackedOut() { glBindTexture(GL_TEXTURE_2D, mProtectedTexName); Texture texture(Texture::TEXTURE_2D, mProtectedTexName); texture.setDimensions(1, 1); // FIXME: we should get that from somewhere mState.texture = texture; mState.textureEnabled = true; } void GLESRenderEngine::setColorTransform(const mat4& colorTransform) { mState.colorMatrix = colorTransform; } void GLESRenderEngine::disableTexturing() { mState.textureEnabled = false; } void GLESRenderEngine::disableBlending() { glDisable(GL_BLEND); } void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) { mState.isPremultipliedAlpha = true; mState.isOpaque = false; mState.color = half4(r, g, b, a); mState.textureEnabled = false; glDisable(GL_BLEND); } void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) { mState.cropSize = half2(width, height); } void GLESRenderEngine::drawMesh(const Mesh& mesh) { ATRACE_CALL(); if (mesh.getTexCoordsSize()) { glEnableVertexAttribArray(Program::texCoords); glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, mesh.getByteStride(), mesh.getTexCoords()); } glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, mesh.getByteStride(), mesh.getPositions()); if (mState.cornerRadius > 0.0f) { glEnableVertexAttribArray(Program::cropCoords); glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, mesh.getByteStride(), mesh.getCropCoords()); } // By default, DISPLAY_P3 is the only supported wide color output. However, // when HDR content is present, hardware composer may be able to handle // BT2020 data space, in that case, the output data space is set to be // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need // to respect this and convert non-HDR content to HDR format. if (mUseColorManagement) { Description managedState = mState; Dataspace inputStandard = static_cast(mDataSpace & Dataspace::STANDARD_MASK); Dataspace inputTransfer = static_cast(mDataSpace & Dataspace::TRANSFER_MASK); Dataspace outputStandard = static_cast(mOutputDataSpace & Dataspace::STANDARD_MASK); Dataspace outputTransfer = static_cast(mOutputDataSpace & Dataspace::TRANSFER_MASK); bool needsXYZConversion = needsXYZTransformMatrix(); // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or // STANDARD_BT2020, it will be treated as STANDARD_BT709 if (inputStandard != Dataspace::STANDARD_DCI_P3 && inputStandard != Dataspace::STANDARD_BT2020) { inputStandard = Dataspace::STANDARD_BT709; } if (needsXYZConversion) { // The supported input color spaces are standard RGB, Display P3 and BT2020. switch (inputStandard) { case Dataspace::STANDARD_DCI_P3: managedState.inputTransformMatrix = mDisplayP3ToXyz; break; case Dataspace::STANDARD_BT2020: managedState.inputTransformMatrix = mBt2020ToXyz; break; default: managedState.inputTransformMatrix = mSrgbToXyz; break; } // The supported output color spaces are BT2020, Display P3 and standard RGB. switch (outputStandard) { case Dataspace::STANDARD_BT2020: managedState.outputTransformMatrix = mXyzToBt2020; break; case Dataspace::STANDARD_DCI_P3: managedState.outputTransformMatrix = mXyzToDisplayP3; break; default: managedState.outputTransformMatrix = mXyzToSrgb; break; } } else if (inputStandard != outputStandard) { // At this point, the input data space and output data space could be both // HDR data spaces, but they match each other, we do nothing in this case. // In addition to the case above, the input data space could be // - scRGB linear // - scRGB non-linear // - sRGB // - Display P3 // - BT2020 // The output data spaces could be // - sRGB // - Display P3 // - BT2020 switch (outputStandard) { case Dataspace::STANDARD_BT2020: if (inputStandard == Dataspace::STANDARD_BT709) { managedState.outputTransformMatrix = mSrgbToBt2020; } else if (inputStandard == Dataspace::STANDARD_DCI_P3) { managedState.outputTransformMatrix = mDisplayP3ToBt2020; } break; case Dataspace::STANDARD_DCI_P3: if (inputStandard == Dataspace::STANDARD_BT709) { managedState.outputTransformMatrix = mSrgbToDisplayP3; } else if (inputStandard == Dataspace::STANDARD_BT2020) { managedState.outputTransformMatrix = mBt2020ToDisplayP3; } break; default: if (inputStandard == Dataspace::STANDARD_DCI_P3) { managedState.outputTransformMatrix = mDisplayP3ToSrgb; } else if (inputStandard == Dataspace::STANDARD_BT2020) { managedState.outputTransformMatrix = mBt2020ToSrgb; } break; } } // we need to convert the RGB value to linear space and convert it back when: // - there is a color matrix that is not an identity matrix, or // - there is an output transform matrix that is not an identity matrix, or // - the input transfer function doesn't match the output transfer function. if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() || inputTransfer != outputTransfer) { managedState.inputTransferFunction = Description::dataSpaceToTransferFunction(inputTransfer); managedState.outputTransferFunction = Description::dataSpaceToTransferFunction(outputTransfer); } ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext, managedState); glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); if (outputDebugPPMs) { static uint64_t managedColorFrameCount = 0; std::ostringstream out; out << "/data/texture_out" << managedColorFrameCount++; writePPM(out.str().c_str(), mVpWidth, mVpHeight); } } else { ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext, mState); glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); } if (mesh.getTexCoordsSize()) { glDisableVertexAttribArray(Program::texCoords); } if (mState.cornerRadius > 0.0f) { glDisableVertexAttribArray(Program::cropCoords); } } size_t GLESRenderEngine::getMaxTextureSize() const { return mMaxTextureSize; } size_t GLESRenderEngine::getMaxViewportDims() const { return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; } void GLESRenderEngine::dump(std::string& result) { const GLExtensions& extensions = GLExtensions::getInstance(); ProgramCache& cache = ProgramCache::getInstance(); StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), extensions.getVersion()); StringAppendF(&result, "%s\n", extensions.getExtensions()); StringAppendF(&result, "RenderEngine supports protected context: %d\n", supportsProtectedContent()); StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n", cache.getSize(mEGLContext)); StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n", cache.getSize(mProtectedEGLContext)); StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", dataspaceDetails(static_cast(mDataSpace)).c_str(), dataspaceDetails(static_cast(mOutputDataSpace)).c_str()); { std::lock_guard lock(mRenderingMutex); StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size()); StringAppendF(&result, "Dumping buffer ids...\n"); for (const auto& [id, unused] : mImageCache) { StringAppendF(&result, "0x%" PRIx64 "\n", id); } } { std::lock_guard lock(mFramebufferImageCacheMutex); StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n", mFramebufferImageCache.size()); StringAppendF(&result, "Dumping buffer ids...\n"); for (const auto& [id, unused] : mFramebufferImageCache) { StringAppendF(&result, "0x%" PRIx64 "\n", id); } } } GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { int major, minor; if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); return GLES_VERSION_1_0; } } if (major == 1 && minor == 0) return GLES_VERSION_1_0; if (major == 1 && minor >= 1) return GLES_VERSION_1_1; if (major == 2 && minor >= 0) return GLES_VERSION_2_0; if (major == 3 && minor >= 0) return GLES_VERSION_3_0; ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); return GLES_VERSION_1_0; } EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, bool useContextPriority, Protection protection) { EGLint renderableType = 0; if (config == EGL_NO_CONFIG) { renderableType = EGL_OPENGL_ES3_BIT; } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); } EGLint contextClientVersion = 0; if (renderableType & EGL_OPENGL_ES3_BIT) { contextClientVersion = 3; } else if (renderableType & EGL_OPENGL_ES2_BIT) { contextClientVersion = 2; } else if (renderableType & EGL_OPENGL_ES_BIT) { contextClientVersion = 1; } else { LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); } std::vector contextAttributes; contextAttributes.reserve(7); contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); contextAttributes.push_back(contextClientVersion); if (useContextPriority) { contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); } if (protection == Protection::PROTECTED) { contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); contextAttributes.push_back(EGL_TRUE); } contextAttributes.push_back(EGL_NONE); EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus // EGL_NO_CONTEXT so that we can abort. if (config != EGL_NO_CONFIG) { return context; } // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should // try to fall back to GLES 2. contextAttributes[1] = 2; context = eglCreateContext(display, config, shareContext, contextAttributes.data()); } return context; } EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, int hwcFormat, Protection protection) { EGLConfig dummyConfig = config; if (dummyConfig == EGL_NO_CONFIG) { dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); } std::vector attributes; attributes.reserve(7); attributes.push_back(EGL_WIDTH); attributes.push_back(1); attributes.push_back(EGL_HEIGHT); attributes.push_back(1); if (protection == Protection::PROTECTED) { attributes.push_back(EGL_PROTECTED_CONTENT_EXT); attributes.push_back(EGL_TRUE); } attributes.push_back(EGL_NONE); return eglCreatePbufferSurface(display, dummyConfig, attributes.data()); } bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { const Dataspace standard = static_cast(dataSpace & Dataspace::STANDARD_MASK); const Dataspace transfer = static_cast(dataSpace & Dataspace::TRANSFER_MASK); return standard == Dataspace::STANDARD_BT2020 && (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); } // For convenience, we want to convert the input color space to XYZ color space first, // and then convert from XYZ color space to output color space when // - SDR and HDR contents are mixed, either SDR content will be converted to HDR or // HDR content will be tone-mapped to SDR; Or, // - there are HDR PQ and HLG contents presented at the same time, where we want to convert // HLG content to PQ content. // In either case above, we need to operate the Y value in XYZ color space. Thus, when either // input data space or output data space is HDR data space, and the input transfer function // doesn't match the output transfer function, we would enable an intermediate transfrom to // XYZ color space. bool GLESRenderEngine::needsXYZTransformMatrix() const { const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); const Dataspace inputTransfer = static_cast(mDataSpace & Dataspace::TRANSFER_MASK); const Dataspace outputTransfer = static_cast(mOutputDataSpace & Dataspace::TRANSFER_MASK); return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; } bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { std::lock_guard lock(mRenderingMutex); const auto& cachedImage = mImageCache.find(bufferId); return cachedImage != mImageCache.end(); } bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { std::lock_guard lock(mFramebufferImageCacheMutex); return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), [=](std::pair image) { return image.first == bufferId; }); } // FlushTracer implementation GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) { mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this); } GLESRenderEngine::FlushTracer::~FlushTracer() { { std::lock_guard lock(mMutex); mRunning = false; } mCondition.notify_all(); if (mThread.joinable()) { mThread.join(); } } void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) { std::lock_guard lock(mMutex); char name[64]; const uint64_t frameNum = mFramesQueued++; snprintf(name, sizeof(name), "Queueing sync for frame: %lu", static_cast(frameNum)); ATRACE_NAME(name); mQueue.push({sync, frameNum}); ATRACE_INT("GPU Frames Outstanding", mQueue.size()); mCondition.notify_one(); } void GLESRenderEngine::FlushTracer::loop() { while (mRunning) { QueueEntry entry; { std::lock_guard lock(mMutex); mCondition.wait(mMutex, [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); if (!mRunning) { // if mRunning is false, then FlushTracer is being destroyed, so // bail out now. break; } entry = mQueue.front(); mQueue.pop(); } { char name[64]; snprintf(name, sizeof(name), "waiting for frame %lu", static_cast(entry.mFrameNum)); ATRACE_NAME(name); mEngine->waitSync(entry.mSync, 0); } } } } // namespace gl } // namespace renderengine } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLESRenderEngine.h�������������������������������������������������������������0100644 0000000 0000000 00000026261 13756501735 017021� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_GLESRENDERENGINE_H_ #define SF_GLESRENDERENGINE_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ImageManager.h" #define EGL_NO_CONFIG ((EGLConfig)0) namespace android { namespace renderengine { class Mesh; class Texture; namespace gl { class GLImage; class GLESRenderEngine : public impl::RenderEngine { public: static std::unique_ptr create(int hwcFormat, uint32_t featureFlags, uint32_t imageCacheSize); static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext, EGLSurface protectedDummy, uint32_t imageCacheSize); ~GLESRenderEngine() override EXCLUDES(mRenderingMutex); std::unique_ptr createFramebuffer() override; std::unique_ptr createImage() override; void primeCache() const override; bool isCurrent() const override; base::unique_fd flush() override; bool finish() override; bool waitFence(base::unique_fd fenceFd) override; void clearWithColor(float red, float green, float blue, float alpha) override; void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha) override; void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; void bindExternalTextureImage(uint32_t texName, const Image& image) override; status_t bindExternalTextureBuffer(uint32_t texName, const sp& buffer, const sp& fence) EXCLUDES(mRenderingMutex); void cacheExternalTextureBuffer(const sp& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); status_t bindFrameBuffer(Framebuffer* framebuffer) override; void unbindFrameBuffer(Framebuffer* framebuffer) override; void checkErrors() const override; bool isProtected() const override { return mInProtectedContext; } bool supportsProtectedContent() const override; bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector& layers, ANativeWindowBuffer* buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; // internal to RenderEngine EGLDisplay getEGLDisplay() const { return mEGLDisplay; } EGLConfig getEGLConfig() const { return mEGLConfig; } // Creates an output image for rendering to EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, bool useFramebufferCache) EXCLUDES(mFramebufferImageCacheMutex); // Test-only methods // Returns true iff mImageCache contains an image keyed by bufferId bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); // Returns true iff mFramebufferImageCache contains an image keyed by bufferId bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mFramebufferImageCacheMutex); // These are wrappers around public methods above, but exposing Barrier // objects so that tests can block. std::shared_ptr cacheExternalTextureBufferForTesting( const sp& buffer); std::shared_ptr unbindExternalTextureBufferForTesting(uint64_t bufferId); protected: Framebuffer* getFramebufferForDrawing() override; void dump(std::string& result) override EXCLUDES(mRenderingMutex) EXCLUDES(mFramebufferImageCacheMutex); void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, ui::Transform::orientation_flags rotation) override; void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, const half4& color, float cornerRadius) override; void setupLayerTexturing(const Texture& texture) override; void setupLayerBlackedOut() override; void setupFillWithColor(float r, float g, float b, float a) override; void setColorTransform(const mat4& colorTransform) override; void disableTexturing() override; void disableBlending() override; void setupCornerRadiusCropSize(float width, float height) override; // HDR and color management related functions and state void setSourceY410BT2020(bool enable) override; void setSourceDataSpace(ui::Dataspace source) override; void setOutputDataSpace(ui::Dataspace dataspace) override; void setDisplayMaxLuminance(const float maxLuminance) override; // drawing void drawMesh(const Mesh& mesh) override; size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; private: enum GlesVersion { GLES_VERSION_1_0 = 0x10000, GLES_VERSION_1_1 = 0x10001, GLES_VERSION_2_0 = 0x20000, GLES_VERSION_3_0 = 0x30000, }; static GlesVersion parseGlesVersion(const char* str); static EGLContext createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, bool useContextPriority, Protection protection); static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, int hwcFormat, Protection protection); void setScissor(const Rect& region); void disableScissor(); bool waitSync(EGLSyncKHR sync, EGLint flags); status_t cacheExternalTextureBufferInternal(const sp& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex); // A data space is considered HDR data space if it has BT2020 color space // with PQ or HLG transfer function. bool isHdrDataSpace(const ui::Dataspace dataSpace) const; bool needsXYZTransformMatrix() const; // Defines the viewport, and sets the projection matrix to the projection // defined by the clip. void setViewportAndProjection(Rect viewport, Rect clip); // Evicts stale images from the buffer cache. void evictImages(const std::vector& layers); // Computes the cropping window for the layer and sets up cropping // coordinates for the mesh. FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh); // We do a special handling for rounded corners when it's possible to turn off blending // for the majority of the layer. The rounded corners needs to turn on blending such that // we can set the alpha value correctly, however, only the corners need this, and since // blending is an expensive operation, we want to turn off blending when it's not necessary. void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer, const Mesh& mesh); EGLDisplay mEGLDisplay; EGLConfig mEGLConfig; EGLContext mEGLContext; EGLSurface mDummySurface; EGLContext mProtectedEGLContext; EGLSurface mProtectedDummySurface; GLuint mProtectedTexName; GLint mMaxViewportDims[2]; GLint mMaxTextureSize; GLuint mVpWidth; GLuint mVpHeight; Description mState; mat4 mSrgbToXyz; mat4 mDisplayP3ToXyz; mat4 mBt2020ToXyz; mat4 mXyzToSrgb; mat4 mXyzToDisplayP3; mat4 mXyzToBt2020; mat4 mSrgbToDisplayP3; mat4 mSrgbToBt2020; mat4 mDisplayP3ToSrgb; mat4 mDisplayP3ToBt2020; mat4 mBt2020ToSrgb; mat4 mBt2020ToDisplayP3; bool mInProtectedContext = false; // If set to true, then enables tracing flush() and finish() to systrace. bool mTraceGpuCompletion = false; // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately) // the last recently used buffer should be kicked out. uint32_t mFramebufferImageCacheSize = 0; // Cache of output images, keyed by corresponding GraphicBuffer ID. std::deque> mFramebufferImageCache GUARDED_BY(mFramebufferImageCacheMutex); // The only reason why we have this mutex is so that we don't segfault when // dumping info. std::mutex mFramebufferImageCacheMutex; // Current dataspace of layer being rendered ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; // Current output dataspace of the render engine ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN; // Whether device supports color management, currently color management // supports sRGB, DisplayP3 color spaces. const bool mUseColorManagement = false; // Cache of GL images that we'll store per GraphicBuffer ID std::unordered_map> mImageCache GUARDED_BY(mRenderingMutex); // Mutex guarding rendering operations, so that: // 1. GL operations aren't interleaved, and // 2. Internal state related to rendering that is potentially modified by // multiple threads is guaranteed thread-safe. std::mutex mRenderingMutex; std::unique_ptr mDrawingBuffer; class FlushTracer { public: FlushTracer(GLESRenderEngine* engine); ~FlushTracer(); void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex); struct QueueEntry { EGLSyncKHR mSync = nullptr; uint64_t mFrameNum = 0; }; private: void loop(); GLESRenderEngine* const mEngine; std::thread mThread; std::condition_variable_any mCondition; std::mutex mMutex; std::queue mQueue GUARDED_BY(mMutex); uint64_t mFramesQueued GUARDED_BY(mMutex) = 0; bool mRunning = true; }; friend class FlushTracer; friend class ImageManager; std::unique_ptr mFlushTracer; std::unique_ptr mImageManager = std::make_unique(this); }; } // namespace gl } // namespace renderengine } // namespace android #endif /* SF_GLESRENDERENGINE_H_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLExtensions.cpp���������������������������������������������������������������0100644 0000000 0000000 00000007707 13756501735 016722� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #include "GLExtensions.h" #include #include #include #include #include ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions) namespace android { namespace renderengine { namespace gl { namespace { class ExtensionSet { public: ExtensionSet(const char* extensions) { char const* curr = extensions; char const* head = curr; do { head = strchr(curr, ' '); size_t len = head ? head - curr : strlen(curr); if (len > 0) { mExtensions.emplace(curr, len); } curr = head + 1; } while (head); } bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; } private: std::unordered_set mExtensions; }; } // anonymous namespace void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, GLubyte const* extensions) { mVendor = (char const*)vendor; mRenderer = (char const*)renderer; mVersion = (char const*)version; mExtensions = (char const*)extensions; ExtensionSet extensionSet(mExtensions.c_str()); if (extensionSet.hasExtension("GL_EXT_protected_textures")) { mHasProtectedTexture = true; } } char const* GLExtensions::getVendor() const { return mVendor.string(); } char const* GLExtensions::getRenderer() const { return mRenderer.string(); } char const* GLExtensions::getVersion() const { return mVersion.string(); } char const* GLExtensions::getExtensions() const { return mExtensions.string(); } void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) { mEGLVersion = eglVersion; mEGLExtensions = eglExtensions; ExtensionSet extensionSet(eglExtensions); // EGL_ANDROIDX_no_config_context is an experimental extension with no // written specification. It will be replaced by something more formal. // SurfaceFlinger is using it to allow a single EGLContext to render to // both a 16-bit primary display framebuffer and a 32-bit virtual display // framebuffer. // // EGL_KHR_no_config_context is official extension to allow creating a // context that works with any surface of a display. if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") || extensionSet.hasExtension("EGL_KHR_no_config_context")) { mHasNoConfigContext = true; } if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) { mHasNativeFenceSync = true; } if (extensionSet.hasExtension("EGL_KHR_fence_sync")) { mHasFenceSync = true; } if (extensionSet.hasExtension("EGL_KHR_wait_sync")) { mHasWaitSync = true; } if (extensionSet.hasExtension("EGL_EXT_protected_content")) { mHasProtectedContent = true; } if (extensionSet.hasExtension("EGL_IMG_context_priority")) { mHasContextPriority = true; } if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) { mHasSurfacelessContext = true; } } char const* GLExtensions::getEGLVersion() const { return mEGLVersion.string(); } char const* GLExtensions::getEGLExtensions() const { return mEGLExtensions.string(); } } // namespace gl } // namespace renderengine } // namespace android ���������������������������������������������������������libs/renderengine/gl/GLExtensions.h�����������������������������������������������������������������0100644 0000000 0000000 00000005251 13756501735 016357� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_SF_GLEXTENSION_H #define ANDROID_SF_GLEXTENSION_H #include #include #include #include #include #include #include #include namespace android { namespace renderengine { namespace gl { class GLExtensions : public Singleton { public: bool hasNoConfigContext() const { return mHasNoConfigContext; } bool hasNativeFenceSync() const { return mHasNativeFenceSync; } bool hasFenceSync() const { return mHasFenceSync; } bool hasWaitSync() const { return mHasWaitSync; } bool hasProtectedContent() const { return mHasProtectedContent; } bool hasContextPriority() const { return mHasContextPriority; } bool hasSurfacelessContext() const { return mHasSurfacelessContext; } bool hasProtectedTexture() const { return mHasProtectedTexture; } void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, GLubyte const* extensions); char const* getVendor() const; char const* getRenderer() const; char const* getVersion() const; char const* getExtensions() const; void initWithEGLStrings(char const* eglVersion, char const* eglExtensions); char const* getEGLVersion() const; char const* getEGLExtensions() const; protected: GLExtensions() = default; private: friend class Singleton; bool mHasNoConfigContext = false; bool mHasNativeFenceSync = false; bool mHasFenceSync = false; bool mHasWaitSync = false; bool mHasProtectedContent = false; bool mHasContextPriority = false; bool mHasSurfacelessContext = false; bool mHasProtectedTexture = false; String8 mVendor; String8 mRenderer; String8 mVersion; String8 mExtensions; String8 mEGLVersion; String8 mEGLExtensions; GLExtensions(const GLExtensions&); GLExtensions& operator=(const GLExtensions&); }; } // namespace gl } // namespace renderengine } // namespace android #endif // ANDROID_SF_GLEXTENSION_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLFramebuffer.cpp��������������������������������������������������������������0100644 0000000 0000000 00000004414 13756501735 016777� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "GLFramebuffer.h" #include #include #include #include #include #include #include #include "GLESRenderEngine.h" namespace android { namespace renderengine { namespace gl { GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine) : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { glGenTextures(1, &mTextureName); glGenFramebuffers(1, &mFramebufferName); } GLFramebuffer::~GLFramebuffer() { glDeleteFramebuffers(1, &mFramebufferName); glDeleteTextures(1, &mTextureName); } bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, const bool useFramebufferCache) { ATRACE_CALL(); if (mEGLImage != EGL_NO_IMAGE_KHR) { if (!usingFramebufferCache) { eglDestroyImageKHR(mEGLDisplay, mEGLImage); DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mEGLImage = EGL_NO_IMAGE_KHR; mBufferWidth = 0; mBufferHeight = 0; } if (nativeBuffer) { mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected, useFramebufferCache); if (mEGLImage == EGL_NO_IMAGE_KHR) { return false; } usingFramebufferCache = useFramebufferCache; mBufferWidth = nativeBuffer->width; mBufferHeight = nativeBuffer->height; } return true; } } // namespace gl } // namespace renderengine } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLFramebuffer.h����������������������������������������������������������������0100644 0000000 0000000 00000003370 13756501735 016444� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include #include #include struct ANativeWindowBuffer; namespace android { namespace renderengine { namespace gl { class GLESRenderEngine; class GLFramebuffer : public renderengine::Framebuffer { public: explicit GLFramebuffer(GLESRenderEngine& engine); ~GLFramebuffer() override; bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, const bool useFramebufferCache) override; EGLImageKHR getEGLImage() const { return mEGLImage; } uint32_t getTextureName() const { return mTextureName; } uint32_t getFramebufferName() const { return mFramebufferName; } int32_t getBufferHeight() const { return mBufferHeight; } int32_t getBufferWidth() const { return mBufferWidth; } private: GLESRenderEngine& mEngine; EGLDisplay mEGLDisplay; EGLImageKHR mEGLImage; bool usingFramebufferCache = false; uint32_t mTextureName, mFramebufferName; int32_t mBufferHeight = 0; int32_t mBufferWidth = 0; }; } // namespace gl } // namespace renderengine } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLImage.cpp��������������������������������������������������������������������0100644 0000000 0000000 00000004612 13756501735 015575� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "GLImage.h" #include #include #include #include #include "GLESRenderEngine.h" #include "GLExtensions.h" namespace android { namespace renderengine { namespace gl { static std::vector buildAttributeList(bool isProtected) { std::vector attrs; attrs.reserve(16); attrs.push_back(EGL_IMAGE_PRESERVED_KHR); attrs.push_back(EGL_TRUE); if (isProtected && GLExtensions::getInstance().hasProtectedContent()) { attrs.push_back(EGL_PROTECTED_CONTENT_EXT); attrs.push_back(EGL_TRUE); } attrs.push_back(EGL_NONE); return attrs; } GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} GLImage::~GLImage() { setNativeWindowBuffer(nullptr, false); } bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) { ATRACE_CALL(); if (mEGLImage != EGL_NO_IMAGE_KHR) { if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { ALOGE("failed to destroy image: %#x", eglGetError()); } DEBUG_EGL_IMAGE_TRACKER_DESTROY(); mEGLImage = EGL_NO_IMAGE_KHR; } if (buffer) { std::vector attrs = buildAttributeList(isProtected); mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, static_cast(buffer), attrs.data()); if (mEGLImage == EGL_NO_IMAGE_KHR) { ALOGE("failed to create EGLImage: %#x", eglGetError()); return false; } DEBUG_EGL_IMAGE_TRACKER_CREATE(); mProtected = isProtected; } return true; } } // namespace gl } // namespace renderengine } // namespace android ����������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/GLImage.h����������������������������������������������������������������������0100644 0000000 0000000 00000002633 13756501735 015243� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include #include #include #include struct ANativeWindowBuffer; namespace android { namespace renderengine { namespace gl { class GLESRenderEngine; class GLImage : public renderengine::Image { public: explicit GLImage(const GLESRenderEngine& engine); ~GLImage() override; bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override; EGLImageKHR getEGLImage() const { return mEGLImage; } bool isProtected() const { return mProtected; } private: EGLDisplay mEGLDisplay; EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR; bool mProtected = false; DISALLOW_COPY_AND_ASSIGN(GLImage); }; } // namespace gl } // namespace renderengine } // namespace android �����������������������������������������������������������������������������������������������������libs/renderengine/gl/ImageManager.cpp���������������������������������������������������������������0100644 0000000 0000000 00000010447 13756501735 016650� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include "GLESRenderEngine.h" #include "ImageManager.h" namespace android { namespace renderengine { namespace gl { ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) { pthread_setname_np(mThread.native_handle(), "ImageManager"); // Use SCHED_FIFO to minimize jitter struct sched_param param = {0}; param.sched_priority = 2; if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) { ALOGE("Couldn't set SCHED_FIFO for ImageManager"); } } ImageManager::~ImageManager() { { std::lock_guard lock(mMutex); mRunning = false; } mCondition.notify_all(); if (mThread.joinable()) { mThread.join(); } } void ImageManager::cacheAsync(const sp& buffer, const std::shared_ptr& barrier) { if (buffer == nullptr) { { std::lock_guard lock(barrier->mutex); barrier->isOpen = true; barrier->result = BAD_VALUE; } barrier->condition.notify_one(); return; } ATRACE_CALL(); QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier}; queueOperation(std::move(entry)); } status_t ImageManager::cache(const sp& buffer) { ATRACE_CALL(); auto barrier = std::make_shared(); cacheAsync(buffer, barrier); std::lock_guard lock(barrier->mutex); barrier->condition.wait(barrier->mutex, [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; }); return barrier->result; } void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr& barrier) { ATRACE_CALL(); QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier}; queueOperation(std::move(entry)); } void ImageManager::queueOperation(const QueueEntry&& entry) { { std::lock_guard lock(mMutex); mQueue.emplace(entry); ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); } mCondition.notify_one(); } void ImageManager::threadMain() { set_sched_policy(0, SP_FOREGROUND); bool run; { std::lock_guard lock(mMutex); run = mRunning; } while (run) { QueueEntry entry; { std::lock_guard lock(mMutex); mCondition.wait(mMutex, [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); run = mRunning; if (!mRunning) { // if mRunning is false, then ImageManager is being destroyed, so // bail out now. break; } entry = mQueue.front(); mQueue.pop(); ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); } status_t result = NO_ERROR; switch (entry.op) { case QueueEntry::Operation::Delete: mEngine->unbindExternalTextureBufferInternal(entry.bufferId); break; case QueueEntry::Operation::Insert: result = mEngine->cacheExternalTextureBufferInternal(entry.buffer); break; } if (entry.barrier != nullptr) { { std::lock_guard entryLock(entry.barrier->mutex); entry.barrier->result = result; entry.barrier->isOpen = true; } entry.barrier->condition.notify_one(); } } } } // namespace gl } // namespace renderengine } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/ImageManager.h�����������������������������������������������������������������0100644 0000000 0000000 00000004043 13756501735 016310� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #pragma once #include #include #include #include #include namespace android { namespace renderengine { namespace gl { class GLESRenderEngine; class ImageManager { public: struct Barrier { std::mutex mutex; std::condition_variable_any condition; bool isOpen GUARDED_BY(mutex) = false; status_t result GUARDED_BY(mutex) = NO_ERROR; }; ImageManager(GLESRenderEngine* engine); ~ImageManager(); void cacheAsync(const sp& buffer, const std::shared_ptr& barrier) EXCLUDES(mMutex); status_t cache(const sp& buffer); void releaseAsync(uint64_t bufferId, const std::shared_ptr& barrier) EXCLUDES(mMutex); private: struct QueueEntry { enum class Operation { Delete, Insert }; Operation op = Operation::Delete; sp buffer = nullptr; uint64_t bufferId = 0; std::shared_ptr barrier = nullptr; }; void queueOperation(const QueueEntry&& entry); void threadMain(); GLESRenderEngine* const mEngine; std::thread mThread = std::thread([this]() { threadMain(); }); std::condition_variable_any mCondition; std::mutex mMutex; std::queue mQueue GUARDED_BY(mMutex); bool mRunning GUARDED_BY(mMutex) = true; }; } // namespace gl } // namespace renderengine } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/Program.cpp��������������������������������������������������������������������0100644 0000000 0000000 00000013127 13756501735 015740� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/*Gluint * Copyright 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. */ #include "Program.h" #include #include #include #include #include "ProgramCache.h" namespace android { namespace renderengine { namespace gl { Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment) : mInitialized(false) { GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER); GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER); GLuint programId = glCreateProgram(); glAttachShader(programId, vertexId); glAttachShader(programId, fragmentId); glBindAttribLocation(programId, position, "position"); glBindAttribLocation(programId, texCoords, "texCoords"); glBindAttribLocation(programId, cropCoords, "cropCoords"); glLinkProgram(programId); GLint status; glGetProgramiv(programId, GL_LINK_STATUS, &status); if (status != GL_TRUE) { ALOGE("Error while linking shaders:"); GLint infoLen = 0; glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { GLchar log[infoLen]; glGetProgramInfoLog(programId, infoLen, 0, &log[0]); ALOGE("%s", log); } glDetachShader(programId, vertexId); glDetachShader(programId, fragmentId); glDeleteShader(vertexId); glDeleteShader(fragmentId); glDeleteProgram(programId); } else { mProgram = programId; mVertexShader = vertexId; mFragmentShader = fragmentId; mInitialized = true; mProjectionMatrixLoc = glGetUniformLocation(programId, "projection"); mTextureMatrixLoc = glGetUniformLocation(programId, "texture"); mSamplerLoc = glGetUniformLocation(programId, "sampler"); mColorLoc = glGetUniformLocation(programId, "color"); mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance"); mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix"); mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix"); mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius"); mCropCenterLoc = glGetUniformLocation(programId, "cropCenter"); // set-up the default values for our uniforms glUseProgram(programId); glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray()); glEnableVertexAttribArray(0); } } bool Program::isValid() const { return mInitialized; } void Program::use() { glUseProgram(mProgram); } GLuint Program::getAttrib(const char* name) const { // TODO: maybe use a local cache return glGetAttribLocation(mProgram, name); } GLint Program::getUniform(const char* name) const { // TODO: maybe use a local cache return glGetUniformLocation(mProgram, name); } GLuint Program::buildShader(const char* source, GLenum type) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, 0); glCompileShader(shader); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { // Some drivers return wrong values for GL_INFO_LOG_LENGTH // use a fixed size instead GLchar log[512]; glGetShaderInfoLog(shader, sizeof(log), 0, log); ALOGE("Error while compiling shader: \n%s\n%s", source, log); glDeleteShader(shader); return 0; } return shader; } void Program::setUniforms(const Description& desc) { // TODO: we should have a mechanism here to not always reset uniforms that // didn't change for this program. if (mSamplerLoc >= 0) { glUniform1i(mSamplerLoc, 0); glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray()); } if (mColorLoc >= 0) { const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a}; glUniform4fv(mColorLoc, 1, color); } if (mInputTransformMatrixLoc >= 0) { mat4 inputTransformMatrix = desc.inputTransformMatrix; glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray()); } if (mOutputTransformMatrixLoc >= 0) { // The output transform matrix and color matrix can be combined as one matrix // that is applied right before applying OETF. mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix; glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray()); } if (mDisplayMaxLuminanceLoc >= 0) { glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance); } if (mCornerRadiusLoc >= 0) { glUniform1f(mCornerRadiusLoc, desc.cornerRadius); } if (mCropCenterLoc >= 0) { glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f); } // these uniforms are always present glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray()); } } // namespace gl } // namespace renderengine } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/Program.h����������������������������������������������������������������������0100644 0000000 0000000 00000005462 13756501735 015410� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_RENDER_ENGINE_PROGRAM_H #define SF_RENDER_ENGINE_PROGRAM_H #include #include #include #include "ProgramCache.h" namespace android { class String8; namespace renderengine { namespace gl { /* * Abstracts a GLSL program comprising a vertex and fragment shader */ class Program { public: // known locations for position and texture coordinates enum { /* position of each vertex for vertex shader */ position = 0, /* UV coordinates for texture mapping */ texCoords = 1, /* Crop coordinates, in pixels */ cropCoords = 2 }; Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); ~Program() = default; /* whether this object is usable */ bool isValid() const; /* Binds this program to the GLES context */ void use(); /* Returns the location of the specified attribute */ GLuint getAttrib(const char* name) const; /* Returns the location of the specified uniform */ GLint getUniform(const char* name) const; /* set-up uniforms from the description */ void setUniforms(const Description& desc); private: GLuint buildShader(const char* source, GLenum type); // whether the initialization succeeded bool mInitialized; // Name of the OpenGL program and shaders GLuint mProgram; GLuint mVertexShader; GLuint mFragmentShader; /* location of the projection matrix uniform */ GLint mProjectionMatrixLoc; /* location of the texture matrix uniform */ GLint mTextureMatrixLoc; /* location of the sampler uniform */ GLint mSamplerLoc; /* location of the color uniform */ GLint mColorLoc; /* location of display luminance uniform */ GLint mDisplayMaxLuminanceLoc; /* location of transform matrix */ GLint mInputTransformMatrixLoc; GLint mOutputTransformMatrixLoc; /* location of corner radius uniform */ GLint mCornerRadiusLoc; /* location of surface crop origin uniform, for rounded corner clipping */ GLint mCropCenterLoc; }; } // namespace gl } // namespace renderengine } // namespace android #endif /* SF_RENDER_ENGINE_PROGRAM_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/ProgramCache.cpp���������������������������������������������������������������0100644 0000000 0000000 00000071024 13756501735 016664� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "ProgramCache.h" #include #include #include #include #include #include #include "Program.h" ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache) namespace android { namespace renderengine { namespace gl { /* * A simple formatter class to automatically add the endl and * manage the indentation. */ class Formatter; static Formatter& indent(Formatter& f); static Formatter& dedent(Formatter& f); class Formatter { String8 mString; int mIndent; typedef Formatter& (*FormaterManipFunc)(Formatter&); friend Formatter& indent(Formatter& f); friend Formatter& dedent(Formatter& f); public: Formatter() : mIndent(0) {} String8 getString() const { return mString; } friend Formatter& operator<<(Formatter& out, const char* in) { for (int i = 0; i < out.mIndent; i++) { out.mString.append(" "); } out.mString.append(in); out.mString.append("\n"); return out; } friend inline Formatter& operator<<(Formatter& out, const String8& in) { return operator<<(out, in.string()); } friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) { return (*func)(to); } }; Formatter& indent(Formatter& f) { f.mIndent++; return f; } Formatter& dedent(Formatter& f) { f.mIndent--; return f; } void ProgramCache::primeCache(EGLContext context, bool useColorManagement) { auto& cache = mCaches[context]; uint32_t shaderCount = 0; uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK | Key::ROUNDED_CORNERS_MASK; // Prime the cache for all combinations of the above masks, // leaving off the experimental color matrix mask options. nsecs_t timeBefore = systemTime(); for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) { Key shaderKey; shaderKey.set(keyMask, keyVal); uint32_t tex = shaderKey.getTextureTarget(); if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) { continue; } if (cache.count(shaderKey) == 0) { cache.emplace(shaderKey, generateProgram(shaderKey)); shaderCount++; } } // Prime for sRGB->P3 conversion if (useColorManagement) { Key shaderKey; shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK, Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB | Key::OUTPUT_TF_SRGB); for (int i = 0; i < 16; i++) { shaderKey.set(Key::OPACITY_MASK, (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT); shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE); // Cache rounded corners shaderKey.set(Key::ROUNDED_CORNERS_MASK, (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); // Cache texture off option for window transition shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF); if (cache.count(shaderKey) == 0) { cache.emplace(shaderKey, generateProgram(shaderKey)); shaderCount++; } } } nsecs_t timeAfter = systemTime(); float compileTimeMs = static_cast(timeAfter - timeBefore) / 1.0E6; ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs); } ProgramCache::Key ProgramCache::computeKey(const Description& description) { Key needs; needs.set(Key::TEXTURE_MASK, !description.textureEnabled ? Key::TEXTURE_OFF : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT : description.texture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D : Key::TEXTURE_OFF) .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) .set(Key::BLEND_MASK, description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) .set(Key::OPACITY_MASK, description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK, description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF) .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK, description.hasOutputTransformMatrix() || description.hasColorMatrix() ? Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF) .set(Key::ROUNDED_CORNERS_MASK, description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); needs.set(Key::Y410_BT2020_MASK, description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); if (needs.hasTransformMatrix() || (description.inputTransferFunction != description.outputTransferFunction)) { switch (description.inputTransferFunction) { case Description::TransferFunction::LINEAR: default: needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR); break; case Description::TransferFunction::SRGB: needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB); break; case Description::TransferFunction::ST2084: needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084); break; case Description::TransferFunction::HLG: needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG); break; } switch (description.outputTransferFunction) { case Description::TransferFunction::LINEAR: default: needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR); break; case Description::TransferFunction::SRGB: needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB); break; case Description::TransferFunction::ST2084: needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084); break; case Description::TransferFunction::HLG: needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG); break; } } return needs; } // Generate EOTF that converts signal values to relative display light, // both normalized to [0, 1]. void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) { switch (needs.getInputTF()) { case Key::INPUT_TF_SRGB: fs << R"__SHADER__( float EOTF_sRGB(float srgb) { return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); } vec3 EOTF_sRGB(const vec3 srgb) { return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); } vec3 EOTF(const vec3 srgb) { return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); } )__SHADER__"; break; case Key::INPUT_TF_ST2084: fs << R"__SHADER__( vec3 EOTF(const highp vec3 color) { const highp float m1 = (2610.0 / 4096.0) / 4.0; const highp float m2 = (2523.0 / 4096.0) * 128.0; const highp float c1 = (3424.0 / 4096.0); const highp float c2 = (2413.0 / 4096.0) * 32.0; const highp float c3 = (2392.0 / 4096.0) * 32.0; highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2)); tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); return pow(tmp, 1.0 / vec3(m1)); } )__SHADER__"; break; case Key::INPUT_TF_HLG: fs << R"__SHADER__( highp float EOTF_channel(const highp float channel) { const highp float a = 0.17883277; const highp float b = 0.28466892; const highp float c = 0.55991073; return channel <= 0.5 ? channel * channel / 3.0 : (exp((channel - c) / a) + b) / 12.0; } vec3 EOTF(const highp vec3 color) { return vec3(EOTF_channel(color.r), EOTF_channel(color.g), EOTF_channel(color.b)); } )__SHADER__"; break; default: fs << R"__SHADER__( vec3 EOTF(const vec3 linear) { return linear; } )__SHADER__"; break; } } void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { // Convert relative light to absolute light. switch (needs.getInputTF()) { case Key::INPUT_TF_ST2084: fs << R"__SHADER__( highp vec3 ScaleLuminance(highp vec3 color) { return color * 10000.0; } )__SHADER__"; break; case Key::INPUT_TF_HLG: fs << R"__SHADER__( highp vec3 ScaleLuminance(highp vec3 color) { // The formula is: // alpha * pow(Y, gamma - 1.0) * color + beta; // where alpha is 1000.0, gamma is 1.2, beta is 0.0. return color * 1000.0 * pow(color.y, 0.2); } )__SHADER__"; break; default: fs << R"__SHADER__( highp vec3 ScaleLuminance(highp vec3 color) { return color * displayMaxLuminance; } )__SHADER__"; break; } // Tone map absolute light to display luminance range. switch (needs.getInputTF()) { case Key::INPUT_TF_ST2084: case Key::INPUT_TF_HLG: switch (needs.getOutputTF()) { case Key::OUTPUT_TF_HLG: // Right now when mixed PQ and HLG contents are presented, // HLG content will always be converted to PQ. However, for // completeness, we simply clamp the value to [0.0, 1000.0]. fs << R"__SHADER__( highp vec3 ToneMap(highp vec3 color) { return clamp(color, 0.0, 1000.0); } )__SHADER__"; break; case Key::OUTPUT_TF_ST2084: fs << R"__SHADER__( highp vec3 ToneMap(highp vec3 color) { return color; } )__SHADER__"; break; default: fs << R"__SHADER__( highp vec3 ToneMap(highp vec3 color) { const float maxMasteringLumi = 1000.0; const float maxContentLumi = 1000.0; const float maxInLumi = min(maxMasteringLumi, maxContentLumi); float maxOutLumi = displayMaxLuminance; float nits = color.y; // clamp to max input luminance nits = clamp(nits, 0.0, maxInLumi); // scale [0.0, maxInLumi] to [0.0, maxOutLumi] if (maxInLumi <= maxOutLumi) { return color * (maxOutLumi / maxInLumi); } else { // three control points const float x0 = 10.0; const float y0 = 17.0; float x1 = maxOutLumi * 0.75; float y1 = x1; float x2 = x1 + (maxInLumi - x1) / 2.0; float y2 = y1 + (maxOutLumi - y1) * 0.75; // horizontal distances between the last three control points float h12 = x2 - x1; float h23 = maxInLumi - x2; // tangents at the last three control points float m1 = (y2 - y1) / h12; float m3 = (maxOutLumi - y2) / h23; float m2 = (m1 + m3) / 2.0; if (nits < x0) { // scale [0.0, x0] to [0.0, y0] linearly float slope = y0 / x0; return color * slope; } else if (nits < x1) { // scale [x0, x1] to [y0, y1] linearly float slope = (y1 - y0) / (x1 - x0); nits = y0 + (nits - x0) * slope; } else if (nits < x2) { // scale [x1, x2] to [y1, y2] using Hermite interp float t = (nits - x1) / h12; nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; } else { // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp float t = (nits - x2) / h23; nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t; } } // color.y is greater than x0 and is thus non-zero return color * (nits / color.y); } )__SHADER__"; break; } break; default: // inverse tone map; the output luminance can be up to maxOutLumi. fs << R"__SHADER__( highp vec3 ToneMap(highp vec3 color) { const float maxOutLumi = 3000.0; const float x0 = 5.0; const float y0 = 2.5; float x1 = displayMaxLuminance * 0.7; float y1 = maxOutLumi * 0.15; float x2 = displayMaxLuminance * 0.9; float y2 = maxOutLumi * 0.45; float x3 = displayMaxLuminance; float y3 = maxOutLumi; float c1 = y1 / 3.0; float c2 = y2 / 2.0; float c3 = y3 / 1.5; float nits = color.y; float scale; if (nits <= x0) { // scale [0.0, x0] to [0.0, y0] linearly const float slope = y0 / x0; return color * slope; } else if (nits <= x1) { // scale [x0, x1] to [y0, y1] using a curve float t = (nits - x0) / (x1 - x0); nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1; } else if (nits <= x2) { // scale [x1, x2] to [y1, y2] using a curve float t = (nits - x1) / (x2 - x1); nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2; } else { // scale [x2, x3] to [y2, y3] using a curve float t = (nits - x2) / (x3 - x2); nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3; } // color.y is greater than x0 and is thus non-zero return color * (nits / color.y); } )__SHADER__"; break; } // convert absolute light to relative light. switch (needs.getOutputTF()) { case Key::OUTPUT_TF_ST2084: fs << R"__SHADER__( highp vec3 NormalizeLuminance(highp vec3 color) { return color / 10000.0; } )__SHADER__"; break; case Key::OUTPUT_TF_HLG: fs << R"__SHADER__( highp vec3 NormalizeLuminance(highp vec3 color) { return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2); } )__SHADER__"; break; default: fs << R"__SHADER__( highp vec3 NormalizeLuminance(highp vec3 color) { return color / displayMaxLuminance; } )__SHADER__"; break; } } // Generate OOTF that modifies the relative scence light to relative display light. void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) { if (!needs.needsToneMapping()) { fs << R"__SHADER__( highp vec3 OOTF(const highp vec3 color) { return color; } )__SHADER__"; } else { generateToneMappingProcess(fs, needs); fs << R"__SHADER__( highp vec3 OOTF(const highp vec3 color) { return NormalizeLuminance(ToneMap(ScaleLuminance(color))); } )__SHADER__"; } } // Generate OETF that converts relative display light to signal values, // both normalized to [0, 1] void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { switch (needs.getOutputTF()) { case Key::OUTPUT_TF_SRGB: fs << R"__SHADER__( float OETF_sRGB(const float linear) { return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; } vec3 OETF_sRGB(const vec3 linear) { return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); } vec3 OETF(const vec3 linear) { return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); } )__SHADER__"; break; case Key::OUTPUT_TF_ST2084: fs << R"__SHADER__( vec3 OETF(const vec3 linear) { const highp float m1 = (2610.0 / 4096.0) / 4.0; const highp float m2 = (2523.0 / 4096.0) * 128.0; const highp float c1 = (3424.0 / 4096.0); const highp float c2 = (2413.0 / 4096.0) * 32.0; const highp float c3 = (2392.0 / 4096.0) * 32.0; highp vec3 tmp = pow(linear, vec3(m1)); tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); return pow(tmp, vec3(m2)); } )__SHADER__"; break; case Key::OUTPUT_TF_HLG: fs << R"__SHADER__( highp float OETF_channel(const highp float channel) { const highp float a = 0.17883277; const highp float b = 0.28466892; const highp float c = 0.55991073; return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : a * log(12.0 * channel - b) + c; } vec3 OETF(const highp vec3 color) { return vec3(OETF_channel(color.r), OETF_channel(color.g), OETF_channel(color.b)); } )__SHADER__"; break; default: fs << R"__SHADER__( vec3 OETF(const vec3 linear) { return linear; } )__SHADER__"; break; } } String8 ProgramCache::generateVertexShader(const Key& needs) { Formatter vs; if (needs.isTexturing()) { vs << "attribute vec4 texCoords;" << "varying vec2 outTexCoords;"; } if (needs.hasRoundedCorners()) { vs << "attribute lowp vec4 cropCoords;"; vs << "varying lowp vec2 outCropCoords;"; } vs << "attribute vec4 position;" << "uniform mat4 projection;" << "uniform mat4 texture;" << "void main(void) {" << indent << "gl_Position = projection * position;"; if (needs.isTexturing()) { vs << "outTexCoords = (texture * texCoords).st;"; } if (needs.hasRoundedCorners()) { vs << "outCropCoords = cropCoords.st;"; } vs << dedent << "}"; return vs.getString(); } String8 ProgramCache::generateFragmentShader(const Key& needs) { Formatter fs; if (needs.getTextureTarget() == Key::TEXTURE_EXT) { fs << "#extension GL_OES_EGL_image_external : require"; } // default precision is required-ish in fragment shaders fs << "precision mediump float;"; if (needs.getTextureTarget() == Key::TEXTURE_EXT) { fs << "uniform samplerExternalOES sampler;" << "varying vec2 outTexCoords;"; } else if (needs.getTextureTarget() == Key::TEXTURE_2D) { fs << "uniform sampler2D sampler;" << "varying vec2 outTexCoords;"; } if (needs.hasRoundedCorners()) { // Rounded corners implementation using a signed distance function. fs << R"__SHADER__( uniform float cornerRadius; uniform vec2 cropCenter; varying vec2 outCropCoords; /** * This function takes the current crop coordinates and calculates an alpha value based * on the corner radius and distance from the crop center. */ float applyCornerRadius(vec2 cropCoords) { vec2 position = cropCoords - cropCenter; // Scale down the dist vector here, as otherwise large corner // radii can cause floating point issues when computing the norm vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0; // Once we've found the norm, then scale back up. float plane = length(max(dist, vec2(0.0))) * 16.0; return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0); } )__SHADER__"; } if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) { fs << "uniform vec4 color;"; } if (needs.isY410BT2020()) { fs << R"__SHADER__( vec3 convertY410BT2020(const vec3 color) { const vec3 offset = vec3(0.0625, 0.5, 0.5); const mat3 transform = mat3( vec3(1.1678, 1.1678, 1.1678), vec3( 0.0, -0.1878, 2.1481), vec3(1.6836, -0.6523, 0.0)); // Y is in G, U is in R, and V is in B return clamp(transform * (color.grb - offset), 0.0, 1.0); } )__SHADER__"; } if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { // Currently, display maximum luminance is needed when doing tone mapping. if (needs.needsToneMapping()) { fs << "uniform float displayMaxLuminance;"; } if (needs.hasInputTransformMatrix()) { fs << "uniform mat4 inputTransformMatrix;"; fs << R"__SHADER__( highp vec3 InputTransform(const highp vec3 color) { return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); } )__SHADER__"; } else { fs << R"__SHADER__( highp vec3 InputTransform(const highp vec3 color) { return color; } )__SHADER__"; } // the transformation from a wider colorspace to a narrower one can // result in >1.0 or <0.0 pixel values if (needs.hasOutputTransformMatrix()) { fs << "uniform mat4 outputTransformMatrix;"; fs << R"__SHADER__( highp vec3 OutputTransform(const highp vec3 color) { return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); } )__SHADER__"; } else { fs << R"__SHADER__( highp vec3 OutputTransform(const highp vec3 color) { return clamp(color, 0.0, 1.0); } )__SHADER__"; } generateEOTF(fs, needs); generateOOTF(fs, needs); generateOETF(fs, needs); } fs << "void main(void) {" << indent; if (needs.isTexturing()) { fs << "gl_FragColor = texture2D(sampler, outTexCoords);"; if (needs.isY410BT2020()) { fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);"; } } else { fs << "gl_FragColor.rgb = color.rgb;"; fs << "gl_FragColor.a = 1.0;"; } if (needs.isOpaque()) { fs << "gl_FragColor.a = 1.0;"; } if (needs.hasAlpha()) { // modulate the current alpha value with alpha set if (needs.isPremultiplied()) { // ... and the color too if we're premultiplied fs << "gl_FragColor *= color.a;"; } else { fs << "gl_FragColor.a *= color.a;"; } } if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { if (!needs.isOpaque() && needs.isPremultiplied()) { // un-premultiply if needed before linearization // avoid divide by 0 by adding 0.5/256 to the alpha channel fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);"; } fs << "gl_FragColor.rgb = " "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));"; if (!needs.isOpaque() && needs.isPremultiplied()) { // and re-premultiply if needed after gamma correction fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);"; } } if (needs.hasRoundedCorners()) { if (needs.isPremultiplied()) { fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));"; } else { fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);"; } } fs << dedent << "}"; return fs.getString(); } std::unique_ptr ProgramCache::generateProgram(const Key& needs) { ATRACE_CALL(); // vertex shader String8 vs = generateVertexShader(needs); // fragment shader String8 fs = generateFragmentShader(needs); return std::make_unique(needs, vs.string(), fs.string()); } void ProgramCache::useProgram(EGLContext context, const Description& description) { // generate the key for the shader based on the description Key needs(computeKey(description)); // look-up the program in the cache auto& cache = mCaches[context]; auto it = cache.find(needs); if (it == cache.end()) { // we didn't find our program, so generate one... nsecs_t time = systemTime(); it = cache.emplace(needs, generateProgram(needs)).first; time = systemTime() - time; ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)", context, needs.mKey, uint32_t(ns2ms(time)), cache.size()); } // here we have a suitable program for this description std::unique_ptr& program = it->second; if (program->isValid()) { program->use(); program->setUniforms(description); } } } // namespace gl } // namespace renderengine } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/gl/ProgramCache.h�����������������������������������������������������������������0100644 0000000 0000000 00000020075 13756501735 016331� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H #define SF_RENDER_ENGINE_PROGRAMCACHE_H #include #include #include #include #include #include #include namespace android { class String8; namespace renderengine { struct Description; namespace gl { class Formatter; class Program; /* * This class generates GLSL programs suitable to handle a given * Description. It's responsible for figuring out what to * generate from a Description. * It also maintains a cache of these Programs. */ class ProgramCache : public Singleton { public: /* * Key is used to retrieve a Program in the cache. * A Key is generated from a Description. */ class Key { friend class ProgramCache; typedef uint32_t key_t; key_t mKey; public: enum { BLEND_SHIFT = 0, BLEND_MASK = 1 << BLEND_SHIFT, BLEND_PREMULT = 1 << BLEND_SHIFT, BLEND_NORMAL = 0 << BLEND_SHIFT, OPACITY_SHIFT = 1, OPACITY_MASK = 1 << OPACITY_SHIFT, OPACITY_OPAQUE = 1 << OPACITY_SHIFT, OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT, ALPHA_SHIFT = 2, ALPHA_MASK = 1 << ALPHA_SHIFT, ALPHA_LT_ONE = 1 << ALPHA_SHIFT, ALPHA_EQ_ONE = 0 << ALPHA_SHIFT, TEXTURE_SHIFT = 3, TEXTURE_MASK = 3 << TEXTURE_SHIFT, TEXTURE_OFF = 0 << TEXTURE_SHIFT, TEXTURE_EXT = 1 << TEXTURE_SHIFT, TEXTURE_2D = 2 << TEXTURE_SHIFT, ROUNDED_CORNERS_SHIFT = 5, ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT, ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT, ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT, INPUT_TRANSFORM_MATRIX_SHIFT = 6, INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT, INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, OUTPUT_TRANSFORM_MATRIX_SHIFT = 7, OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT, OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, INPUT_TF_SHIFT = 8, INPUT_TF_MASK = 3 << INPUT_TF_SHIFT, INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT, INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT, INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT, INPUT_TF_HLG = 3 << INPUT_TF_SHIFT, OUTPUT_TF_SHIFT = 10, OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT, OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT, OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT, OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT, OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT, Y410_BT2020_SHIFT = 12, Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT, Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT, Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT, }; inline Key() : mKey(0) {} inline Key(const Key& rhs) : mKey(rhs.mKey) {} inline Key& set(key_t mask, key_t value) { mKey = (mKey & ~mask) | value; return *this; } inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; } inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); } inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; } inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; } inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; } inline bool hasRoundedCorners() const { return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON; } inline bool hasInputTransformMatrix() const { return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON; } inline bool hasOutputTransformMatrix() const { return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON; } inline bool hasTransformMatrix() const { return hasInputTransformMatrix() || hasOutputTransformMatrix(); } inline int getInputTF() const { return (mKey & INPUT_TF_MASK); } inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); } // When HDR and non-HDR contents are mixed, or different types of HDR contents are // mixed, we will do a tone mapping process to tone map the input content to output // content. Currently, the following conversions handled, they are: // * SDR -> HLG // * SDR -> PQ // * HLG -> PQ inline bool needsToneMapping() const { int inputTF = getInputTF(); int outputTF = getOutputTF(); // Return false when converting from SDR to SDR. if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) { return false; } if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) { return false; } inputTF >>= Key::INPUT_TF_SHIFT; outputTF >>= Key::OUTPUT_TF_SHIFT; return inputTF != outputTF; } inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; } // for use by std::unordered_map bool operator==(const Key& other) const { return mKey == other.mKey; } struct Hash { size_t operator()(const Key& key) const { return static_cast(key.mKey); } }; }; ProgramCache() = default; ~ProgramCache() = default; // Generate shaders to populate the cache void primeCache(const EGLContext context, bool useColorManagement); size_t getSize(const EGLContext context) { return mCaches[context].size(); } // useProgram lookup a suitable program in the cache or generates one // if none can be found. void useProgram(const EGLContext context, const Description& description); private: // compute a cache Key from a Description static Key computeKey(const Description& description); // Generate EOTF based from Key. static void generateEOTF(Formatter& fs, const Key& needs); // Generate necessary tone mapping methods for OOTF. static void generateToneMappingProcess(Formatter& fs, const Key& needs); // Generate OOTF based from Key. static void generateOOTF(Formatter& fs, const Key& needs); // Generate OETF based from Key. static void generateOETF(Formatter& fs, const Key& needs); // generates a program from the Key static std::unique_ptr generateProgram(const Key& needs); // generates the vertex shader from the Key static String8 generateVertexShader(const Key& needs); // generates the fragment shader from the Key static String8 generateFragmentShader(const Key& needs); // Key/Value map used for caching Programs. Currently the cache // is never shrunk (and the GL program objects are never deleted). std::unordered_map, Key::Hash>> mCaches; }; } // namespace gl } // namespace renderengine ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key) } // namespace android #endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/��������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014645� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/�������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 017312� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/DisplaySettings.h��������������������������������������������0100644 0000000 0000000 00000004432 13756501735 022611� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include #include #include #include namespace android { namespace renderengine { // DisplaySettings contains the settings that are applicable when drawing all // layers for a given display. struct DisplaySettings { // Rectangle describing the physical display. We will project from the // logical clip onto this rectangle. Rect physicalDisplay = Rect::INVALID_RECT; // Rectangle bounded by the x,y- clipping planes in the logical display, so // that the orthographic projection matrix can be computed. When // constructing this matrix, z-coordinate bound are assumed to be at z=0 and // z=1. Rect clip = Rect::INVALID_RECT; // Global transform to apply to all layers. mat4 globalTransform = mat4(); // Maximum luminance pulled from the display's HDR capabilities. float maxLuminance = 1.0f; // Output dataspace that will be populated if wide color gamut is used, or // DataSpace::UNKNOWN otherwise. ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; // Additional color transform to apply in linear space after transforming // to the output dataspace. mat4 colorTransform = mat4(); // Region that will be cleared to (0, 0, 0, 1) prior to rendering. // RenderEngine will transform the clearRegion passed in here, by // globalTransform, so that it will be in the same coordinate space as the // rendered layers. Region clearRegion = Region::INVALID_REGION; // The orientation of the physical display. uint32_t orientation = ui::Transform::ROT_0; }; } // namespace renderengine } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/Framebuffer.h������������������������������������������������0100644 0000000 0000000 00000001770 13756501735 021711� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include struct ANativeWindowBuffer; namespace android { namespace renderengine { class Framebuffer { public: virtual ~Framebuffer() = default; virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, const bool useFramebufferCache) = 0; }; } // namespace renderengine } // namespace android ��������libs/renderengine/include/renderengine/Image.h������������������������������������������������������0100644 0000000 0000000 00000001612 13756501735 020502� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 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. */ #pragma once struct ANativeWindowBuffer; namespace android { namespace renderengine { class Image { public: virtual ~Image() = default; virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0; }; } // namespace renderengine } // namespace android ����������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/LayerSettings.h����������������������������������������������0100644 0000000 0000000 00000010154 13756501735 022256� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace android { namespace renderengine { // Metadata describing the input buffer to render from. struct Buffer { // Buffer containing the image that we will render. // If buffer == nullptr, then the rest of the fields in this struct will be // ignored. sp buffer = nullptr; // Fence that will fire when the buffer is ready to be bound. sp fence = nullptr; // Texture identifier to bind the external texture to. // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. uint32_t textureName = 0; // Whether to use filtering when rendering the texture. bool useTextureFiltering = false; // Transform matrix to apply to texture coordinates. mat4 textureTransform = mat4(); // Wheteher to use pre-multiplied alpha bool usePremultipliedAlpha = true; // Override flag that alpha for each pixel in the buffer *must* be 1.0. // LayerSettings::alpha is still used if isOpaque==true - this flag only // overrides the alpha channel of the buffer. bool isOpaque = false; // HDR color-space setting for Y410. bool isY410BT2020 = false; }; // Metadata describing the layer geometry. struct Geometry { // Boundaries of the layer. FloatRect boundaries = FloatRect(); // Transform matrix to apply to mesh coordinates. mat4 positionTransform = mat4(); // Radius of rounded corners, if greater than 0. Otherwise, this layer's // corners are not rounded. // Having corner radius will force GPU composition on the layer and its children, drawing it // with a special shader. The shader will receive the radius and the crop rectangle as input, // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1. // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be // in local layer coordinate space, so we have to take the layer transform into account when // walking up the tree. float roundedCornersRadius = 0.0; // Rectangle within which corners will be rounded. FloatRect roundedCornersCrop = FloatRect(); }; // Descriptor of the source pixels for this layer. struct PixelSource { // Source buffer Buffer buffer = Buffer(); // The solid color with which to fill the layer. // This should only be populated if we don't render from an application // buffer. half3 solidColor = half3(0.0f, 0.0f, 0.0f); }; // The settings that RenderEngine requires for correctly rendering a Layer. struct LayerSettings { // Geometry information Geometry geometry = Geometry(); // Source pixels for this layer. PixelSource source = PixelSource(); // Alpha option to blend with the source pixels half alpha = half(0.0); // Color space describing how the source pixels should be interpreted. ui::Dataspace sourceDataspace = ui::Dataspace::UNKNOWN; // Additional layer-specific color transform to be applied before the global // transform. mat4 colorTransform = mat4(); // True if blending will be forced to be disabled. bool disableBlending = false; }; } // namespace renderengine } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/Mesh.h�������������������������������������������������������0100644 0000000 0000000 00000006132 13756501735 020356� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_RENDER_ENGINE_MESH_H #define SF_RENDER_ENGINE_MESH_H #include #include namespace android { namespace renderengine { class Mesh { public: enum Primitive { TRIANGLES = 0x0004, // GL_TRIANGLES TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN }; Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0); ~Mesh() = default; /* * VertexArray handles the stride automatically. */ template class VertexArray { friend class Mesh; float* mData; size_t mStride; VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {} public: TYPE& operator[](size_t index) { return *reinterpret_cast(&mData[index * mStride]); } TYPE const& operator[](size_t index) const { return *reinterpret_cast(&mData[index * mStride]); } }; template VertexArray getPositionArray() { return VertexArray(getPositions(), mStride); } template VertexArray getTexCoordArray() { return VertexArray(getTexCoords(), mStride); } template VertexArray getCropCoordArray() { return VertexArray(getCropCoords(), mStride); } Primitive getPrimitive() const; // returns a pointer to the vertices positions float const* getPositions() const; // returns a pointer to the vertices texture coordinates float const* getTexCoords() const; // returns a pointer to the vertices crop coordinates float const* getCropCoords() const; // number of vertices in this mesh size_t getVertexCount() const; // dimension of vertices size_t getVertexSize() const; // dimension of texture coordinates size_t getTexCoordsSize() const; // return stride in bytes size_t getByteStride() const; // return stride in floats size_t getStride() const; private: Mesh(const Mesh&); Mesh& operator=(const Mesh&); Mesh const& operator=(const Mesh&) const; float* getPositions(); float* getTexCoords(); float* getCropCoords(); std::vector mVertices; size_t mVertexCount; size_t mVertexSize; size_t mTexCoordsSize; size_t mStride; Primitive mPrimitive; }; } // namespace renderengine } // namespace android #endif /* SF_RENDER_ENGINE_MESH_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/RenderEngine.h�����������������������������������������������0100644 0000000 0000000 00000026562 13756501735 022040� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_RENDERENGINE_H_ #define SF_RENDERENGINE_H_ #include #include #include #include #include #include #include #include #include #include #include /** * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported). */ #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" struct ANativeWindowBuffer; namespace android { class Rect; class Region; namespace renderengine { class BindNativeBufferAsFramebuffer; class Image; class Mesh; class Texture; namespace impl { class RenderEngine; } enum class Protection { UNPROTECTED = 1, PROTECTED = 2, }; class RenderEngine { public: enum FeatureFlag { USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context // Create a protected context when if possible ENABLE_PROTECTED_CONTEXT = 1 << 2, }; static std::unique_ptr create(int hwcFormat, uint32_t featureFlags, uint32_t imageCacheSize); virtual ~RenderEngine() = 0; // ----- BEGIN DEPRECATED INTERFACE ----- // This interface, while still in use until a suitable replacement is built, // should be considered deprecated, minus some methods which still may be // used to support legacy behavior. virtual std::unique_ptr createFramebuffer() = 0; virtual std::unique_ptr createImage() = 0; virtual void primeCache() const = 0; // dump the extension strings. always call the base class. virtual void dump(std::string& result) = 0; virtual bool useNativeFenceSync() const = 0; virtual bool useWaitSync() const = 0; virtual bool isCurrent() const = 0; // helpers // flush submits RenderEngine command stream for execution and returns a // native fence fd that is signaled when the execution has completed. It // returns -1 on errors. virtual base::unique_fd flush() = 0; // finish waits until RenderEngine command stream has been executed. It // returns false on errors. virtual bool finish() = 0; // waitFence inserts a wait on an external fence fd to RenderEngine // command stream. It returns false on errors. virtual bool waitFence(base::unique_fd fenceFd) = 0; virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; virtual void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha) = 0; virtual void genTextures(size_t count, uint32_t* names) = 0; virtual void deleteTextures(size_t count, uint32_t const* names) = 0; virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; // Legacy public method used by devices that don't support native fence // synchronization in their GPU driver, as this method provides implicit // synchronization for latching buffers. virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp& buffer, const sp& fence) = 0; // Caches Image resources for this buffer, but does not bind the buffer to // a particular texture. // Note that work is deferred to an additional thread, i.e. this call // is made asynchronously, but the caller can expect that cache/unbind calls // are performed in a manner that's conflict serializable, i.e. unbinding // a buffer should never occur before binding the buffer if the caller // called {bind, cache}ExternalTextureBuffer before calling unbind. virtual void cacheExternalTextureBuffer(const sp& buffer) = 0; // Removes internal resources referenced by the bufferId. This method should be // invoked when the caller will no longer hold a reference to a GraphicBuffer // and needs to clean up its resources. // Note that work is deferred to an additional thread, i.e. this call // is made asynchronously, but the caller can expect that cache/unbind calls // are performed in a manner that's conflict serializable, i.e. unbinding // a buffer should never occur before binding the buffer if the caller // called {bind, cache}ExternalTextureBuffer before calling unbind. virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; // When binding a native buffer, it must be done before setViewportAndProjection // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; // set-up virtual void checkErrors() const = 0; virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, ui::Transform::orientation_flags rotation) = 0; virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, const half4& color, float cornerRadius) = 0; virtual void setupLayerTexturing(const Texture& texture) = 0; virtual void setupLayerBlackedOut() = 0; virtual void setupFillWithColor(float r, float g, float b, float a) = 0; // Sets up the crop size for corner radius clipping. // // Having corner radius will force GPU composition on the layer and its children, drawing it // with a special shader. The shader will receive the radius and the crop rectangle as input, // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1. // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be // in local layer coordinate space, so we have to take the layer transform into account when // walking up the tree. virtual void setupCornerRadiusCropSize(float width, float height) = 0; // Set a color transform matrix that is applied in linear space right before OETF. virtual void setColorTransform(const mat4& /* colorTransform */) = 0; virtual void disableTexturing() = 0; virtual void disableBlending() = 0; // HDR and color management support virtual void setSourceY410BT2020(bool enable) = 0; virtual void setSourceDataSpace(ui::Dataspace source) = 0; virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0; virtual void setDisplayMaxLuminance(const float maxLuminance) = 0; // drawing virtual void drawMesh(const Mesh& mesh) = 0; // queries virtual size_t getMaxTextureSize() const = 0; virtual size_t getMaxViewportDims() const = 0; // ----- END DEPRECATED INTERFACE ----- // ----- BEGIN NEW INTERFACE ----- virtual bool isProtected() const = 0; virtual bool supportsProtectedContent() const = 0; virtual bool useProtectedContext(bool useProtectedContext) = 0; // Renders layers for a particular display via GPU composition. This method // should be called for every display that needs to be rendered via the GPU. // @param display The display-wide settings that should be applied prior to // drawing any layers. // // Assumptions when calling this method: // 1. There is exactly one caller - i.e. multi-threading is not supported. // 2. Additional threads may be calling the {bind,cache}ExternalTexture // methods above. But the main thread is responsible for holding resources // such that Image destruction does not occur while this method is called. // // TODO(b/136806342): This should behavior should ideally be fixed since // the above two assumptions are brittle, as conditional thread safetyness // may be insufficient when maximizing rendering performance in the future. // // @param layers The layers to draw onto the display, in Z-order. // @param buffer The buffer which will be drawn to. This buffer will be // ready once drawFence fires. // @param useFramebufferCache True if the framebuffer cache should be used. // If an implementation does not cache output framebuffers, then this // parameter does nothing. // @param bufferFence Fence signalling that the buffer is ready to be drawn // to. // @param drawFence A pointer to a fence, which will fire when the buffer // has been drawn to and is ready to be examined. The fence will be // initialized by this method. The caller will be responsible for owning the // fence. // @return An error code indicating whether drawing was successful. For // now, this always returns NO_ERROR. virtual status_t drawLayers(const DisplaySettings& display, const std::vector& layers, ANativeWindowBuffer* buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0; protected: // Gets a framebuffer to render to. This framebuffer may or may not be // cached depending on the implementation. // // Note that this method does not transfer ownership, so the caller most not // live longer than RenderEngine. virtual Framebuffer* getFramebufferForDrawing() = 0; friend class BindNativeBufferAsFramebuffer; }; class BindNativeBufferAsFramebuffer { public: BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer, const bool useFramebufferCache) : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), useFramebufferCache) ? mEngine.bindFrameBuffer(mFramebuffer) : NO_MEMORY; } ~BindNativeBufferAsFramebuffer() { mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); mEngine.unbindFrameBuffer(mFramebuffer); } status_t getStatus() const { return mStatus; } private: RenderEngine& mEngine; Framebuffer* mFramebuffer; status_t mStatus; }; namespace impl { // impl::RenderEngine contains common implementation that is graphics back-end agnostic. class RenderEngine : public renderengine::RenderEngine { public: virtual ~RenderEngine() = 0; bool useNativeFenceSync() const override; bool useWaitSync() const override; protected: RenderEngine(uint32_t featureFlags); const uint32_t mFeatureFlags; }; } // namespace impl } // namespace renderengine } // namespace android #endif /* SF_RENDERENGINE_H_ */ ����������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/Texture.h����������������������������������������������������0100644 0000000 0000000 00000003101 13756501735 021113� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_RENDER_ENGINE_TEXTURE_H #define SF_RENDER_ENGINE_TEXTURE_H #include #include namespace android { namespace renderengine { class Texture { public: enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 }; Texture(); Texture(Target textureTarget, uint32_t textureName); ~Texture(); void init(Target textureTarget, uint32_t textureName); void setMatrix(float const* matrix); void setFiltering(bool enabled); void setDimensions(size_t width, size_t height); uint32_t getTextureName() const; uint32_t getTextureTarget() const; const mat4& getMatrix() const; bool getFiltering() const; size_t getWidth() const; size_t getHeight() const; private: uint32_t mTextureName; uint32_t mTextureTarget; size_t mWidth; size_t mHeight; bool mFiltering; mat4 mTextureMatrix; }; } // namespace renderengine } // namespace android #endif /* SF_RENDER_ENGINE_TEXTURE_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/mock/��������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020243� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/mock/Framebuffer.h�������������������������������������������0100644 0000000 0000000 00000002010 13756501735 022626� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include namespace android { namespace renderengine { namespace mock { class Framebuffer : public renderengine::Framebuffer { public: Framebuffer(); ~Framebuffer() override; MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool)); }; } // namespace mock } // namespace renderengine } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/mock/Image.h�������������������������������������������������0100644 0000000 0000000 00000001761 13756501735 021440� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include namespace android { namespace renderengine { namespace mock { class Image : public renderengine::Image { public: Image(); ~Image() override; MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected)); }; } // namespace mock } // namespace renderengine } // namespace android ���������������libs/renderengine/include/renderengine/mock/RenderEngine.h������������������������������������������0100644 0000000 0000000 00000007706 13756501735 022770� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include #include #include #include #include #include #include namespace android { namespace renderengine { namespace mock { class RenderEngine : public renderengine::RenderEngine { public: RenderEngine(); ~RenderEngine() override; MOCK_METHOD0(createFramebuffer, std::unique_ptr()); MOCK_METHOD0(createImage, std::unique_ptr()); MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*()); MOCK_CONST_METHOD0(primeCache, void()); MOCK_METHOD1(dump, void(std::string&)); MOCK_CONST_METHOD0(useNativeFenceSync, bool()); MOCK_CONST_METHOD0(useWaitSync, bool()); MOCK_CONST_METHOD0(isCurrent, bool()); MOCK_METHOD0(flush, base::unique_fd()); MOCK_METHOD0(finish, bool()); MOCK_METHOD1(waitFence, bool(base::unique_fd*)); bool waitFence(base::unique_fd fd) override { return waitFence(&fd); }; MOCK_METHOD4(clearWithColor, void(float, float, float, float)); MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float)); MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp&)); MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, const sp&, const sp&)); MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); MOCK_CONST_METHOD0(checkErrors, void()); MOCK_METHOD4(setViewportAndProjection, void(size_t, size_t, Rect, ui::Transform::orientation_flags)); MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float)); MOCK_METHOD1(setupLayerTexturing, void(const Texture&)); MOCK_METHOD0(setupLayerBlackedOut, void()); MOCK_METHOD4(setupFillWithColor, void(float, float, float, float)); MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float)); MOCK_METHOD1(setColorTransform, void(const mat4&)); MOCK_METHOD1(setSaturationMatrix, void(const mat4&)); MOCK_METHOD0(disableTexturing, void()); MOCK_METHOD0(disableBlending, void()); MOCK_METHOD1(setSourceY410BT2020, void(bool)); MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace)); MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace)); MOCK_METHOD1(setDisplayMaxLuminance, void(const float)); MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*)); MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*)); MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&)); MOCK_CONST_METHOD0(getMaxTextureSize, size_t()); MOCK_CONST_METHOD0(getMaxViewportDims, size_t()); MOCK_CONST_METHOD0(isProtected, bool()); MOCK_CONST_METHOD0(supportsProtectedContent, bool()); MOCK_METHOD1(useProtectedContext, bool(bool)); MOCK_METHOD6(drawLayers, status_t(const DisplaySettings&, const std::vector&, ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*)); }; } // namespace mock } // namespace renderengine } // namespace android ����������������������������������������������������������libs/renderengine/include/renderengine/private/�����������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020764� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/include/renderengine/private/Description.h����������������������������������������0100644 0000000 0000000 00000004723 13756501735 023423� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SF_RENDER_ENGINE_DESCRIPTION_H_ #define SF_RENDER_ENGINE_DESCRIPTION_H_ #include #include namespace android { namespace renderengine { /* * This is the structure that holds the state of the rendering engine. * This class is used to generate a corresponding GLSL program and set the * appropriate uniform. */ struct Description { enum class TransferFunction : int { LINEAR, SRGB, ST2084, HLG, // Hybrid Log-Gamma for HDR. }; static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace); Description() = default; ~Description() = default; bool hasInputTransformMatrix() const; bool hasOutputTransformMatrix() const; bool hasColorMatrix() const; // whether textures are premultiplied bool isPremultipliedAlpha = false; // whether this layer is marked as opaque bool isOpaque = true; // corner radius of the layer float cornerRadius = 0; // Size of the rounded rectangle we are cropping to half2 cropSize; // Texture this layer uses Texture texture; bool textureEnabled = false; // color used when texturing is disabled or when setting alpha. half4 color; // true if the sampled pixel values are in Y410/BT2020 rather than RGBA bool isY410BT2020 = false; // transfer functions for the input/output TransferFunction inputTransferFunction = TransferFunction::LINEAR; TransferFunction outputTransferFunction = TransferFunction::LINEAR; float displayMaxLuminance; // projection matrix mat4 projectionMatrix; // The color matrix will be applied in linear space right before OETF. mat4 colorMatrix; mat4 inputTransformMatrix; mat4 outputTransformMatrix; }; } // namespace renderengine } // namespace android #endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */ ���������������������������������������������libs/renderengine/mock/�����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014153� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/mock/Framebuffer.cpp��������������������������������������������������������������0100644 0000000 0000000 00000001740 13756501735 017102� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #include namespace android { namespace renderengine { namespace mock { // The Google Mock documentation recommends explicit non-header instantiations // for better compile time performance. Framebuffer::Framebuffer() = default; Framebuffer::~Framebuffer() = default; } // namespace mock } // namespace renderengine } // namespace android ��������������������������������libs/renderengine/mock/Image.cpp��������������������������������������������������������������������0100644 0000000 0000000 00000001702 13756501735 015676� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #include namespace android { namespace renderengine { namespace mock { // The Google Mock documentation recommends explicit non-header instantiations // for better compile time performance. Image::Image() = default; Image::~Image() = default; } // namespace mock } // namespace renderengine } // namespace android ��������������������������������������������������������������libs/renderengine/mock/RenderEngine.cpp�������������������������������������������������������������0100644 0000000 0000000 00000001745 13756501735 017230� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #include namespace android { namespace renderengine { namespace mock { // The Google Mock documentation recommends explicit non-header instantiations // for better compile time performance. RenderEngine::RenderEngine() = default; RenderEngine::~RenderEngine() = default; } // namespace mock } // namespace renderengine } // namespace android ���������������������������libs/renderengine/tests/����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014364� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/tests/Android.bp������������������������������������������������������������������0100644 0000000 0000000 00000002125 13756501735 016264� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2018 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. cc_test { name: "librenderengine_test", defaults: ["surfaceflinger_defaults"], test_suites: ["device-tests"], srcs: [ "RenderEngineTest.cpp", ], static_libs: [ "libgmock", "librenderengine", ], shared_libs: [ "libbase", "libcutils", "libEGL", "libGLESv2", "libgui", "liblog", "libnativewindow", "libprocessgroup", "libsync", "libui", "libutils", ], } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/renderengine/tests/RenderEngineTest.cpp��������������������������������������������������������0100644 0000000 0000000 00000117023 13756501735 020276� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #include #include #include #include #include #include #include "../gl/GLESRenderEngine.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; constexpr int DEFAULT_DISPLAY_HEIGHT = 256; constexpr int DEFAULT_DISPLAY_OFFSET = 64; namespace android { struct RenderEngineTest : public ::testing::Test { static void SetUpTestSuite() { sRE = renderengine::gl::GLESRenderEngine::create(static_cast( ui::PixelFormat::RGBA_8888), 0, 1); } static void TearDownTestSuite() { // The ordering here is important - sCurrentBuffer must live longer // than RenderEngine to avoid a null reference on tear-down. sRE = nullptr; sCurrentBuffer = nullptr; } static sp allocateDefaultBuffer() { return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER, "output"); } // Allocates a 1x1 buffer to fill with a solid color static sp allocateSourceBuffer(uint32_t width, uint32_t height) { return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_TEXTURE, "input"); } RenderEngineTest() { mBuffer = allocateDefaultBuffer(); } ~RenderEngineTest() { for (uint32_t texName : mTexNames) { sRE->deleteTextures(1, &texName); } } void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { uint8_t* pixels; mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast(&pixels)); auto colorCompare = [tolerance](uint8_t a, uint8_t b) { uint8_t tmp = a >= b ? a - b : b - a; return tmp <= tolerance; }; int32_t maxFails = 10; int32_t fails = 0; for (int32_t j = 0; j < region.getHeight(); j++) { const uint8_t* src = pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4; for (int32_t i = 0; i < region.getWidth(); i++) { const uint8_t expected[4] = {r, g, b, a}; bool equal = std::equal(src, src + 4, expected, colorCompare); EXPECT_TRUE(equal) << "pixel @ (" << region.left + i << ", " << region.top + j << "): " << "expected (" << static_cast(r) << ", " << static_cast(g) << ", " << static_cast(b) << ", " << static_cast(a) << "), " << "got (" << static_cast(src[0]) << ", " << static_cast(src[1]) << ", " << static_cast(src[2]) << ", " << static_cast(src[3]) << ")"; src += 4; if (!equal && ++fails >= maxFails) { break; } } if (fails >= maxFails) { break; } } mBuffer->unlock(); } static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); } static Rect offsetRect() { return Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); } static Rect offsetRectAtZero() { return Rect(DEFAULT_DISPLAY_WIDTH - DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET); } void invokeDraw(renderengine::DisplaySettings settings, std::vector layers, sp buffer) { base::unique_fd fence; status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true, base::unique_fd(), &fence); sCurrentBuffer = buffer; int fd = fence.release(); if (fd >= 0) { sync_wait(fd, -1); close(fd); } ASSERT_EQ(NO_ERROR, status); if (layers.size() > 0) { ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId())); } } void drawEmptyLayers() { renderengine::DisplaySettings settings; std::vector layers; // Meaningless buffer since we don't do any drawing sp buffer = new GraphicBuffer(); invokeDraw(settings, layers, buffer); } template void fillBuffer(half r, half g, half b, half a); template void fillRedBuffer(); template void fillGreenBuffer(); template void fillBlueBuffer(); template void fillRedTransparentBuffer(); template void fillRedOffsetBuffer(); template void fillBufferPhysicalOffset(); template void fillBufferCheckers(mat4 transform); template void fillBufferCheckersRotate0(); template void fillBufferCheckersRotate90(); template void fillBufferCheckersRotate180(); template void fillBufferCheckersRotate270(); template void fillBufferWithLayerTransform(); template void fillBufferLayerTransform(); template void fillBufferWithColorTransform(); template void fillBufferColorTransform(); template void fillRedBufferWithRoundedCorners(); template void fillBufferWithRoundedCorners(); template void overlayCorners(); void fillRedBufferTextureTransform(); void fillBufferTextureTransform(); void fillRedBufferWithPremultiplyAlpha(); void fillBufferWithPremultiplyAlpha(); void fillRedBufferWithoutPremultiplyAlpha(); void fillBufferWithoutPremultiplyAlpha(); void fillGreenColorBufferThenClearRegion(); void clearLeftRegion(); void clearRegion(); // Keep around the same renderengine object to save on initialization time. // For now, exercise the GL backend directly so that some caching specifics // can be tested without changing the interface. static std::unique_ptr sRE; // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to // be freed *after* RenderEngine is destroyed, so that the EGL image is // destroyed first. static sp sCurrentBuffer; sp mBuffer; std::vector mTexNames; }; std::unique_ptr RenderEngineTest::sRE = nullptr; sp RenderEngineTest::sCurrentBuffer = nullptr; struct ColorSourceVariant { static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, RenderEngineTest* /*fixture*/) { layer.source.solidColor = half3(r, g, b); } }; struct RelaxOpaqueBufferVariant { static void setOpaqueBit(renderengine::LayerSettings& layer) { layer.source.buffer.isOpaque = false; } static uint8_t getAlphaChannel() { return 255; } }; struct ForceOpaqueBufferVariant { static void setOpaqueBit(renderengine::LayerSettings& layer) { layer.source.buffer.isOpaque = true; } static uint8_t getAlphaChannel() { // The isOpaque bit will override the alpha channel, so this should be // arbitrary. return 10; } }; template struct BufferSourceVariant { static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, RenderEngineTest* fixture) { sp buf = RenderEngineTest::allocateSourceBuffer(1, 1); uint32_t texName; fixture->sRE->genTextures(1, &texName); fixture->mTexNames.push_back(texName); uint8_t* pixels; buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast(&pixels)); for (int32_t j = 0; j < buf->getHeight(); j++) { uint8_t* iter = pixels + (buf->getStride() * j) * 4; for (int32_t i = 0; i < buf->getWidth(); i++) { iter[0] = uint8_t(r * 255); iter[1] = uint8_t(g * 255); iter[2] = uint8_t(b * 255); iter[3] = OpaquenessVariant::getAlphaChannel(); iter += 4; } } buf->unlock(); layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; OpaquenessVariant::setOpaqueBit(layer); } }; template void RenderEngineTest::fillBuffer(half r, half g, half b, half a) { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); SourceVariant::fillColor(layer, r, g, b, this); layer.alpha = a; layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } template void RenderEngineTest::fillRedBuffer() { fillBuffer(1.0f, 0.0f, 0.0f, 1.0f); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } template void RenderEngineTest::fillGreenBuffer() { fillBuffer(0.0f, 1.0f, 0.0f, 1.0f); expectBufferColor(fullscreenRect(), 0, 255, 0, 255); } template void RenderEngineTest::fillBlueBuffer() { fillBuffer(0.0f, 0.0f, 1.0f, 1.0f); expectBufferColor(fullscreenRect(), 0, 0, 255, 255); } template void RenderEngineTest::fillRedTransparentBuffer() { fillBuffer(1.0f, 0.0f, 0.0f, .2f); expectBufferColor(fullscreenRect(), 51, 0, 0, 51); } template void RenderEngineTest::fillRedOffsetBuffer() { renderengine::DisplaySettings settings; settings.physicalDisplay = offsetRect(); settings.clip = offsetRectAtZero(); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = offsetRectAtZero().toFloatRect(); SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layer.alpha = 1.0f; layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } template void RenderEngineTest::fillBufferPhysicalOffset() { fillRedOffsetBuffer(); expectBufferColor(Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255); Rect offsetRegionLeft(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT); Rect offsetRegionTop(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_OFFSET); expectBufferColor(offsetRegionLeft, 0, 0, 0, 0); expectBufferColor(offsetRegionTop, 0, 0, 0, 0); } template void RenderEngineTest::fillBufferCheckers(mat4 transform) { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); // Here logical space is 2x2 settings.clip = Rect(2, 2); settings.globalTransform = transform; std::vector layers; renderengine::LayerSettings layerOne; Rect rectOne(0, 0, 1, 1); layerOne.geometry.boundaries = rectOne.toFloatRect(); SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this); layerOne.alpha = 1.0f; renderengine::LayerSettings layerTwo; Rect rectTwo(0, 1, 1, 2); layerTwo.geometry.boundaries = rectTwo.toFloatRect(); SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this); layerTwo.alpha = 1.0f; renderengine::LayerSettings layerThree; Rect rectThree(1, 0, 2, 1); layerThree.geometry.boundaries = rectThree.toFloatRect(); SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this); layerThree.alpha = 1.0f; layers.push_back(layerOne); layers.push_back(layerTwo); layers.push_back(layerThree); invokeDraw(settings, layers, mBuffer); } template void RenderEngineTest::fillBufferCheckersRotate0() { fillBufferCheckers(mat4()); expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 255, 0, 255); } template void RenderEngineTest::fillBufferCheckersRotate90() { mat4 matrix = mat4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1); fillBufferCheckers(matrix); expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 0, 255, 255); expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); } template void RenderEngineTest::fillBufferCheckersRotate180() { mat4 matrix = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1); fillBufferCheckers(matrix); expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255); expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 255, 255); } template void RenderEngineTest::fillBufferCheckersRotate270() { mat4 matrix = mat4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1); fillBufferCheckers(matrix); expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 255, 0, 255); expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255); } template void RenderEngineTest::fillBufferWithLayerTransform() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); // Here logical space is 2x2 settings.clip = Rect(2, 2); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); // Translate one pixel diagonally layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1); SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layer.source.solidColor = half3(1.0f, 0.0f, 0.0f); layer.alpha = 1.0f; layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } template void RenderEngineTest::fillBufferLayerTransform() { fillBufferWithLayerTransform(); expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0); expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255); } template void RenderEngineTest::fillBufferWithColorTransform() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = Rect(1, 1); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this); layer.alpha = 1.0f; // construct a fake color matrix // annihilate green and blue channels settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1)); // set red channel to red + green layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); layer.alpha = 1.0f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } template void RenderEngineTest::fillBufferColorTransform() { fillBufferWithColorTransform(); expectBufferColor(fullscreenRect(), 191, 0, 0, 255); } template void RenderEngineTest::fillRedBufferWithRoundedCorners() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); layer.geometry.roundedCornersRadius = 5.0f; layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layer.alpha = 1.0f; layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } template void RenderEngineTest::fillBufferWithRoundedCorners() { fillRedBufferWithRoundedCorners(); // Corners should be ignored... expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0); expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); // ...And the non-rounded portion should be red. // Other pixels may be anti-aliased, so let's not check those. expectBufferColor(Rect(5, 5, DEFAULT_DISPLAY_WIDTH - 5, DEFAULT_DISPLAY_HEIGHT - 5), 255, 0, 0, 255); } template void RenderEngineTest::overlayCorners() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector layersFirst; renderengine::LayerSettings layerOne; layerOne.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0); SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this); layerOne.alpha = 0.2; layersFirst.push_back(layerOne); invokeDraw(settings, layersFirst, mBuffer); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); std::vector layersSecond; renderengine::LayerSettings layerTwo; layerTwo.geometry.boundaries = FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this); layerTwo.alpha = 1.0f; layersSecond.push_back(layerTwo); invokeDraw(settings, layersSecond, mBuffer); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 255, 0, 255); } void RenderEngineTest::fillRedBufferTextureTransform() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = Rect(1, 1); std::vector layers; renderengine::LayerSettings layer; // Here will allocate a checker board texture, but transform texture // coordinates so that only the upper left is applied. sp buf = allocateSourceBuffer(2, 2); uint32_t texName; RenderEngineTest::sRE->genTextures(1, &texName); this->mTexNames.push_back(texName); uint8_t* pixels; buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast(&pixels)); // Red top left, Green top right, Blue bottom left, Black bottom right pixels[0] = 255; pixels[1] = 0; pixels[2] = 0; pixels[3] = 255; pixels[4] = 0; pixels[5] = 255; pixels[6] = 0; pixels[7] = 255; pixels[8] = 0; pixels[9] = 0; pixels[10] = 255; pixels[11] = 255; buf->unlock(); layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; // Transform coordinates to only be inside the red quadrant. layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1)); layer.alpha = 1.0f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } void RenderEngineTest::fillBufferTextureTransform() { fillRedBufferTextureTransform(); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); // Here logical space is 1x1 settings.clip = Rect(1, 1); std::vector layers; renderengine::LayerSettings layer; sp buf = allocateSourceBuffer(1, 1); uint32_t texName; RenderEngineTest::sRE->genTextures(1, &texName); this->mTexNames.push_back(texName); uint8_t* pixels; buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast(&pixels)); pixels[0] = 255; pixels[1] = 0; pixels[2] = 0; pixels[3] = 255; buf->unlock(); layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; layer.source.buffer.usePremultipliedAlpha = true; layer.alpha = 0.5f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } void RenderEngineTest::fillBufferWithPremultiplyAlpha() { fillRedBufferWithPremultiplyAlpha(); expectBufferColor(fullscreenRect(), 128, 0, 0, 128); } void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); // Here logical space is 1x1 settings.clip = Rect(1, 1); std::vector layers; renderengine::LayerSettings layer; sp buf = allocateSourceBuffer(1, 1); uint32_t texName; RenderEngineTest::sRE->genTextures(1, &texName); this->mTexNames.push_back(texName); uint8_t* pixels; buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast(&pixels)); pixels[0] = 255; pixels[1] = 0; pixels[2] = 0; pixels[3] = 255; buf->unlock(); layer.source.buffer.buffer = buf; layer.source.buffer.textureName = texName; layer.source.buffer.usePremultipliedAlpha = false; layer.alpha = 0.5f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() { fillRedBufferWithoutPremultiplyAlpha(); expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1); } void RenderEngineTest::clearLeftRegion() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); // Here logical space is 4x4 settings.clip = Rect(4, 4); settings.globalTransform = mat4::scale(vec4(2, 4, 0, 1)); settings.clearRegion = Region(Rect(1, 1)); std::vector layers; // dummy layer, without bounds should not render anything renderengine::LayerSettings layer; layers.push_back(layer); invokeDraw(settings, layers, mBuffer); } void RenderEngineTest::clearRegion() { // Reuse mBuffer clearLeftRegion(); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255); expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); } TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { drawEmptyLayers(); } TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) { renderengine::DisplaySettings settings; std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layers.push_back(layer); base::unique_fd fence; status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence); ASSERT_EQ(BAD_VALUE, status); } TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layer.alpha = 1.0; layers.push_back(layer); status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), nullptr); sCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layer.alpha = 1.0; layers.push_back(layer); status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false, base::unique_fd(), nullptr); sCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId())); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); } TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { fillRedBuffer(); } TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { fillGreenBuffer(); } TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { fillBlueBuffer(); } TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { fillRedTransparentBuffer(); } TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { fillBufferPhysicalOffset(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { fillBufferCheckersRotate0(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { fillBufferCheckersRotate90(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { fillBufferCheckersRotate180(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { fillBufferCheckersRotate270(); } TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { fillBufferLayerTransform(); } TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { fillBufferLayerTransform(); } TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { fillBufferWithRoundedCorners(); } TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) { overlayCorners(); } TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { fillRedBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { fillGreenBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { fillBlueBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { fillRedTransparentBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { fillBufferPhysicalOffset>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { fillBufferCheckersRotate0>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { fillBufferCheckersRotate90>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { fillBufferCheckersRotate180>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { fillBufferCheckersRotate270>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { fillBufferLayerTransform>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { fillBufferLayerTransform>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { fillBufferWithRoundedCorners>(); } TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { overlayCorners>(); } TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { fillRedBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { fillGreenBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { fillBlueBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { fillRedTransparentBuffer>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { fillBufferPhysicalOffset>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { fillBufferCheckersRotate0>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { fillBufferCheckersRotate90>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { fillBufferCheckersRotate180>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { fillBufferCheckersRotate270>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { fillBufferLayerTransform>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { fillBufferLayerTransform>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { fillBufferWithRoundedCorners>(); } TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { overlayCorners>(); } TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) { fillBufferTextureTransform(); } TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { fillBufferWithPremultiplyAlpha(); } TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { fillBufferWithoutPremultiplyAlpha(); } TEST_F(RenderEngineTest, drawLayers_clearRegion) { clearRegion(); } TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); settings.clip = fullscreenRect(); std::vector layers; renderengine::LayerSettings layer; layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layers.push_back(layer); invokeDraw(settings, layers, mBuffer); uint64_t bufferId = layer.source.buffer.buffer->getId(); EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); std::shared_ptr barrier = sRE->unbindExternalTextureBufferForTesting(bufferId); std::lock_guard lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; })); EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); EXPECT_EQ(NO_ERROR, barrier->result); } TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) { status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr); ASSERT_EQ(BAD_VALUE, result); } TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) { sp buf = allocateSourceBuffer(1, 1); uint32_t texName; sRE->genTextures(1, &texName); mTexNames.push_back(texName); sRE->bindExternalTextureBuffer(texName, buf, nullptr); uint64_t bufferId = buf->getId(); EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); std::shared_ptr barrier = sRE->unbindExternalTextureBufferForTesting(bufferId); std::lock_guard lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; })); EXPECT_EQ(NO_ERROR, barrier->result); EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); } TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) { std::shared_ptr barrier = sRE->cacheExternalTextureBufferForTesting(nullptr); std::lock_guard lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; })); EXPECT_TRUE(barrier->isOpen); EXPECT_EQ(BAD_VALUE, barrier->result); } TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) { sp buf = allocateSourceBuffer(1, 1); uint64_t bufferId = buf->getId(); std::shared_ptr barrier = sRE->cacheExternalTextureBufferForTesting(buf); { std::lock_guard lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; })); EXPECT_EQ(NO_ERROR, barrier->result); } EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); barrier = sRE->unbindExternalTextureBufferForTesting(bufferId); { std::lock_guard lock(barrier->mutex); ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; })); EXPECT_EQ(NO_ERROR, barrier->result); } EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); } } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/����������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012066� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/Android.bp������������������������������������������������������������������������������0100644 0000000 0000000 00000003311 13756501735 013764� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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. cc_library_shared { name: "libsensor", clang: true, cflags: [ "-Wall", "-Werror", ], cppflags: [ "-Weverything", // The static constructors and destructors in this library have not been noted to // introduce significant overheads "-Wno-exit-time-destructors", "-Wno-global-constructors", // We only care about compiling as C++14 "-Wno-c++98-compat-pedantic", // android/sensors.h uses nested anonymous unions and anonymous structs "-Wno-nested-anon-types", "-Wno-gnu-anonymous-struct", // Don't warn about struct padding "-Wno-padded", ], srcs: [ "BitTube.cpp", "ISensorEventConnection.cpp", "ISensorServer.cpp", "Sensor.cpp", "SensorEventQueue.cpp", "SensorManager.cpp", ], shared_libs: [ "libbinder", "libcutils", "libutils", "liblog", "libhardware", ], export_include_dirs: ["include"], export_shared_lib_headers: ["libbinder", "libhardware"], } subdirs = ["tests"] �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/BitTube.cpp�����������������������������������������������������������������������������0100644 0000000 0000000 00000012103 13756501735 014122� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- // Socket buffer size. The default is typically about 128KB, which is much larger than // we really need. So we make it smaller. static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024; BitTube::BitTube() : mSendFd(-1), mReceiveFd(-1) { init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE); } BitTube::BitTube(size_t bufsize) : mSendFd(-1), mReceiveFd(-1) { init(bufsize, bufsize); } BitTube::BitTube(const Parcel& data) : mSendFd(-1), mReceiveFd(-1) { mReceiveFd = dup(data.readFileDescriptor()); if (mReceiveFd < 0) { mReceiveFd = -errno; ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", strerror(-mReceiveFd)); } } BitTube::~BitTube() { if (mSendFd >= 0) close(mSendFd); if (mReceiveFd >= 0) close(mReceiveFd); } void BitTube::init(size_t rcvbuf, size_t sndbuf) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) { size_t size = DEFAULT_SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); // sine we don't use the "return channel", we keep it small... setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); fcntl(sockets[0], F_SETFL, O_NONBLOCK); fcntl(sockets[1], F_SETFL, O_NONBLOCK); mReceiveFd = sockets[0]; mSendFd = sockets[1]; } else { mReceiveFd = -errno; ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); } } status_t BitTube::initCheck() const { if (mReceiveFd < 0) { return status_t(mReceiveFd); } return NO_ERROR; } int BitTube::getFd() const { return mReceiveFd; } int BitTube::getSendFd() const { return mSendFd; } ssize_t BitTube::write(void const* vaddr, size_t size) { ssize_t err, len; do { len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL); // cannot return less than size, since we're using SOCK_SEQPACKET err = len < 0 ? errno : 0; } while (err == EINTR); return err == 0 ? len : -err; } ssize_t BitTube::read(void* vaddr, size_t size) { ssize_t err, len; do { len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT); err = len < 0 ? errno : 0; } while (err == EINTR); if (err == EAGAIN || err == EWOULDBLOCK) { // EAGAIN means that we have non-blocking I/O but there was // no data to be read. Nothing the client should care about. return 0; } return err == 0 ? len : -err; } status_t BitTube::writeToParcel(Parcel* reply) const { if (mReceiveFd < 0) return -EINVAL; status_t result = reply->writeDupFileDescriptor(mReceiveFd); close(mReceiveFd); mReceiveFd = -1; return result; } ssize_t BitTube::sendObjects(const sp& tube, void const* events, size_t count, size_t objSize) { const char* vaddr = reinterpret_cast(events); ssize_t size = tube->write(vaddr, count*objSize); // should never happen because of SOCK_SEQPACKET LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast(objSize)), "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)", count, objSize, size); //ALOGE_IF(size<0, "error %d sending %d events", size, count); return size < 0 ? size : size / static_cast(objSize); } ssize_t BitTube::recvObjects(const sp& tube, void* events, size_t count, size_t objSize) { char* vaddr = reinterpret_cast(events); ssize_t size = tube->read(vaddr, count*objSize); // should never happen because of SOCK_SEQPACKET LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast(objSize)), "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)", count, objSize, size); //ALOGE_IF(size<0, "error %d receiving %d events", size, count); return size < 0 ? size : size / static_cast(objSize); } // ---------------------------------------------------------------------------- }; // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/ISensorEventConnection.cpp��������������������������������������������������������������0100644 0000000 0000000 00000013425 13756501735 017200� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- enum { GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, ENABLE_DISABLE, SET_EVENT_RATE, FLUSH_SENSOR, CONFIGURE_CHANNEL, DESTROY, }; class BpSensorEventConnection : public BpInterface { public: explicit BpSensorEventConnection(const sp& impl) : BpInterface(impl) { } virtual ~BpSensorEventConnection(); virtual sp getSensorChannel() const { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); remote()->transact(GET_SENSOR_CHANNEL, data, &reply); return new BitTube(reply); } virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags) { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); data.writeInt32(handle); data.writeInt32(enabled); data.writeInt64(samplingPeriodNs); data.writeInt64(maxBatchReportLatencyNs); data.writeInt32(reservedFlags); remote()->transact(ENABLE_DISABLE, data, &reply); return reply.readInt32(); } virtual status_t setEventRate(int handle, nsecs_t ns) { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); data.writeInt32(handle); data.writeInt64(ns); remote()->transact(SET_EVENT_RATE, data, &reply); return reply.readInt32(); } virtual status_t flush() { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); remote()->transact(FLUSH_SENSOR, data, &reply); return reply.readInt32(); } virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); data.writeInt32(handle); data.writeInt32(rateLevel); remote()->transact(CONFIGURE_CHANNEL, data, &reply); return reply.readInt32(); } virtual void onLastStrongRef(const void* id) { destroy(); BpInterface::onLastStrongRef(id); } protected: virtual void destroy() { Parcel data, reply; remote()->transact(DESTROY, data, &reply); } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpSensorEventConnection::~BpSensorEventConnection() { } IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection"); // ---------------------------------------------------------------------------- status_t BnSensorEventConnection::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case GET_SENSOR_CHANNEL: { CHECK_INTERFACE(ISensorEventConnection, data, reply); sp channel(getSensorChannel()); channel->writeToParcel(reply); return NO_ERROR; } case ENABLE_DISABLE: { CHECK_INTERFACE(ISensorEventConnection, data, reply); int handle = data.readInt32(); int enabled = data.readInt32(); nsecs_t samplingPeriodNs = data.readInt64(); nsecs_t maxBatchReportLatencyNs = data.readInt64(); int reservedFlags = data.readInt32(); status_t result = enableDisable(handle, enabled, samplingPeriodNs, maxBatchReportLatencyNs, reservedFlags); reply->writeInt32(result); return NO_ERROR; } case SET_EVENT_RATE: { CHECK_INTERFACE(ISensorEventConnection, data, reply); int handle = data.readInt32(); nsecs_t ns = data.readInt64(); status_t result = setEventRate(handle, ns); reply->writeInt32(result); return NO_ERROR; } case FLUSH_SENSOR: { CHECK_INTERFACE(ISensorEventConnection, data, reply); status_t result = flush(); reply->writeInt32(result); return NO_ERROR; } case CONFIGURE_CHANNEL: { CHECK_INTERFACE(ISensorEventConnection, data, reply); int handle = data.readInt32(); int rateLevel = data.readInt32(); status_t result = configureChannel(handle, rateLevel); reply->writeInt32(result); return NO_ERROR; } case DESTROY: { destroy(); return NO_ERROR; } } return BBinder::onTransact(code, data, reply, flags); } // ---------------------------------------------------------------------------- }; // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/ISensorServer.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000022313 13756501735 015341� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- enum { GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION, CREATE_SENSOR_EVENT_CONNECTION, ENABLE_DATA_INJECTION, GET_DYNAMIC_SENSOR_LIST, CREATE_SENSOR_DIRECT_CONNECTION, SET_OPERATION_PARAMETER, }; class BpSensorServer : public BpInterface { public: explicit BpSensorServer(const sp& impl) : BpInterface(impl) { } virtual ~BpSensorServer(); virtual Vector getSensorList(const String16& opPackageName) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeString16(opPackageName); remote()->transact(GET_SENSOR_LIST, data, &reply); Sensor s; Vector v; uint32_t n = reply.readUint32(); v.setCapacity(n); while (n) { n--; reply.read(s); v.add(s); } return v; } virtual Vector getDynamicSensorList(const String16& opPackageName) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeString16(opPackageName); remote()->transact(GET_DYNAMIC_SENSOR_LIST, data, &reply); Sensor s; Vector v; uint32_t n = reply.readUint32(); v.setCapacity(n); while (n) { n--; reply.read(s); v.add(s); } return v; } virtual sp createSensorEventConnection(const String8& packageName, int mode, const String16& opPackageName) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeString8(packageName); data.writeInt32(mode); data.writeString16(opPackageName); remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply); return interface_cast(reply.readStrongBinder()); } virtual int isDataInjectionEnabled() { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); remote()->transact(ENABLE_DATA_INJECTION, data, &reply); return reply.readInt32(); } virtual sp createSensorDirectConnection(const String16& opPackageName, uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeString16(opPackageName); data.writeUint32(size); data.writeInt32(type); data.writeInt32(format); data.writeNativeHandle(resource); remote()->transact(CREATE_SENSOR_DIRECT_CONNECTION, data, &reply); return interface_cast(reply.readStrongBinder()); } virtual int setOperationParameter(int32_t handle, int32_t type, const Vector &floats, const Vector &ints) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeInt32(handle); data.writeInt32(type); data.writeUint32(static_cast(floats.size())); for (auto i : floats) { data.writeFloat(i); } data.writeUint32(static_cast(ints.size())); for (auto i : ints) { data.writeInt32(i); } remote()->transact(SET_OPERATION_PARAMETER, data, &reply); return reply.readInt32(); } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpSensorServer::~BpSensorServer() {} IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer"); // ---------------------------------------------------------------------- status_t BnSensorServer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case GET_SENSOR_LIST: { CHECK_INTERFACE(ISensorServer, data, reply); const String16& opPackageName = data.readString16(); Vector v(getSensorList(opPackageName)); size_t n = v.size(); reply->writeUint32(static_cast(n)); for (size_t i = 0; i < n; i++) { reply->write(v[i]); } return NO_ERROR; } case CREATE_SENSOR_EVENT_CONNECTION: { CHECK_INTERFACE(ISensorServer, data, reply); String8 packageName = data.readString8(); int32_t mode = data.readInt32(); const String16& opPackageName = data.readString16(); sp connection(createSensorEventConnection(packageName, mode, opPackageName)); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } case ENABLE_DATA_INJECTION: { CHECK_INTERFACE(ISensorServer, data, reply); int32_t ret = isDataInjectionEnabled(); reply->writeInt32(static_cast(ret)); return NO_ERROR; } case GET_DYNAMIC_SENSOR_LIST: { CHECK_INTERFACE(ISensorServer, data, reply); const String16& opPackageName = data.readString16(); Vector v(getDynamicSensorList(opPackageName)); size_t n = v.size(); reply->writeUint32(static_cast(n)); for (size_t i = 0; i < n; i++) { reply->write(v[i]); } return NO_ERROR; } case CREATE_SENSOR_DIRECT_CONNECTION: { CHECK_INTERFACE(ISensorServer, data, reply); const String16& opPackageName = data.readString16(); uint32_t size = data.readUint32(); int32_t type = data.readInt32(); int32_t format = data.readInt32(); native_handle_t *resource = data.readNativeHandle(); sp ch = createSensorDirectConnection(opPackageName, size, type, format, resource); native_handle_close(resource); native_handle_delete(resource); reply->writeStrongBinder(IInterface::asBinder(ch)); return NO_ERROR; } case SET_OPERATION_PARAMETER: { CHECK_INTERFACE(ISensorServer, data, reply); int32_t handle; int32_t type; Vector floats; Vector ints; handle = data.readInt32(); type = data.readInt32(); floats.resize(data.readUint32()); for (auto &i : floats) { i = data.readFloat(); } ints.resize(data.readUint32()); for (auto &i : ints) { i = data.readInt32(); } int32_t ret = setOperationParameter(handle, type, floats, ints); reply->writeInt32(ret); return NO_ERROR; } case SHELL_COMMAND_TRANSACTION: { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); int argc = data.readInt32(); Vector args; for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } sp unusedCallback; sp resultReceiver; status_t status; if ((status = data.readNullableStrongBinder(&unusedCallback)) != NO_ERROR) { return status; } if ((status = data.readNullableStrongBinder(&resultReceiver)) != NO_ERROR) { return status; } status = shellCommand(in, out, err, args); if (resultReceiver != nullptr) { resultReceiver->send(status); } return NO_ERROR; } } return BBinder::onTransact(code, data, reply, flags); } // ---------------------------------------------------------------------------- }; // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/OWNERS����������������������������������������������������������������������������������0100644 0000000 0000000 00000000070 13756501735 013020� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������arthuri@google.com bduddie@google.com bstack@google.com ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/Sensor.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000052736 13756501735 014055� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #include #include #include #include #include /* * The permission to use for activity recognition sensors (like step counter). * See sensor types for more details on what sensors should require this * permission. */ #define SENSOR_PERMISSION_ACTIVITY_RECOGNITION "android.permission.ACTIVITY_RECOGNITION" // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- Sensor::Sensor(const char * name) : mName(name), mHandle(0), mType(0), mMinValue(0), mMaxValue(0), mResolution(0), mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0), mFifoMaxEventCount(0), mRequiredAppOp(-1), mMaxDelay(0), mFlags(0) { } Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) : Sensor(*hwSensor, uuid_t(), halVersion) { } Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion) : Sensor("") { mName = hwSensor.name; mVendor = hwSensor.vendor; mVersion = hwSensor.version; mHandle = hwSensor.handle; mType = hwSensor.type; mMinValue = 0; // FIXME: minValue mMaxValue = hwSensor.maxRange; // FIXME: maxValue mResolution = hwSensor.resolution; mPower = hwSensor.power; mMinDelay = hwSensor.minDelay; mFlags = 0; mUuid = uuid; // Set fifo event count zero for older devices which do not support batching. Fused // sensors also have their fifo counts set to zero. if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) { mFifoReservedEventCount = hwSensor.fifoReservedEventCount; mFifoMaxEventCount = hwSensor.fifoMaxEventCount; } else { mFifoReservedEventCount = 0; mFifoMaxEventCount = 0; } if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { if (hwSensor.maxDelay > INT_MAX) { // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should // always fit in a 32 bit integer, log error and cap it to INT_MAX. ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(), static_cast(hwSensor.maxDelay)); mMaxDelay = INT_MAX; } else { mMaxDelay = static_cast(hwSensor.maxDelay); } } else { // For older hals set maxDelay to 0. mMaxDelay = 0; } // Ensure existing sensors have correct string type, required permissions and reporting mode. // Set reportingMode for all android defined sensor types, set wake-up flag only for proximity // sensor, significant motion, tilt, pick_up gesture, wake gesture and glance gesture on older // HALs. Newer HALs can define both wake-up and non wake-up proximity sensors. // All the OEM defined defined sensors have flags set to whatever is provided by the HAL. switch (mType) { case SENSOR_TYPE_ACCELEROMETER: mStringType = SENSOR_STRING_TYPE_ACCELEROMETER; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_AMBIENT_TEMPERATURE: mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; break; case SENSOR_TYPE_GAME_ROTATION_VECTOR: mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR: mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_GRAVITY: mStringType = SENSOR_STRING_TYPE_GRAVITY; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_GYROSCOPE: mStringType = SENSOR_STRING_TYPE_GYROSCOPE; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_HEART_RATE: { mStringType = SENSOR_STRING_TYPE_HEART_RATE; mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS; AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; } break; case SENSOR_TYPE_LIGHT: mStringType = SENSOR_STRING_TYPE_LIGHT; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; break; case SENSOR_TYPE_LINEAR_ACCELERATION: mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_MAGNETIC_FIELD: mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_ORIENTATION: mStringType = SENSOR_STRING_TYPE_ORIENTATION; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_PRESSURE: mStringType = SENSOR_STRING_TYPE_PRESSURE; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_PROXIMITY: mStringType = SENSOR_STRING_TYPE_PROXIMITY; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_RELATIVE_HUMIDITY: mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; break; case SENSOR_TYPE_ROTATION_VECTOR: mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_SIGNIFICANT_MOTION: mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION; mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_STEP_COUNTER: { mStringType = SENSOR_STRING_TYPE_STEP_COUNTER; mRequiredPermission = SENSOR_PERMISSION_ACTIVITY_RECOGNITION; AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; } break; case SENSOR_TYPE_STEP_DETECTOR: { mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR; mRequiredPermission = SENSOR_PERMISSION_ACTIVITY_RECOGNITION; AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; } break; case SENSOR_TYPE_TEMPERATURE: mStringType = SENSOR_STRING_TYPE_TEMPERATURE; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; break; case SENSOR_TYPE_TILT_DETECTOR: mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_WAKE_GESTURE: mStringType = SENSOR_STRING_TYPE_WAKE_GESTURE; mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_GLANCE_GESTURE: mStringType = SENSOR_STRING_TYPE_GLANCE_GESTURE; mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_PICK_UP_GESTURE: mStringType = SENSOR_STRING_TYPE_PICK_UP_GESTURE; mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT: mStringType = SENSOR_STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; break; case SENSOR_TYPE_WRIST_TILT_GESTURE: mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_DYNAMIC_SENSOR_META: mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_POSE_6DOF: mStringType = SENSOR_STRING_TYPE_POSE_6DOF; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; case SENSOR_TYPE_STATIONARY_DETECT: mStringType = SENSOR_STRING_TYPE_STATIONARY_DETECT; mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_MOTION_DETECT: mStringType = SENSOR_STRING_TYPE_MOTION_DETECT; mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= SENSOR_FLAG_WAKE_UP; } break; case SENSOR_TYPE_HEART_BEAT: mStringType = SENSOR_STRING_TYPE_HEART_BEAT; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; break; // TODO: Placeholder for LLOB sensor type case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED: mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; default: // Only pipe the stringType, requiredPermission and flags for custom sensors. if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) { mStringType = hwSensor.stringType; } if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) { mRequiredPermission = hwSensor.requiredPermission; if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) { AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS)); } } if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { mFlags = static_cast(hwSensor.flags); } else { // This is an OEM defined sensor on an older HAL. Use minDelay to determine the // reporting mode of the sensor. if (mMinDelay > 0) { mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; } else if (mMinDelay == 0) { mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; } else if (mMinDelay < 0) { mFlags |= SENSOR_FLAG_ONE_SHOT_MODE; } } break; } if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { // Wake-up flag of HAL 1.3 and above is set here mFlags |= (hwSensor.flags & SENSOR_FLAG_WAKE_UP); // Log error if the reporting mode is not as expected, but respect HAL setting. int actualReportingMode = (hwSensor.flags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT; int expectedReportingMode = (mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT; if (actualReportingMode != expectedReportingMode) { ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " " "actual=%d expected=%d", mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode); } } // Feature flags // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3. if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { mFlags |= hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK); } // Set DIRECT_REPORT_MASK and DIRECT_CHANNEL_MASK flags. Compatible with HAL 1_3. if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) { // only on continuous sensors direct report mode is defined if ((mFlags & REPORTING_MODE_MASK) == SENSOR_FLAG_CONTINUOUS_MODE) { mFlags |= hwSensor.flags & (SENSOR_FLAG_MASK_DIRECT_REPORT | SENSOR_FLAG_MASK_DIRECT_CHANNEL); } } // Set DATA_INJECTION flag here. Defined in HAL 1_4. if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) { mFlags |= (hwSensor.flags & DATA_INJECTION_MASK); } if (mRequiredPermission.length() > 0) { // If the sensor is protected by a permission we need to know if it is // a runtime one to determine whether we can use the permission cache. sp binder = defaultServiceManager()->getService(String16("permission")); if (binder != nullptr) { sp permCtrl = interface_cast(binder); mRequiredPermissionRuntime = permCtrl->isRuntimePermission( String16(mRequiredPermission)); } } } Sensor::~Sensor() { } const String8& Sensor::getName() const { return mName; } const String8& Sensor::getVendor() const { return mVendor; } int32_t Sensor::getHandle() const { return mHandle; } int32_t Sensor::getType() const { return mType; } float Sensor::getMinValue() const { return mMinValue; } float Sensor::getMaxValue() const { return mMaxValue; } float Sensor::getResolution() const { return mResolution; } float Sensor::getPowerUsage() const { return mPower; } int32_t Sensor::getMinDelay() const { return mMinDelay; } nsecs_t Sensor::getMinDelayNs() const { return getMinDelay() * 1000; } int32_t Sensor::getVersion() const { return mVersion; } uint32_t Sensor::getFifoReservedEventCount() const { return mFifoReservedEventCount; } uint32_t Sensor::getFifoMaxEventCount() const { return mFifoMaxEventCount; } const String8& Sensor::getStringType() const { return mStringType; } const String8& Sensor::getRequiredPermission() const { return mRequiredPermission; } bool Sensor::isRequiredPermissionRuntime() const { return mRequiredPermissionRuntime; } int32_t Sensor::getRequiredAppOp() const { return mRequiredAppOp; } int32_t Sensor::getMaxDelay() const { return mMaxDelay; } uint32_t Sensor::getFlags() const { return mFlags; } bool Sensor::isWakeUpSensor() const { return (mFlags & SENSOR_FLAG_WAKE_UP) != 0; } bool Sensor::isDynamicSensor() const { return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0; } bool Sensor::isDataInjectionSupported() const { return (mFlags & SENSOR_FLAG_DATA_INJECTION) != 0; } bool Sensor::hasAdditionalInfo() const { return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0; } int32_t Sensor::getHighestDirectReportRateLevel() const { return ((mFlags & SENSOR_FLAG_MASK_DIRECT_REPORT) >> SENSOR_FLAG_SHIFT_DIRECT_REPORT); } bool Sensor::isDirectChannelTypeSupported(int32_t sharedMemType) const { switch (sharedMemType) { case SENSOR_DIRECT_MEM_TYPE_ASHMEM: return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM; case SENSOR_DIRECT_MEM_TYPE_GRALLOC: return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC; default: return false; } } int32_t Sensor::getReportingMode() const { return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT); } const Sensor::uuid_t& Sensor::getUuid() const { return mUuid; } void Sensor::setId(int32_t id) { mUuid.i64[0] = id; mUuid.i64[1] = 0; } int32_t Sensor::getId() const { return int32_t(mUuid.i64[0]); } size_t Sensor::getFlattenedSize() const { size_t fixedSize = sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid); size_t variableSize = sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) + sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) + sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) + sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length()); return fixedSize + variableSize; } status_t Sensor::flatten(void* buffer, size_t size) const { if (size < getFlattenedSize()) { return NO_MEMORY; } flattenString8(buffer, size, mName); flattenString8(buffer, size, mVendor); FlattenableUtils::write(buffer, size, mVersion); FlattenableUtils::write(buffer, size, mHandle); FlattenableUtils::write(buffer, size, mType); FlattenableUtils::write(buffer, size, mMinValue); FlattenableUtils::write(buffer, size, mMaxValue); FlattenableUtils::write(buffer, size, mResolution); FlattenableUtils::write(buffer, size, mPower); FlattenableUtils::write(buffer, size, mMinDelay); FlattenableUtils::write(buffer, size, mFifoReservedEventCount); FlattenableUtils::write(buffer, size, mFifoMaxEventCount); flattenString8(buffer, size, mStringType); flattenString8(buffer, size, mRequiredPermission); FlattenableUtils::write(buffer, size, mRequiredPermissionRuntime); FlattenableUtils::write(buffer, size, mRequiredAppOp); FlattenableUtils::write(buffer, size, mMaxDelay); FlattenableUtils::write(buffer, size, mFlags); if (mUuid.i64[1] != 0) { // We should never hit this case with our current API, but we // could via a careless API change. If that happens, // this code will keep us from leaking our UUID (while probably // breaking dynamic sensors). See b/29547335. ALOGW("Sensor with UUID being flattened; sending 0. Expect " "bad dynamic sensor behavior"); uuid_t tmpUuid; // default constructor makes this 0. FlattenableUtils::write(buffer, size, tmpUuid); } else { FlattenableUtils::write(buffer, size, mUuid); } return NO_ERROR; } status_t Sensor::unflatten(void const* buffer, size_t size) { if (!unflattenString8(buffer, size, mName)) { return NO_MEMORY; } if (!unflattenString8(buffer, size, mVendor)) { return NO_MEMORY; } size_t fixedSize1 = sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount); if (size < fixedSize1) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, mVersion); FlattenableUtils::read(buffer, size, mHandle); FlattenableUtils::read(buffer, size, mType); FlattenableUtils::read(buffer, size, mMinValue); FlattenableUtils::read(buffer, size, mMaxValue); FlattenableUtils::read(buffer, size, mResolution); FlattenableUtils::read(buffer, size, mPower); FlattenableUtils::read(buffer, size, mMinDelay); FlattenableUtils::read(buffer, size, mFifoReservedEventCount); FlattenableUtils::read(buffer, size, mFifoMaxEventCount); if (!unflattenString8(buffer, size, mStringType)) { return NO_MEMORY; } if (!unflattenString8(buffer, size, mRequiredPermission)) { return NO_MEMORY; } size_t fixedSize2 = sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid); if (size < fixedSize2) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, mRequiredPermissionRuntime); FlattenableUtils::read(buffer, size, mRequiredAppOp); FlattenableUtils::read(buffer, size, mMaxDelay); FlattenableUtils::read(buffer, size, mFlags); FlattenableUtils::read(buffer, size, mUuid); return NO_ERROR; } void Sensor::flattenString8(void*& buffer, size_t& size, const String8& string8) { uint32_t len = static_cast(string8.length()); FlattenableUtils::write(buffer, size, len); memcpy(static_cast(buffer), string8.string(), len); FlattenableUtils::advance(buffer, size, len); size -= FlattenableUtils::align<4>(buffer); } bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) { uint32_t len; if (size < sizeof(len)) { return false; } FlattenableUtils::read(buffer, size, len); if (size < len) { return false; } outputString8.setTo(static_cast(buffer), len); FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); return true; } // ---------------------------------------------------------------------------- }; // namespace android ����������������������������������libs/sensor/SensorEventQueue.cpp��������������������������������������������������������������������0100644 0000000 0000000 00000020174 13756501735 016053� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #define LOG_TAG "Sensors" #include #include #include #include #include #include #include #include #include #include using std::min; // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp& connection) : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0), mNumAcksToSend(0) { mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } SensorEventQueue::~SensorEventQueue() { delete [] mRecBuffer; } void SensorEventQueue::onFirstRef() { mSensorChannel = mSensorEventConnection->getSensorChannel(); } int SensorEventQueue::getFd() const { return mSensorChannel->getFd(); } ssize_t SensorEventQueue::write(const sp& tube, ASensorEvent const* events, size_t numEvents) { return BitTube::sendObjects(tube, events, numEvents); } ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { if (mAvailable == 0) { ssize_t err = BitTube::recvObjects(mSensorChannel, mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT); if (err < 0) { return err; } mAvailable = static_cast(err); mConsumed = 0; } size_t count = min(numEvents, mAvailable); memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent)); mAvailable -= count; mConsumed += count; return static_cast(count); } sp SensorEventQueue::getLooper() const { Mutex::Autolock _l(mLock); if (mLooper == nullptr) { mLooper = new Looper(true); mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, nullptr, nullptr); } return mLooper; } status_t SensorEventQueue::waitForEvent() const { const int fd = getFd(); sp looper(getLooper()); int events; int32_t result; do { result = looper->pollOnce(-1, nullptr, &events, nullptr); if (result == ALOOPER_POLL_ERROR) { ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno); result = -EPIPE; // unknown error, so we make up one break; } if (events & ALOOPER_EVENT_HANGUP) { // the other-side has died ALOGE("SensorEventQueue::waitForEvent error HANGUP"); result = -EPIPE; // unknown error, so we make up one break; } } while (result != fd); return (result == fd) ? status_t(NO_ERROR) : result; } status_t SensorEventQueue::wake() const { sp looper(getLooper()); looper->wake(); return NO_ERROR; } status_t SensorEventQueue::enableSensor(Sensor const* sensor) const { return enableSensor(sensor, SENSOR_DELAY_NORMAL); } status_t SensorEventQueue::enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const { return mSensorEventConnection->enableDisable(sensor->getHandle(), true, us2ns(samplingPeriodUs), 0, 0); } status_t SensorEventQueue::disableSensor(Sensor const* sensor) const { return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, 0); } status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs, int reservedFlags) const { return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs), us2ns(maxBatchReportLatencyUs), reservedFlags); } status_t SensorEventQueue::flush() const { return mSensorEventConnection->flush(); } status_t SensorEventQueue::disableSensor(int32_t handle) const { return mSensorEventConnection->enableDisable(handle, false, 0, 0, false); } status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const { return mSensorEventConnection->setEventRate(sensor->getHandle(), ns); } status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) { do { // Blocking call. ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL); if (size >= 0) { return NO_ERROR; } else if (size < 0 && errno == EAGAIN) { // If send is returning a "Try again" error, sleep for 100ms and try again. In all // other cases log a failure and exit. usleep(100000); } else { ALOGE("injectSensorEvent failure %s %zd", strerror(errno), size); return INVALID_OPERATION; } } while (true); } void SensorEventQueue::sendAck(const ASensorEvent* events, int count) { for (int i = 0; i < count; ++i) { if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) { ++mNumAcksToSend; } } // Send mNumAcksToSend to acknowledge for the wake up sensor events received. if (mNumAcksToSend > 0) { ssize_t size = ::send(mSensorChannel->getFd(), &mNumAcksToSend, sizeof(mNumAcksToSend), MSG_DONTWAIT | MSG_NOSIGNAL); if (size < 0) { ALOGE("sendAck failure %zd %d", size, mNumAcksToSend); } else { mNumAcksToSend = 0; } } return; } ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const { // Check if this Sensor Event Queue is registered to receive each type of event. If it is not, // then do not copy the event into the final buffer. Minimize the number of copy operations by // finding consecutive sequences of events that the Sensor Event Queue should receive and only // copying the events once an unregistered event type is reached. bool intervalStartLocSet = false; size_t intervalStartLoc = 0; size_t eventsInInterval = 0; ssize_t eventsCopied = 0; for (size_t i = 0; i < count; i++) { bool includeEvent = (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo); if (includeEvent) { // Do not copy events yet since there may be more consecutive events that should be // copied together. Track the start location and number of events in the current // sequence. if (!intervalStartLocSet) { intervalStartLoc = i; intervalStartLocSet = true; eventsInInterval = 0; } eventsInInterval++; } // Shift the events from the already processed interval once an event that should not be // included is reached or if this is the final event to be processed. if (!includeEvent || (i + 1 == count)) { // Only shift the events if the interval did not start with the first event. If the // interval started with the first event, the events are already in their correct // location. if (intervalStartLoc > 0) { memmove(&events[eventsCopied], &events[intervalStartLoc], eventsInInterval * sizeof(ASensorEvent)); } eventsCopied += eventsInInterval; // Reset the interval information eventsInInterval = 0; intervalStartLocSet = false; } } return eventsCopied; } // ---------------------------------------------------------------------------- }; // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/SensorManager.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000025651 13756501735 015344� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #define LOG_TAG "Sensors" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- Mutex SensorManager::sLock; std::map SensorManager::sPackageInstances; SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) { waitForSensorService(nullptr); Mutex::Autolock _l(sLock); SensorManager* sensorManager; auto iterator = sPackageInstances.find(packageName); if (iterator != sPackageInstances.end()) { sensorManager = iterator->second; } else { String16 opPackageName = packageName; // It is possible that the calling code has no access to the package name. // In this case we will get the packages for the calling UID and pick the // first one for attributing the app op. This will work correctly for // runtime permissions as for legacy apps we will toggle the app op for // all packages in the UID. The caveat is that the operation may be attributed // to the wrong package and stats based on app ops may be slightly off. if (opPackageName.size() <= 0) { sp binder = defaultServiceManager()->getService(String16("permission")); if (binder != nullptr) { const uid_t uid = IPCThreadState::self()->getCallingUid(); Vector packages; interface_cast(binder)->getPackagesForUid(uid, packages); if (!packages.isEmpty()) { opPackageName = packages[0]; } else { ALOGE("No packages for calling UID"); } } else { ALOGE("Cannot get permission service"); } } sensorManager = new SensorManager(opPackageName); // If we had no package name, we looked it up from the UID and the sensor // manager instance we created should also be mapped to the empty package // name, to avoid looking up the packages for a UID and get the same result. if (packageName.size() <= 0) { sPackageInstances.insert(std::make_pair(String16(), sensorManager)); } // Stash the per package sensor manager. sPackageInstances.insert(std::make_pair(opPackageName, sensorManager)); } return *sensorManager; } SensorManager::SensorManager(const String16& opPackageName) : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { Mutex::Autolock _l(mLock); assertStateLocked(); } SensorManager::~SensorManager() { free(mSensorList); } status_t SensorManager::waitForSensorService(sp *server) { // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ... sp s; const String16 name("sensorservice"); for (int i = 0; i < 60; i++) { status_t err = getService(name, &s); switch (err) { case NAME_NOT_FOUND: sleep(1); continue; case NO_ERROR: if (server != nullptr) { *server = s; } return NO_ERROR; default: return err; } } return TIMED_OUT; } void SensorManager::sensorManagerDied() { Mutex::Autolock _l(mLock); mSensorServer.clear(); free(mSensorList); mSensorList = nullptr; mSensors.clear(); } status_t SensorManager::assertStateLocked() { bool initSensorManager = false; if (mSensorServer == nullptr) { initSensorManager = true; } else { // Ping binder to check if sensorservice is alive. status_t err = IInterface::asBinder(mSensorServer)->pingBinder(); if (err != NO_ERROR) { initSensorManager = true; } } if (initSensorManager) { waitForSensorService(&mSensorServer); LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL"); class DeathObserver : public IBinder::DeathRecipient { SensorManager& mSensorManager; virtual void binderDied(const wp& who) { ALOGW("sensorservice died [%p]", who.unsafe_get()); mSensorManager.sensorManagerDied(); } public: explicit DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { } }; mDeathObserver = new DeathObserver(*const_cast(this)); IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver); mSensors = mSensorServer->getSensorList(mOpPackageName); size_t count = mSensors.size(); mSensorList = static_cast(malloc(count * sizeof(Sensor*))); LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL"); for (size_t i=0 ; i(err); } *list = mSensorList; return static_cast(mSensors.size()); } ssize_t SensorManager::getDynamicSensorList(Vector & dynamicSensors) { Mutex::Autolock _l(mLock); status_t err = assertStateLocked(); if (err < 0) { return static_cast(err); } dynamicSensors = mSensorServer->getDynamicSensorList(mOpPackageName); size_t count = dynamicSensors.size(); return static_cast(count); } Sensor const* SensorManager::getDefaultSensor(int type) { Mutex::Autolock _l(mLock); if (assertStateLocked() == NO_ERROR) { bool wakeUpSensor = false; // For the following sensor types, return a wake-up sensor. These types are by default // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return // a non_wake-up version. if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION || type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE || type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE || type == SENSOR_TYPE_WRIST_TILT_GESTURE || type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) { wakeUpSensor = true; } // For now we just return the first sensor of that type we find. // in the future it will make sense to let the SensorService make // that decision. for (size_t i=0 ; igetType() == type && mSensorList[i]->isWakeUpSensor() == wakeUpSensor) { return mSensorList[i]; } } } return nullptr; } sp SensorManager::createEventQueue(String8 packageName, int mode) { sp queue; Mutex::Autolock _l(mLock); while (assertStateLocked() == NO_ERROR) { sp connection = mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName); if (connection == nullptr) { // SensorService just died or the app doesn't have required permissions. ALOGE("createEventQueue: connection is NULL."); return nullptr; } queue = new SensorEventQueue(connection); break; } return queue; } bool SensorManager::isDataInjectionEnabled() { Mutex::Autolock _l(mLock); if (assertStateLocked() == NO_ERROR) { return mSensorServer->isDataInjectionEnabled(); } return false; } int SensorManager::createDirectChannel( size_t size, int channelType, const native_handle_t *resourceHandle) { Mutex::Autolock _l(mLock); if (assertStateLocked() != NO_ERROR) { return NO_INIT; } if (channelType != SENSOR_DIRECT_MEM_TYPE_ASHMEM && channelType != SENSOR_DIRECT_MEM_TYPE_GRALLOC) { ALOGE("Bad channel shared memory type %d", channelType); return BAD_VALUE; } sp conn = mSensorServer->createSensorDirectConnection(mOpPackageName, static_cast(size), static_cast(channelType), SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle); if (conn == nullptr) { return NO_MEMORY; } int nativeHandle = mDirectConnectionHandle++; mDirectConnection.emplace(nativeHandle, conn); return nativeHandle; } void SensorManager::destroyDirectChannel(int channelNativeHandle) { Mutex::Autolock _l(mLock); if (assertStateLocked() == NO_ERROR) { mDirectConnection.erase(channelNativeHandle); } } int SensorManager::configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel) { Mutex::Autolock _l(mLock); if (assertStateLocked() != NO_ERROR) { return NO_INIT; } auto i = mDirectConnection.find(channelNativeHandle); if (i == mDirectConnection.end()) { ALOGE("Cannot find the handle in client direct connection table"); return BAD_VALUE; } int ret; ret = i->second->configureChannel(sensorHandle, rateLevel); ALOGE_IF(ret < 0, "SensorManager::configureChannel (%d, %d) returns %d", static_cast(sensorHandle), static_cast(rateLevel), static_cast(ret)); return ret; } int SensorManager::setOperationParameter( int handle, int type, const Vector &floats, const Vector &ints) { Mutex::Autolock _l(mLock); if (assertStateLocked() != NO_ERROR) { return NO_INIT; } return mSensorServer->setOperationParameter(handle, type, floats, ints); } // ---------------------------------------------------------------------------- }; // namespace android ���������������������������������������������������������������������������������������libs/sensor/include/��������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013511� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015022� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/BitTube.h����������������������������������������������������������������0100644 0000000 0000000 00000005357 13756501735 016540� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #pragma once #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class Parcel; class BitTube : public RefBase { public: // creates a BitTube with a default (4KB) send buffer BitTube(); // creates a BitTube with a a specified send and receive buffer size explicit BitTube(size_t bufsize); explicit BitTube(const Parcel& data); virtual ~BitTube(); // check state after construction status_t initCheck() const; // get receive file-descriptor int getFd() const; // get the send file-descriptor. int getSendFd() const; // send objects (sized blobs). All objects are guaranteed to be written or the call fails. template static ssize_t sendObjects(const sp& tube, T const* events, size_t count) { return sendObjects(tube, events, count, sizeof(T)); } // receive objects (sized blobs). If the receiving buffer isn't large enough, // excess messages are silently discarded. template static ssize_t recvObjects(const sp& tube, T* events, size_t count) { return recvObjects(tube, events, count, sizeof(T)); } // parcels this BitTube status_t writeToParcel(Parcel* reply) const; private: void init(size_t rcvbuf, size_t sndbuf); // send a message. The write is guaranteed to send the whole message or fail. ssize_t write(void const* vaddr, size_t size); // receive a message. the passed buffer must be at least as large as the // write call used to send the message, excess data is silently discarded. ssize_t read(void* vaddr, size_t size); int mSendFd; mutable int mReceiveFd; static ssize_t sendObjects(const sp& tube, void const* events, size_t count, size_t objSize); static ssize_t recvObjects(const sp& tube, void* events, size_t count, size_t objSize); }; // ---------------------------------------------------------------------------- }; // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/ISensorEventConnection.h�������������������������������������������������0100644 0000000 0000000 00000003765 13756501735 021607� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #pragma once #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class BitTube; class Parcel; class ISensorEventConnection : public IInterface { public: DECLARE_META_INTERFACE(SensorEventConnection) virtual sp getSensorChannel() const = 0; virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0; virtual status_t setEventRate(int handle, nsecs_t ns) = 0; virtual status_t flush() = 0; virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) = 0; protected: virtual void destroy() = 0; // synchronously release resource hold by remote object }; // ---------------------------------------------------------------------------- class BnSensorEventConnection : public BnInterface { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------------- }; // namespace android �����������libs/sensor/include/sensor/ISensorServer.h����������������������������������������������������������0100644 0000000 0000000 00000004625 13756501735 017750� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #pragma once #include #include #include #include #include #include struct native_handle; typedef struct native_handle native_handle_t; namespace android { // ---------------------------------------------------------------------------- class ISensorEventConnection; class Parcel; class Sensor; class String8; class String16; class ISensorServer : public IInterface { public: DECLARE_META_INTERFACE(SensorServer) virtual Vector getSensorList(const String16& opPackageName) = 0; virtual Vector getDynamicSensorList(const String16& opPackageName) = 0; virtual sp createSensorEventConnection(const String8& packageName, int mode, const String16& opPackageName) = 0; virtual int32_t isDataInjectionEnabled() = 0; virtual sp createSensorDirectConnection(const String16& opPackageName, uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0; virtual int setOperationParameter( int32_t handle, int32_t type, const Vector &floats, const Vector &ints) = 0; }; // ---------------------------------------------------------------------------- class BnSensorServer : public BnInterface { public: virtual status_t shellCommand(int in, int out, int err, Vector& args) = 0; virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------------- }; // namespace android �����������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/Sensor.h�����������������������������������������������������������������0100644 0000000 0000000 00000011266 13756501735 016447� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #pragma once #include #include #include #include #include #include // FIXME: including from android/ breaks layering, as libandroid ultimately depends on libsensors #include #include // ---------------------------------------------------------------------------- // Concrete types for the NDK struct ASensor { }; // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- class Parcel; // ---------------------------------------------------------------------------- class Sensor : public ASensor, public LightFlattenable { public: enum { TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD, TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE, TYPE_LIGHT = ASENSOR_TYPE_LIGHT, TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY }; struct uuid_t{ union { uint8_t b[16]; int64_t i64[2]; }; explicit uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));} uuid_t() : b{0} {} }; Sensor(const Sensor&) = default; Sensor& operator=(const Sensor&) = default; explicit Sensor(const char * name = ""); explicit Sensor(struct sensor_t const* hwSensor, int halVersion = 0); Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0); ~Sensor(); const String8& getName() const; const String8& getVendor() const; int32_t getHandle() const; int32_t getType() const; float getMinValue() const; float getMaxValue() const; float getResolution() const; float getPowerUsage() const; int32_t getMinDelay() const; nsecs_t getMinDelayNs() const; int32_t getVersion() const; uint32_t getFifoReservedEventCount() const; uint32_t getFifoMaxEventCount() const; const String8& getStringType() const; const String8& getRequiredPermission() const; bool isRequiredPermissionRuntime() const; int32_t getRequiredAppOp() const; int32_t getMaxDelay() const; uint32_t getFlags() const; bool isWakeUpSensor() const; bool isDynamicSensor() const; bool isDataInjectionSupported() const; bool hasAdditionalInfo() const; int32_t getHighestDirectReportRateLevel() const; bool isDirectChannelTypeSupported(int32_t sharedMemType) const; int32_t getReportingMode() const; // Note that after setId() has been called, getUuid() no longer // returns the UUID. // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and // make sure setId() doesn't change the UuidIndex. const uuid_t& getUuid() const; int32_t getId() const; void setId(int32_t id); // LightFlattenable protocol inline bool isFixedSize() const { return false; } size_t getFlattenedSize() const; status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); private: String8 mName; String8 mVendor; int32_t mHandle; int32_t mType; float mMinValue; float mMaxValue; float mResolution; float mPower; int32_t mMinDelay; int32_t mVersion; uint32_t mFifoReservedEventCount; uint32_t mFifoMaxEventCount; String8 mStringType; String8 mRequiredPermission; bool mRequiredPermissionRuntime = false; int32_t mRequiredAppOp; int32_t mMaxDelay; uint32_t mFlags; // TODO(b/29547335): Get rid of this field and replace with an index. // The index will be into a separate global vector of UUIDs. // Also add an mId field (and change flatten/unflatten appropriately). uuid_t mUuid; static void flattenString8(void*& buffer, size_t& size, const String8& string8); static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8); }; // ---------------------------------------------------------------------------- }; // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/SensorEventQueue.h�������������������������������������������������������0100644 0000000 0000000 00000007577 13756501735 020470� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 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. */ #pragma once #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- #define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31) struct ALooper; struct ASensorEvent; // Concrete types for the NDK struct ASensorEventQueue { ALooper* looper; bool requestAdditionalInfo; }; // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- class ISensorEventConnection; class Sensor; class Looper; // ---------------------------------------------------------------------------- class SensorEventQueue : public ASensorEventQueue, public RefBase { public: enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 }; /** * Typical sensor delay (sample period) in microseconds. */ // Fastest sampling, system will bound it to minDelay static constexpr int32_t SENSOR_DELAY_FASTEST = 0; // Typical sample period for game, 50Hz; static constexpr int32_t SENSOR_DELAY_GAME = 20000; // Typical sample period for UI, 15Hz static constexpr int32_t SENSOR_DELAY_UI = 66667; // Default sensor sample period static constexpr int32_t SENSOR_DELAY_NORMAL = 200000; explicit SensorEventQueue(const sp& connection); virtual ~SensorEventQueue(); virtual void onFirstRef(); int getFd() const; static ssize_t write(const sp& tube, ASensorEvent const* events, size_t numEvents); ssize_t read(ASensorEvent* events, size_t numEvents); status_t waitForEvent() const; status_t wake() const; status_t enableSensor(Sensor const* sensor) const; status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const; status_t disableSensor(Sensor const* sensor) const; status_t setEventRate(Sensor const* sensor, nsecs_t ns) const; // these are here only to support SensorManager.java and HIDL Frameworks SensorManager. status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs, int reservedFlags) const; status_t disableSensor(int32_t handle) const; status_t flush() const; // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK. void sendAck(const ASensorEvent* events, int count); status_t injectSensorEvent(const ASensorEvent& event); // Filters the given sensor events in place and returns the new number of events. // // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed. ssize_t filterEvents(ASensorEvent* events, size_t count) const; private: sp getLooper() const; sp mSensorEventConnection; sp mSensorChannel; mutable Mutex mLock; mutable sp mLooper; ASensorEvent* mRecBuffer; size_t mAvailable; size_t mConsumed; uint32_t mNumAcksToSend; }; // ---------------------------------------------------------------------------- }; // namespace android ���������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/SensorManager.h����������������������������������������������������������0100644 0000000 0000000 00000006110 13756501735 017732� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_GUI_SENSOR_MANAGER_H #define ANDROID_GUI_SENSOR_MANAGER_H #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Concrete types for the NDK struct ASensorManager { }; struct native_handle; typedef struct native_handle native_handle_t; // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- class ISensorServer; class Sensor; class SensorEventQueue; // ---------------------------------------------------------------------------- class SensorManager : public ASensorManager { public: static SensorManager& getInstanceForPackage(const String16& packageName); ~SensorManager(); ssize_t getSensorList(Sensor const* const** list); ssize_t getDynamicSensorList(Vector& list); Sensor const* getDefaultSensor(int type); sp createEventQueue(String8 packageName = String8(""), int mode = 0); bool isDataInjectionEnabled(); int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData); void destroyDirectChannel(int channelNativeHandle); int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel); int setOperationParameter(int handle, int type, const Vector &floats, const Vector &ints); private: // DeathRecipient interface void sensorManagerDied(); static status_t waitForSensorService(sp *server); explicit SensorManager(const String16& opPackageName); status_t assertStateLocked(); private: static Mutex sLock; static std::map sPackageInstances; Mutex mLock; sp mSensorServer; Sensor const** mSensorList; Vector mSensors; sp mDeathObserver; const String16 mOpPackageName; std::unordered_map> mDirectConnection; int32_t mDirectConnectionHandle; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_SENSOR_MANAGER_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/tests/����������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013230� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/tests/Android.bp������������������������������������������������������������������������0100644 0000000 0000000 00000001551 13756501735 015132� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2017 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. cc_test { name: "libsensor_test", clang: true, cflags: ["-Wall", "-Werror"], srcs: [ "Sensor_test.cpp", "SensorEventQueue_test.cpp", ], shared_libs: [ "liblog", "libsensor", "libutils", ], } �������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/tests/SensorEventQueue_test.cpp���������������������������������������������������������0100644 0000000 0000000 00000013335 13756501735 020255� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2019 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. */ #include #include #include #include #include #include #include namespace android { class SensorEventQueueTest : public ::testing::Test { protected: typedef std::vector Events; SensorEventQueueTest() {}; virtual void SetUp() override { SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest")); mQueue = manager.createEventQueue(); } void configureAdditionalInfo(bool enable) { mQueue->requestAdditionalInfo = enable; } Events filterEvents(const Events &types) const { // Convert the events into SensorEvent array ASensorEvent* events = new ASensorEvent[types.size()]; for (size_t i = 0; i < types.size(); i++) { events[i].type = types[i]; } // Filter the events ssize_t filteredCount = mQueue->filterEvents(events, types.size()); // Copy the result into an output vector Events result; for (size_t i = 0; i < filteredCount; i++) { result.push_back(events[i].type); } delete[] events; return result; } Events getExpectedEvents(const Events &events) const { Events output; for (size_t i = 0; i != events.size(); i++) { // Copy events if the event queue is configured to receive them if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) { output.push_back(events[i]); } } return output; } void runFilterTest(const Events& events) { Events filtered = filterEvents(events); Events expected = getExpectedEvents(events); EXPECT_EQ(expected.size(), filtered.size()); EXPECT_EQ(expected, filtered); } private: sp mQueue; }; TEST_F(SensorEventQueueTest, FilterZeroEvents) { configureAdditionalInfo(false /* enable */); runFilterTest({}); } TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) { configureAdditionalInfo(true /* enable */); runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_MAGNETIC_FIELD}); } TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) { configureAdditionalInfo(false /* enable */); runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO}); } TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) { configureAdditionalInfo(false /* enable */); runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, SENSOR_TYPE_MAGNETIC_FIELD}); } TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) { configureAdditionalInfo(false /* enable */); runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_ADDITIONAL_INFO}); } TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) { configureAdditionalInfo(false /* enable */); runFilterTest({SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, SENSOR_TYPE_MAGNETIC_FIELD, SENSOR_TYPE_ADDITIONAL_INFO}); } TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) { configureAdditionalInfo(false /* enable */); runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ACCELEROMETER}); } TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) { configureAdditionalInfo(false /* enable */); runFilterTest({SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_MAGNETIC_FIELD}); } TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) { configureAdditionalInfo(false /* enable */); const Events events = {SENSOR_TYPE_ACCELEROMETER, SENSOR_TYPE_GYROSCOPE, SENSOR_TYPE_ADDITIONAL_INFO, SENSOR_TYPE_MAGNETIC_FIELD, SENSOR_TYPE_ADDITIONAL_INFO}; runFilterTest(events); // Update setting to request Additional Info configureAdditionalInfo(true /* enable */); runFilterTest(events); // Update setting to stop requesting Additional Info configureAdditionalInfo(true /* enable */); runFilterTest(events); } } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/tests/Sensor_test.cpp�������������������������������������������������������������������0100644 0000000 0000000 00000006424 13756501735 016247� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2012 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. */ #define LOG_TAG "Sensor_test" #include #include #include #include namespace android { // Returns true if the two sensors have the same attributes. Does not compare // UUID since that should not be transmitted via flatten/unflatten. static bool sensorsMatch(const Sensor& a, const Sensor& b) { return a.getName() == b.getName () && a.getVendor() == b.getVendor () && a.getHandle() == b.getHandle () && a.getType() == b.getType () && a.getMinValue() == b.getMinValue () && a.getMaxValue() == b.getMaxValue () && a.getResolution() == b.getResolution () && a.getPowerUsage() == b.getPowerUsage () && a.getMinDelay() == b.getMinDelay () && a.getMinDelayNs() == b.getMinDelayNs () && a.getVersion() == b.getVersion () && a.getFifoReservedEventCount() == b.getFifoReservedEventCount () && a.getFifoMaxEventCount() == b.getFifoMaxEventCount () && a.getStringType() == b.getStringType () && a.getRequiredPermission() == b.getRequiredPermission () && a.isRequiredPermissionRuntime() == b.isRequiredPermissionRuntime () && a.getRequiredAppOp() == b.getRequiredAppOp () && a.getMaxDelay() == b.getMaxDelay () && a.getFlags() == b.getFlags () && a.isWakeUpSensor() == b.isWakeUpSensor () && a.isDynamicSensor() == b.isDynamicSensor () && a.hasAdditionalInfo() == b.hasAdditionalInfo () && a.getReportingMode() == b.getReportingMode(); } // Creates and returns a sensor_t struct with some default values filled in. static sensor_t getTestSensorT() { sensor_t hwSensor = {}; hwSensor.name = "Test Sensor"; hwSensor.vendor = "Test Vendor"; hwSensor.version = 1; hwSensor.handle = 2; hwSensor.type = SENSOR_TYPE_ACCELEROMETER; hwSensor.maxRange = 10.f; hwSensor.resolution = 1.f; hwSensor.power = 5.f; hwSensor.minDelay = 1000; hwSensor.fifoReservedEventCount = 50; hwSensor.fifoMaxEventCount = 100; hwSensor.stringType = SENSOR_STRING_TYPE_ACCELEROMETER; hwSensor.requiredPermission = ""; hwSensor.maxDelay = 5000; hwSensor.flags = SENSOR_FLAG_CONTINUOUS_MODE; return hwSensor; } TEST(SensorTest, FlattenAndUnflatten) { sensor_t hwSensor = getTestSensorT(); Sensor sensor1(&hwSensor, SENSORS_DEVICE_API_VERSION_1_4); Sensor sensor2; std::vector buffer(sensor1.getFlattenedSize()); ASSERT_EQ(OK, sensor1.flatten(buffer.data(), buffer.size())); ASSERT_EQ(OK, sensor2.unflatten(buffer.data(), buffer.size())); EXPECT_TRUE(sensorsMatch(sensor1, sensor2)); } } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/���������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013464� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/Android.bp�����������������������������������������������������������������������0100644 0000000 0000000 00000002364 13756501735 015371� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2018 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. cc_library_shared { name: "libsensorprivacy", aidl: { export_aidl_headers: true, local_include_dirs: ["aidl"], }, cflags: [ "-Wall", "-Wextra", "-Werror", "-Wzero-as-null-pointer-constant", ], srcs: [ "aidl/android/hardware/ISensorPrivacyListener.aidl", "aidl/android/hardware/ISensorPrivacyManager.aidl", "SensorPrivacyManager.cpp", ], shared_libs: [ "libbinder", "libcutils", "libutils", "liblog", "libhardware", ], export_include_dirs: ["include"], export_shared_lib_headers: ["libbinder"], } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/SensorPrivacyManager.cpp���������������������������������������������������������0100644 0000000 0000000 00000006560 13756501735 020276� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 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. */ #include #include #include #include #include #include namespace android { SensorPrivacyManager::SensorPrivacyManager() { } sp SensorPrivacyManager::getService() { std::lock_guard scoped_lock(mLock); int64_t startTime = 0; sp service = mService; while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { sp binder = defaultServiceManager()->checkService(String16("sensor_privacy")); if (binder == nullptr) { // Wait for the sensor privacy service to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting for sensor privacy service"); } else if ((uptimeMillis() - startTime) > 1000000) { ALOGW("Waiting too long for sensor privacy service, giving up"); service = nullptr; break; } usleep(25000); } else { service = interface_cast(binder); mService = service; } } return service; } void SensorPrivacyManager::addSensorPrivacyListener( const sp& listener) { sp service = getService(); if (service != nullptr) { service->addSensorPrivacyListener(listener); } } void SensorPrivacyManager::removeSensorPrivacyListener( const sp& listener) { sp service = getService(); if (service != nullptr) { service->removeSensorPrivacyListener(listener); } } bool SensorPrivacyManager::isSensorPrivacyEnabled() { sp service = getService(); if (service != nullptr) { bool result; service->isSensorPrivacyEnabled(&result); return result; } // if the SensorPrivacyManager is not available then assume sensor privacy is disabled return false; } status_t SensorPrivacyManager::linkToDeath(const sp& recipient) { sp service = getService(); if (service != nullptr) { return IInterface::asBinder(service)->linkToDeath(recipient); } return INVALID_OPERATION; } status_t SensorPrivacyManager::unlinkToDeath(const sp& recipient) { sp service = getService(); if (service != nullptr) { return IInterface::asBinder(service)->unlinkToDeath(recipient); } return INVALID_OPERATION; } }; // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/aidl/����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014375� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/aidl/android/��������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016015� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/aidl/android/hardware/�����������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 017612� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl��������������������������������0100644 0000000 0000000 00000001370 13756501735 025071� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * Copyright (c) 2018, 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.hardware; /** * @hide */ oneway interface ISensorPrivacyListener { void onSensorPrivacyChanged(boolean enabled); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl���������������������������������0100644 0000000 0000000 00000001720 13756501735 024655� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * Copyright (c) 2018, 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.hardware; import android.hardware.ISensorPrivacyListener; /** @hide */ interface ISensorPrivacyManager { void addSensorPrivacyListener(in ISensorPrivacyListener listener); void removeSensorPrivacyListener(in ISensorPrivacyListener listener); boolean isSensorPrivacyEnabled(); void setSensorPrivacy(boolean enable); } ������������������������������������������������libs/sensorprivacy/include/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015107� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/include/sensorprivacy/�����������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020016� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h�������������������������������������0100644 0000000 0000000 00000003162 13756501735 024270� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_SENSOR_PRIVACY_MANAGER_H #define ANDROID_SENSOR_PRIVACY_MANAGER_H #include "android/hardware/ISensorPrivacyListener.h" #include "android/hardware/ISensorPrivacyManager.h" #include // --------------------------------------------------------------------------- namespace android { class SensorPrivacyManager { public: SensorPrivacyManager(); void addSensorPrivacyListener(const sp& listener); void removeSensorPrivacyListener(const sp& listener); bool isSensorPrivacyEnabled(); status_t linkToDeath(const sp& recipient); status_t unlinkToDeath(const sp& recipient); private: Mutex mLock; sp mService; sp getService(); }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_SENSOR_PRIVACY_MANAGER_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/��������������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 011172� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Android.bp����������������������������������������������������������������������������������0100644 0000000 0000000 00000011505 13756501735 013074� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2010 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. cc_library_shared { name: "libui", vendor_available: true, vndk: { enabled: true, }, double_loadable: true, clang: true, cflags: [ "-Wall", "-Werror", ], cppflags: [ "-Weverything", // The static constructors and destructors in this library have not been noted to // introduce significant overheads "-Wno-exit-time-destructors", "-Wno-global-constructors", // We only care about compiling as C++14 "-Wno-c++98-compat-pedantic", // We are aware of the risks inherent in comparing floats for equality "-Wno-float-equal", // We use four-character constants for the GraphicBuffer header, and don't care // that they're non-portable as long as they're consistent within one execution "-Wno-four-char-constants", // Don't warn about struct padding "-Wno-padded", "-Wno-switch-enum", ], sanitize: { integer_overflow: true, }, srcs: [ "ColorSpace.cpp", "BufferHubBuffer.cpp", "BufferHubEventFd.cpp", "BufferHubMetadata.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", "Gralloc.cpp", "Gralloc2.cpp", "Gralloc3.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", "Rect.cpp", "Region.cpp", "Size.cpp", "Transform.cpp", "UiConfig.cpp", ], include_dirs: [ "frameworks/native/include", ], // Uncomment the following line to enable VALIDATE_REGIONS traces //defaults: ["libui-validate-regions-defaults"], shared_libs: [ "android.frameworks.bufferhub@1.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.graphics.mapper@3.0", "libbase", "libcutils", "libhardware", "libhidlbase", "libhidltransport", "libhwbinder", "libsync", "libutils", "liblog", ], export_shared_lib_headers: [ "android.hardware.graphics.common@1.2", ], static_libs: [ "libarect", "libgrallocusage", "libmath", ], // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: ["-DLIBUI_IN_VNDK"], exclude_srcs: [ "BufferHubBuffer.cpp", "BufferHubEventFd.cpp", "BufferHubMetadata.cpp", ], exclude_header_libs: [ "libbufferhub_headers", "libdvr_headers", ], exclude_shared_libs: [ "android.frameworks.bufferhub@1.0", "libpdx_default_transport", ], }, }, header_libs: [ "libbase_headers", "libbufferhub_headers", "libdvr_headers", "libnativebase_headers", "libnativewindow_headers", "libhardware_headers", "libui_headers", "libpdx_headers", ], export_static_lib_headers: [ "libarect", "libmath", ], export_header_lib_headers: [ "libbase_headers", "libnativebase_headers", "libnativewindow_headers", "libhardware_headers", "libui_headers", ], } cc_library_headers { name: "libui_headers", export_include_dirs: ["include"], vendor_available: true, target: { vendor: { cflags: ["-DLIBUI_IN_VNDK"], override_export_include_dirs: ["include_vndk"], }, }, header_libs: [ "libnativewindow_headers", ], export_header_lib_headers: [ "libnativewindow_headers", ], } // defaults to enable VALIDATE_REGIONS traces cc_defaults { name: "libui-validate-regions-defaults", shared_libs: ["libutilscallstack"], cflags: ["-DVALIDATE_REGIONS"], } subdirs = [ "tests", "tools", ] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/BufferHubBuffer.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000035274 13756501735 014710� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 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. */ #include #include #include #include #include #include #include using ::android::base::unique_fd; using ::android::BufferHubDefs::isAnyClientAcquired; using ::android::BufferHubDefs::isAnyClientGained; using ::android::BufferHubDefs::isClientAcquired; using ::android::BufferHubDefs::isClientGained; using ::android::BufferHubDefs::isClientPosted; using ::android::BufferHubDefs::isClientReleased; using ::android::frameworks::bufferhub::V1_0::BufferHubStatus; using ::android::frameworks::bufferhub::V1_0::BufferTraits; using ::android::frameworks::bufferhub::V1_0::IBufferClient; using ::android::frameworks::bufferhub::V1_0::IBufferHub; using ::android::hardware::hidl_handle; using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription; namespace android { std::unique_ptr BufferHubBuffer::create(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, uint64_t usage, size_t userMetadataSize) { auto buffer = std::unique_ptr( new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize)); return buffer->isValid() ? std::move(buffer) : nullptr; } std::unique_ptr BufferHubBuffer::import(const sp& token) { if (token == nullptr || token.get() == nullptr) { ALOGE("%s: token cannot be nullptr!", __FUNCTION__); return nullptr; } auto buffer = std::unique_ptr(new BufferHubBuffer(token)); return buffer->isValid() ? std::move(buffer) : nullptr; } BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, uint64_t usage, size_t userMetadataSize) { ATRACE_CALL(); ALOGD("%s: width=%u height=%u layerCount=%u, format=%u " "usage=%" PRIx64 " mUserMetadataSize=%zu", __FUNCTION__, width, height, layerCount, format, usage, userMetadataSize); sp bufferhub = IBufferHub::getService(); if (bufferhub.get() == nullptr) { ALOGE("%s: BufferHub service not found!", __FUNCTION__); return; } AHardwareBuffer_Desc aDesc = {width, height, layerCount, format, usage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL}; HardwareBufferDescription desc; memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription)); BufferHubStatus ret; sp client; BufferTraits bufferTraits; IBufferHub::allocateBuffer_cb allocCb = [&](const auto& status, const auto& outClient, const auto& outTraits) { ret = status; client = std::move(outClient); bufferTraits = std::move(outTraits); }; if (!bufferhub->allocateBuffer(desc, static_cast(userMetadataSize), allocCb).isOk()) { ALOGE("%s: allocateBuffer transaction failed!", __FUNCTION__); return; } else if (ret != BufferHubStatus::NO_ERROR) { ALOGE("%s: allocateBuffer failed with error %u.", __FUNCTION__, ret); return; } else if (client == nullptr) { ALOGE("%s: allocateBuffer got null BufferClient.", __FUNCTION__); return; } const int importRet = initWithBufferTraits(bufferTraits); if (importRet < 0) { ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet)); client->close(); } mBufferClient = std::move(client); } BufferHubBuffer::BufferHubBuffer(const sp& token) { sp bufferhub = IBufferHub::getService(); if (bufferhub.get() == nullptr) { ALOGE("%s: BufferHub service not found!", __FUNCTION__); return; } BufferHubStatus ret; sp client; BufferTraits bufferTraits; IBufferHub::importBuffer_cb importCb = [&](const auto& status, const auto& outClient, const auto& outTraits) { ret = status; client = std::move(outClient); bufferTraits = std::move(outTraits); }; // hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership // transfer. if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), importCb).isOk()) { ALOGE("%s: importBuffer transaction failed!", __FUNCTION__); return; } else if (ret != BufferHubStatus::NO_ERROR) { ALOGE("%s: importBuffer failed with error %u.", __FUNCTION__, ret); return; } else if (client == nullptr) { ALOGE("%s: importBuffer got null BufferClient.", __FUNCTION__); return; } const int importRet = initWithBufferTraits(bufferTraits); if (importRet < 0) { ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet)); client->close(); } mBufferClient = std::move(client); } BufferHubBuffer::~BufferHubBuffer() { // Close buffer client to avoid possible race condition: user could first duplicate and hold // token with the original buffer gone, and then try to import the token. The close function // will explicitly invalidate the token to avoid this. if (mBufferClient != nullptr) { if (!mBufferClient->close().isOk()) { ALOGE("%s: close BufferClient transaction failed!", __FUNCTION__); } } } int BufferHubBuffer::initWithBufferTraits(const BufferTraits& bufferTraits) { ATRACE_CALL(); if (bufferTraits.bufferInfo.getNativeHandle() == nullptr) { ALOGE("%s: missing buffer info handle.", __FUNCTION__); return -EINVAL; } if (bufferTraits.bufferHandle.getNativeHandle() == nullptr) { ALOGE("%s: missing gralloc handle.", __FUNCTION__); return -EINVAL; } // Import fds. Dup fds because hidl_handle owns the fds. unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0)); mMetadata = BufferHubMetadata::import(std::move(ashmemFd)); if (!mMetadata.isValid()) { ALOGE("%s: Received an invalid metadata.", __FUNCTION__); return -EINVAL; } mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0)); if (!mEventFd.isValid()) { ALOGE("%s: Received ad invalid event fd.", __FUNCTION__); return -EINVAL; } int bufferId = bufferTraits.bufferInfo->data[2]; if (bufferId < 0) { ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__); return -EINVAL; } uint32_t clientBitMask; memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask)); if (clientBitMask == 0U) { ALOGE("%s: Received an invalid client state mask.", __FUNCTION__); return -EINVAL; } uint32_t userMetadataSize; memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize)); if (mMetadata.userMetadataSize() != userMetadataSize) { ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__, userMetadataSize, mMetadata.userMetadataSize()); return -EINVAL; } size_t metadataSize = static_cast(mMetadata.metadataSize()); if (metadataSize < BufferHubDefs::kMetadataHeaderSize) { ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize); return -EINVAL; } // Populate shortcuts to the atomics in metadata. auto metadataHeader = mMetadata.metadataHeader(); mBufferState = &metadataHeader->bufferState; mFenceState = &metadataHeader->fenceState; mActiveClientsBitMask = &metadataHeader->activeClientsBitMask; // The C++ standard recommends (but does not require) that lock-free atomic operations are // also address-free, that is, suitable for communication between processes using shared // memory. LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) || !std::atomic_is_lock_free(mFenceState) || !std::atomic_is_lock_free(mActiveClientsBitMask), "Atomic variables in ashmen are not lock free."); // Import the buffer: We only need to hold on the native_handle_t here so that // GraphicBuffer instance can be created in future. mBufferHandle = std::move(bufferTraits.bufferHandle); memcpy(&mBufferDesc, &bufferTraits.bufferDesc, sizeof(AHardwareBuffer_Desc)); mId = bufferId; mClientStateMask = clientBitMask; // TODO(b/112012161) Set up shared fences. ALOGD("%s: id=%d, mBufferState=%" PRIx32 ".", __FUNCTION__, mId, mBufferState->load(std::memory_order_acquire)); return 0; } int BufferHubBuffer::gain() { uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); if (isClientGained(currentBufferState, mClientStateMask)) { ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__, mClientStateMask); return 0; } do { if (isAnyClientGained(currentBufferState & (~mClientStateMask)) || isAnyClientAcquired(currentBufferState)) { ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", __FUNCTION__, mId, mClientStateMask, currentBufferState); return -EBUSY; } // Change the buffer state to gained state, whose value happens to be the same as // mClientStateMask. } while (!mBufferState->compare_exchange_weak(currentBufferState, mClientStateMask, std::memory_order_acq_rel, std::memory_order_acquire)); // TODO(b/119837586): Update fence state and return GPU fence. return 0; } int BufferHubBuffer::post() { uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); uint32_t updatedBufferState = (~mClientStateMask) & BufferHubDefs::kHighBitsMask; do { if (!isClientGained(currentBufferState, mClientStateMask)) { ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d " "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", __FUNCTION__, mId, mClientStateMask, currentBufferState); return -EBUSY; } // Set the producer client buffer state to released, other clients' buffer state to posted. // Post to all existing and non-existing clients. } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState, std::memory_order_acq_rel, std::memory_order_acquire)); // TODO(b/119837586): Update fence state and return GPU fence if needed. return 0; } int BufferHubBuffer::acquire() { uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); if (isClientAcquired(currentBufferState, mClientStateMask)) { ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__, mClientStateMask); return 0; } uint32_t updatedBufferState = 0U; do { if (!isClientPosted(currentBufferState, mClientStateMask)) { ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d " "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", __FUNCTION__, mId, mClientStateMask, currentBufferState); return -EBUSY; } // Change the buffer state for this consumer from posted to acquired. updatedBufferState = currentBufferState ^ mClientStateMask; } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState, std::memory_order_acq_rel, std::memory_order_acquire)); // TODO(b/119837586): Update fence state and return GPU fence. return 0; } int BufferHubBuffer::release() { uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); if (isClientReleased(currentBufferState, mClientStateMask)) { ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__, mClientStateMask); return 0; } uint32_t updatedBufferState = 0U; do { updatedBufferState = currentBufferState & (~mClientStateMask); } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState, std::memory_order_acq_rel, std::memory_order_acquire)); // TODO(b/119837586): Update fence state and return GPU fence if needed. return 0; } bool BufferHubBuffer::isReleased() const { return (mBufferState->load(std::memory_order_acquire) & mActiveClientsBitMask->load(std::memory_order_acquire)) == 0; } bool BufferHubBuffer::isValid() const { return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U && mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr; } sp BufferHubBuffer::duplicate() { if (mBufferClient == nullptr) { ALOGE("%s: missing BufferClient!", __FUNCTION__); return nullptr; } hidl_handle token; BufferHubStatus ret; IBufferClient::duplicate_cb dupCb = [&](const auto& outToken, const auto& status) { token = std::move(outToken); ret = status; }; if (!mBufferClient->duplicate(dupCb).isOk()) { ALOGE("%s: duplicate transaction failed!", __FUNCTION__); return nullptr; } else if (ret != BufferHubStatus::NO_ERROR) { ALOGE("%s: duplicate failed with error %u.", __FUNCTION__, ret); return nullptr; } else if (token.getNativeHandle() == nullptr) { ALOGE("%s: duplicate got null token.", __FUNCTION__); return nullptr; } return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true); } } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/BufferHubEventFd.cpp������������������������������������������������������������������������0100644 0000000 0000000 00000002515 13756501735 015022� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 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. */ #include #include #include namespace android { BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {} BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {} status_t BufferHubEventFd::signal() const { if (!isValid()) { ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__); return DEAD_OBJECT; } eventfd_write(mFd.get(), 1); return OK; } status_t BufferHubEventFd::clear() const { if (!isValid()) { ALOGE("%s: cannot clear an invalid eventfd.", __FUNCTION__); return DEAD_OBJECT; } eventfd_t value; eventfd_read(mFd.get(), &value); return OK; } } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/BufferHubMetadata.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000007336 13756501735 015215� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 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. */ #include #include #include #include #include #include namespace android { namespace { static const int kAshmemProt = PROT_READ | PROT_WRITE; } // namespace using BufferHubDefs::kMetadataHeaderSize; using BufferHubDefs::MetadataHeader; /* static */ BufferHubMetadata BufferHubMetadata::create(size_t userMetadataSize) { // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it // cannot overflow uint32_t. if (userMetadataSize >= (std::numeric_limits::max() - kMetadataHeaderSize)) { ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize); return {}; } const size_t metadataSize = userMetadataSize + kMetadataHeaderSize; int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize); if (fd < 0) { ALOGE("BufferHubMetadata::Create: failed to create ashmem region."); return {}; } // Hand over the ownership of the fd to a unique_fd immediately after the successful // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd // leaks during error handling. unique_fd ashmemFd{fd}; if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) { ALOGE("BufferHubMetadata::Create: failed to set protect region."); return {}; } return BufferHubMetadata::import(std::move(ashmemFd)); } /* static */ BufferHubMetadata BufferHubMetadata::import(unique_fd ashmemFd) { if (!ashmem_valid(ashmemFd.get())) { ALOGE("BufferHubMetadata::Import: invalid ashmem fd."); return {}; } size_t metadataSize = static_cast(ashmem_get_size_region(ashmemFd.get())); size_t userMetadataSize = metadataSize - kMetadataHeaderSize; // Note that here the buffer state is mapped from shared memory as an atomic object. The // std::atomic's constructor will not be called so that the original value stored in the memory // region can be preserved. auto metadataHeader = static_cast(mmap(nullptr, metadataSize, kAshmemProt, MAP_SHARED, ashmemFd.get(), /*offset=*/0)); if (metadataHeader == nullptr) { ALOGE("BufferHubMetadata::Import: failed to map region."); return {}; } return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader); } BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd, MetadataHeader* metadataHeader) : mUserMetadataSize(userMetadataSize), mAshmemFd(std::move(ashmemFd)), mMetadataHeader(metadataHeader) {} BufferHubMetadata::~BufferHubMetadata() { if (mMetadataHeader != nullptr) { int ret = munmap(mMetadataHeader, metadataSize()); ALOGE_IF(ret != 0, "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno); mMetadataHeader = nullptr; } } } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/ColorSpace.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000031763 13756501735 013737� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #include using namespace std::placeholders; namespace android { static constexpr float linearResponse(float v) { return v; } static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c; } static constexpr float response(float x, const ColorSpace::TransferParameters& p) { return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x; } static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c; } static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) { return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f; } static float absRcpResponse(float x, float g,float a, float b, float c, float d) { float xx = std::abs(x); return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x); } static float absResponse(float x, float g, float a, float b, float c, float d) { float xx = std::abs(x); return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x); } static float safePow(float x, float e) { return powf(x < 0.0f ? 0.0f : x, e); } static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) { if (parameters.e == 0.0f && parameters.f == 0.0f) { return std::bind(rcpResponse, _1, parameters); } return std::bind(rcpFullResponse, _1, parameters); } static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) { if (parameters.e == 0.0f && parameters.f == 0.0f) { return std::bind(response, _1, parameters); } return std::bind(fullResponse, _1, parameters); } static ColorSpace::transfer_function toOETF(float gamma) { if (gamma == 1.0f) { return linearResponse; } return std::bind(safePow, _1, 1.0f / gamma); } static ColorSpace::transfer_function toEOTF(float gamma) { if (gamma == 1.0f) { return linearResponse; } return std::bind(safePow, _1, gamma); } static constexpr std::array computePrimaries(const mat3& rgbToXYZ) { float3 r(rgbToXYZ * float3{1, 0, 0}); float3 g(rgbToXYZ * float3{0, 1, 0}); float3 b(rgbToXYZ * float3{0, 0, 1}); return {{r.xy / dot(r, float3{1}), g.xy / dot(g, float3{1}), b.xy / dot(b, float3{1})}}; } static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) { float3 w(rgbToXYZ * float3{1}); return w.xy / dot(w, float3{1}); } ColorSpace::ColorSpace( const std::string& name, const mat3& rgbToXYZ, transfer_function OETF, transfer_function EOTF, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mOETF(std::move(OETF)) , mEOTF(std::move(EOTF)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } ColorSpace::ColorSpace( const std::string& name, const mat3& rgbToXYZ, const TransferParameters parameters, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mParameters(parameters) , mOETF(toOETF(mParameters)) , mEOTF(toEOTF(mParameters)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } ColorSpace::ColorSpace( const std::string& name, const mat3& rgbToXYZ, float gamma, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(rgbToXYZ) , mXYZtoRGB(inverse(rgbToXYZ)) , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) , mOETF(toOETF(gamma)) , mEOTF(toEOTF(gamma)) , mClamper(std::move(clamper)) , mPrimaries(computePrimaries(rgbToXYZ)) , mWhitePoint(computeWhitePoint(rgbToXYZ)) { } ColorSpace::ColorSpace( const std::string& name, const std::array& primaries, const float2& whitePoint, transfer_function OETF, transfer_function EOTF, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mOETF(std::move(OETF)) , mEOTF(std::move(EOTF)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } ColorSpace::ColorSpace( const std::string& name, const std::array& primaries, const float2& whitePoint, const TransferParameters parameters, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mParameters(parameters) , mOETF(toOETF(mParameters)) , mEOTF(toEOTF(mParameters)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } ColorSpace::ColorSpace( const std::string& name, const std::array& primaries, const float2& whitePoint, float gamma, clamping_function clamper) noexcept : mName(name) , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint)) , mXYZtoRGB(inverse(mRGBtoXYZ)) , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}) , mOETF(toOETF(gamma)) , mEOTF(toEOTF(gamma)) , mClamper(std::move(clamper)) , mPrimaries(primaries) , mWhitePoint(whitePoint) { } constexpr mat3 ColorSpace::computeXYZMatrix( const std::array& primaries, const float2& whitePoint) { const float2& R = primaries[0]; const float2& G = primaries[1]; const float2& B = primaries[2]; const float2& W = whitePoint; float oneRxRy = (1 - R.x) / R.y; float oneGxGy = (1 - G.x) / G.y; float oneBxBy = (1 - B.x) / B.y; float oneWxWy = (1 - W.x) / W.y; float RxRy = R.x / R.y; float GxGy = G.x / G.y; float BxBy = B.x / B.y; float WxWy = W.x / W.y; float BY = ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) / ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy)); float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy); float RY = 1 - GY - BY; float RYRy = RY / R.y; float GYGy = GY / G.y; float BYBy = BY / B.y; return { float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)}, float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)}, float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)} }; } const ColorSpace ColorSpace::sRGB() { return { "sRGB IEC61966-2.1", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f} }; } const ColorSpace ColorSpace::linearSRGB() { return { "sRGB IEC61966-2.1 (Linear)", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f} }; } const ColorSpace ColorSpace::extendedSRGB() { return { "scRGB-nl IEC 61966-2-2:2003", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), std::bind(absResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f), std::bind(clamp, _1, -0.799f, 2.399f) }; } const ColorSpace ColorSpace::linearExtendedSRGB() { return { "scRGB IEC 61966-2-2:2003", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, 1.0f, std::bind(clamp, _1, -0.5f, 7.499f) }; } const ColorSpace ColorSpace::NTSC() { return { "NTSC (1953)", {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}}, {0.310f, 0.316f}, {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} }; } const ColorSpace ColorSpace::BT709() { return { "Rec. ITU-R BT.709-5", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} }; } const ColorSpace ColorSpace::BT2020() { return { "Rec. ITU-R BT.2020-1", {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}}, {0.3127f, 0.3290f}, {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f} }; } const ColorSpace ColorSpace::AdobeRGB() { return { "Adobe RGB (1998)", {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}}, {0.3127f, 0.3290f}, 2.2f }; } const ColorSpace ColorSpace::ProPhotoRGB() { return { "ROMM RGB ISO 22028-2:2013", {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}}, {0.34567f, 0.35850f}, {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f} }; } const ColorSpace ColorSpace::DisplayP3() { return { "Display P3", {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f}, {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f} }; } const ColorSpace ColorSpace::DCIP3() { return { "SMPTE RP 431-2-2007 DCI (P3)", {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}}, {0.314f, 0.351f}, 2.6f }; } const ColorSpace ColorSpace::ACES() { return { "SMPTE ST 2065-1:2012 ACES", {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}}, {0.32168f, 0.33767f}, 1.0f, std::bind(clamp, _1, -65504.0f, 65504.0f) }; } const ColorSpace ColorSpace::ACEScg() { return { "Academy S-2014-004 ACEScg", {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}}, {0.32168f, 0.33767f}, 1.0f, std::bind(clamp, _1, -65504.0f, 65504.0f) }; } std::unique_ptr ColorSpace::createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); std::unique_ptr lut(new float3[size * size * size]); float3* data = lut.get(); ColorSpaceConnector connector(src, dst); for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { *data++ = connector.transform({x * m, y * m, z * m}); } } } return lut; } static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const mat3 BRADFORD = mat3{ float3{ 0.8951f, -0.7502f, 0.0389f}, float3{ 0.2664f, 1.7135f, -0.0685f}, float3{-0.1614f, 0.0367f, 1.0296f} }; static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { float3 srcLMS = matrix * srcWhitePoint; float3 dstLMS = matrix * dstWhitePoint; return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; } ColorSpaceConnector::ColorSpaceConnector( const ColorSpace& src, const ColorSpace& dst) noexcept : mSource(src) , mDestination(dst) { if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) { mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ(); } else { mat3 rgbToXYZ(src.getRGBtoXYZ()); mat3 xyzToRGB(dst.getXYZtoRGB()); float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1}); float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1}); if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ(); } if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) { xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ()); } mTransform = xyzToRGB * rgbToXYZ; } } }; // namespace android �������������libs/ui/DebugUtils.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000025714 13756501735 013753� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 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. */ #include #include #include #include #include using android::base::StringPrintf; using android::ui::ColorMode; using android::ui::RenderIntent; std::string decodeStandard(android_dataspace dataspace) { const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); switch (dataspaceSelect) { case HAL_DATASPACE_STANDARD_BT709: return std::string("BT709"); case HAL_DATASPACE_STANDARD_BT601_625: return std::string("BT601_625"); case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: return std::string("BT601_625_UNADJUSTED"); case HAL_DATASPACE_STANDARD_BT601_525: return std::string("BT601_525"); case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: return std::string("BT601_525_UNADJUSTED"); case HAL_DATASPACE_STANDARD_BT2020: return std::string("BT2020"); case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: return std::string("BT2020 (constant luminance)"); case HAL_DATASPACE_STANDARD_BT470M: return std::string("BT470M"); case HAL_DATASPACE_STANDARD_FILM: return std::string("FILM"); case HAL_DATASPACE_STANDARD_DCI_P3: return std::string("DCI-P3"); case HAL_DATASPACE_STANDARD_ADOBE_RGB: return std::string("AdobeRGB"); case 0: switch (dataspace & 0xffff) { case HAL_DATASPACE_JFIF: return std::string("(deprecated) JFIF (BT601_625)"); case HAL_DATASPACE_BT601_625: return std::string("(deprecated) BT601_625"); case HAL_DATASPACE_BT601_525: return std::string("(deprecated) BT601_525"); case HAL_DATASPACE_SRGB_LINEAR: case HAL_DATASPACE_SRGB: return std::string("(deprecated) sRGB"); case HAL_DATASPACE_BT709: return std::string("(deprecated) BT709"); case HAL_DATASPACE_ARBITRARY: return std::string("ARBITRARY"); case HAL_DATASPACE_UNKNOWN: // Fallthrough default: return android::base::StringPrintf("Unknown deprecated dataspace code %d", dataspace); } } return android::base::StringPrintf("Unknown dataspace code %d", dataspaceSelect); } std::string decodeTransfer(android_dataspace dataspace) { const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); if (dataspaceSelect == 0) { switch (dataspace & 0xffff) { case HAL_DATASPACE_JFIF: case HAL_DATASPACE_BT601_625: case HAL_DATASPACE_BT601_525: case HAL_DATASPACE_BT709: return std::string("SMPTE_170M"); case HAL_DATASPACE_SRGB_LINEAR: case HAL_DATASPACE_ARBITRARY: return std::string("Linear"); case HAL_DATASPACE_SRGB: return std::string("sRGB"); case HAL_DATASPACE_UNKNOWN: // Fallthrough default: return std::string(""); } } const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK); switch (dataspaceTransfer) { case HAL_DATASPACE_TRANSFER_UNSPECIFIED: return std::string("Unspecified"); case HAL_DATASPACE_TRANSFER_LINEAR: return std::string("Linear"); case HAL_DATASPACE_TRANSFER_SRGB: return std::string("sRGB"); case HAL_DATASPACE_TRANSFER_SMPTE_170M: return std::string("SMPTE_170M"); case HAL_DATASPACE_TRANSFER_GAMMA2_2: return std::string("gamma 2.2"); case HAL_DATASPACE_TRANSFER_GAMMA2_6: return std::string("gamma 2.6"); case HAL_DATASPACE_TRANSFER_GAMMA2_8: return std::string("gamma 2.8"); case HAL_DATASPACE_TRANSFER_ST2084: return std::string("SMPTE 2084"); case HAL_DATASPACE_TRANSFER_HLG: return std::string("STD-B67"); } return android::base::StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer); } std::string decodeRange(android_dataspace dataspace) { const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); if (dataspaceSelect == 0) { switch (dataspace & 0xffff) { case HAL_DATASPACE_JFIF: case HAL_DATASPACE_SRGB_LINEAR: case HAL_DATASPACE_SRGB: return std::string("Full range"); case HAL_DATASPACE_BT601_625: case HAL_DATASPACE_BT601_525: case HAL_DATASPACE_BT709: return std::string("Limited range"); case HAL_DATASPACE_ARBITRARY: case HAL_DATASPACE_UNKNOWN: // Fallthrough default: return std::string("unspecified range"); } } const uint32_t dataspaceRange = (dataspace & HAL_DATASPACE_RANGE_MASK); switch (dataspaceRange) { case HAL_DATASPACE_RANGE_UNSPECIFIED: return std::string("Range Unspecified"); case HAL_DATASPACE_RANGE_FULL: return std::string("Full range"); case HAL_DATASPACE_RANGE_LIMITED: return std::string("Limited range"); case HAL_DATASPACE_RANGE_EXTENDED: return std::string("Extended range"); } return android::base::StringPrintf("Unknown dataspace range %d", dataspaceRange); } std::string dataspaceDetails(android_dataspace dataspace) { if (dataspace == 0) { return "Default"; } return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(), decodeTransfer(dataspace).c_str(), decodeRange(dataspace).c_str()); } std::string decodeColorMode(ColorMode colorMode) { switch (colorMode) { case ColorMode::NATIVE: return std::string("ColorMode::NATIVE"); case ColorMode::STANDARD_BT601_625: return std::string("ColorMode::BT601_625"); case ColorMode::STANDARD_BT601_625_UNADJUSTED: return std::string("ColorMode::BT601_625_UNADJUSTED"); case ColorMode::STANDARD_BT601_525: return std::string("ColorMode::BT601_525"); case ColorMode::STANDARD_BT601_525_UNADJUSTED: return std::string("ColorMode::BT601_525_UNADJUSTED"); case ColorMode::STANDARD_BT709: return std::string("ColorMode::BT709"); case ColorMode::DCI_P3: return std::string("ColorMode::DCI_P3"); case ColorMode::SRGB: return std::string("ColorMode::SRGB"); case ColorMode::ADOBE_RGB: return std::string("ColorMode::ADOBE_RGB"); case ColorMode::DISPLAY_P3: return std::string("ColorMode::DISPLAY_P3"); case ColorMode::BT2020: return std::string("ColorMode::BT2020"); case ColorMode::DISPLAY_BT2020: return std::string("ColorMode::DISPLAY_BT2020"); case ColorMode::BT2100_PQ: return std::string("ColorMode::BT2100_PQ"); case ColorMode::BT2100_HLG: return std::string("ColorMode::BT2100_HLG"); } return android::base::StringPrintf("Unknown color mode %d", colorMode); } std::string decodeColorTransform(android_color_transform colorTransform) { switch (colorTransform) { case HAL_COLOR_TRANSFORM_IDENTITY: return std::string("Identity"); case HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX: return std::string("Arbitrary matrix"); case HAL_COLOR_TRANSFORM_VALUE_INVERSE: return std::string("Inverse value"); case HAL_COLOR_TRANSFORM_GRAYSCALE: return std::string("Grayscale"); case HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA: return std::string("Correct protanopia"); case HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA: return std::string("Correct deuteranopia"); case HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA: return std::string("Correct tritanopia"); } return android::base::StringPrintf("Unknown color transform %d", colorTransform); } // Converts a PixelFormat to a human-readable string. Max 11 chars. // (Could use a table of prefab String8 objects.) std::string decodePixelFormat(android::PixelFormat format) { switch (format) { case android::PIXEL_FORMAT_UNKNOWN: return std::string("Unknown/None"); case android::PIXEL_FORMAT_CUSTOM: return std::string("Custom"); case android::PIXEL_FORMAT_TRANSLUCENT: return std::string("Translucent"); case android::PIXEL_FORMAT_TRANSPARENT: return std::string("Transparent"); case android::PIXEL_FORMAT_OPAQUE: return std::string("Opaque"); case android::PIXEL_FORMAT_RGBA_8888: return std::string("RGBA_8888"); case android::PIXEL_FORMAT_RGBX_8888: return std::string("RGBx_8888"); case android::PIXEL_FORMAT_RGBA_FP16: return std::string("RGBA_FP16"); case android::PIXEL_FORMAT_RGBA_1010102: return std::string("RGBA_1010102"); case android::PIXEL_FORMAT_RGB_888: return std::string("RGB_888"); case android::PIXEL_FORMAT_RGB_565: return std::string("RGB_565"); case android::PIXEL_FORMAT_BGRA_8888: return std::string("BGRA_8888"); default: return android::base::StringPrintf("Unknown %#08x", format); } } std::string decodeRenderIntent(RenderIntent renderIntent) { switch(renderIntent) { case RenderIntent::COLORIMETRIC: return std::string("RenderIntent::COLORIMETRIC"); case RenderIntent::ENHANCE: return std::string("RenderIntent::ENHANCE"); case RenderIntent::TONE_MAP_COLORIMETRIC: return std::string("RenderIntent::TONE_MAP_COLORIMETRIC"); case RenderIntent::TONE_MAP_ENHANCE: return std::string("RenderIntent::TONE_MAP_ENHANCE"); } return std::string("Unknown RenderIntent"); } std::string to_string(const android::Rect& rect) { return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom); } ����������������������������������������������������libs/ui/Fence.cpp�����������������������������������������������������������������������������������0100644 0000000 0000000 00000011767 13756501735 012727� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2012 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. */ #include #define LOG_TAG "Fence" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 // We would eliminate the non-conforming zero-length array, but we can't since // this is effectively included from the Linux kernel #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-length-array" #include #pragma clang diagnostic pop #include #include #include #include #include namespace android { const sp Fence::NO_FENCE = sp(new Fence); Fence::Fence(int fenceFd) : mFenceFd(fenceFd) { } Fence::Fence(base::unique_fd fenceFd) : mFenceFd(std::move(fenceFd)) { } status_t Fence::wait(int timeout) { ATRACE_CALL(); if (mFenceFd == -1) { return NO_ERROR; } int err = sync_wait(mFenceFd, timeout); return err < 0 ? -errno : status_t(NO_ERROR); } status_t Fence::waitForever(const char* logname) { ATRACE_CALL(); if (mFenceFd == -1) { return NO_ERROR; } int warningTimeout = 3000; int err = sync_wait(mFenceFd, warningTimeout); if (err < 0 && errno == ETIME) { ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(), warningTimeout); err = sync_wait(mFenceFd, TIMEOUT_NEVER); } return err < 0 ? -errno : status_t(NO_ERROR); } sp Fence::merge(const char* name, const sp& f1, const sp& f2) { ATRACE_CALL(); int result; // Merge the two fences. In the case where one of the fences is not a // valid fence (e.g. NO_FENCE) we merge the one valid fence with itself so // that a new fence with the given name is created. if (f1->isValid() && f2->isValid()) { result = sync_merge(name, f1->mFenceFd, f2->mFenceFd); } else if (f1->isValid()) { result = sync_merge(name, f1->mFenceFd, f1->mFenceFd); } else if (f2->isValid()) { result = sync_merge(name, f2->mFenceFd, f2->mFenceFd); } else { return NO_FENCE; } if (result == -1) { status_t err = -errno; ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)", name, f1->mFenceFd.get(), f2->mFenceFd.get(), strerror(-err), err); return NO_FENCE; } return sp(new Fence(result)); } sp Fence::merge(const String8& name, const sp& f1, const sp& f2) { return merge(name.string(), f1, f2); } int Fence::dup() const { return ::dup(mFenceFd); } nsecs_t Fence::getSignalTime() const { if (mFenceFd == -1) { return SIGNAL_TIME_INVALID; } struct sync_file_info* finfo = sync_file_info(mFenceFd); if (finfo == nullptr) { ALOGE("sync_file_info returned NULL for fd %d", mFenceFd.get()); return SIGNAL_TIME_INVALID; } if (finfo->status != 1) { sync_file_info_free(finfo); return SIGNAL_TIME_PENDING; } uint64_t timestamp = 0; struct sync_fence_info* pinfo = sync_get_fence_info(finfo); for (size_t i = 0; i < finfo->num_fences; i++) { if (pinfo[i].timestamp_ns > timestamp) { timestamp = pinfo[i].timestamp_ns; } } sync_file_info_free(finfo); return nsecs_t(timestamp); } size_t Fence::getFlattenedSize() const { return 4; } size_t Fence::getFdCount() const { return isValid() ? 1 : 0; } status_t Fence::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { if (size < getFlattenedSize() || count < getFdCount()) { return NO_MEMORY; } // Cast to uint32_t since the size of a size_t can vary between 32- and // 64-bit processes FlattenableUtils::write(buffer, size, static_cast(getFdCount())); if (isValid()) { *fds++ = mFenceFd; count--; } return NO_ERROR; } status_t Fence::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (mFenceFd != -1) { // Don't unflatten if we already have a valid fd. return INVALID_OPERATION; } if (size < getFlattenedSize()) { return NO_MEMORY; } uint32_t numFds; FlattenableUtils::read(buffer, size, numFds); if (numFds > 1) { return BAD_VALUE; } if (count < numFds) { return NO_MEMORY; } if (numFds) { mFenceFd.reset(*fds++); count--; } return NO_ERROR; } } // namespace android ���������libs/ui/FenceTime.cpp�������������������������������������������������������������������������������0100644 0000000 0000000 00000030246 13756501735 013537� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include #define LOG_TAG "FenceTime" #include // For CC_[UN]LIKELY #include #include #include #include namespace android { // ============================================================================ // FenceTime // ============================================================================ const auto FenceTime::NO_FENCE = std::make_shared(Fence::NO_FENCE); FenceTime::FenceTime(const sp& fence) : mState(((fence.get() != nullptr) && fence->isValid()) ? State::VALID : State::INVALID), mFence(fence), mSignalTime(mState == State::INVALID ? Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { } FenceTime::FenceTime(sp&& fence) : mState(((fence.get() != nullptr) && fence->isValid()) ? State::VALID : State::INVALID), mFence(std::move(fence)), mSignalTime(mState == State::INVALID ? Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { } FenceTime::FenceTime(nsecs_t signalTime) : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID), mFence(nullptr), mSignalTime(signalTime) { if (CC_UNLIKELY(mSignalTime == Fence::SIGNAL_TIME_PENDING)) { ALOGE("Pending signal time not allowed after signal."); mSignalTime = Fence::SIGNAL_TIME_INVALID; } } void FenceTime::applyTrustedSnapshot(const Snapshot& src) { if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) { // Applying Snapshot::State::FENCE, could change the valid state of the // FenceTime, which is not allowed. Callers should create a new // FenceTime from the snapshot instead. ALOGE("applyTrustedSnapshot: Unexpected fence."); return; } if (src.state == Snapshot::State::EMPTY) { return; } nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); if (signalTime != Fence::SIGNAL_TIME_PENDING) { // We should always get the same signalTime here that we did in // getSignalTime(). This check races with getSignalTime(), but it is // only a sanity check so that's okay. if (CC_UNLIKELY(signalTime != src.signalTime)) { ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. " "(%" PRId64 " (old) != %" PRId64 " (new))", signalTime, src.signalTime); } return; } std::lock_guard lock(mMutex); mFence.clear(); mSignalTime.store(src.signalTime, std::memory_order_relaxed); } bool FenceTime::isValid() const { // We store the valid state in the constructors and return it here. // This lets release code remember the valid state even after the // underlying fence is destroyed. return mState != State::INVALID; } nsecs_t FenceTime::getSignalTime() { // See if we already have a cached value we can return. nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); if (signalTime != Fence::SIGNAL_TIME_PENDING) { return signalTime; } // Hold a reference to the fence on the stack in case the class' // reference is removed by another thread. This prevents the // fence from being destroyed until the end of this method, where // we conveniently do not have the lock held. sp fence; { // With the lock acquired this time, see if we have the cached // value or if we need to poll the fence. std::lock_guard lock(mMutex); if (!mFence.get()) { // Another thread set the signal time just before we added the // reference to mFence. return mSignalTime.load(std::memory_order_relaxed); } fence = mFence; } // Make the system call without the lock held. signalTime = fence->getSignalTime(); // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests // use invalid underlying Fences without real file descriptors. if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) { if (signalTime == Fence::SIGNAL_TIME_INVALID) { signalTime = Fence::SIGNAL_TIME_PENDING; } } // Make the signal time visible to everyone if it is no longer pending // and remove the class' reference to the fence. if (signalTime != Fence::SIGNAL_TIME_PENDING) { std::lock_guard lock(mMutex); mFence.clear(); mSignalTime.store(signalTime, std::memory_order_relaxed); } return signalTime; } nsecs_t FenceTime::getCachedSignalTime() const { // memory_order_acquire since we don't have a lock fallback path // that will do an acquire. return mSignalTime.load(std::memory_order_acquire); } FenceTime::Snapshot FenceTime::getSnapshot() const { // Quick check without the lock. nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); if (signalTime != Fence::SIGNAL_TIME_PENDING) { return Snapshot(signalTime); } // Do the full check with the lock. std::lock_guard lock(mMutex); signalTime = mSignalTime.load(std::memory_order_relaxed); if (signalTime != Fence::SIGNAL_TIME_PENDING) { return Snapshot(signalTime); } return Snapshot(mFence); } // For tests only. If forceValidForTest is true, then getSignalTime will // never return SIGNAL_TIME_INVALID and isValid will always return true. FenceTime::FenceTime(const sp& fence, bool forceValidForTest) : mState(forceValidForTest ? State::FORCED_VALID_FOR_TEST : State::INVALID), mFence(fence), mSignalTime(mState == State::INVALID ? Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { } void FenceTime::signalForTest(nsecs_t signalTime) { // To be realistic, this should really set a hidden value that // gets picked up in the next call to getSignalTime, but this should // be good enough. std::lock_guard lock(mMutex); mFence.clear(); mSignalTime.store(signalTime, std::memory_order_relaxed); } // ============================================================================ // FenceTime::Snapshot // ============================================================================ FenceTime::Snapshot::Snapshot(const sp& srcFence) : state(State::FENCE), fence(srcFence) { } FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime) : state(State::SIGNAL_TIME), signalTime(srcSignalTime) { } size_t FenceTime::Snapshot::getFlattenedSize() const { constexpr size_t min = sizeof(state); switch (state) { case State::EMPTY: return min; case State::FENCE: return min + fence->getFlattenedSize(); case State::SIGNAL_TIME: return min + sizeof(signalTime); } return 0; } size_t FenceTime::Snapshot::getFdCount() const { return state == State::FENCE ? fence->getFdCount() : 0u; } status_t FenceTime::Snapshot::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { if (size < getFlattenedSize()) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, state); switch (state) { case State::EMPTY: return NO_ERROR; case State::FENCE: return fence->flatten(buffer, size, fds, count); case State::SIGNAL_TIME: FlattenableUtils::write(buffer, size, signalTime); return NO_ERROR; } return NO_ERROR; } status_t FenceTime::Snapshot::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { if (size < sizeof(state)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, state); switch (state) { case State::EMPTY: return NO_ERROR; case State::FENCE: fence = new Fence; return fence->unflatten(buffer, size, fds, count); case State::SIGNAL_TIME: if (size < sizeof(signalTime)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, signalTime); return NO_ERROR; } return NO_ERROR; } // ============================================================================ // FenceTimeline // ============================================================================ void FenceTimeline::push(const std::shared_ptr& fence) { std::lock_guard lock(mMutex); while (mQueue.size() >= MAX_ENTRIES) { // This is a sanity check to make sure the queue doesn't grow unbounded. // MAX_ENTRIES should be big enough not to trigger this path. // In case this path is taken though, users of FenceTime must make sure // not to rely solely on FenceTimeline to get the final timestamp and // should eventually call Fence::getSignalTime on their own. std::shared_ptr front = mQueue.front().lock(); if (front) { // Make a last ditch effort to get the signalTime here since // we are removing it from the timeline. front->getSignalTime(); } mQueue.pop(); } mQueue.push(fence); } void FenceTimeline::updateSignalTimes() { std::lock_guard lock(mMutex); while (!mQueue.empty()) { std::shared_ptr fence = mQueue.front().lock(); if (!fence) { // The shared_ptr no longer exists and no one cares about the // timestamp anymore. mQueue.pop(); continue; } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { // The fence has signaled and we've removed the sp ref. mQueue.pop(); continue; } else { // The fence didn't signal yet. Break since the later ones // shouldn't have signaled either. break; } } } // ============================================================================ // FenceToFenceTimeMap // ============================================================================ std::shared_ptr FenceToFenceTimeMap::createFenceTimeForTest( const sp& fence) { std::lock_guard lock(mMutex); // Always garbage collecting isn't efficient, but this is only for testing. garbageCollectLocked(); std::shared_ptr fenceTime(new FenceTime(fence, true)); mMap[fence.get()].push_back(fenceTime); return fenceTime; } void FenceToFenceTimeMap::signalAllForTest( const sp& fence, nsecs_t signalTime) { bool signaled = false; std::lock_guard lock(mMutex); auto it = mMap.find(fence.get()); if (it != mMap.end()) { for (auto& weakFenceTime : it->second) { std::shared_ptr fenceTime = weakFenceTime.lock(); if (!fenceTime) { continue; } ALOGE_IF(!fenceTime->isValid(), "signalAllForTest: Signaling invalid fence."); fenceTime->signalForTest(signalTime); signaled = true; } } ALOGE_IF(!signaled, "signalAllForTest: Nothing to signal."); } void FenceToFenceTimeMap::garbageCollectLocked() { for (auto& it : mMap) { // Erase all expired weak pointers from the vector. auto& vect = it.second; vect.erase( std::remove_if(vect.begin(), vect.end(), [](const std::weak_ptr& ft) { return ft.expired(); }), vect.end()); // Also erase the map entry if the vector is now empty. if (vect.empty()) { mMap.erase(it.first); } } } } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/FrameStats.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000005031 13756501735 013743� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #include namespace android { bool FrameStats::isFixedSize() const { return false; } size_t FrameStats::getFlattenedSize() const { const size_t timestampSize = sizeof(nsecs_t); size_t size = timestampSize; size += 3 * desiredPresentTimesNano.size() * timestampSize; return size; } status_t FrameStats::flatten(void* buffer, size_t size) const { if (size < getFlattenedSize()) { return NO_MEMORY; } nsecs_t* timestamps = reinterpret_cast(buffer); const size_t timestampSize = sizeof(nsecs_t); size_t frameCount = desiredPresentTimesNano.size(); memcpy(timestamps, &refreshPeriodNano, timestampSize); timestamps += 1; memcpy(timestamps, desiredPresentTimesNano.array(), frameCount * timestampSize); timestamps += frameCount; memcpy(timestamps, actualPresentTimesNano.array(), frameCount * timestampSize); timestamps += frameCount; memcpy(timestamps, frameReadyTimesNano.array(), frameCount * timestampSize); return NO_ERROR; } status_t FrameStats::unflatten(void const* buffer, size_t size) { const size_t timestampSize = sizeof(nsecs_t); if (size < timestampSize) { return NO_MEMORY; } nsecs_t const* timestamps = reinterpret_cast(buffer); size_t frameCount = (size - timestampSize) / (3 * timestampSize); memcpy(&refreshPeriodNano, timestamps, timestampSize); timestamps += 1; desiredPresentTimesNano.resize(frameCount); memcpy(desiredPresentTimesNano.editArray(), timestamps, frameCount * timestampSize); timestamps += frameCount; actualPresentTimesNano.resize(frameCount); memcpy(actualPresentTimesNano.editArray(), timestamps, frameCount * timestampSize); timestamps += frameCount; frameReadyTimesNano.resize(frameCount); memcpy(frameReadyTimesNano.editArray(), timestamps, frameCount * timestampSize); return NO_ERROR; } } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Gralloc.cpp���������������������������������������������������������������������������������0100644 0000000 0000000 00000001427 13756501735 013262� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "Gralloc" #include namespace android { GrallocMapper::~GrallocMapper() {} GrallocAllocator::~GrallocAllocator() {} } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Gralloc2.cpp��������������������������������������������������������������������������������0100644 0000000 0000000 00000035564 13756501735 013355� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "Gralloc2" #include #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-length-array" #include #pragma clang diagnostic pop using android::hardware::graphics::allocator::V2_0::IAllocator; using android::hardware::graphics::common::V1_1::BufferUsage; using android::hardware::graphics::common::V1_1::PixelFormat; using android::hardware::graphics::mapper::V2_0::BufferDescriptor; using android::hardware::graphics::mapper::V2_0::Error; using android::hardware::graphics::mapper::V2_0::YCbCrLayout; using android::hardware::graphics::mapper::V2_1::IMapper; namespace android { namespace { static constexpr Error kTransactionError = Error::NO_RESOURCES; uint64_t getValid10UsageBits() { static const uint64_t valid10UsageBits = []() -> uint64_t { using hardware::graphics::common::V1_0::BufferUsage; uint64_t bits = 0; for (const auto bit : hardware::hidl_enum_range()) { bits = bits | bit; } return bits; }(); return valid10UsageBits; } uint64_t getValid11UsageBits() { static const uint64_t valid11UsageBits = []() -> uint64_t { using hardware::graphics::common::V1_1::BufferUsage; uint64_t bits = 0; for (const auto bit : hardware::hidl_enum_range()) { bits = bits | bit; } return bits; }(); return valid11UsageBits; } static inline IMapper::Rect sGralloc2Rect(const Rect& rect) { IMapper::Rect outRect{}; outRect.left = rect.left; outRect.top = rect.top; outRect.width = rect.width(); outRect.height = rect.height(); return outRect; } } // anonymous namespace void Gralloc2Mapper::preload() { android::hardware::preloadPassthroughService(); } Gralloc2Mapper::Gralloc2Mapper() { mMapper = hardware::graphics::mapper::V2_0::IMapper::getService(); if (mMapper == nullptr) { ALOGW("mapper 2.x is not supported"); return; } if (mMapper->isRemote()) { LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); } // IMapper 2.1 is optional mMapperV2_1 = IMapper::castFrom(mMapper); } bool Gralloc2Mapper::isLoaded() const { return mMapper != nullptr; } status_t Gralloc2Mapper::validateBufferDescriptorInfo( IMapper::BufferDescriptorInfo* descriptorInfo) const { uint64_t validUsageBits = getValid10UsageBits(); if (mMapperV2_1 != nullptr) { validUsageBits = validUsageBits | getValid11UsageBits(); } if (descriptorInfo->usage & ~validUsageBits) { ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, descriptorInfo->usage & ~validUsageBits); return BAD_VALUE; } return NO_ERROR; } status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const { IMapper::BufferDescriptorInfo* descriptorInfo = static_cast(bufferDescriptorInfo); BufferDescriptor* outDescriptor = static_cast(outBufferDescriptor); status_t status = validateBufferDescriptorInfo(descriptorInfo); if (status != NO_ERROR) { return status; } Error error; auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) { error = tmpError; if (error != Error::NONE) { return; } *outDescriptor = tmpDescriptor; }; hardware::Return ret; if (mMapperV2_1 != nullptr) { ret = mMapperV2_1->createDescriptor_2_1(*descriptorInfo, hidl_cb); } else { const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = { descriptorInfo->width, descriptorInfo->height, descriptorInfo->layerCount, static_cast(descriptorInfo->format), descriptorInfo->usage, }; ret = mMapper->createDescriptor(info, hidl_cb); } return static_cast((ret.isOk()) ? error : kTransactionError); } status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle, buffer_handle_t* outBufferHandle) const { Error error; auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { error = tmpError; if (error != Error::NONE) { return; } *outBufferHandle = static_cast(tmpBuffer); }); return static_cast((ret.isOk()) ? error : kTransactionError); } void Gralloc2Mapper::freeBuffer(buffer_handle_t bufferHandle) const { auto buffer = const_cast(bufferHandle); auto ret = mMapper->freeBuffer(buffer); auto error = (ret.isOk()) ? static_cast(ret) : kTransactionError; ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error); } status_t Gralloc2Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) const { if (mMapperV2_1 == nullptr) { return NO_ERROR; } IMapper::BufferDescriptorInfo descriptorInfo = {}; descriptorInfo.width = width; descriptorInfo.height = height; descriptorInfo.layerCount = layerCount; descriptorInfo.format = static_cast(format); descriptorInfo.usage = usage; auto buffer = const_cast(bufferHandle); auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride); return static_cast((ret.isOk()) ? static_cast(ret) : kTransactionError); } void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, uint32_t* outNumInts) const { *outNumFds = uint32_t(bufferHandle->numFds); *outNumInts = uint32_t(bufferHandle->numInts); if (mMapperV2_1 == nullptr) { return; } Error error; auto buffer = const_cast(bufferHandle); auto ret = mMapperV2_1->getTransportSize(buffer, [&](const auto& tmpError, const auto& tmpNumFds, const auto& tmpNumInts) { error = tmpError; if (error != Error::NONE) { return; } *outNumFds = tmpNumFds; *outNumInts = tmpNumInts; }); error = (ret.isOk()) ? error : kTransactionError; ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error); } status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const { if (outBytesPerPixel) { *outBytesPerPixel = -1; } if (outBytesPerStride) { *outBytesPerStride = -1; } auto buffer = const_cast(bufferHandle); IMapper::Rect accessRegion = sGralloc2Rect(bounds); // put acquireFence in a hidl_handle hardware::hidl_handle acquireFenceHandle; NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); if (acquireFence >= 0) { auto h = native_handle_init(acquireFenceStorage, 1, 0); h->data[0] = acquireFence; acquireFenceHandle = h; } Error error; auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle, [&](const auto& tmpError, const auto& tmpData) { error = tmpError; if (error != Error::NONE) { return; } *outData = tmpData; }); // we own acquireFence even on errors if (acquireFence >= 0) { close(acquireFence); } error = (ret.isOk()) ? error : kTransactionError; ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error); return static_cast(error); } status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const { auto buffer = const_cast(bufferHandle); IMapper::Rect accessRegion = sGralloc2Rect(bounds); // put acquireFence in a hidl_handle hardware::hidl_handle acquireFenceHandle; NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); if (acquireFence >= 0) { auto h = native_handle_init(acquireFenceStorage, 1, 0); h->data[0] = acquireFence; acquireFenceHandle = h; } YCbCrLayout layout; Error error; auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle, [&](const auto& tmpError, const auto& tmpLayout) { error = tmpError; if (error != Error::NONE) { return; } layout = tmpLayout; }); if (error == Error::NONE) { ycbcr->y = layout.y; ycbcr->cb = layout.cb; ycbcr->cr = layout.cr; ycbcr->ystride = static_cast(layout.yStride); ycbcr->cstride = static_cast(layout.cStride); ycbcr->chroma_step = static_cast(layout.chromaStep); } // we own acquireFence even on errors if (acquireFence >= 0) { close(acquireFence); } return static_cast((ret.isOk()) ? error : kTransactionError); } int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const { auto buffer = const_cast(bufferHandle); int releaseFence = -1; Error error; auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) { error = tmpError; if (error != Error::NONE) { return; } auto fenceHandle = tmpReleaseFence.getNativeHandle(); if (fenceHandle && fenceHandle->numFds == 1) { int fd = dup(fenceHandle->data[0]); if (fd >= 0) { releaseFence = fd; } else { ALOGD("failed to dup unlock release fence"); sync_wait(fenceHandle->data[0], -1); } } }); error = (ret.isOk()) ? error : kTransactionError; if (error != Error::NONE) { ALOGE("unlock(%p) failed with %d", buffer, error); } return releaseFence; } status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/, android::PixelFormat /*format*/, uint32_t /*layerCount*/, uint64_t /*usage*/, bool* /*outSupported*/) const { return INVALID_OPERATION; } Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) { mAllocator = IAllocator::getService(); if (mAllocator == nullptr) { ALOGW("allocator 2.x is not supported"); return; } } bool Gralloc2Allocator::isLoaded() const { return mAllocator != nullptr; } std::string Gralloc2Allocator::dumpDebugInfo() const { std::string debugInfo; mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); }); return debugInfo; } status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles) const { IMapper::BufferDescriptorInfo descriptorInfo = {}; descriptorInfo.width = width; descriptorInfo.height = height; descriptorInfo.layerCount = layerCount; descriptorInfo.format = static_cast(format); descriptorInfo.usage = usage; BufferDescriptor descriptor; status_t error = mMapper.createDescriptor(static_cast(&descriptorInfo), static_cast(&descriptor)); if (error != NO_ERROR) { return error; } auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { error = static_cast(tmpError); if (tmpError != Error::NONE) { return; } // import buffers for (uint32_t i = 0; i < bufferCount; i++) { error = mMapper.importBuffer(tmpBuffers[i], &outBufferHandles[i]); if (error != NO_ERROR) { for (uint32_t j = 0; j < i; j++) { mMapper.freeBuffer(outBufferHandles[j]); outBufferHandles[j] = nullptr; } return; } } *outStride = tmpStride; }); // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now hardware::IPCThreadState::self()->flushCommands(); return (ret.isOk()) ? error : static_cast(kTransactionError); } } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Gralloc3.cpp��������������������������������������������������������������������������������0100644 0000000 0000000 00000036333 13756501735 013351� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "Gralloc3" #include #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-length-array" #include #pragma clang diagnostic pop using android::hardware::graphics::allocator::V3_0::IAllocator; using android::hardware::graphics::common::V1_2::BufferUsage; using android::hardware::graphics::mapper::V3_0::BufferDescriptor; using android::hardware::graphics::mapper::V3_0::Error; using android::hardware::graphics::mapper::V3_0::IMapper; using android::hardware::graphics::mapper::V3_0::YCbCrLayout; namespace android { namespace { static constexpr Error kTransactionError = Error::NO_RESOURCES; uint64_t getValidUsageBits() { static const uint64_t validUsageBits = []() -> uint64_t { uint64_t bits = 0; for (const auto bit : hardware::hidl_enum_range()) { bits = bits | bit; } return bits; }(); return validUsageBits; } static inline IMapper::Rect sGralloc3Rect(const Rect& rect) { IMapper::Rect outRect{}; outRect.left = rect.left; outRect.top = rect.top; outRect.width = rect.width(); outRect.height = rect.height(); return outRect; } static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, IMapper::BufferDescriptorInfo* outDescriptorInfo) { outDescriptorInfo->width = width; outDescriptorInfo->height = height; outDescriptorInfo->layerCount = layerCount; outDescriptorInfo->format = static_cast(format); outDescriptorInfo->usage = usage; } } // anonymous namespace void Gralloc3Mapper::preload() { android::hardware::preloadPassthroughService(); } Gralloc3Mapper::Gralloc3Mapper() { mMapper = IMapper::getService(); if (mMapper == nullptr) { ALOGW("mapper 3.x is not supported"); return; } if (mMapper->isRemote()) { LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); } } bool Gralloc3Mapper::isLoaded() const { return mMapper != nullptr; } status_t Gralloc3Mapper::validateBufferDescriptorInfo( IMapper::BufferDescriptorInfo* descriptorInfo) const { uint64_t validUsageBits = getValidUsageBits(); if (descriptorInfo->usage & ~validUsageBits) { ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, descriptorInfo->usage & ~validUsageBits); return BAD_VALUE; } return NO_ERROR; } status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const { IMapper::BufferDescriptorInfo* descriptorInfo = static_cast(bufferDescriptorInfo); BufferDescriptor* outDescriptor = static_cast(outBufferDescriptor); status_t status = validateBufferDescriptorInfo(descriptorInfo); if (status != NO_ERROR) { return status; } Error error; auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) { error = tmpError; if (error != Error::NONE) { return; } *outDescriptor = tmpDescriptor; }; hardware::Return ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb); return static_cast((ret.isOk()) ? error : kTransactionError); } status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle, buffer_handle_t* outBufferHandle) const { Error error; auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { error = tmpError; if (error != Error::NONE) { return; } *outBufferHandle = static_cast(tmpBuffer); }); return static_cast((ret.isOk()) ? error : kTransactionError); } void Gralloc3Mapper::freeBuffer(buffer_handle_t bufferHandle) const { auto buffer = const_cast(bufferHandle); auto ret = mMapper->freeBuffer(buffer); auto error = (ret.isOk()) ? static_cast(ret) : kTransactionError; ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error); } status_t Gralloc3Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); auto buffer = const_cast(bufferHandle); auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride); return static_cast((ret.isOk()) ? static_cast(ret) : kTransactionError); } void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, uint32_t* outNumInts) const { *outNumFds = uint32_t(bufferHandle->numFds); *outNumInts = uint32_t(bufferHandle->numInts); Error error; auto buffer = const_cast(bufferHandle); auto ret = mMapper->getTransportSize(buffer, [&](const auto& tmpError, const auto& tmpNumFds, const auto& tmpNumInts) { error = tmpError; if (error != Error::NONE) { return; } *outNumFds = tmpNumFds; *outNumInts = tmpNumInts; }); error = (ret.isOk()) ? error : kTransactionError; ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error); } status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const { auto buffer = const_cast(bufferHandle); IMapper::Rect accessRegion = sGralloc3Rect(bounds); // put acquireFence in a hidl_handle hardware::hidl_handle acquireFenceHandle; NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); if (acquireFence >= 0) { auto h = native_handle_init(acquireFenceStorage, 1, 0); h->data[0] = acquireFence; acquireFenceHandle = h; } Error error; auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle, [&](const auto& tmpError, const auto& tmpData, const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) { error = tmpError; if (error != Error::NONE) { return; } *outData = tmpData; if (outBytesPerPixel) { *outBytesPerPixel = tmpBytesPerPixel; } if (outBytesPerStride) { *outBytesPerStride = tmpBytesPerStride; } }); // we own acquireFence even on errors if (acquireFence >= 0) { close(acquireFence); } error = (ret.isOk()) ? error : kTransactionError; ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error); return static_cast(error); } status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const { auto buffer = const_cast(bufferHandle); IMapper::Rect accessRegion = sGralloc3Rect(bounds); // put acquireFence in a hidl_handle hardware::hidl_handle acquireFenceHandle; NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); if (acquireFence >= 0) { auto h = native_handle_init(acquireFenceStorage, 1, 0); h->data[0] = acquireFence; acquireFenceHandle = h; } YCbCrLayout layout; Error error; auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle, [&](const auto& tmpError, const auto& tmpLayout) { error = tmpError; if (error != Error::NONE) { return; } layout = tmpLayout; }); if (error == Error::NONE) { ycbcr->y = layout.y; ycbcr->cb = layout.cb; ycbcr->cr = layout.cr; ycbcr->ystride = static_cast(layout.yStride); ycbcr->cstride = static_cast(layout.cStride); ycbcr->chroma_step = static_cast(layout.chromaStep); } // we own acquireFence even on errors if (acquireFence >= 0) { close(acquireFence); } return static_cast((ret.isOk()) ? error : kTransactionError); } int Gralloc3Mapper::unlock(buffer_handle_t bufferHandle) const { auto buffer = const_cast(bufferHandle); int releaseFence = -1; Error error; auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) { error = tmpError; if (error != Error::NONE) { return; } auto fenceHandle = tmpReleaseFence.getNativeHandle(); if (fenceHandle && fenceHandle->numFds == 1) { int fd = dup(fenceHandle->data[0]); if (fd >= 0) { releaseFence = fd; } else { ALOGD("failed to dup unlock release fence"); sync_wait(fenceHandle->data[0], -1); } } }); if (!ret.isOk()) { error = kTransactionError; } if (error != Error::NONE) { ALOGE("unlock(%p) failed with %d", buffer, error); } return releaseFence; } status_t Gralloc3Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); Error error; auto ret = mMapper->isSupported(descriptorInfo, [&](const auto& tmpError, const auto& tmpSupported) { error = tmpError; if (error != Error::NONE) { return; } if (outSupported) { *outSupported = tmpSupported; } }); if (!ret.isOk()) { error = kTransactionError; } if (error != Error::NONE) { ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount, error); } return static_cast(error); } Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) { mAllocator = IAllocator::getService(); if (mAllocator == nullptr) { ALOGW("allocator 3.x is not supported"); return; } } bool Gralloc3Allocator::isLoaded() const { return mAllocator != nullptr; } std::string Gralloc3Allocator::dumpDebugInfo() const { std::string debugInfo; mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); }); return debugInfo; } status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles) const { IMapper::BufferDescriptorInfo descriptorInfo; sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); BufferDescriptor descriptor; status_t error = mMapper.createDescriptor(static_cast(&descriptorInfo), static_cast(&descriptor)); if (error != NO_ERROR) { return error; } auto ret = mAllocator->allocate(descriptor, bufferCount, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { error = static_cast(tmpError); if (tmpError != Error::NONE) { return; } // import buffers for (uint32_t i = 0; i < bufferCount; i++) { error = mMapper.importBuffer(tmpBuffers[i], &outBufferHandles[i]); if (error != NO_ERROR) { for (uint32_t j = 0; j < i; j++) { mMapper.freeBuffer(outBufferHandles[j]); outBufferHandles[j] = nullptr; } return; } } *outStride = tmpStride; }); // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now hardware::IPCThreadState::self()->flushCommands(); return (ret.isOk()) ? error : static_cast(kTransactionError); } } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/GraphicBuffer.cpp���������������������������������������������������������������������������0100644 0000000 0000000 00000054734 13756501735 014417� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ #define LOG_TAG "GraphicBuffer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #ifndef LIBUI_IN_VNDK #include #endif // LIBUI_IN_VNDK #include #include #include #include namespace android { // =========================================================================== // Buffer and implementation of ANativeWindowBuffer // =========================================================================== static uint64_t getUniqueId() { static volatile int32_t nextId = 0; uint64_t id = static_cast(getpid()) << 32; id |= static_cast(android_atomic_inc(&nextId)); return id; } sp GraphicBuffer::from(ANativeWindowBuffer* anwb) { return static_cast(anwb); } GraphicBuffer* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer* buffer) { return reinterpret_cast(buffer); } GraphicBuffer const* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer const* buffer) { return reinterpret_cast(buffer); } AHardwareBuffer* GraphicBuffer::toAHardwareBuffer() { return reinterpret_cast(this); } AHardwareBuffer const* GraphicBuffer::toAHardwareBuffer() const { return reinterpret_cast(this); } GraphicBuffer::GraphicBuffer() : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) { width = height = stride = format = usage_deprecated = 0; usage = 0; layerCount = 0; handle = nullptr; } // deprecated GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage, std::string requestorName) : GraphicBuffer(inWidth, inHeight, inFormat, 1, static_cast(inUsage), requestorName) { } GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName) : GraphicBuffer() { mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, std::move(requestorName)); } // deprecated GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle, bool keepOwnership) : GraphicBuffer(inHandle, keepOwnership ? TAKE_HANDLE : WRAP_HANDLE, inWidth, inHeight, inFormat, inLayerCount, static_cast(inUsage), inStride) { } GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride) : GraphicBuffer() { mInitCheck = initWithHandle(inHandle, method, inWidth, inHeight, inFormat, inLayerCount, inUsage, inStride); } #ifndef LIBUI_IN_VNDK GraphicBuffer::GraphicBuffer(std::unique_ptr buffer) : GraphicBuffer() { if (buffer == nullptr) { mInitCheck = BAD_VALUE; return; } mInitCheck = initWithHandle(buffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE, buffer->desc().width, buffer->desc().height, static_cast(buffer->desc().format), buffer->desc().layers, buffer->desc().usage, buffer->desc().stride); mBufferId = buffer->id(); mBufferHubBuffer = std::move(buffer); } #endif // LIBUI_IN_VNDK GraphicBuffer::~GraphicBuffer() { ATRACE_CALL(); if (handle) { free_handle(); } for (auto& [callback, context] : mDeathCallbacks) { callback(context, mId); } } void GraphicBuffer::free_handle() { if (mOwner == ownHandle) { mBufferMapper.freeBuffer(handle); } else if (mOwner == ownData) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); } handle = nullptr; } status_t GraphicBuffer::initCheck() const { return static_cast(mInitCheck); } void GraphicBuffer::dumpAllocationsToSystemLog() { GraphicBufferAllocator::dumpToSystemLog(); } ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const { return static_cast( const_cast(this)); } status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { if (mOwner != ownData) return INVALID_OPERATION; if (handle && static_cast(inWidth) == width && static_cast(inHeight) == height && inFormat == format && inLayerCount == layerCount && inUsage == usage) return NO_ERROR; if (handle) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); handle = nullptr; } return initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, "[Reallocation]"); } bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage) { if (static_cast(inWidth) != width) return true; if (static_cast(inHeight) != height) return true; if (inFormat != format) return true; if (inLayerCount != layerCount) return true; if ((usage & inUsage) != inUsage) return true; if ((usage & USAGE_PROTECTED) != (inUsage & USAGE_PROTECTED)) return true; return false; } status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName) { GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); uint32_t outStride = 0; status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount, inUsage, &handle, &outStride, mId, std::move(requestorName)); if (err == NO_ERROR) { mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); width = static_cast(inWidth); height = static_cast(inHeight); format = inFormat; layerCount = inLayerCount; usage = inUsage; usage_deprecated = int(usage); stride = static_cast(outStride); } return err; } status_t GraphicBuffer::initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride) { ANativeWindowBuffer::width = static_cast(inWidth); ANativeWindowBuffer::height = static_cast(inHeight); ANativeWindowBuffer::stride = static_cast(inStride); ANativeWindowBuffer::format = inFormat; ANativeWindowBuffer::usage = inUsage; ANativeWindowBuffer::usage_deprecated = int(inUsage); ANativeWindowBuffer::layerCount = inLayerCount; mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle; if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) { buffer_handle_t importedHandle; status_t err = mBufferMapper.importBuffer(inHandle, inWidth, inHeight, inLayerCount, inFormat, inUsage, inStride, &importedHandle); if (err != NO_ERROR) { initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0); return err; } if (method == TAKE_UNREGISTERED_HANDLE) { native_handle_close(inHandle); native_handle_delete(const_cast(inHandle)); } inHandle = importedHandle; mBufferMapper.getTransportSize(inHandle, &mTransportNumFds, &mTransportNumInts); } ANativeWindowBuffer::handle = inHandle; return NO_ERROR; } status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { const Rect lockBounds(width, height); status_t res = lock(inUsage, lockBounds, vaddr, outBytesPerPixel, outBytesPerStride); return res; } status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", rect.left, rect.top, rect.right, rect.bottom, width, height); return BAD_VALUE; } status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel, outBytesPerStride); return res; } status_t GraphicBuffer::lockYCbCr(uint32_t inUsage, android_ycbcr* ycbcr) { const Rect lockBounds(width, height); status_t res = lockYCbCr(inUsage, lockBounds, ycbcr); return res; } status_t GraphicBuffer::lockYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr* ycbcr) { if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", rect.left, rect.top, rect.right, rect.bottom, width, height); return BAD_VALUE; } status_t res = getBufferMapper().lockYCbCr(handle, inUsage, rect, ycbcr); return res; } status_t GraphicBuffer::unlock() { status_t res = getBufferMapper().unlock(handle); return res; } status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { const Rect lockBounds(width, height); status_t res = lockAsync(inUsage, lockBounds, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); return res; } status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); } status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", rect.left, rect.top, rect.right, rect.bottom, width, height); return BAD_VALUE; } status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); return res; } status_t GraphicBuffer::lockAsyncYCbCr(uint32_t inUsage, android_ycbcr* ycbcr, int fenceFd) { const Rect lockBounds(width, height); status_t res = lockAsyncYCbCr(inUsage, lockBounds, ycbcr, fenceFd); return res; } status_t GraphicBuffer::lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr* ycbcr, int fenceFd) { if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", rect.left, rect.top, rect.right, rect.bottom, width, height); return BAD_VALUE; } status_t res = getBufferMapper().lockAsyncYCbCr(handle, inUsage, rect, ycbcr, fenceFd); return res; } status_t GraphicBuffer::unlockAsync(int *fenceFd) { status_t res = getBufferMapper().unlockAsync(handle, fenceFd); return res; } status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, bool* outSupported) const { return mBufferMapper.isSupported(inWidth, inHeight, inFormat, inLayerCount, inUsage, outSupported); } size_t GraphicBuffer::getFlattenedSize() const { #ifndef LIBUI_IN_VNDK if (mBufferHubBuffer != nullptr) { return 48; } #endif return static_cast(13 + (handle ? mTransportNumInts : 0)) * sizeof(int); } size_t GraphicBuffer::getFdCount() const { #ifndef LIBUI_IN_VNDK if (mBufferHubBuffer != nullptr) { return 0; } #endif return static_cast(handle ? mTransportNumFds : 0); } status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { #ifndef LIBUI_IN_VNDK if (mBufferHubBuffer != nullptr) { return flattenBufferHubBuffer(buffer, size); } #endif size_t sizeNeeded = GraphicBuffer::getFlattenedSize(); if (size < sizeNeeded) return NO_MEMORY; size_t fdCountNeeded = GraphicBuffer::getFdCount(); if (count < fdCountNeeded) return NO_MEMORY; int32_t* buf = static_cast(buffer); buf[0] = 'GB01'; buf[1] = width; buf[2] = height; buf[3] = stride; buf[4] = format; buf[5] = static_cast(layerCount); buf[6] = int(usage); // low 32-bits buf[7] = static_cast(mId >> 32); buf[8] = static_cast(mId & 0xFFFFFFFFull); buf[9] = static_cast(mGenerationNumber); buf[10] = 0; buf[11] = 0; buf[12] = int(usage >> 32); // high 32-bits if (handle) { buf[10] = int32_t(mTransportNumFds); buf[11] = int32_t(mTransportNumInts); memcpy(fds, handle->data, static_cast(mTransportNumFds) * sizeof(int)); memcpy(buf + 13, handle->data + handle->numFds, static_cast(mTransportNumInts) * sizeof(int)); } buffer = static_cast(static_cast(buffer) + sizeNeeded); size -= sizeNeeded; if (handle) { fds += mTransportNumFds; count -= static_cast(mTransportNumFds); } return NO_ERROR; } status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count) { // Check if size is not smaller than buf[0] is supposed to take. if (size < sizeof(int)) { return NO_MEMORY; } int const* buf = static_cast(buffer); // NOTE: it turns out that some media code generates a flattened GraphicBuffer manually!!!!! // see H2BGraphicBufferProducer.cpp uint32_t flattenWordCount = 0; if (buf[0] == 'GB01') { // new version with 64-bits usage bits flattenWordCount = 13; } else if (buf[0] == 'GBFR') { // old version, when usage bits were 32-bits flattenWordCount = 12; } else if (buf[0] == 'BHBB') { // BufferHub backed buffer. #ifndef LIBUI_IN_VNDK return unflattenBufferHubBuffer(buffer, size); #else return BAD_TYPE; #endif } else { return BAD_TYPE; } if (size < 12 * sizeof(int)) { android_errorWriteLog(0x534e4554, "114223584"); return NO_MEMORY; } const size_t numFds = static_cast(buf[10]); const size_t numInts = static_cast(buf[11]); // Limit the maxNumber to be relatively small. The number of fds or ints // should not come close to this number, and the number itself was simply // chosen to be high enough to not cause issues and low enough to prevent // overflow problems. const size_t maxNumber = 4096; if (numFds >= maxNumber || numInts >= (maxNumber - flattenWordCount)) { width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; handle = nullptr; ALOGE("unflatten: numFds or numInts is too large: %zd, %zd", numFds, numInts); return BAD_VALUE; } const size_t sizeNeeded = (flattenWordCount + numInts) * sizeof(int); if (size < sizeNeeded) return NO_MEMORY; size_t fdCountNeeded = numFds; if (count < fdCountNeeded) return NO_MEMORY; if (handle) { // free previous handle if any free_handle(); } if (numFds || numInts) { width = buf[1]; height = buf[2]; stride = buf[3]; format = buf[4]; layerCount = static_cast(buf[5]); usage_deprecated = buf[6]; if (flattenWordCount == 13) { usage = (uint64_t(buf[12]) << 32) | uint32_t(buf[6]); } else { usage = uint64_t(usage_deprecated); } native_handle* h = native_handle_create(static_cast(numFds), static_cast(numInts)); if (!h) { width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; handle = nullptr; ALOGE("unflatten: native_handle_create failed"); return NO_MEMORY; } memcpy(h->data, fds, numFds * sizeof(int)); memcpy(h->data + numFds, buf + flattenWordCount, numInts * sizeof(int)); handle = h; } else { width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; handle = nullptr; } mId = static_cast(buf[7]) << 32; mId |= static_cast(buf[8]); mGenerationNumber = static_cast(buf[9]); mOwner = ownHandle; if (handle != nullptr) { buffer_handle_t importedHandle; status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height), uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle); if (err != NO_ERROR) { width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; handle = nullptr; ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err); return err; } native_handle_close(handle); native_handle_delete(const_cast(handle)); handle = importedHandle; mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); } buffer = static_cast(static_cast(buffer) + sizeNeeded); size -= sizeNeeded; fds += numFds; count -= numFds; return NO_ERROR; } void GraphicBuffer::addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context) { mDeathCallbacks.emplace_back(deathCallback, context); } #ifndef LIBUI_IN_VNDK status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const { sp tokenHandle = mBufferHubBuffer->duplicate(); if (tokenHandle == nullptr || tokenHandle->handle() == nullptr || tokenHandle->handle()->numFds != 0) { return BAD_VALUE; } // Size needed for one label, one number of ints inside the token, one generation number and // the token itself. int numIntsInToken = tokenHandle->handle()->numInts; const size_t sizeNeeded = static_cast(3 + numIntsInToken) * sizeof(int); if (size < sizeNeeded) { ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__, static_cast(sizeNeeded), static_cast(size)); return NO_MEMORY; } size -= sizeNeeded; int* buf = static_cast(buffer); buf[0] = 'BHBB'; buf[1] = numIntsInToken; memcpy(buf + 2, tokenHandle->handle()->data, static_cast(numIntsInToken) * sizeof(int)); buf[2 + numIntsInToken] = static_cast(mGenerationNumber); return NO_ERROR; } status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) { const int* buf = static_cast(buffer); int numIntsInToken = buf[1]; // Size needed for one label, one number of ints inside the token, one generation number and // the token itself. const size_t sizeNeeded = static_cast(3 + numIntsInToken) * sizeof(int); if (size < sizeNeeded) { ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__, static_cast(sizeNeeded), static_cast(size)); return NO_MEMORY; } size -= sizeNeeded; native_handle_t* importToken = native_handle_create(/*numFds=*/0, /*numInts=*/numIntsInToken); memcpy(importToken->data, buf + 2, static_cast(buf[1]) * sizeof(int)); sp importTokenHandle = NativeHandle::create(importToken, /*ownHandle=*/true); std::unique_ptr bufferHubBuffer = BufferHubBuffer::import(importTokenHandle); if (bufferHubBuffer == nullptr || bufferHubBuffer.get() == nullptr) { return BAD_VALUE; } // Reconstruct this GraphicBuffer object using the new BufferHubBuffer object. if (handle) { free_handle(); } mId = 0; mGenerationNumber = static_cast(buf[2 + numIntsInToken]); mInitCheck = initWithHandle(bufferHubBuffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE, bufferHubBuffer->desc().width, bufferHubBuffer->desc().height, static_cast(bufferHubBuffer->desc().format), bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage, bufferHubBuffer->desc().stride); mBufferId = bufferHubBuffer->id(); mBufferHubBuffer.reset(std::move(bufferHubBuffer.get())); return NO_ERROR; } bool GraphicBuffer::isBufferHubBuffer() const { return mBufferHubBuffer != nullptr; } #endif // LIBUI_IN_VNDK // --------------------------------------------------------------------------- }; // namespace android ������������������������������������libs/ui/GraphicBufferAllocator.cpp������������������������������������������������������������������0100644 0000000 0000000 00000014322 13756501735 016245� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** ** Copyright 2009, 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. */ #define LOG_TAG "GraphicBufferAllocator" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- using base::StringAppendF; ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator ) Mutex GraphicBufferAllocator::sLock; KeyedVector GraphicBufferAllocator::sAllocList; GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) { mAllocator = std::make_unique( reinterpret_cast(mMapper.getGrallocMapper())); if (!mAllocator->isLoaded()) { mAllocator = std::make_unique( reinterpret_cast(mMapper.getGrallocMapper())); } if (!mAllocator->isLoaded()) { LOG_ALWAYS_FATAL("gralloc-allocator is missing"); } } GraphicBufferAllocator::~GraphicBufferAllocator() {} size_t GraphicBufferAllocator::getTotalSize() const { Mutex::Autolock _l(sLock); size_t total = 0; for (size_t i = 0; i < sAllocList.size(); ++i) { total += sAllocList.valueAt(i).size; } return total; } void GraphicBufferAllocator::dump(std::string& result) const { Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); size_t total = 0; result.append("Allocated buffers:\n"); const size_t c = list.size(); for (size_t i=0 ; idumpDebugInfo()); } void GraphicBufferAllocator::dumpToSystemLog() { std::string s; GraphicBufferAllocator::getInstance().dump(s); ALOGD("%s", s.c_str()); } status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, uint64_t /*graphicBufferId*/, std::string requestorName) { ATRACE_CALL(); // make sure to not allocate a N x 0 or 0 x N buffer, since this is // allowed from an API stand-point allocate a 1x1 buffer instead. if (!width || !height) width = height = 1; const uint32_t bpp = bytesPerPixel(format); if (std::numeric_limits::max() / width / height < static_cast(bpp)) { ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " "usage %" PRIx64 ": Requesting too large a buffer size", width, height, layerCount, format, usage); return BAD_VALUE; } // Ensure that layerCount is valid. if (layerCount < 1) layerCount = 1; // TODO(b/72323293, b/72703005): Remove these invalid bits from callers usage &= ~static_cast((1 << 10) | (1 << 13)); status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle); size_t bufSize; // if stride has no meaning or is too large, // approximate size with the input width instead if ((*stride) != 0 && std::numeric_limits::max() / height / (*stride) < static_cast(bpp)) { bufSize = static_cast(width) * height * bpp; } else { bufSize = static_cast((*stride)) * height * bpp; } if (error == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); alloc_rec_t rec; rec.width = width; rec.height = height; rec.stride = *stride; rec.format = format; rec.layerCount = layerCount; rec.usage = usage; rec.size = bufSize; rec.requestorName = std::move(requestorName); list.add(*handle, rec); return NO_ERROR; } else { ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " "usage %" PRIx64 ": %d", width, height, layerCount, format, usage, error); return NO_MEMORY; } } status_t GraphicBufferAllocator::free(buffer_handle_t handle) { ATRACE_CALL(); // We allocated a buffer from the allocator and imported it into the // mapper to get the handle. We just need to free the handle now. mMapper.freeBuffer(handle); Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); list.removeItem(handle); return NO_ERROR; } // --------------------------------------------------------------------------- }; // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/GraphicBufferMapper.cpp���������������������������������������������������������������������0100644 0000000 0000000 00000013307 13756501735 015553� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ #define LOG_TAG "GraphicBufferMapper" #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #include #include // We would eliminate the non-conforming zero-length array, but we can't since // this is effectively included from the Linux kernel #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-length-array" #include #pragma clang diagnostic pop #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) void GraphicBufferMapper::preloadHal() { Gralloc2Mapper::preload(); Gralloc3Mapper::preload(); } GraphicBufferMapper::GraphicBufferMapper() { mMapper = std::make_unique(); if (!mMapper->isLoaded()) { mMapper = std::make_unique(); mMapperVersion = Version::GRALLOC_2; } else { mMapperVersion = Version::GRALLOC_3; } if (!mMapper->isLoaded()) { LOG_ALWAYS_FATAL("gralloc-mapper is missing"); } } status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, uint32_t width, uint32_t height, uint32_t layerCount, PixelFormat format, uint64_t usage, uint32_t stride, buffer_handle_t* outHandle) { ATRACE_CALL(); buffer_handle_t bufferHandle; status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle); if (error != NO_ERROR) { ALOGW("importBuffer(%p) failed: %d", rawHandle, error); return error; } error = mMapper->validateBufferSize(bufferHandle, width, height, format, layerCount, usage, stride); if (error != NO_ERROR) { ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error); freeBuffer(bufferHandle); return static_cast(error); } *outHandle = bufferHandle; return NO_ERROR; } void GraphicBufferMapper::getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts) { mMapper->getTransportSize(handle, outTransportNumFds, outTransportNumInts); } status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle) { ATRACE_CALL(); mMapper->freeBuffer(handle); return NO_ERROR; } status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride); } status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr) { return lockAsyncYCbCr(handle, usage, bounds, ycbcr, -1); } status_t GraphicBufferMapper::unlock(buffer_handle_t handle) { int32_t fenceFd = -1; status_t error = unlockAsync(handle, &fenceFd); if (error == NO_ERROR && fenceFd >= 0) { sync_wait(fenceFd, -1); close(fenceFd); } return error; } status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); } status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, void** vaddr, int fenceFd, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { ATRACE_CALL(); const uint64_t usage = static_cast( android_convertGralloc1To0Usage(producerUsage, consumerUsage)); return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel, outBytesPerStride); } status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd) { ATRACE_CALL(); return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr); } status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) { ATRACE_CALL(); *fenceFd = mMapper->unlock(handle); return NO_ERROR; } status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported) { return mMapper->isSupported(width, height, format, layerCount, usage, outSupported); } // --------------------------------------------------------------------------- }; // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/HdrCapabilities.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000005555 13756501735 014734� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #include namespace android { #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" #endif HdrCapabilities::~HdrCapabilities() = default; HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) noexcept = default; HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) noexcept = default; size_t HdrCapabilities::getFlattenedSize() const { return sizeof(mMaxLuminance) + sizeof(mMaxAverageLuminance) + sizeof(mMinLuminance) + sizeof(int32_t) + mSupportedHdrTypes.size() * sizeof(ui::Hdr); } status_t HdrCapabilities::flatten(void* buffer, size_t size) const { if (size < getFlattenedSize()) { return NO_MEMORY; } int32_t* const buf = static_cast(buffer); reinterpret_cast(buf[0]) = mMaxLuminance; reinterpret_cast(buf[1]) = mMaxAverageLuminance; reinterpret_cast(buf[2]) = mMinLuminance; buf[3] = static_cast(mSupportedHdrTypes.size()); for (size_t i = 0, c = mSupportedHdrTypes.size(); i < c; ++i) { buf[4 + i] = static_cast(mSupportedHdrTypes[i]); } return NO_ERROR; } status_t HdrCapabilities::unflatten(void const* buffer, size_t size) { size_t minSize = sizeof(mMaxLuminance) + sizeof(mMaxAverageLuminance) + sizeof(mMinLuminance) + sizeof(int32_t); if (size < minSize) { return NO_MEMORY; } int32_t const * const buf = static_cast(buffer); const size_t itemCount = size_t(buf[3]); // check the buffer is large enough if (size < minSize + itemCount * sizeof(int32_t)) { return BAD_VALUE; } mMaxLuminance = reinterpret_cast(buf[0]); mMaxAverageLuminance = reinterpret_cast(buf[1]); mMinLuminance = reinterpret_cast(buf[2]); if (itemCount) { mSupportedHdrTypes.resize(itemCount); for (size_t i = 0; i < itemCount; ++i) { mSupportedHdrTypes[i] = static_cast(buf[4 + i]); } } return NO_ERROR; } #if defined(__clang__) #pragma clang diagnostic pop #endif } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/MODULE_LICENSE_APACHE2����������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 014312� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/NOTICE��������������������������������������������������������������������������������������0100644 0000000 0000000 00000024707 13756501735 012105� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ���������������������������������������������������������libs/ui/OWNERS��������������������������������������������������������������������������������������0100644 0000000 0000000 00000000200 13756501735 012117� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������lpy@google.com marissaw@google.com mathias@google.com romainguy@google.com stoza@google.com jwcai@google.com tianyuj@google.com ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/PixelFormat.cpp�����������������������������������������������������������������������������0100644 0000000 0000000 00000003766 13756501735 014141� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ #include // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- uint32_t bytesPerPixel(PixelFormat format) { switch (format) { case PIXEL_FORMAT_RGBA_FP16: return 8; case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_BGRA_8888: case PIXEL_FORMAT_RGBA_1010102: return 4; case PIXEL_FORMAT_RGB_888: return 3; case PIXEL_FORMAT_RGB_565: case PIXEL_FORMAT_RGBA_5551: case PIXEL_FORMAT_RGBA_4444: return 2; } return 0; } uint32_t bitsPerPixel(PixelFormat format) { switch (format) { case PIXEL_FORMAT_RGBA_FP16: return 64; case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_BGRA_8888: case PIXEL_FORMAT_RGBA_1010102: return 32; case PIXEL_FORMAT_RGB_888: return 24; case PIXEL_FORMAT_RGB_565: case PIXEL_FORMAT_RGBA_5551: case PIXEL_FORMAT_RGBA_4444: return 16; } return 0; } // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- ����������libs/ui/PublicFormat.cpp����������������������������������������������������������������������������0100644 0000000 0000000 00000013135 13756501735 014265� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2019 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. */ #include // ui::Dataspace #include // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- using ui::Dataspace; int mapPublicFormatToHalFormat(PublicFormat f) { switch (f) { case PublicFormat::JPEG: case PublicFormat::DEPTH_POINT_CLOUD: case PublicFormat::DEPTH_JPEG: case PublicFormat::HEIC: return HAL_PIXEL_FORMAT_BLOB; case PublicFormat::DEPTH16: return HAL_PIXEL_FORMAT_Y16; case PublicFormat::RAW_SENSOR: case PublicFormat::RAW_DEPTH: return HAL_PIXEL_FORMAT_RAW16; default: // Most formats map 1:1 return static_cast(f); } } android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { Dataspace dataspace; switch (f) { case PublicFormat::JPEG: dataspace = Dataspace::V0_JFIF; break; case PublicFormat::DEPTH_POINT_CLOUD: case PublicFormat::DEPTH16: case PublicFormat::RAW_DEPTH: dataspace = Dataspace::DEPTH; break; case PublicFormat::RAW_SENSOR: case PublicFormat::RAW_PRIVATE: case PublicFormat::RAW10: case PublicFormat::RAW12: dataspace = Dataspace::ARBITRARY; break; case PublicFormat::YUV_420_888: case PublicFormat::NV21: case PublicFormat::YV12: dataspace = Dataspace::V0_JFIF; break; case PublicFormat::DEPTH_JPEG: dataspace = Dataspace::DYNAMIC_DEPTH; break; case PublicFormat::HEIC: dataspace = Dataspace::HEIF; break; default: // Most formats map to UNKNOWN dataspace = Dataspace::UNKNOWN; break; } return static_cast(dataspace); } PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { Dataspace ds = static_cast(dataSpace); switch (format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_RGBA_FP16: case HAL_PIXEL_FORMAT_RGBA_1010102: case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_YCbCr_420_888: case HAL_PIXEL_FORMAT_YV12: // Enums overlap in both name and value return static_cast(format); case HAL_PIXEL_FORMAT_RAW16: switch (ds) { case Dataspace::DEPTH: return PublicFormat::RAW_DEPTH; default: return PublicFormat::RAW_SENSOR; } case HAL_PIXEL_FORMAT_RAW_OPAQUE: // Name differs, though value is the same return PublicFormat::RAW_PRIVATE; case HAL_PIXEL_FORMAT_YCbCr_422_SP: // Name differs, though the value is the same return PublicFormat::NV16; case HAL_PIXEL_FORMAT_YCrCb_420_SP: // Name differs, though the value is the same return PublicFormat::NV21; case HAL_PIXEL_FORMAT_YCbCr_422_I: // Name differs, though the value is the same return PublicFormat::YUY2; case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: // Name differs, though the value is the same return PublicFormat::PRIVATE; case HAL_PIXEL_FORMAT_Y16: // Dataspace-dependent switch (ds) { case Dataspace::DEPTH: return PublicFormat::DEPTH16; default: // Assume non-depth Y16 is just Y16. return PublicFormat::Y16; } case HAL_PIXEL_FORMAT_BLOB: // Dataspace-dependent switch (ds) { case Dataspace::DEPTH: return PublicFormat::DEPTH_POINT_CLOUD; case Dataspace::V0_JFIF: return PublicFormat::JPEG; case Dataspace::HEIF: return PublicFormat::HEIC; default: if (dataSpace == static_cast(HAL_DATASPACE_DYNAMIC_DEPTH)) { return PublicFormat::DEPTH_JPEG; } else { // Assume otherwise-marked blobs are also JPEG return PublicFormat::JPEG; } } case HAL_PIXEL_FORMAT_BGRA_8888: // Not defined in public API return PublicFormat::UNKNOWN; default: return PublicFormat::UNKNOWN; } } // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Rect.cpp������������������������������������������������������������������������������������0100644 0000000 0000000 00000010162 13756501735 012570� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2009 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. */ #include #include namespace android { const Rect Rect::INVALID_RECT{0, 0, -1, -1}; const Rect Rect::EMPTY_RECT{0, 0, 0, 0}; static inline int32_t min(int32_t a, int32_t b) { return (a < b) ? a : b; } static inline int32_t max(int32_t a, int32_t b) { return (a > b) ? a : b; } void Rect::makeInvalid() { left = 0; top = 0; right = -1; bottom = -1; } bool Rect::operator <(const Rect& rhs) const { if (top < rhs.top) { return true; } else if (top == rhs.top) { if (left < rhs.left) { return true; } else if (left == rhs.left) { if (bottom < rhs.bottom) { return true; } else if (bottom == rhs.bottom) { if (right < rhs.right) { return true; } } } } return false; } Rect& Rect::offsetTo(int32_t x, int32_t y) { right -= left - x; bottom -= top - y; left = x; top = y; return *this; } Rect& Rect::offsetBy(int32_t x, int32_t y) { left += x; top += y; right += x; bottom += y; return *this; } Rect& Rect::inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom) { this->left += _left; this->top += _top; this->right -= _right; this->bottom -= _bottom; return *this; } const Rect Rect::operator +(const Point& rhs) const { const Rect result(left + rhs.x, top + rhs.y, right + rhs.x, bottom + rhs.y); return result; } const Rect Rect::operator -(const Point& rhs) const { const Rect result(left - rhs.x, top - rhs.y, right - rhs.x, bottom - rhs.y); return result; } bool Rect::intersect(const Rect& with, Rect* result) const { result->left = max(left, with.left); result->top = max(top, with.top); result->right = min(right, with.right); result->bottom = min(bottom, with.bottom); return !(result->isEmpty()); } Rect Rect::transform(uint32_t xform, int32_t width, int32_t height) const { Rect result(*this); if (xform & HAL_TRANSFORM_FLIP_H) { result = Rect(width - result.right, result.top, width - result.left, result.bottom); } if (xform & HAL_TRANSFORM_FLIP_V) { result = Rect(result.left, height - result.bottom, result.right, height - result.top); } if (xform & HAL_TRANSFORM_ROT_90) { int left = height - result.bottom; int top = result.left; int right = height - result.top; int bottom = result.right; result = Rect(left, top, right, bottom); } return result; } Rect Rect::reduce(const Rect& exclude) const { Rect result(Rect::EMPTY_RECT); uint32_t mask = 0; mask |= (exclude.left > left) ? 1 : 0; mask |= (exclude.top > top) ? 2 : 0; mask |= (exclude.right < right) ? 4 : 0; mask |= (exclude.bottom < bottom) ? 8 : 0; if (mask == 0) { // crop entirely covers us result.clear(); } else { result = *this; if (!(mask & (mask - 1))) { // power-of-2, i.e.: just one bit is set if (mask & 1) { result.right = min(result.right, exclude.left); } else if (mask & 2) { result.bottom = min(result.bottom, exclude.top); } else if (mask & 4) { result.left = max(result.left, exclude.right); } else if (mask & 8) { result.top = max(result.top, exclude.bottom); } } } return result; } }; // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Region.cpp����������������������������������������������������������������������������������0100644 0000000 0000000 00000065764 13756501735 013140� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ #define LOG_TAG "Region" #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // ### VALIDATE_REGIONS ### // To enable VALIDATE_REGIONS traces, use the "libui-validate-regions-defaults" // in Android.bp. Do not #define VALIDATE_REGIONS here as it requires extra libs. #define VALIDATE_WITH_CORECG (false) // ---------------------------------------------------------------------------- #if defined(VALIDATE_REGIONS) #include #endif #if VALIDATE_WITH_CORECG #include #endif namespace android { // ---------------------------------------------------------------------------- using base::StringAppendF; enum { op_nand = region_operator::op_nand, op_and = region_operator::op_and, op_or = region_operator::op_or, op_xor = region_operator::op_xor }; enum { direction_LTR, direction_RTL }; const Region Region::INVALID_REGION(Rect::INVALID_RECT); // ---------------------------------------------------------------------------- Region::Region() { mStorage.add(Rect(0,0)); } Region::Region(const Region& rhs) : mStorage(rhs.mStorage) { #if defined(VALIDATE_REGIONS) validate(rhs, "rhs copy-ctor"); #endif } Region::Region(const Rect& rhs) { mStorage.add(rhs); } Region::~Region() { } /** * Copy rects from the src vector into the dst vector, resolving vertical T-Junctions along the way * * First pass through, divideSpanRTL will be set because the 'previous span' (indexing into the dst * vector) will be reversed. Each rectangle in the original list, starting from the bottom, will be * compared with the span directly below, and subdivided as needed to resolve T-junctions. * * The resulting temporary vector will be a completely reversed copy of the original, without any * bottom-up T-junctions. * * Second pass through, divideSpanRTL will be false since the previous span will index into the * final, correctly ordered region buffer. Each rectangle will be compared with the span directly * above it, and subdivided to resolve any remaining T-junctions. */ static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, Vector& dst, int spanDirection) { dst.clear(); const Rect* current = end - 1; int lastTop = current->top; // add first span immediately do { dst.add(*current); current--; } while (current->top == lastTop && current >= begin); int beginLastSpan = -1; int endLastSpan = -1; int top = -1; int bottom = -1; // for all other spans, split if a t-junction exists in the span directly above while (current >= begin) { if (current->top != (current + 1)->top) { // new span if ((spanDirection == direction_RTL && current->bottom != (current + 1)->top) || (spanDirection == direction_LTR && current->top != (current + 1)->bottom)) { // previous span not directly adjacent, don't check for T junctions beginLastSpan = INT_MAX; } else { beginLastSpan = endLastSpan + 1; } endLastSpan = static_cast(dst.size()) - 1; top = current->top; bottom = current->bottom; } int left = current->left; int right = current->right; for (int prevIndex = beginLastSpan; prevIndex <= endLastSpan; prevIndex++) { // prevIndex can't be -1 here because if endLastSpan is set to a // value greater than -1 (allowing the loop to execute), // beginLastSpan (and therefore prevIndex) will also be increased const Rect prev = dst[static_cast(prevIndex)]; if (spanDirection == direction_RTL) { // iterating over previous span RTL, quit if it's too far left if (prev.right <= left) break; if (prev.right > left && prev.right < right) { dst.add(Rect(prev.right, top, right, bottom)); right = prev.right; } if (prev.left > left && prev.left < right) { dst.add(Rect(prev.left, top, right, bottom)); right = prev.left; } // if an entry in the previous span is too far right, nothing further left in the // current span will need it if (prev.left >= right) { beginLastSpan = prevIndex; } } else { // iterating over previous span LTR, quit if it's too far right if (prev.left >= right) break; if (prev.left > left && prev.left < right) { dst.add(Rect(left, top, prev.left, bottom)); left = prev.left; } if (prev.right > left && prev.right < right) { dst.add(Rect(left, top, prev.right, bottom)); left = prev.right; } // if an entry in the previous span is too far left, nothing further right in the // current span will need it if (prev.right <= left) { beginLastSpan = prevIndex; } } } if (left < right) { dst.add(Rect(left, top, right, bottom)); } current--; } } /** * Creates a new region with the same data as the argument, but divides rectangles as necessary to * remove T-Junctions * * Note: the output will not necessarily be a very efficient representation of the region, since it * may be that a triangle-based approach would generate significantly simpler geometry */ Region Region::createTJunctionFreeRegion(const Region& r) { if (r.isEmpty()) return r; if (r.isRect()) return r; Vector reversed; reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL); Region outputRegion; reverseRectsResolvingJunctions(reversed.begin(), reversed.end(), outputRegion.mStorage, direction_LTR); outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds #if defined(VALIDATE_REGIONS) validate(outputRegion, "T-Junction free region"); #endif return outputRegion; } Region& Region::operator = (const Region& rhs) { #if defined(VALIDATE_REGIONS) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif mStorage = rhs.mStorage; return *this; } Region& Region::makeBoundsSelf() { if (mStorage.size() >= 2) { const Rect bounds(getBounds()); mStorage.clear(); mStorage.add(bounds); } return *this; } bool Region::contains(const Point& point) const { return contains(point.x, point.y); } bool Region::contains(int x, int y) const { const_iterator cur = begin(); const_iterator const tail = end(); while (cur != tail) { if (y >= cur->top && y < cur->bottom && x >= cur->left && x < cur->right) { return true; } cur++; } return false; } void Region::clear() { mStorage.clear(); mStorage.add(Rect(0,0)); } void Region::set(const Rect& r) { mStorage.clear(); mStorage.add(r); } void Region::set(int32_t w, int32_t h) { mStorage.clear(); mStorage.add(Rect(w, h)); } void Region::set(uint32_t w, uint32_t h) { mStorage.clear(); mStorage.add(Rect(w, h)); } bool Region::isTriviallyEqual(const Region& region) const { return begin() == region.begin(); } // ---------------------------------------------------------------------------- void Region::addRectUnchecked(int l, int t, int r, int b) { Rect rect(l,t,r,b); size_t where = mStorage.size() - 1; mStorage.insertAt(rect, where, 1); } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Rect& r) { return operationSelf(r, op_or); } Region& Region::xorSelf(const Rect& r) { return operationSelf(r, op_xor); } Region& Region::andSelf(const Rect& r) { return operationSelf(r, op_and); } Region& Region::subtractSelf(const Rect& r) { return operationSelf(r, op_nand); } Region& Region::operationSelf(const Rect& r, uint32_t op) { Region lhs(*this); boolean_operation(op, *this, lhs, r); return *this; } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Region& rhs) { return operationSelf(rhs, op_or); } Region& Region::xorSelf(const Region& rhs) { return operationSelf(rhs, op_xor); } Region& Region::andSelf(const Region& rhs) { return operationSelf(rhs, op_and); } Region& Region::subtractSelf(const Region& rhs) { return operationSelf(rhs, op_nand); } Region& Region::operationSelf(const Region& rhs, uint32_t op) { Region lhs(*this); boolean_operation(op, *this, lhs, rhs); return *this; } Region& Region::translateSelf(int x, int y) { if (x|y) translate(*this, x, y); return *this; } Region& Region::scaleSelf(float sx, float sy) { size_t count = mStorage.size(); Rect* rects = mStorage.editArray(); while (count) { rects->left = static_cast(rects->left * sx + 0.5f); rects->right = static_cast(rects->right * sx + 0.5f); rects->top = static_cast(rects->top * sy + 0.5f); rects->bottom = static_cast(rects->bottom * sy + 0.5f); rects++; count--; } return *this; } // ---------------------------------------------------------------------------- const Region Region::merge(const Rect& rhs) const { return operation(rhs, op_or); } const Region Region::mergeExclusive(const Rect& rhs) const { return operation(rhs, op_xor); } const Region Region::intersect(const Rect& rhs) const { return operation(rhs, op_and); } const Region Region::subtract(const Rect& rhs) const { return operation(rhs, op_nand); } const Region Region::operation(const Rect& rhs, uint32_t op) const { Region result; boolean_operation(op, result, *this, rhs); return result; } // ---------------------------------------------------------------------------- const Region Region::merge(const Region& rhs) const { return operation(rhs, op_or); } const Region Region::mergeExclusive(const Region& rhs) const { return operation(rhs, op_xor); } const Region Region::intersect(const Region& rhs) const { return operation(rhs, op_and); } const Region Region::subtract(const Region& rhs) const { return operation(rhs, op_nand); } const Region Region::operation(const Region& rhs, uint32_t op) const { Region result; boolean_operation(op, result, *this, rhs); return result; } const Region Region::translate(int x, int y) const { Region result; translate(result, *this, x, y); return result; } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Region& rhs, int dx, int dy) { return operationSelf(rhs, dx, dy, op_or); } Region& Region::xorSelf(const Region& rhs, int dx, int dy) { return operationSelf(rhs, dx, dy, op_xor); } Region& Region::andSelf(const Region& rhs, int dx, int dy) { return operationSelf(rhs, dx, dy, op_and); } Region& Region::subtractSelf(const Region& rhs, int dx, int dy) { return operationSelf(rhs, dx, dy, op_nand); } Region& Region::operationSelf(const Region& rhs, int dx, int dy, uint32_t op) { Region lhs(*this); boolean_operation(op, *this, lhs, rhs, dx, dy); return *this; } // ---------------------------------------------------------------------------- const Region Region::merge(const Region& rhs, int dx, int dy) const { return operation(rhs, dx, dy, op_or); } const Region Region::mergeExclusive(const Region& rhs, int dx, int dy) const { return operation(rhs, dx, dy, op_xor); } const Region Region::intersect(const Region& rhs, int dx, int dy) const { return operation(rhs, dx, dy, op_and); } const Region Region::subtract(const Region& rhs, int dx, int dy) const { return operation(rhs, dx, dy, op_nand); } const Region Region::operation(const Region& rhs, int dx, int dy, uint32_t op) const { Region result; boolean_operation(op, result, *this, rhs, dx, dy); return result; } // ---------------------------------------------------------------------------- // This is our region rasterizer, which merges rects and spans together // to obtain an optimal region. class Region::rasterizer : public region_operator::region_rasterizer { Rect bounds; Vector& storage; Rect* head; Rect* tail; Vector span; Rect* cur; public: explicit rasterizer(Region& reg) : bounds(INT_MAX, 0, INT_MIN, 0), storage(reg.mStorage), head(), tail(), cur() { storage.clear(); } virtual ~rasterizer(); virtual void operator()(const Rect& rect); private: template static inline T min(T rhs, T lhs) { return rhs < lhs ? rhs : lhs; } template static inline T max(T rhs, T lhs) { return rhs > lhs ? rhs : lhs; } void flushSpan(); }; Region::rasterizer::~rasterizer() { if (span.size()) { flushSpan(); } if (storage.size()) { bounds.top = storage.itemAt(0).top; bounds.bottom = storage.top().bottom; if (storage.size() == 1) { storage.clear(); } } else { bounds.left = 0; bounds.right = 0; } storage.add(bounds); } void Region::rasterizer::operator()(const Rect& rect) { //ALOGD(">>> %3d, %3d, %3d, %3d", // rect.left, rect.top, rect.right, rect.bottom); if (span.size()) { if (cur->top != rect.top) { flushSpan(); } else if (cur->right == rect.left) { cur->right = rect.right; return; } } span.add(rect); cur = span.editArray() + (span.size() - 1); } void Region::rasterizer::flushSpan() { bool merge = false; if (tail-head == ssize_t(span.size())) { Rect const* p = span.editArray(); Rect const* q = head; if (p->top == q->bottom) { merge = true; while (q != tail) { if ((p->left != q->left) || (p->right != q->right)) { merge = false; break; } p++; q++; } } } if (merge) { const int bottom = span[0].bottom; Rect* r = head; while (r != tail) { r->bottom = bottom; r++; } } else { bounds.left = min(span.itemAt(0).left, bounds.left); bounds.right = max(span.top().right, bounds.right); storage.appendVector(span); tail = storage.editArray() + storage.size(); head = tail - span.size(); } span.clear(); } bool Region::validate(const Region& reg, const char* name, bool silent) { if (reg.mStorage.isEmpty()) { ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name); // return immediately as the code below assumes mStorage is non-empty return false; } bool result = true; const_iterator cur = reg.begin(); const_iterator const tail = reg.end(); const_iterator prev = cur; Rect b(*prev); while (cur != tail) { if (cur->isValid() == false) { // We allow this particular flavor of invalid Rect, since it is used // as a signal value in various parts of the system if (*cur != Rect::INVALID_RECT) { ALOGE_IF(!silent, "%s: region contains an invalid Rect", name); result = false; } } if (cur->right > region_operator::max_value) { ALOGE_IF(!silent, "%s: rect->right > max_value", name); result = false; } if (cur->bottom > region_operator::max_value) { ALOGE_IF(!silent, "%s: rect->right > max_value", name); result = false; } if (prev != cur) { b.left = b.left < cur->left ? b.left : cur->left; b.top = b.top < cur->top ? b.top : cur->top; b.right = b.right > cur->right ? b.right : cur->right; b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom; if ((*prev < *cur) == false) { ALOGE_IF(!silent, "%s: region's Rects not sorted", name); result = false; } if (cur->top == prev->top) { if (cur->bottom != prev->bottom) { ALOGE_IF(!silent, "%s: invalid span %p", name, cur); result = false; } else if (cur->left < prev->right) { ALOGE_IF(!silent, "%s: spans overlap horizontally prev=%p, cur=%p", name, prev, cur); result = false; } } else if (cur->top < prev->bottom) { ALOGE_IF(!silent, "%s: spans overlap vertically prev=%p, cur=%p", name, prev, cur); result = false; } prev = cur; } cur++; } if (b != reg.getBounds()) { result = false; ALOGE_IF(!silent, "%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name, b.left, b.top, b.right, b.bottom, reg.getBounds().left, reg.getBounds().top, reg.getBounds().right, reg.getBounds().bottom); } if (reg.mStorage.size() == 2) { result = false; ALOGE_IF(!silent, "%s: mStorage size is 2, which is never valid", name); } #if defined(VALIDATE_REGIONS) if (result == false && !silent) { reg.dump(name); CallStack stack(LOG_TAG); } #endif return result; } void Region::boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Region& rhs, int dx, int dy) { #if defined(VALIDATE_REGIONS) validate(lhs, "boolean_operation (before): lhs"); validate(rhs, "boolean_operation (before): rhs"); validate(dst, "boolean_operation (before): dst"); #endif size_t lhs_count; Rect const * const lhs_rects = lhs.getArray(&lhs_count); size_t rhs_count; Rect const * const rhs_rects = rhs.getArray(&rhs_count); region_operator::region lhs_region(lhs_rects, lhs_count); region_operator::region rhs_region(rhs_rects, rhs_count, dx, dy); region_operator operation(op, lhs_region, rhs_region); { // scope for rasterizer (dtor has side effects) rasterizer r(dst); operation(r); } #if defined(VALIDATE_REGIONS) validate(lhs, "boolean_operation: lhs"); validate(rhs, "boolean_operation: rhs"); validate(dst, "boolean_operation: dst"); #endif #if VALIDATE_WITH_CORECG SkRegion sk_lhs; SkRegion sk_rhs; SkRegion sk_dst; for (size_t i=0 ; ileft != it.rect().fLeft || head->top != it.rect().fTop || head->right != it.rect().fRight || head->bottom != it.rect().fBottom ) { same = false; break; } } else { same = false; break; } head++; it.next(); } if (head != tail) { same = false; } if(!same) { ALOGD("---\nregion boolean %s failed", name); lhs.dump("lhs"); rhs.dump("rhs"); dst.dump("dst"); ALOGD("should be"); SkRegion::Iterator it(sk_dst); while (!it.done()) { ALOGD(" [%3d, %3d, %3d, %3d]", it.rect().fLeft, it.rect().fTop, it.rect().fRight, it.rect().fBottom); it.next(); } } #endif } void Region::boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Rect& rhs, int dx, int dy) { // We allow this particular flavor of invalid Rect, since it is used as a // signal value in various parts of the system if (!rhs.isValid() && rhs != Rect::INVALID_RECT) { ALOGE("Region::boolean_operation(op=%d) invalid Rect={%d,%d,%d,%d}", op, rhs.left, rhs.top, rhs.right, rhs.bottom); return; } #if VALIDATE_WITH_CORECG || defined(VALIDATE_REGIONS) boolean_operation(op, dst, lhs, Region(rhs), dx, dy); #else size_t lhs_count; Rect const * const lhs_rects = lhs.getArray(&lhs_count); region_operator::region lhs_region(lhs_rects, lhs_count); region_operator::region rhs_region(&rhs, 1, dx, dy); region_operator operation(op, lhs_region, rhs_region); { // scope for rasterizer (dtor has side effects) rasterizer r(dst); operation(r); } #endif } void Region::boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Region& rhs) { boolean_operation(op, dst, lhs, rhs, 0, 0); } void Region::boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Rect& rhs) { boolean_operation(op, dst, lhs, rhs, 0, 0); } void Region::translate(Region& reg, int dx, int dy) { if ((dx || dy) && !reg.isEmpty()) { #if defined(VALIDATE_REGIONS) validate(reg, "translate (before)"); #endif size_t count = reg.mStorage.size(); Rect* rects = reg.mStorage.editArray(); while (count) { rects->offsetBy(dx, dy); rects++; count--; } #if defined(VALIDATE_REGIONS) validate(reg, "translate (after)"); #endif } } void Region::translate(Region& dst, const Region& reg, int dx, int dy) { dst = reg; translate(dst, dx, dy); } // ---------------------------------------------------------------------------- size_t Region::getFlattenedSize() const { return sizeof(uint32_t) + mStorage.size() * sizeof(Rect); } status_t Region::flatten(void* buffer, size_t size) const { #if defined(VALIDATE_REGIONS) validate(*this, "Region::flatten"); #endif if (size < getFlattenedSize()) { return NO_MEMORY; } // Cast to uint32_t since the size of a size_t can vary between 32- and // 64-bit processes FlattenableUtils::write(buffer, size, static_cast(mStorage.size())); for (auto rect : mStorage) { status_t result = rect.flatten(buffer, size); if (result != NO_ERROR) { return result; } FlattenableUtils::advance(buffer, size, sizeof(rect)); } return NO_ERROR; } status_t Region::unflatten(void const* buffer, size_t size) { if (size < sizeof(uint32_t)) { return NO_MEMORY; } uint32_t numRects = 0; FlattenableUtils::read(buffer, size, numRects); if (size < numRects * sizeof(Rect)) { return NO_MEMORY; } if (numRects > (UINT32_MAX / sizeof(Rect))) { android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, nullptr, 0); return NO_MEMORY; } Region result; result.mStorage.clear(); for (size_t r = 0; r < numRects; ++r) { Rect rect(Rect::EMPTY_RECT); status_t status = rect.unflatten(buffer, size); if (status != NO_ERROR) { return status; } FlattenableUtils::advance(buffer, size, sizeof(rect)); result.mStorage.push_back(rect); } #if defined(VALIDATE_REGIONS) validate(result, "Region::unflatten"); #endif if (!result.validate(result, "Region::unflatten", true)) { ALOGE("Region::unflatten() failed, invalid region"); return BAD_VALUE; } mStorage = result.mStorage; return NO_ERROR; } // ---------------------------------------------------------------------------- Region::const_iterator Region::begin() const { return mStorage.array(); } Region::const_iterator Region::end() const { // Workaround for b/77643177 // mStorage should never be empty, but somehow it is and it's causing // an abort in ubsan if (mStorage.isEmpty()) return mStorage.array(); size_t numRects = isRect() ? 1 : mStorage.size() - 1; return mStorage.array() + numRects; } Rect const* Region::getArray(size_t* count) const { if (count) *count = static_cast(end() - begin()); return begin(); } // ---------------------------------------------------------------------------- void Region::dump(std::string& out, const char* what, uint32_t /* flags */) const { const_iterator head = begin(); const_iterator const tail = end(); StringAppendF(&out, " Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail - head); while (head != tail) { StringAppendF(&out, " [%3d, %3d, %3d, %3d]\n", head->left, head->top, head->right, head->bottom); ++head; } } void Region::dump(const char* what, uint32_t /* flags */) const { const_iterator head = begin(); const_iterator const tail = end(); ALOGD(" Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail-head); while (head != tail) { ALOGD(" [%3d, %3d, %3d, %3d]\n", head->left, head->top, head->right, head->bottom); head++; } } // ---------------------------------------------------------------------------- }; // namespace android ������������libs/ui/Size.cpp������������������������������������������������������������������������������������0100644 0000000 0000000 00000001364 13756501735 012611� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #include namespace android::ui { const Size Size::INVALID{-1, -1}; const Size Size::EMPTY{0, 0}; } // namespace android::ui ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Transform.cpp�������������������������������������������������������������������������������0100644 0000000 0000000 00000027625 13756501735 013662� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 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. */ #include #include #include #include #include #include namespace android { namespace ui { Transform::Transform() { reset(); } Transform::Transform(const Transform& other) : mMatrix(other.mMatrix), mType(other.mType) { } Transform::Transform(uint32_t orientation) { set(orientation, 0, 0); } Transform::~Transform() = default; static const float EPSILON = 0.0f; bool Transform::isZero(float f) { return fabs(f) <= EPSILON; } bool Transform::absIsOne(float f) { return isZero(fabs(f) - 1.0f); } Transform Transform::operator * (const Transform& rhs) const { if (CC_LIKELY(mType == IDENTITY)) return rhs; Transform r(*this); if (rhs.mType == IDENTITY) return r; // TODO: we could use mType to optimize the matrix multiply const mat33& A(mMatrix); const mat33& B(rhs.mMatrix); mat33& D(r.mMatrix); for (size_t i = 0; i < 3; i++) { const float v0 = A[0][i]; const float v1 = A[1][i]; const float v2 = A[2][i]; D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2]; D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2]; D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2]; } r.mType |= rhs.mType; // TODO: we could recompute this value from r and rhs r.mType &= 0xFF; r.mType |= UNKNOWN_TYPE; return r; } Transform& Transform::operator=(const Transform& other) { mMatrix = other.mMatrix; mType = other.mType; return *this; } const vec3& Transform::operator [] (size_t i) const { return mMatrix[i]; } float Transform::tx() const { return mMatrix[2][0]; } float Transform::ty() const { return mMatrix[2][1]; } float Transform::sx() const { return mMatrix[0][0]; } float Transform::sy() const { return mMatrix[1][1]; } void Transform::reset() { mType = IDENTITY; for(size_t i = 0; i < 3; i++) { vec3& v(mMatrix[i]); for (size_t j = 0; j < 3; j++) v[j] = ((i == j) ? 1.0f : 0.0f); } } void Transform::set(float tx, float ty) { mMatrix[2][0] = tx; mMatrix[2][1] = ty; mMatrix[2][2] = 1.0f; if (isZero(tx) && isZero(ty)) { mType &= ~TRANSLATE; } else { mType |= TRANSLATE; } } void Transform::set(float a, float b, float c, float d) { mat33& M(mMatrix); M[0][0] = a; M[1][0] = b; M[0][1] = c; M[1][1] = d; M[0][2] = 0; M[1][2] = 0; mType = UNKNOWN_TYPE; } status_t Transform::set(uint32_t flags, float w, float h) { if (flags & ROT_INVALID) { // that's not allowed! reset(); return BAD_VALUE; } Transform H, V, R; if (flags & ROT_90) { // w & h are inverted when rotating by 90 degrees std::swap(w, h); } if (flags & FLIP_H) { H.mType = (FLIP_H << 8) | SCALE; H.mType |= isZero(w) ? IDENTITY : TRANSLATE; mat33& M(H.mMatrix); M[0][0] = -1; M[2][0] = w; } if (flags & FLIP_V) { V.mType = (FLIP_V << 8) | SCALE; V.mType |= isZero(h) ? IDENTITY : TRANSLATE; mat33& M(V.mMatrix); M[1][1] = -1; M[2][1] = h; } if (flags & ROT_90) { const float original_w = h; R.mType = (ROT_90 << 8) | ROTATE; R.mType |= isZero(original_w) ? IDENTITY : TRANSLATE; mat33& M(R.mMatrix); M[0][0] = 0; M[1][0] =-1; M[2][0] = original_w; M[0][1] = 1; M[1][1] = 0; } *this = (R*(H*V)); return NO_ERROR; } vec2 Transform::transform(const vec2& v) const { vec2 r; const mat33& M(mMatrix); r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]; return r; } vec3 Transform::transform(const vec3& v) const { vec3 r; const mat33& M(mMatrix); r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2]; r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2]; return r; } vec2 Transform::transform(int x, int y) const { return transform(vec2(x,y)); } Rect Transform::makeBounds(int w, int h) const { return transform( Rect(w, h) ); } Rect Transform::transform(const Rect& bounds, bool roundOutwards) const { Rect r; vec2 lt( bounds.left, bounds.top ); vec2 rt( bounds.right, bounds.top ); vec2 lb( bounds.left, bounds.bottom ); vec2 rb( bounds.right, bounds.bottom ); lt = transform(lt); rt = transform(rt); lb = transform(lb); rb = transform(rb); if (roundOutwards) { r.left = static_cast(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}))); r.top = static_cast(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}))); r.right = static_cast(ceilf(std::max({lt[0], rt[0], lb[0], rb[0]}))); r.bottom = static_cast(ceilf(std::max({lt[1], rt[1], lb[1], rb[1]}))); } else { r.left = static_cast(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); r.top = static_cast(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); r.right = static_cast(floorf(std::max({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); r.bottom = static_cast(floorf(std::max({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); } return r; } FloatRect Transform::transform(const FloatRect& bounds) const { vec2 lt(bounds.left, bounds.top); vec2 rt(bounds.right, bounds.top); vec2 lb(bounds.left, bounds.bottom); vec2 rb(bounds.right, bounds.bottom); lt = transform(lt); rt = transform(rt); lb = transform(lb); rb = transform(rb); FloatRect r; r.left = std::min({lt[0], rt[0], lb[0], rb[0]}); r.top = std::min({lt[1], rt[1], lb[1], rb[1]}); r.right = std::max({lt[0], rt[0], lb[0], rb[0]}); r.bottom = std::max({lt[1], rt[1], lb[1], rb[1]}); return r; } Region Transform::transform(const Region& reg) const { Region out; if (CC_UNLIKELY(type() > TRANSLATE)) { if (CC_LIKELY(preserveRects())) { Region::const_iterator it = reg.begin(); Region::const_iterator const end = reg.end(); while (it != end) { out.orSelf(transform(*it++)); } } else { out.set(transform(reg.bounds())); } } else { int xpos = static_cast(floorf(tx() + 0.5f)); int ypos = static_cast(floorf(ty() + 0.5f)); out = reg.translate(xpos, ypos); } return out; } uint32_t Transform::type() const { if (mType & UNKNOWN_TYPE) { // recompute what this transform is const mat33& M(mMatrix); const float a = M[0][0]; const float b = M[1][0]; const float c = M[0][1]; const float d = M[1][1]; const float x = M[2][0]; const float y = M[2][1]; bool scale = false; uint32_t flags = ROT_0; if (isZero(b) && isZero(c)) { if (a<0) flags |= FLIP_H; if (d<0) flags |= FLIP_V; if (!absIsOne(a) || !absIsOne(d)) { scale = true; } } else if (isZero(a) && isZero(d)) { flags |= ROT_90; if (b>0) flags |= FLIP_V; if (c<0) flags |= FLIP_H; if (!absIsOne(b) || !absIsOne(c)) { scale = true; } } else { // there is a skew component and/or a non 90 degrees rotation flags = ROT_INVALID; } mType = flags << 8; if (flags & ROT_INVALID) { mType |= UNKNOWN; } else { if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180)) mType |= ROTATE; if (flags & FLIP_H) mType ^= SCALE; if (flags & FLIP_V) mType ^= SCALE; if (scale) mType |= SCALE; } if (!isZero(x) || !isZero(y)) mType |= TRANSLATE; } return mType; } Transform Transform::inverse() const { // our 3x3 matrix is always of the form of a 2x2 transformation // followed by a translation: T*M, therefore: // (T*M)^-1 = M^-1 * T^-1 Transform result; if (mType <= TRANSLATE) { // 1 0 0 // 0 1 0 // x y 1 result = *this; result.mMatrix[2][0] = -result.mMatrix[2][0]; result.mMatrix[2][1] = -result.mMatrix[2][1]; } else { // a c 0 // b d 0 // x y 1 const mat33& M(mMatrix); const float a = M[0][0]; const float b = M[1][0]; const float c = M[0][1]; const float d = M[1][1]; const float x = M[2][0]; const float y = M[2][1]; const float idet = 1.0f / (a*d - b*c); result.mMatrix[0][0] = d*idet; result.mMatrix[0][1] = -c*idet; result.mMatrix[1][0] = -b*idet; result.mMatrix[1][1] = a*idet; result.mType = mType; vec2 T(-x, -y); T = result.transform(T); result.mMatrix[2][0] = T[0]; result.mMatrix[2][1] = T[1]; } return result; } uint32_t Transform::getType() const { return type() & 0xFF; } uint32_t Transform::getOrientation() const { return (type() >> 8) & 0xFF; } bool Transform::preserveRects() const { return (getOrientation() & ROT_INVALID) ? false : true; } mat4 Transform::asMatrix4() const { // Internally Transform uses a 3x3 matrix since the transform is meant for // two-dimensional values. An equivalent 4x4 matrix means inserting an extra // row and column which adds as an identity transform on the third // dimension. mat4 m = mat4{mat4::NO_INIT}; // NO_INIT since we explicitly set every element m[0][0] = mMatrix[0][0]; m[0][1] = mMatrix[0][1]; m[0][2] = 0.f; m[0][3] = mMatrix[0][2]; m[1][0] = mMatrix[1][0]; m[1][1] = mMatrix[1][1]; m[1][2] = 0.f; m[1][3] = mMatrix[1][2]; m[2][0] = 0.f; m[2][1] = 0.f; m[2][2] = 1.f; m[2][3] = 0.f; m[3][0] = mMatrix[2][0]; m[3][1] = mMatrix[2][1]; m[3][2] = 0.f; m[3][3] = mMatrix[2][2]; return m; } void Transform::dump(std::string& out, const char* name) const { using android::base::StringAppendF; type(); // Ensure the information in mType is up to date const uint32_t type = mType; const uint32_t orient = type >> 8; StringAppendF(&out, "%s 0x%08x (", name, orient); if (orient & ROT_INVALID) { out.append("ROT_INVALID "); } else { if (orient & ROT_90) { out.append("ROT_90 "); } else { out.append("ROT_0 "); } if (orient & FLIP_V) out.append("FLIP_V "); if (orient & FLIP_H) out.append("FLIP_H "); } StringAppendF(&out, ") 0x%02x (", type); if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY "); if (type & SCALE) out.append("SCALE "); if (type & ROTATE) out.append("ROTATE "); if (type & TRANSLATE) out.append("TRANSLATE "); out.append(")\n"); for (size_t i = 0; i < 3; i++) { StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast(mMatrix[0][i]), static_cast(mMatrix[1][i]), static_cast(mMatrix[2][i])); } } void Transform::dump(const char* name) const { std::string out; dump(out, name); ALOGD("%s", out.c_str()); } } // namespace ui } // namespace android �����������������������������������������������������������������������������������������������������������libs/ui/UiConfig.cpp��������������������������������������������������������������������������������0100644 0000000 0000000 00000001502 13756501735 013374� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2012 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. */ #include namespace android { void appendUiConfigString(std::string& configStr) { static const char* config = " [libui]"; configStr.append(config); } }; // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012615� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/���������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013232� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/ANativeObjectBase.h��������������������������������������������������������������0100644 0000000 0000000 00000005013 13756501735 016650� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_ANDROID_NATIVES_H #define ANDROID_ANDROID_NATIVES_H #include #include /*****************************************************************************/ #ifdef __cplusplus #include namespace android { /* * This helper class turns a ANativeXXX object type into a C++ * reference-counted object; with proper type conversions. */ template class ANativeObjectBase : public NATIVE_TYPE, public REF { public: // Disambiguate between the incStrong in REF and NATIVE_TYPE void incStrong(const void* id) const { REF::incStrong(id); } void decStrong(const void* id) const { REF::decStrong(id); } protected: typedef ANativeObjectBase BASE; ANativeObjectBase() : NATIVE_TYPE(), REF() { NATIVE_TYPE::common.incRef = incRef; NATIVE_TYPE::common.decRef = decRef; } static inline TYPE* getSelf(NATIVE_TYPE* self) { return static_cast(self); } static inline TYPE const* getSelf(NATIVE_TYPE const* self) { return static_cast(self); } static inline TYPE* getSelf(NATIVE_BASE* base) { return getSelf(reinterpret_cast(base)); } static inline TYPE const * getSelf(NATIVE_BASE const* base) { return getSelf(reinterpret_cast(base)); } static void incRef(NATIVE_BASE* base) { ANativeObjectBase* self = getSelf(base); self->incStrong(self); } static void decRef(NATIVE_BASE* base) { ANativeObjectBase* self = getSelf(base); self->decStrong(self); } }; } // namespace android #endif // __cplusplus /*****************************************************************************/ #endif /* ANDROID_ANDROID_NATIVES_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/BufferHubBuffer.h����������������������������������������������������������������0100644 0000000 0000000 00000013361 13756501735 016406� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BUFFER_HUB_BUFFER_H_ #define ANDROID_BUFFER_HUB_BUFFER_H_ #include #include #include #include #include #include #include namespace android { class BufferHubBuffer { public: // Allocates a standalone BufferHubBuffer. static std::unique_ptr create(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, uint64_t usage, size_t userMetadataSize); // Imports the given token to a BufferHubBuffer. Not taking ownership of the token. static std::unique_ptr import(const sp& token); BufferHubBuffer(const BufferHubBuffer&) = delete; void operator=(const BufferHubBuffer&) = delete; virtual ~BufferHubBuffer(); // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in // BufferHub share the same buffer id. int id() const { return mId; } // Returns the buffer description, which is guaranteed to be faithful values from BufferHub. const AHardwareBuffer_Desc& desc() const { return mBufferDesc; } // Duplicate the underlying Gralloc buffer handle. Caller is responsible to free the handle // after use. native_handle_t* duplicateHandle() { return native_handle_clone(mBufferHandle.getNativeHandle()); } const BufferHubEventFd& eventFd() const { return mEventFd; } // Returns the current value of MetadataHeader::bufferState. uint32_t bufferState() const { return mBufferState->load(std::memory_order_acquire); } // A state mask which is unique to a buffer hub client among all its siblings sharing the same // concrete graphic buffer. uint32_t clientStateMask() const { return mClientStateMask; } size_t userMetadataSize() const { return mMetadata.userMetadataSize(); } // Returns true if the BufferClient is still alive. bool isConnected() const { return mBufferClient->ping().isOk(); } // Returns true if the buffer is valid: non-null buffer handle, valid id, valid client bit mask, // valid metadata and valid buffer client bool isValid() const; // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is // gained. // The buffer can be gained as long as there is no other client in acquired or gained state. int gain(); // Posts the gained buffer for other buffer clients to use the buffer. // The buffer can be posted iff the buffer state for this client is gained. // After posting the buffer, this client is put to released state and does not have access to // the buffer for this cycle of the usage of the buffer. int post(); // Acquires the buffer for shared read permission. // The buffer can be acquired iff the buffer state for this client is posted. int acquire(); // Releases the buffer. // The buffer can be released from any buffer state. // After releasing the buffer, this client no longer have any permissions to the buffer for the // current cycle of the usage of the buffer. int release(); // Returns whether the buffer is released by all active clients or not. bool isReleased() const; // Creates a token that stands for this BufferHubBuffer client and could be used for Import to // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying // gralloc buffer and ashmem region for metadata. Not taking ownership of the token. // Returns a valid token on success, nullptr on failure. sp duplicate(); private: BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, uint64_t usage, size_t userMetadataSize); BufferHubBuffer(const sp& token); int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits); // Global id for the buffer that is consistent across processes. int mId = 0; // Client state mask of this BufferHubBuffer object. It is unique amoung all // clients/users of the buffer. uint32_t mClientStateMask = 0U; // Stores ground truth of the buffer. AHardwareBuffer_Desc mBufferDesc; // Wraps the gralloc buffer handle of this buffer. hardware::hidl_handle mBufferHandle; // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer. BufferHubEventFd mEventFd; // An ashmem-based metadata object. The same shared memory are mapped to the // bufferhubd daemon and all buffer clients. BufferHubMetadata mMetadata; // Shortcuts to the atomics inside the header of mMetadata. std::atomic* mBufferState = nullptr; std::atomic* mFenceState = nullptr; std::atomic* mActiveClientsBitMask = nullptr; // HwBinder backend sp mBufferClient; }; } // namespace android #endif // ANDROID_BUFFER_HUB_BUFFER_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/BufferHubDefs.h������������������������������������������������������������������0100644 0000000 0000000 00000015420 13756501735 016054� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BUFFER_HUB_DEFS_H_ #define ANDROID_BUFFER_HUB_DEFS_H_ #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpacked" // TODO(b/118893702): remove dependency once DvrNativeBufferMetadata moved out of libdvr #include #pragma clang diagnostic pop namespace android { namespace BufferHubDefs { // Single buffer clients (up to 16) ownership signal. // 32-bit atomic unsigned int. // Each client takes 2 bits. The first bit locates in the first 16 bits of // bufferState; the second bit locates in the last 16 bits of bufferState. // Client states: // Gained state 11. Exclusive write state. // Posted state 10. // Acquired state 01. Shared read state. // Released state 00. // // MSB LSB // | | // v v // [C15|...|C1|C0|C15| ... |C1|C0] // Maximum number of clients a buffer can have. static constexpr int kMaxNumberOfClients = 16; // Definition of bit masks. // MSB LSB // | kHighBitsMask | kLowbitsMask | // v v v // [b31| ... |b16|b15| ... |b0] // The location of lower 16 bits in the 32-bit buffer state. static constexpr uint32_t kLowbitsMask = (1U << kMaxNumberOfClients) - 1U; // The location of higher 16 bits in the 32-bit buffer state. static constexpr uint32_t kHighBitsMask = ~kLowbitsMask; // The client bit mask of the first client. static constexpr uint32_t kFirstClientBitMask = (1U << kMaxNumberOfClients) + 1U; // Returns true if any of the client is in gained state. static inline bool isAnyClientGained(uint32_t state) { uint32_t highBits = state >> kMaxNumberOfClients; uint32_t lowBits = state & kLowbitsMask; return highBits == lowBits && lowBits != 0U; } // Returns true if the input client is in gained state. static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) { return state == client_bit_mask; } // Returns true if any of the client is in posted state. static inline bool isAnyClientPosted(uint32_t state) { uint32_t highBits = state >> kMaxNumberOfClients; uint32_t lowBits = state & kLowbitsMask; uint32_t postedOrAcquired = highBits ^ lowBits; return postedOrAcquired & highBits; } // Returns true if the input client is in posted state. static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) { uint32_t clientBits = state & client_bit_mask; if (clientBits == 0U) return false; uint32_t lowBits = clientBits & kLowbitsMask; return lowBits == 0U; } // Return true if any of the client is in acquired state. static inline bool isAnyClientAcquired(uint32_t state) { uint32_t highBits = state >> kMaxNumberOfClients; uint32_t lowBits = state & kLowbitsMask; uint32_t postedOrAcquired = highBits ^ lowBits; return postedOrAcquired & lowBits; } // Return true if the input client is in acquired state. static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) { uint32_t clientBits = state & client_bit_mask; if (clientBits == 0U) return false; uint32_t highBits = clientBits & kHighBitsMask; return highBits == 0U; } // Returns true if the input client is in released state. static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) { return (state & client_bit_mask) == 0U; } // Returns the next available buffer client's client_state_masks. // @params union_bits. Union of all existing clients' client_state_masks. static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) { uint32_t lowUnion = union_bits & kLowbitsMask; if (lowUnion == kLowbitsMask) return 0U; uint32_t incremented = lowUnion + 1U; uint32_t difference = incremented ^ lowUnion; uint32_t newLowBit = (difference + 1U) >> 1; return newLowBit + (newLowBit << kMaxNumberOfClients); } struct __attribute__((aligned(8))) MetadataHeader { // Internal data format, which can be updated as long as the size, padding and field alignment // of the struct is consistent within the same ABI. As this part is subject for future updates, // it's not stable cross Android version, so don't have it visible from outside of the Android // platform (include Apps and vendor HAL). // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in // bufferState. std::atomic bufferState; // Every client takes up one bit in fenceState. Only the lower 32 bits are valid. The upper 32 // bits are there for easier manipulation, but the value should be ignored. std::atomic fenceState; // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in // activeClientsBitMask. std::atomic activeClientsBitMask; // Explicit padding 4 bytes. uint32_t padding; // The index of the buffer queue where the buffer belongs to. uint64_t queueIndex; // Public data format, which should be updated with caution. See more details in dvr_api.h DvrNativeBufferMetadata metadata; }; static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size"); static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); /** * android.frameworks.bufferhub@1.0::BufferTraits.bufferInfo is an opaque handle. See * https://cs.corp.google.com/android/frameworks/hardware/interfaces/bufferhub/1.0/types.hal for * more details about android.frameworks.bufferhub@1.0::BufferTraits. * * This definition could be changed, but implementation of BufferHubService::buildBufferInfo * (frameworks/native/services/bufferhub), VtsHalBufferHubV1_0TargetTest * (frameworks/hardware/interfaces/bufferhub) and BufferHubBuffer::readBufferTraits (libui) will * also need to be updated. * * It's definition should follow the following format: * { * NumFds = 2, * NumInts = 3, * data[0] = Ashmem fd for BufferHubMetadata, * data[1] = event fd, * data[2] = buffer id, * data[3] = client state bit mask, * data[4] = user metadata size, * } */ static constexpr int kBufferInfoNumFds = 2; static constexpr int kBufferInfoNumInts = 3; } // namespace BufferHubDefs } // namespace android #endif // ANDROID_BUFFER_HUB_DEFS_H_ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/BufferHubEventFd.h���������������������������������������������������������������0100644 0000000 0000000 00000003153 13756501735 016526� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BUFFER_HUB_EVENT_FD_H_ #define ANDROID_BUFFER_HUB_EVENT_FD_H_ #include #include namespace android { class BufferHubEventFd { public: /** * Constructs a valid event fd. */ BufferHubEventFd(); /** * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes * ownership. */ BufferHubEventFd(int fd); /** * Returns whether this BufferHubEventFd holds a valid event_fd. */ bool isValid() const { return get() >= 0; } /** * Returns the fd number of the BufferHubEventFd object. Note that there is no ownership * transfer. */ int get() const { return mFd.get(); } /** * Signals the eventfd. */ status_t signal() const; /** * Clears the signal from this eventfd if it is signaled. */ status_t clear() const; private: base::unique_fd mFd; }; } // namespace android #endif // ANDROID_BUFFER_HUB_EVENT_FD_H_ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/BufferHubMetadata.h��������������������������������������������������������������0100644 0000000 0000000 00000006075 13756501735 016721� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_BUFFER_HUB_METADATA_H_ #define ANDROID_BUFFER_HUB_METADATA_H_ #include #include namespace android { namespace { using base::unique_fd; } // namespace class BufferHubMetadata { public: // Creates a new BufferHubMetadata backed by an ashmem region. // // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata // shared memory region to be allocated is the size of canonical // BufferHubDefs::MetadataHeader plus userMetadataSize. static BufferHubMetadata create(size_t userMetadataSize); // Imports an existing BufferHubMetadata from an ashmem FD. // // @param ashmemFd Ashmem file descriptor representing an ashmem region. static BufferHubMetadata import(unique_fd ashmemFd); BufferHubMetadata() = default; BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); } ~BufferHubMetadata(); BufferHubMetadata& operator=(BufferHubMetadata&& other) { if (this != &other) { mUserMetadataSize = other.mUserMetadataSize; other.mUserMetadataSize = 0; mAshmemFd = std::move(other.mAshmemFd); // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will // automatically mummap() the shared memory. mMetadataHeader = other.mMetadataHeader; other.mMetadataHeader = nullptr; } return *this; } // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem // has been mapped into virtual address space. bool isValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; } size_t userMetadataSize() const { return mUserMetadataSize; } size_t metadataSize() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; } const unique_fd& ashmemFd() const { return mAshmemFd; } BufferHubDefs::MetadataHeader* metadataHeader() { return mMetadataHeader; } private: BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd, BufferHubDefs::MetadataHeader* metadataHeader); BufferHubMetadata(const BufferHubMetadata&) = delete; void operator=(const BufferHubMetadata&) = delete; size_t mUserMetadataSize = 0; unique_fd mAshmemFd; BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr; }; } // namespace android #endif // ANDROID_BUFFER_HUB_METADATA_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/BufferQueueDefs.h����������������������������������������������������������������0100644 0000000 0000000 00000002646 13756501735 016430� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_BUFFERQUEUEDEFS_H #define ANDROID_UI_BUFFERQUEUEDEFS_H namespace android { namespace BufferQueueDefs { // BufferQueue will keep track of at most this value of buffers. // Attempts at runtime to increase the number of buffers past this // will fail. static constexpr int NUM_BUFFER_SLOTS = 64; enum { // A flag returned by dequeueBuffer when the client needs to call // requestBuffer immediately thereafter. BUFFER_NEEDS_REALLOCATION = 0x1, // A flag returned by dequeueBuffer when all mirrored slots should be // released by the client. This flag should always be processed first. RELEASE_ALL_BUFFERS = 0x2, }; } // namespace BufferQueueDefs } // namespace android #endif ������������������������������������������������������������������������������������������libs/ui/include/ui/ColorSpace.h���������������������������������������������������������������������0100644 0000000 0000000 00000023013 13756501735 015431� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #ifndef ANDROID_UI_COLOR_SPACE #define ANDROID_UI_COLOR_SPACE #include #include #include #include #include #include #include #include #include namespace android { class ColorSpace { public: typedef std::function transfer_function; typedef std::function clamping_function; struct TransferParameters { float g = 0.0f; float a = 0.0f; float b = 0.0f; float c = 0.0f; float d = 0.0f; float e = 0.0f; float f = 0.0f; }; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The default transfer functions are a linear response x->x * and the default clamping function is a simple saturate * (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, transfer_function OETF = linearResponse, transfer_function EOTF = linearResponse, clamping_function clamper = saturate ) noexcept; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The transfer functions are defined by the set of supplied * transfer parameters. The default clamping function is a * simple saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, const TransferParameters parameters, clamping_function clamper = saturate ) noexcept; /** * Creates a named color space with the specified RGB->XYZ * conversion matrix. The white point and primaries will be * computed from the supplied matrix. * * The transfer functions are defined by a simple gamma value. * The default clamping function is a saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const mat3& rgbToXYZ, float gamma, clamping_function clamper = saturate ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The default transfer functions are a linear response x->x * and the default clamping function is a simple saturate * (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array& primaries, const float2& whitePoint, transfer_function OETF = linearResponse, transfer_function EOTF = linearResponse, clamping_function clamper = saturate ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The transfer functions are defined by the set of supplied * transfer parameters. The default clamping function is a * simple saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array& primaries, const float2& whitePoint, const TransferParameters parameters, clamping_function clamper = saturate ) noexcept; /** * Creates a named color space with the specified primaries * and white point. The RGB<>XYZ conversion matrices are * computed from the primaries and white point. * * The transfer functions are defined by a single gamma value. * The default clamping function is a saturate (clamp(x, 0, 1)). */ ColorSpace( const std::string& name, const std::array& primaries, const float2& whitePoint, float gamma, clamping_function clamper = saturate ) noexcept; ColorSpace() noexcept = delete; /** * Encodes the supplied RGB value using this color space's * opto-electronic transfer function. */ constexpr float3 fromLinear(const float3& v) const noexcept { return apply(v, mOETF); } /** * Decodes the supplied RGB value using this color space's * electro-optical transfer function. */ constexpr float3 toLinear(const float3& v) const noexcept { return apply(v, mEOTF); } /** * Converts the supplied XYZ value to RGB. The returned value * is encoded with this color space's opto-electronic transfer * function and clamped by this color space's clamping function. */ constexpr float3 xyzToRGB(const float3& xyz) const noexcept { return apply(fromLinear(mXYZtoRGB * xyz), mClamper); } /** * Converts the supplied RGB value to XYZ. The input RGB value * is decoded using this color space's electro-optical function * before being converted to XYZ. */ constexpr float3 rgbToXYZ(const float3& rgb) const noexcept { return mRGBtoXYZ * toLinear(rgb); } constexpr const std::string& getName() const noexcept { return mName; } constexpr const mat3& getRGBtoXYZ() const noexcept { return mRGBtoXYZ; } constexpr const mat3& getXYZtoRGB() const noexcept { return mXYZtoRGB; } constexpr const transfer_function& getOETF() const noexcept { return mOETF; } constexpr const transfer_function& getEOTF() const noexcept { return mEOTF; } constexpr const clamping_function& getClamper() const noexcept { return mClamper; } constexpr const std::array& getPrimaries() const noexcept { return mPrimaries; } constexpr const float2& getWhitePoint() const noexcept { return mWhitePoint; } constexpr const TransferParameters& getTransferParameters() const noexcept { return mParameters; } /** * Converts the supplied XYZ value to xyY. */ static constexpr float2 xyY(const float3& XYZ) { return XYZ.xy / dot(XYZ, float3{1}); } /** * Converts the supplied xyY value to XYZ. */ static constexpr float3 XYZ(const float3& xyY) { return float3{(xyY.x * xyY.z) / xyY.y, xyY.z, ((1 - xyY.x - xyY.y) * xyY.z) / xyY.y}; } static const ColorSpace sRGB(); static const ColorSpace linearSRGB(); static const ColorSpace extendedSRGB(); static const ColorSpace linearExtendedSRGB(); static const ColorSpace NTSC(); static const ColorSpace BT709(); static const ColorSpace BT2020(); static const ColorSpace AdobeRGB(); static const ColorSpace ProPhotoRGB(); static const ColorSpace DisplayP3(); static const ColorSpace DCIP3(); static const ColorSpace ACES(); static const ColorSpace ACEScg(); // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256) // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B // The generated 3D LUT is meant to be used as a 3D texture and its Y // axis is thus already flipped // The source color space must define its values in the domain [0..1] // The generated LUT transforms from gamma space to gamma space static std::unique_ptr createLUT(uint32_t size, const ColorSpace& src, const ColorSpace& dst); private: static constexpr mat3 computeXYZMatrix( const std::array& primaries, const float2& whitePoint); static constexpr float linearResponse(float v) { return v; } std::string mName; mat3 mRGBtoXYZ; mat3 mXYZtoRGB; TransferParameters mParameters; transfer_function mOETF; transfer_function mEOTF; clamping_function mClamper; std::array mPrimaries; float2 mWhitePoint; }; class ColorSpaceConnector { public: ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept; constexpr const ColorSpace& getSource() const noexcept { return mSource; } constexpr const ColorSpace& getDestination() const noexcept { return mDestination; } constexpr const mat3& getTransform() const noexcept { return mTransform; } constexpr float3 transform(const float3& v) const noexcept { float3 linear = mSource.toLinear(apply(v, mSource.getClamper())); return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper()); } constexpr float3 transformLinear(const float3& v) const noexcept { float3 linear = apply(v, mSource.getClamper()); return apply(mTransform * linear, mDestination.getClamper()); } private: ColorSpace mSource; ColorSpace mDestination; mat3 mTransform; }; }; // namespace android #endif // ANDROID_UI_COLOR_SPACE ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/ConfigStoreTypes.h���������������������������������������������������������������0100644 0000000 0000000 00000001711 13756501735 016647� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #pragma once // android::ui::* in this header file will alias different types as // the HIDL interface is updated. namespace android { namespace ui { struct CieXyz { float X; float Y; float Z; }; struct DisplayPrimaries { CieXyz red; CieXyz green; CieXyz blue; CieXyz white; }; } // namespace ui } // namespace android �������������������������������������������������������libs/ui/include/ui/DebugUtils.h���������������������������������������������������������������������0100644 0000000 0000000 00000002411 13756501735 015445� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 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. */ #pragma once #include #include #include namespace android { class Rect; } std::string decodeStandard(android_dataspace dataspace); std::string decodeTransfer(android_dataspace dataspace); std::string decodeRange(android_dataspace dataspace); std::string dataspaceDetails(android_dataspace dataspace); std::string decodeColorMode(android::ui::ColorMode colormode); std::string decodeColorTransform(android_color_transform colorTransform); std::string decodePixelFormat(android::PixelFormat format); std::string decodeRenderIntent(android::ui::RenderIntent renderIntent); std::string to_string(const android::Rect& rect); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/DisplayInfo.h��������������������������������������������������������������������0100644 0000000 0000000 00000002545 13756501735 015627� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_DISPLAY_INFO_H #define ANDROID_UI_DISPLAY_INFO_H #include #include #include namespace android { struct DisplayInfo { uint32_t w{0}; uint32_t h{0}; float xdpi{0}; float ydpi{0}; float fps{0}; float density{0}; uint8_t orientation{0}; bool secure{false}; nsecs_t appVsyncOffset{0}; nsecs_t presentationDeadline{0}; uint32_t viewportW{0}; uint32_t viewportH{0}; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ enum { DISPLAY_ORIENTATION_0 = 0, DISPLAY_ORIENTATION_90 = 1, DISPLAY_ORIENTATION_180 = 2, DISPLAY_ORIENTATION_270 = 3 }; }; // namespace android #endif // ANDROID_COMPOSER_DISPLAY_INFO_H �����������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/DisplayStatInfo.h����������������������������������������������������������������0100644 0000000 0000000 00000001603 13756501735 016455� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #ifndef ANDROID_UI_DISPLAY_STAT_INFO_H #define ANDROID_UI_DISPLAY_STAT_INFO_H #include namespace android { struct DisplayStatInfo { nsecs_t vsyncTime{0}; nsecs_t vsyncPeriod{0}; }; }; // namespace android #endif // ANDROID_COMPOSER_DISPLAY_STAT_INFO_H �����������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/DisplayedFrameStats.h������������������������������������������������������������0100644 0000000 0000000 00000003261 13756501735 017312� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include namespace android { struct DisplayedFrameStats { /* The number of frames represented by this sample. */ uint64_t numFrames = 0; /* A histogram counting how many times a pixel of a given value was displayed onscreen for * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels * were displayed onscreen in range 0x40->0x7F, etc. */ std::vector component_0_sample = {}; /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */ std::vector component_1_sample = {}; /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */ std::vector component_2_sample = {}; /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */ std::vector component_3_sample = {}; }; } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Fence.h��������������������������������������������������������������������������0100644 0000000 0000000 00000013557 13756501735 014433� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_FENCE_H #define ANDROID_FENCE_H #include #include #include #include #include namespace android { class String8; // =========================================================================== // Fence // =========================================================================== class Fence : public LightRefBase, public Flattenable { public: static const sp NO_FENCE; static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX; static constexpr nsecs_t SIGNAL_TIME_INVALID = -1; static inline bool isValidTimestamp(nsecs_t time) { return time >= 0 && time < INT64_MAX; } // TIMEOUT_NEVER may be passed to the wait method to indicate that it // should wait indefinitely for the fence to signal. enum { TIMEOUT_NEVER = -1 }; // Construct a new Fence object with an invalid file descriptor. This // should be done when the Fence object will be set up by unflattening // serialized data. Fence() = default; // Construct a new Fence object to manage a given fence file descriptor. // When the new Fence object is destructed the file descriptor will be // closed. explicit Fence(int fenceFd); explicit Fence(base::unique_fd fenceFd); // Not copyable or movable. Fence(const Fence& rhs) = delete; Fence& operator=(const Fence& rhs) = delete; Fence(Fence&& rhs) = delete; Fence& operator=(Fence&& rhs) = delete; // Check whether the Fence has an open fence file descriptor. Most Fence // methods treat an invalid file descriptor just like a valid fence that // is already signalled, so using this is usually not necessary. bool isValid() const { return mFenceFd != -1; } // wait waits for up to timeout milliseconds for the fence to signal. If // the fence signals then NO_ERROR is returned. If the timeout expires // before the fence signals then -ETIME is returned. A timeout of // TIMEOUT_NEVER may be used to indicate that the call should wait // indefinitely for the fence to signal. status_t wait(int timeout); // waitForever is a convenience function for waiting forever for a fence to // signal (just like wait(TIMEOUT_NEVER)), but issuing an error to the // system log and fence state to the kernel log if the wait lasts longer // than a warning timeout. // The logname argument should be a string identifying // the caller and will be included in the log message. status_t waitForever(const char* logname); // merge combines two Fence objects, creating a new Fence object that // becomes signaled when both f1 and f2 are signaled (even if f1 or f2 is // destroyed before it becomes signaled). The name argument specifies the // human-readable name to associated with the new Fence object. static sp merge(const char* name, const sp& f1, const sp& f2); static sp merge(const String8& name, const sp& f1, const sp& f2); // Return a duplicate of the fence file descriptor. The caller is // responsible for closing the returned file descriptor. On error, -1 will // be returned and errno will indicate the problem. int dup() const; // Return the underlying file descriptor without giving up ownership. The // returned file descriptor is only valid for as long as the owning Fence // object lives. (If the situation is unclear, dup() is always a safer // option.) int get() const { return mFenceFd.get(); } // getSignalTime returns the system monotonic clock time at which the // fence transitioned to the signaled state. If the fence is not signaled // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an // error occurs then SIGNAL_TIME_INVALID is returned. nsecs_t getSignalTime() const; enum class Status { Invalid, // Fence is invalid Unsignaled, // Fence is valid but has not yet signaled Signaled, // Fence is valid and has signaled }; // getStatus() returns whether the fence has signaled yet. Prefer this to // getSignalTime() or wait() if all you care about is whether the fence has // signaled. inline Status getStatus() { // The sync_wait call underlying wait() has been measured to be // significantly faster than the sync_fence_info call underlying // getSignalTime(), which might otherwise appear to be the more obvious // way to check whether a fence has signaled. switch (wait(0)) { case NO_ERROR: return Status::Signaled; case -ETIME: return Status::Unsignaled; default: return Status::Invalid; } } // Flattenable interface size_t getFlattenedSize() const; size_t getFdCount() const; status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); private: // Only allow instantiation using ref counting. friend class LightRefBase; ~Fence() = default; base::unique_fd mFenceFd; }; }; // namespace android #endif // ANDROID_FENCE_H �������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/FenceTime.h����������������������������������������������������������������������0100644 0000000 0000000 00000016271 13756501735 015246� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #ifndef ANDROID_FENCE_TIME_H #define ANDROID_FENCE_TIME_H #include #include #include #include #include #include #include #include namespace android { class FenceToFenceTimeMap; // A wrapper around fence that only implements isValid and getSignalTime. // It automatically closes the fence in a thread-safe manner once the signal // time is known. class FenceTime { friend class FenceToFenceTimeMap; public: // An atomic snapshot of the FenceTime that is flattenable. // // This class is needed because the FenceTime class may not stay // consistent for all steps of the flattening process. // // Not thread safe. struct Snapshot : public Flattenable { enum class State { EMPTY, FENCE, SIGNAL_TIME, }; Snapshot() = default; // Creates an empty snapshot. explicit Snapshot(const sp& fence); explicit Snapshot(nsecs_t signalTime); // Movable. Snapshot(Snapshot&& src) = default; Snapshot& operator=(Snapshot&& src) = default; // Not copyable. Snapshot(const Snapshot& src) = delete; Snapshot& operator=(const Snapshot&& src) = delete; // Flattenable implementation. size_t getFlattenedSize() const; size_t getFdCount() const; status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); State state{State::EMPTY}; sp fence{Fence::NO_FENCE}; nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID}; }; static const std::shared_ptr NO_FENCE; explicit FenceTime(const sp& fence); explicit FenceTime(sp&& fence); // Passing in Fence::SIGNAL_TIME_PENDING is not allowed. // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID. explicit FenceTime(nsecs_t signalTime); // Do not allow default construction. Share NO_FENCE or explicitly construct // with Fence::SIGNAL_TIME_INVALID instead. FenceTime() = delete; // Do not allow copy, assign, or move. Use a shared_ptr to share the // signalTime result. Or use getSnapshot() if a thread-safe copy is really // needed. FenceTime(const FenceTime&) = delete; FenceTime(FenceTime&&) = delete; FenceTime& operator=(const FenceTime&) = delete; FenceTime& operator=(FenceTime&&) = delete; // This method should only be called when replacing the fence with // a signalTime. Since this is an indirect way of setting the signal time // of a fence, the snapshot should come from a trusted source. void applyTrustedSnapshot(const Snapshot& src); bool isValid() const; // Attempts to get the timestamp from the Fence if the timestamp isn't // already cached. Otherwise, it returns the cached value. nsecs_t getSignalTime(); // Gets the cached timestamp without attempting to query the Fence. nsecs_t getCachedSignalTime() const; // Returns a snapshot of the FenceTime in its current state. Snapshot getSnapshot() const; void signalForTest(nsecs_t signalTime); private: // For tests only. If forceValidForTest is true, then getSignalTime will // never return SIGNAL_TIME_INVALID and isValid will always return true. FenceTime(const sp& fence, bool forceValidForTest); enum class State { VALID, INVALID, FORCED_VALID_FOR_TEST, }; const State mState{State::INVALID}; // mMutex guards mFence and mSignalTime. // mSignalTime is also atomic since it is sometimes read outside the lock // for quick checks. mutable std::mutex mMutex; sp mFence{Fence::NO_FENCE}; std::atomic mSignalTime{Fence::SIGNAL_TIME_INVALID}; }; // A queue of FenceTimes that are expected to signal in FIFO order. // Only maintains a queue of weak pointers so it doesn't keep references // to Fences on its own. // // Can be used to get the signal time of a fence and close its file descriptor // without making a syscall for every fence later in the timeline. // Additionally, since the FenceTime caches the timestamp internally, // other timelines that reference the same FenceTime can avoid the syscall. // // FenceTimeline only keeps track of a limited number of entries to avoid // growing unbounded. Users of FenceTime must make sure they can work even // if FenceTimeline did nothing. i.e. they should eventually call // Fence::getSignalTime(), not only Fence::getCachedSignalTime(). // // push() and updateSignalTimes() are safe to call simultaneously from // different threads. class FenceTimeline { public: static constexpr size_t MAX_ENTRIES = 64; void push(const std::shared_ptr& fence); void updateSignalTimes(); private: mutable std::mutex mMutex; std::queue> mQueue GUARDED_BY(mMutex); }; // Used by test code to create or get FenceTimes for a given Fence. // // By design, Fences cannot be signaled from user space. However, this class // allows test code to set the apparent signalTime of a Fence and // have it be visible to all FenceTimes. Release code should not use // FenceToFenceTimeMap. // // FenceToFenceTimeMap keeps a weak reference to the FenceTime and automatically // garbage collects entries every time a new FenceTime is created to avoid // leaks. This prevents us from having to make the Fence destructor // automatically notify that the underlying fence has been destroyed, which // would affect release code paths. Garbage collecting so often is inefficient, // but acceptable for testing. // // Since FenceTimes maintain a strong reference to underlying Fences, there // should not be any aliasing issues where a new Fence happens to have the same // address as a previous Fence; the previous entry will be garbage collected // before the new one is added. class FenceToFenceTimeMap { public: // Create a new FenceTime with that wraps the provided Fence. std::shared_ptr createFenceTimeForTest(const sp& fence); // Signals all FenceTimes created through this class that are wrappers // around |fence|. void signalAllForTest(const sp& fence, nsecs_t signalTime); private: // Cleans up the entries that no longer have a strong reference. void garbageCollectLocked(); mutable std::mutex mMutex; std::unordered_map>> mMap; }; }; // namespace android #endif // ANDROID_FENCE_TIME_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/FloatRect.h����������������������������������������������������������������������0100644 0000000 0000000 00000003440 13756501735 015264� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #pragma once namespace android { class FloatRect { public: FloatRect() = default; constexpr FloatRect(float _left, float _top, float _right, float _bottom) : left(_left), top(_top), right(_right), bottom(_bottom) {} float getWidth() const { return right - left; } float getHeight() const { return bottom - top; } FloatRect intersect(const FloatRect& other) const { FloatRect intersection = { // Inline to avoid tromping on other min/max defines or adding a // dependency on STL (left > other.left) ? left : other.left, (top > other.top) ? top : other.top, (right < other.right) ? right : other.right, (bottom < other.bottom) ? bottom : other.bottom }; if (intersection.getWidth() < 0 || intersection.getHeight() < 0) { return {0, 0, 0, 0}; } return intersection; } float left = 0.0f; float top = 0.0f; float right = 0.0f; float bottom = 0.0f; }; inline bool operator==(const FloatRect& a, const FloatRect& b) { return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom; } } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/FrameStats.h���������������������������������������������������������������������0100644 0000000 0000000 00000004010 13756501735 015444� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #ifndef ANDROID_UI_FRAME_STATS_H #define ANDROID_UI_FRAME_STATS_H #include #include #include namespace android { class FrameStats : public LightFlattenable { public: FrameStats() : refreshPeriodNano(0) {} /* * Approximate refresh time, in nanoseconds. */ nsecs_t refreshPeriodNano; /* * The times in nanoseconds for when the frame contents were posted by the producer (e.g. * the application). They are either explicitly set or defaulted to the time when * Surface::queueBuffer() was called. */ Vector desiredPresentTimesNano; /* * The times in milliseconds for when the frame contents were presented on the screen. */ Vector actualPresentTimesNano; /* * The times in nanoseconds for when the frame contents were ready to be presented. Note that * a frame can be posted and still it contents being rendered asynchronously in GL. In such a * case these are the times when the frame contents were completely rendered (i.e. their fences * signaled). */ Vector frameReadyTimesNano; // LightFlattenable bool isFixedSize() const; size_t getFlattenedSize() const; status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); }; }; // namespace android #endif // ANDROID_UI_FRAME_STATS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Gralloc.h������������������������������������������������������������������������0100644 0000000 0000000 00000010140 13756501735 014757� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #ifndef ANDROID_UI_GRALLOC_H #define ANDROID_UI_GRALLOC_H #include #include #include #include #include namespace android { // A wrapper to IMapper class GrallocMapper { public: virtual ~GrallocMapper(); virtual bool isLoaded() const = 0; virtual status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const = 0; // Import a buffer that is from another HAL, another process, or is // cloned. // // The returned handle must be freed with freeBuffer. virtual status_t importBuffer(const hardware::hidl_handle& rawHandle, buffer_handle_t* outBufferHandle) const = 0; virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0; virtual status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) const = 0; virtual void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, uint32_t* outNumInts) const = 0; // The ownership of acquireFence is always transferred to the callee, even // on errors. virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const = 0; // The ownership of acquireFence is always transferred to the callee, even // on errors. virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const = 0; // unlock returns a fence sync object (or -1) and the fence sync object is // owned by the caller virtual int unlock(buffer_handle_t bufferHandle) const = 0; // isSupported queries whether or not a buffer with the given width, height, // format, layer count, and usage can be allocated on the device. If // *outSupported is set to true, a buffer with the given specifications may be successfully // allocated if resources are available. If false, a buffer with the given specifications will // never successfully allocate on this device. Note that this function is not guaranteed to be // supported on all devices, in which case a status_t of INVALID_OPERATION will be returned. virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0; }; // A wrapper to IAllocator class GrallocAllocator { public: virtual ~GrallocAllocator(); virtual bool isLoaded() const = 0; virtual std::string dumpDebugInfo() const = 0; /* * The returned buffers are already imported and must not be imported * again. outBufferHandles must point to a space that can contain at * least "bufferCount" buffer_handle_t. */ virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0; }; } // namespace android #endif // ANDROID_UI_GRALLOC_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Gralloc2.h�����������������������������������������������������������������������0100644 0000000 0000000 00000007036 13756501735 015053� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #ifndef ANDROID_UI_GRALLOC2_H #define ANDROID_UI_GRALLOC2_H #include #include #include #include #include #include #include #include #include namespace android { class Gralloc2Mapper : public GrallocMapper { public: static void preload(); Gralloc2Mapper(); bool isLoaded() const override; status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; status_t importBuffer(const hardware::hidl_handle& rawHandle, buffer_handle_t* outBufferHandle) const override; void freeBuffer(buffer_handle_t bufferHandle) const override; status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) const override; void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, uint32_t* outNumInts) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const override; int unlock(buffer_handle_t bufferHandle) const override; status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported) const override; private: // Determines whether the passed info is compatible with the mapper. status_t validateBufferDescriptorInfo( hardware::graphics::mapper::V2_1::IMapper::BufferDescriptorInfo* descriptorInfo) const; sp mMapper; sp mMapperV2_1; }; class Gralloc2Allocator : public GrallocAllocator { public: // An allocator relies on a mapper, and that mapper must be alive at all // time. Gralloc2Allocator(const Gralloc2Mapper& mapper); bool isLoaded() const override; std::string dumpDebugInfo() const override; status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles) const override; private: const Gralloc2Mapper& mMapper; sp mAllocator; }; } // namespace android #endif // ANDROID_UI_GRALLOC2_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Gralloc3.h�����������������������������������������������������������������������0100644 0000000 0000000 00000006645 13756501735 015061� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #ifndef ANDROID_UI_GRALLOC3_H #define ANDROID_UI_GRALLOC3_H #include #include #include #include #include #include #include #include namespace android { class Gralloc3Mapper : public GrallocMapper { public: static void preload(); Gralloc3Mapper(); bool isLoaded() const override; status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; status_t importBuffer(const hardware::hidl_handle& rawHandle, buffer_handle_t* outBufferHandle) const override; void freeBuffer(buffer_handle_t bufferHandle) const override; status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) const override; void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, uint32_t* outNumInts) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, void** outData, int32_t* outBytesPerPixel, int32_t* outBytesPerStride) const override; status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, int acquireFence, android_ycbcr* ycbcr) const override; int unlock(buffer_handle_t bufferHandle) const override; status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported) const override; private: // Determines whether the passed info is compatible with the mapper. status_t validateBufferDescriptorInfo( hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo* descriptorInfo) const; sp mMapper; }; class Gralloc3Allocator : public GrallocAllocator { public: // An allocator relies on a mapper, and that mapper must be alive at all // time. Gralloc3Allocator(const Gralloc3Mapper& mapper); bool isLoaded() const override; std::string dumpDebugInfo() const override; status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles) const override; private: const Gralloc3Mapper& mMapper; sp mAllocator; }; } // namespace android #endif // ANDROID_UI_GRALLOC3_H �������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicBuffer.h������������������������������������������������������������������0100644 0000000 0000000 00000033273 13756501735 016117� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_GRAPHIC_BUFFER_H #define ANDROID_GRAPHIC_BUFFER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { #ifndef LIBUI_IN_VNDK class BufferHubBuffer; #endif // LIBUI_IN_VNDK class GraphicBufferMapper; using GraphicBufferDeathCallback = std::function; // =========================================================================== // GraphicBuffer // =========================================================================== class GraphicBuffer : public ANativeObjectBase, public Flattenable { friend class Flattenable; public: enum { USAGE_SW_READ_NEVER = GRALLOC_USAGE_SW_READ_NEVER, USAGE_SW_READ_RARELY = GRALLOC_USAGE_SW_READ_RARELY, USAGE_SW_READ_OFTEN = GRALLOC_USAGE_SW_READ_OFTEN, USAGE_SW_READ_MASK = GRALLOC_USAGE_SW_READ_MASK, USAGE_SW_WRITE_NEVER = GRALLOC_USAGE_SW_WRITE_NEVER, USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY, USAGE_SW_WRITE_OFTEN = GRALLOC_USAGE_SW_WRITE_OFTEN, USAGE_SW_WRITE_MASK = GRALLOC_USAGE_SW_WRITE_MASK, USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK, USAGE_PROTECTED = GRALLOC_USAGE_PROTECTED, USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE, USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, USAGE_HW_2D = GRALLOC_USAGE_HW_2D, USAGE_HW_COMPOSER = GRALLOC_USAGE_HW_COMPOSER, USAGE_HW_VIDEO_ENCODER = GRALLOC_USAGE_HW_VIDEO_ENCODER, USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK, USAGE_CURSOR = GRALLOC_USAGE_CURSOR, }; static sp from(ANativeWindowBuffer *); static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*); static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*); AHardwareBuffer* toAHardwareBuffer(); AHardwareBuffer const* toAHardwareBuffer() const; // Create a GraphicBuffer to be unflatten'ed into or be reallocated. GraphicBuffer(); // Create a GraphicBuffer by allocating and managing a buffer internally. // This function is privileged. See reallocate for details. GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName = ""); // Create a GraphicBuffer from an existing handle. enum HandleWrapMethod : uint8_t { // Wrap and use the handle directly. It assumes the handle has been // registered and never fails. The handle must have a longer lifetime // than this wrapping GraphicBuffer. // // This can be used when, for example, you want to wrap a handle that // is already managed by another GraphicBuffer. WRAP_HANDLE, // Take ownership of the handle and use it directly. It assumes the // handle has been registered and never fails. // // This can be used to manage an already registered handle with // GraphicBuffer. TAKE_HANDLE, // Take onwership of an unregistered handle and use it directly. It // can fail when the buffer does not register. There is no ownership // transfer on failures. // // This can be used to, for example, create a GraphicBuffer from a // handle returned by Parcel::readNativeHandle. TAKE_UNREGISTERED_HANDLE, // Make a clone of the handle and use the cloned handle. It can fail // when cloning fails or when the buffer does not register. There is // never ownership transfer. // // This can be used to create a GraphicBuffer from a handle that // cannot be used directly, such as one from hidl_handle. CLONE_HANDLE, }; GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride); // These functions are deprecated because they only take 32 bits of usage GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride) : GraphicBuffer(inHandle, method, inWidth, inHeight, inFormat, inLayerCount, static_cast(inUsage), inStride) {} GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle, bool keepOwnership); GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage, std::string requestorName = ""); #ifndef LIBUI_IN_VNDK // Create a GraphicBuffer from an existing BufferHubBuffer. GraphicBuffer(std::unique_ptr buffer); #endif // LIBUI_IN_VNDK // return status status_t initCheck() const; uint32_t getWidth() const { return static_cast(width); } uint32_t getHeight() const { return static_cast(height); } uint32_t getStride() const { return static_cast(stride); } uint64_t getUsage() const { return usage; } PixelFormat getPixelFormat() const { return format; } uint32_t getLayerCount() const { return static_cast(layerCount); } Rect getBounds() const { return Rect(width, height); } uint64_t getId() const { return mId; } int32_t getBufferId() const { return mBufferId; } uint32_t getGenerationNumber() const { return mGenerationNumber; } void setGenerationNumber(uint32_t generation) { mGenerationNumber = generation; } // This function is privileged. It requires access to the allocator // device or service, which usually involves adding suitable selinux // rules. status_t reallocate(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage); bool needsReallocation(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage); // For the following two lock functions, if bytesPerStride or bytesPerPixel // are unknown or variable, -1 will be returned status_t lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); // For HAL_PIXEL_FORMAT_YCbCr_420_888 status_t lockYCbCr(uint32_t inUsage, android_ycbcr *ycbcr); status_t lockYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr *ycbcr); status_t unlock(); // For the following three lockAsync functions, if bytesPerStride or bytesPerPixel // are unknown or variable, -1 will be returned status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr, int fenceFd); status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr *ycbcr, int fenceFd); status_t unlockAsync(int *fenceFd); status_t isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, bool* outSupported) const; ANativeWindowBuffer* getNativeBuffer() const; // for debugging static void dumpAllocationsToSystemLog(); // Flattenable protocol size_t getFlattenedSize() const; size_t getFdCount() const; status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); GraphicBufferMapper::Version getBufferMapperVersion() const { return mBufferMapper.getMapperVersion(); } void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context); #ifndef LIBUI_IN_VNDK // Returns whether this GraphicBuffer is backed by BufferHubBuffer. bool isBufferHubBuffer() const; #endif // LIBUI_IN_VNDK private: ~GraphicBuffer(); enum { ownNone = 0, ownHandle = 1, ownData = 2, }; inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } uint8_t mOwner; private: friend class Surface; friend class BpSurface; friend class BnSurface; friend class LightRefBase; GraphicBuffer(const GraphicBuffer& rhs); GraphicBuffer& operator = (const GraphicBuffer& rhs); const GraphicBuffer& operator = (const GraphicBuffer& rhs) const; status_t initWithSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName); status_t initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride); void free_handle(); GraphicBufferMapper& mBufferMapper; ssize_t mInitCheck; // numbers of fds/ints in native_handle_t to flatten uint32_t mTransportNumFds; uint32_t mTransportNumInts; uint64_t mId; // System unique buffer ID. Note that this is different from mId, which is process unique. For // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the // same cross process for the same chunck of underlying memory. Also note that this only applies // to GraphicBuffers that are backed by BufferHub. int32_t mBufferId = -1; // Stores the generation number of this buffer. If this number does not // match the BufferQueue's internal generation number (set through // IGBP::setGenerationNumber), attempts to attach the buffer will fail. uint32_t mGenerationNumber; // Send a callback when a GraphicBuffer dies. // // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes // problems when trying to implicitcly cache across process boundaries. Ideally, both sides // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer // would be destroyed. Unfortunately, when SurfaceFlinger has only a wp<> reference to the // GraphicBuffer, it immediately goes out of scope in the SurfaceFlinger process. SurfaceFlinger // must hold onto a sp<> to the buffer. When the GraphicBuffer goes out of scope in the app's // process, the client side cache will get this callback. It erases the buffer from its cache // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer. std::vector> mDeathCallbacks; #ifndef LIBUI_IN_VNDK // Flatten this GraphicBuffer object if backed by BufferHubBuffer. status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const; // Unflatten into BufferHubBuffer backed GraphicBuffer. // Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a // GraphicBuffer backed by BufferHubBuffer_1 flatten in process/thread A, transport the token // to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the // token. Race condition occurs between the invalidation of the token in bufferhub process and // process/thread B trying to unflatten and import the buffer with that token. status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size); // Stores a BufferHubBuffer that handles buffer signaling, identification. std::unique_ptr mBufferHubBuffer; #endif // LIBUI_IN_VNDK }; }; // namespace android #endif // ANDROID_GRAPHIC_BUFFER_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicBufferAllocator.h���������������������������������������������������������0100644 0000000 0000000 00000004276 13756501735 017761� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ** ** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef ANDROID_BUFFER_ALLOCATOR_H #define ANDROID_BUFFER_ALLOCATOR_H #include #include #include #include #include #include #include #include #include namespace android { class GrallocAllocator; class GraphicBufferMapper; class GraphicBufferAllocator : public Singleton { public: static inline GraphicBufferAllocator& get() { return getInstance(); } status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount, uint64_t usage, buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId, std::string requestorName); status_t free(buffer_handle_t handle); size_t getTotalSize() const; void dump(std::string& res) const; static void dumpToSystemLog(); protected: struct alloc_rec_t { uint32_t width; uint32_t height; uint32_t stride; PixelFormat format; uint32_t layerCount; uint64_t usage; size_t size; std::string requestorName; }; static Mutex sLock; static KeyedVector sAllocList; friend class Singleton; GraphicBufferAllocator(); ~GraphicBufferAllocator(); GraphicBufferMapper& mMapper; std::unique_ptr mAllocator; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_BUFFER_ALLOCATOR_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicBufferMapper.h������������������������������������������������������������0100644 0000000 0000000 00000007055 13756501735 017263� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_BUFFER_MAPPER_H #define ANDROID_UI_BUFFER_MAPPER_H #include #include #include #include #include // Needed by code that still uses the GRALLOC_USAGE_* constants. // when/if we get rid of gralloc, we should provide aliases or fix call sites. #include namespace android { // --------------------------------------------------------------------------- class GrallocMapper; class Rect; class GraphicBufferMapper : public Singleton { public: enum Version { GRALLOC_2, GRALLOC_3, }; static void preloadHal(); static inline GraphicBufferMapper& get() { return getInstance(); } // The imported outHandle must be freed with freeBuffer when no longer // needed. rawHandle is owned by the caller. status_t importBuffer(buffer_handle_t rawHandle, uint32_t width, uint32_t height, uint32_t layerCount, PixelFormat format, uint64_t usage, uint32_t stride, buffer_handle_t* outHandle); status_t freeBuffer(buffer_handle_t handle); void getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts); status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr); status_t unlock(buffer_handle_t handle); status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd); status_t unlockAsync(buffer_handle_t handle, int *fenceFd); status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, uint32_t layerCount, uint64_t usage, bool* outSupported); const GrallocMapper& getGrallocMapper() const { return reinterpret_cast(*mMapper); } Version getMapperVersion() const { return mMapperVersion; } private: friend class Singleton; GraphicBufferMapper(); std::unique_ptr mMapper; Version mMapperVersion; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_UI_BUFFER_MAPPER_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicTypes.h�������������������������������������������������������������������0100644 0000000 0000000 00000002550 13756501735 016004� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #pragma once #include #include #include #include #include #define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64 namespace android { using PhysicalDisplayId = uint64_t; // android::ui::* in this header file will alias different types as // the HIDL interface is updated. namespace ui { using android::hardware::graphics::common::V1_1::RenderIntent; using android::hardware::graphics::common::V1_2::ColorMode; using android::hardware::graphics::common::V1_2::Dataspace; using android::hardware::graphics::common::V1_2::Hdr; using android::hardware::graphics::common::V1_2::PixelFormat; } // namespace ui } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/HdrCapabilities.h����������������������������������������������������������������0100644 0000000 0000000 00000004321 13756501735 016427� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #ifndef ANDROID_UI_HDR_CAPABILTIES_H #define ANDROID_UI_HDR_CAPABILTIES_H #include #include #include #include namespace android { class HdrCapabilities : public LightFlattenable { public: HdrCapabilities(const std::vector& types, float maxLuminance, float maxAverageLuminance, float minLuminance) : mSupportedHdrTypes(types), mMaxLuminance(maxLuminance), mMaxAverageLuminance(maxAverageLuminance), mMinLuminance(minLuminance) {} // Make this move-constructable and move-assignable HdrCapabilities(HdrCapabilities&& other) noexcept; HdrCapabilities& operator=(HdrCapabilities&& other) noexcept; HdrCapabilities() : mSupportedHdrTypes(), mMaxLuminance(-1.0f), mMaxAverageLuminance(-1.0f), mMinLuminance(-1.0f) {} ~HdrCapabilities(); const std::vector& getSupportedHdrTypes() const { return mSupportedHdrTypes; } float getDesiredMaxLuminance() const { return mMaxLuminance; } float getDesiredMaxAverageLuminance() const { return mMaxAverageLuminance; } float getDesiredMinLuminance() const { return mMinLuminance; } // Flattenable protocol bool isFixedSize() const { return false; } size_t getFlattenedSize() const; status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); private: std::vector mSupportedHdrTypes; float mMaxLuminance; float mMaxAverageLuminance; float mMinLuminance; }; } // namespace android #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/PixelFormat.h��������������������������������������������������������������������0100644 0000000 0000000 00000005151 13756501735 015634� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2005 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. */ // // Pixel formats used across the system. // These formats might not all be supported by all renderers, for instance // skia or SurfaceFlinger are not required to support all of these formats // (either as source or destination) #ifndef UI_PIXELFORMAT_H #define UI_PIXELFORMAT_H #include namespace android { enum { // // these constants need to match those // in graphics/PixelFormat.java & pixelflinger/format.h // PIXEL_FORMAT_UNKNOWN = 0, PIXEL_FORMAT_NONE = 0, // logical pixel formats used by the SurfaceFlinger ----------------------- PIXEL_FORMAT_CUSTOM = -4, // Custom pixel-format described by a PixelFormatInfo structure PIXEL_FORMAT_TRANSLUCENT = -3, // System chooses a format that supports translucency (many alpha bits) PIXEL_FORMAT_TRANSPARENT = -2, // System chooses a format that supports transparency // (at least 1 alpha bit) PIXEL_FORMAT_OPAQUE = -1, // System chooses an opaque format (no alpha bits required) // real pixel formats supported for rendering ----------------------------- PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA }; typedef int32_t PixelFormat; uint32_t bytesPerPixel(PixelFormat format); uint32_t bitsPerPixel(PixelFormat format); }; // namespace android #endif // UI_PIXELFORMAT_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Point.h��������������������������������������������������������������������������0100644 0000000 0000000 00000004306 13756501735 014474� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_POINT #define ANDROID_UI_POINT #include #include namespace android { class Point : public LightFlattenablePod { public: int x; int y; // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions // Default constructor doesn't initialize the Point inline Point() { } inline Point(int _x, int _y) : x(_x), y(_y) { } inline bool operator == (const Point& rhs) const { return (x == rhs.x) && (y == rhs.y); } inline bool operator != (const Point& rhs) const { return !operator == (rhs); } inline bool isOrigin() const { return !(x|y); } // operator < defines an order which allows to use points in sorted // vectors. bool operator < (const Point& rhs) const { return y namespace android { /** * Enum mirroring the public API definitions for image and pixel formats. * Some of these are hidden in the public API * * Keep up to date with android.graphics.ImageFormat and * android.graphics.PixelFormat * * TODO: PublicFormat is going to be deprecated(b/126594675) */ enum class PublicFormat { UNKNOWN = 0x0, RGBA_8888 = 0x1, RGBX_8888 = 0x2, RGB_888 = 0x3, RGB_565 = 0x4, NV16 = 0x10, NV21 = 0x11, YUY2 = 0x14, RGBA_FP16 = 0x16, RAW_SENSOR = 0x20, PRIVATE = 0x22, YUV_420_888 = 0x23, RAW_PRIVATE = 0x24, RAW10 = 0x25, RAW12 = 0x26, RGBA_1010102 = 0x2b, JPEG = 0x100, DEPTH_POINT_CLOUD = 0x101, RAW_DEPTH = 0x1002, // @hide YV12 = 0x32315659, Y8 = 0x20203859, Y16 = 0x20363159, // @hide DEPTH16 = 0x44363159, DEPTH_JPEG = 0x69656963, HEIC = 0x48454946, }; /* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL * format */ extern int mapPublicFormatToHalFormat(PublicFormat f); /* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL * dataspace */ extern android_dataspace mapPublicFormatToHalDataspace(PublicFormat f); /* Convert from HAL format, dataspace pair to * android.graphics.ImageFormat/PixelFormat. * For unknown/unspecified pairs, returns PublicFormat::UNKNOWN */ extern PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace); }; // namespace android #endif // UI_PUBLICFORMAT_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Rect.h���������������������������������������������������������������������������0100644 0000000 0000000 00000014233 13756501735 014300� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_RECT #define ANDROID_UI_RECT #include #include #include #include #include #include #include #include namespace android { class Rect : public ARect, public LightFlattenablePod { public: typedef ARect::value_type value_type; static const Rect INVALID_RECT; static const Rect EMPTY_RECT; // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions inline Rect() : Rect(INVALID_RECT) {} template inline Rect(T w, T h) { if (w > INT32_MAX) { w = INT32_MAX; } if (h > INT32_MAX) { h = INT32_MAX; } left = top = 0; right = static_cast(w); bottom = static_cast(h); } inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) { left = l; top = t; right = r; bottom = b; } inline Rect(const Point& lt, const Point& rb) { left = lt.x; top = lt.y; right = rb.x; bottom = rb.y; } inline explicit Rect(const FloatRect& floatRect) { // Ideally we would use std::round, but we don't want to add an STL // dependency here, so we use an approximation left = static_cast(floatRect.left + 0.5f); top = static_cast(floatRect.top + 0.5f); right = static_cast(floatRect.right + 0.5f); bottom = static_cast(floatRect.bottom + 0.5f); } inline explicit Rect(const ui::Size& size) { left = 0; top = 0; right = size.width; bottom = size.height; } void makeInvalid(); inline void clear() { left = top = right = bottom = 0; } // a valid rectangle has a non negative width and height inline bool isValid() const { return (getWidth() >= 0) && (getHeight() >= 0); } // an empty rect has a zero width or height, or is invalid inline bool isEmpty() const { return (getWidth() <= 0) || (getHeight() <= 0); } // rectangle's width __attribute__((no_sanitize("signed-integer-overflow"))) inline int32_t getWidth() const { return right - left; } // rectangle's height __attribute__((no_sanitize("signed-integer-overflow"))) inline int32_t getHeight() const { return bottom - top; } ui::Size getSize() const { return ui::Size(getWidth(), getHeight()); } __attribute__((no_sanitize("signed-integer-overflow"))) inline Rect getBounds() const { return Rect(right - left, bottom - top); } void setLeftTop(const Point& lt) { left = lt.x; top = lt.y; } void setRightBottom(const Point& rb) { right = rb.x; bottom = rb.y; } // the following 4 functions return the 4 corners of the rect as Point Point leftTop() const { return Point(left, top); } Point rightBottom() const { return Point(right, bottom); } Point rightTop() const { return Point(right, top); } Point leftBottom() const { return Point(left, bottom); } // comparisons inline bool operator == (const Rect& rhs) const { return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) && (bottom == rhs.bottom); } inline bool operator != (const Rect& rhs) const { return !operator == (rhs); } // operator < defines an order which allows to use rectangles in sorted // vectors. bool operator < (const Rect& rhs) const; const Rect operator + (const Point& rhs) const; const Rect operator - (const Point& rhs) const; Rect& operator += (const Point& rhs) { return offsetBy(rhs.x, rhs.y); } Rect& operator -= (const Point& rhs) { return offsetBy(-rhs.x, -rhs.y); } Rect& offsetToOrigin() { right -= left; bottom -= top; left = top = 0; return *this; } Rect& offsetTo(const Point& p) { return offsetTo(p.x, p.y); } Rect& offsetBy(const Point& dp) { return offsetBy(dp.x, dp.y); } Rect& offsetTo(int32_t x, int32_t y); Rect& offsetBy(int32_t x, int32_t y); /** * Insets the rectangle on all sides specified by the insets. */ Rect& inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom); bool intersect(const Rect& with, Rect* result) const; // Create a new Rect by transforming this one using a graphics HAL // transform. This rectangle is defined in a coordinate space starting at // the origin and extending to (width, height). If the transform includes // a ROT90 then the output rectangle is defined in a space extending to // (height, width). Otherwise the output rectangle is in the same space as // the input. Rect transform(uint32_t xform, int32_t width, int32_t height) const; // this calculates (Region(*this) - exclude).bounds() efficiently Rect reduce(const Rect& exclude) const; // for backward compatibility inline int32_t width() const { return getWidth(); } inline int32_t height() const { return getHeight(); } inline void set(const Rect& rhs) { operator = (rhs); } FloatRect toFloatRect() const { return {static_cast(left), static_cast(top), static_cast(right), static_cast(bottom)}; } }; ANDROID_BASIC_TYPES_TRAITS(Rect) }; // namespace android #endif // ANDROID_UI_RECT ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Region.h�������������������������������������������������������������������������0100644 0000000 0000000 00000017744 13756501735 014640� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_REGION_H #define ANDROID_UI_REGION_H #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- class Region : public LightFlattenable { public: static const Region INVALID_REGION; Region(); Region(const Region& rhs); explicit Region(const Rect& rhs); ~Region(); static Region createTJunctionFreeRegion(const Region& r); Region& operator = (const Region& rhs); inline bool isEmpty() const { return getBounds().isEmpty(); } inline bool isRect() const { return mStorage.size() == 1; } inline Rect getBounds() const { return mStorage[mStorage.size() - 1]; } inline Rect bounds() const { return getBounds(); } bool contains(const Point& point) const; bool contains(int x, int y) const; // the region becomes its bounds Region& makeBoundsSelf(); void clear(); void set(const Rect& r); void set(int32_t w, int32_t h); void set(uint32_t w, uint32_t h); Region& orSelf(const Rect& rhs); Region& xorSelf(const Rect& rhs); Region& andSelf(const Rect& rhs); Region& subtractSelf(const Rect& rhs); // boolean operators, applied on this Region& orSelf(const Region& rhs); Region& xorSelf(const Region& rhs); Region& andSelf(const Region& rhs); Region& subtractSelf(const Region& rhs); // boolean operators const Region merge(const Rect& rhs) const; const Region mergeExclusive(const Rect& rhs) const; const Region intersect(const Rect& rhs) const; const Region subtract(const Rect& rhs) const; // boolean operators const Region merge(const Region& rhs) const; const Region mergeExclusive(const Region& rhs) const; const Region intersect(const Region& rhs) const; const Region subtract(const Region& rhs) const; // these translate rhs first Region& translateSelf(int dx, int dy); Region& scaleSelf(float sx, float sy); Region& orSelf(const Region& rhs, int dx, int dy); Region& xorSelf(const Region& rhs, int dx, int dy); Region& andSelf(const Region& rhs, int dx, int dy); Region& subtractSelf(const Region& rhs, int dx, int dy); // these translate rhs first const Region translate(int dx, int dy) const WARN_UNUSED; const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED; const Region mergeExclusive(const Region& rhs, int dx, int dy) const WARN_UNUSED; const Region intersect(const Region& rhs, int dx, int dy) const WARN_UNUSED; const Region subtract(const Region& rhs, int dx, int dy) const WARN_UNUSED; // convenience operators overloads inline const Region operator | (const Region& rhs) const; inline const Region operator ^ (const Region& rhs) const; inline const Region operator & (const Region& rhs) const; inline const Region operator - (const Region& rhs) const; inline const Region operator + (const Point& pt) const; inline Region& operator |= (const Region& rhs); inline Region& operator ^= (const Region& rhs); inline Region& operator &= (const Region& rhs); inline Region& operator -= (const Region& rhs); inline Region& operator += (const Point& pt); // returns true if the regions share the same underlying storage bool isTriviallyEqual(const Region& region) const; /* various ways to access the rectangle list */ // STL-like iterators typedef Rect const* const_iterator; const_iterator begin() const; const_iterator end() const; // returns an array of rect which has the same life-time has this // Region object. Rect const* getArray(size_t* count) const; /* no user serviceable parts here... */ // add a rectangle to the internal list. This rectangle must // be sorted in Y and X and must not make the region invalid. void addRectUnchecked(int l, int t, int r, int b); inline bool isFixedSize() const { return false; } size_t getFlattenedSize() const; status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); void dump(std::string& out, const char* what, uint32_t flags=0) const; void dump(const char* what, uint32_t flags=0) const; private: class rasterizer; friend class rasterizer; Region& operationSelf(const Rect& r, uint32_t op); Region& operationSelf(const Region& r, uint32_t op); Region& operationSelf(const Region& r, int dx, int dy, uint32_t op); const Region operation(const Rect& rhs, uint32_t op) const; const Region operation(const Region& rhs, uint32_t op) const; const Region operation(const Region& rhs, int dx, int dy, uint32_t op) const; static void boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Region& rhs, int dx, int dy); static void boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Rect& rhs, int dx, int dy); static void boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Region& rhs); static void boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Rect& rhs); static void translate(Region& reg, int dx, int dy); static void translate(Region& dst, const Region& reg, int dx, int dy); static bool validate(const Region& reg, const char* name, bool silent = false); // mStorage is a (manually) sorted array of Rects describing the region // with an extra Rect as the last element which is set to the // bounds of the region. However, if the region is // a simple Rect then mStorage contains only that rect. Vector mStorage; }; const Region Region::operator | (const Region& rhs) const { return merge(rhs); } const Region Region::operator ^ (const Region& rhs) const { return mergeExclusive(rhs); } const Region Region::operator & (const Region& rhs) const { return intersect(rhs); } const Region Region::operator - (const Region& rhs) const { return subtract(rhs); } const Region Region::operator + (const Point& pt) const { return translate(pt.x, pt.y); } Region& Region::operator |= (const Region& rhs) { return orSelf(rhs); } Region& Region::operator ^= (const Region& rhs) { return xorSelf(rhs); } Region& Region::operator &= (const Region& rhs) { return andSelf(rhs); } Region& Region::operator -= (const Region& rhs) { return subtractSelf(rhs); } Region& Region::operator += (const Point& pt) { return translateSelf(pt.x, pt.y); } // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_UI_REGION_H ����������������������������libs/ui/include/ui/Size.h���������������������������������������������������������������������������0100644 0000000 0000000 00000013151 13756501735 014313� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #pragma once #include #include #include #include #include namespace android { namespace ui { // Forward declare a few things. struct Size; bool operator==(const Size& lhs, const Size& rhs); /** * A simple value type representing a two-dimensional size */ struct Size { int32_t width; int32_t height; // Special values static const Size INVALID; static const Size EMPTY; // ------------------------------------------------------------------------ // Construction // ------------------------------------------------------------------------ Size() : Size(INVALID) {} template Size(T&& w, T&& h) : width(Size::clamp(std::forward(w))), height(Size::clamp(std::forward(h))) {} // ------------------------------------------------------------------------ // Accessors // ------------------------------------------------------------------------ int32_t getWidth() const { return width; } int32_t getHeight() const { return height; } template void setWidth(T&& v) { width = Size::clamp(std::forward(v)); } template void setHeight(T&& v) { height = Size::clamp(std::forward(v)); } // ------------------------------------------------------------------------ // Assignment // ------------------------------------------------------------------------ void set(const Size& size) { *this = size; } template void set(T&& w, T&& h) { set(Size(std::forward(w), std::forward(h))); } // Sets the value to INVALID void makeInvalid() { set(INVALID); } // Sets the value to EMPTY void clear() { set(EMPTY); } // ------------------------------------------------------------------------ // Semantic checks // ------------------------------------------------------------------------ // Valid means non-negative width and height bool isValid() const { return width >= 0 && height >= 0; } // Empty means zero width and height bool isEmpty() const { return *this == EMPTY; } // ------------------------------------------------------------------------ // Clamp Helpers // ------------------------------------------------------------------------ // Note: We use only features available in C++11 here for compatibility with // external targets which include this file directly or indirectly and which // themselves use C++11. // C++11 compatible replacement for std::remove_cv_reference_t [C++20] template using remove_cv_reference_t = typename std::remove_cv::type>::type; // Takes a value of type FromType, and ensures it can be represented as a value of type ToType, // clamping the input value to the output range if necessary. template static Size::remove_cv_reference_t clamp( typename std::enable_if< std::numeric_limits>::is_bounded && std::numeric_limits>::is_bounded, FromType&&>::type v) { static constexpr auto toHighest = std::numeric_limits>::max(); static constexpr auto toLowest = std::numeric_limits>::lowest(); static constexpr auto fromHighest = std::numeric_limits>::max(); static constexpr auto fromLowest = std::numeric_limits>::lowest(); // A clamp is needed if the range of FromType is not a subset of the range of ToType static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest); // If a clamp is not needed, the conversion is just a trivial cast. if (!isClampNeeded) { return static_cast(v); } // Otherwise we leverage implicit conversion to safely compare values of // different types, to ensure we return a value clamped to the range of // ToType. return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast(v)); } }; // ------------------------------------------------------------------------ // Comparisons // ------------------------------------------------------------------------ inline bool operator==(const Size& lhs, const Size& rhs) { return lhs.width == rhs.width && lhs.height == rhs.height; } inline bool operator!=(const Size& lhs, const Size& rhs) { return !operator==(lhs, rhs); } inline bool operator<(const Size& lhs, const Size& rhs) { // Orders by increasing width, then height. if (lhs.width != rhs.width) return lhs.width < rhs.width; return lhs.height < rhs.height; } } // namespace ui } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/Transform.h����������������������������������������������������������������������0100644 0000000 0000000 00000006440 13756501735 015357� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_TRANSFORM_H #define ANDROID_TRANSFORM_H #include #include #include #include #include #include #include #include #include namespace android { class Region; namespace ui { class Transform { public: Transform(); Transform(const Transform& other); explicit Transform(uint32_t orientation); ~Transform(); enum orientation_flags { ROT_0 = 0x00000000, FLIP_H = HAL_TRANSFORM_FLIP_H, FLIP_V = HAL_TRANSFORM_FLIP_V, ROT_90 = HAL_TRANSFORM_ROT_90, ROT_180 = FLIP_H|FLIP_V, ROT_270 = ROT_180|ROT_90, ROT_INVALID = 0x80 }; enum type_mask : uint32_t { IDENTITY = 0, TRANSLATE = 0x1, ROTATE = 0x2, SCALE = 0x4, UNKNOWN = 0x8 }; // query the transform bool preserveRects() const; uint32_t getType() const; uint32_t getOrientation() const; const vec3& operator [] (size_t i) const; // returns column i float tx() const; float ty() const; float sx() const; float sy() const; // modify the transform void reset(); void set(float tx, float ty); void set(float a, float b, float c, float d); status_t set(uint32_t flags, float w, float h); // transform data Rect makeBounds(int w, int h) const; vec2 transform(int x, int y) const; Region transform(const Region& reg) const; Rect transform(const Rect& bounds, bool roundOutwards = false) const; FloatRect transform(const FloatRect& bounds) const; Transform& operator = (const Transform& other); Transform operator * (const Transform& rhs) const; // assumes the last row is < 0 , 0 , 1 > vec2 transform(const vec2& v) const; vec3 transform(const vec3& v) const; // Expands from the internal 3x3 matrix to an equivalent 4x4 matrix mat4 asMatrix4() const; Transform inverse() const; // for debugging void dump(std::string& result, const char* name) const; void dump(const char* name) const; private: struct mat33 { vec3 v[3]; inline const vec3& operator [] (size_t i) const { return v[i]; } inline vec3& operator [] (size_t i) { return v[i]; } }; enum { UNKNOWN_TYPE = 0x80000000 }; uint32_t type() const; static bool absIsOne(float f); static bool isZero(float f); mat33 mMatrix; mutable uint32_t mType; }; } // namespace ui } // namespace android #endif /* ANDROID_TRANSFORM_H */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/UiConfig.h�����������������������������������������������������������������������0100644 0000000 0000000 00000001562 13756501735 015107� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_UI_CONFIG_H #define ANDROID_UI_CONFIG_H #include namespace android { // Append the libui configuration details to configStr. void appendUiConfigString(std::string& configStr); }; // namespace android #endif /*ANDROID_UI_CONFIG_H*/ ����������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013637� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014254� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/ANativeObjectBase.h���������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 025771� 2../../include/ui/ANativeObjectBase.h����������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/BufferQueueDefs.h�����������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 025307� 2../../include/ui/BufferQueueDefs.h������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/ColorSpace.h����������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023333� 2../../include/ui/ColorSpace.h�����������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/ConfigStoreTypes.h����������������������������������������������������������0100644 0000000 0000000 00000001711 13756501735 017671� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #pragma once // android::ui::* in this header file will alias different types as // the HIDL interface is updated. namespace android { namespace ui { struct CieXyz { float X; float Y; float Z; }; struct DisplayPrimaries { CieXyz red; CieXyz green; CieXyz blue; CieXyz white; }; } // namespace ui } // namespace android �������������������������������������������������������libs/ui/include_vndk/ui/DebugUtils.h����������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023365� 2../../include/ui/DebugUtils.h�����������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/DisplayInfo.h���������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023711� 2../../include/ui/DisplayInfo.h����������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/DisplayStatInfo.h�����������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 025401� 2../../include/ui/DisplayStatInfo.h������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/DisplayedFrameStats.h�������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 027067� 2../../include/ui/DisplayedFrameStats.h��������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Fence.h���������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 021307� 2../../include/ui/Fence.h����������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/FenceTime.h�����������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 022745� 2../../include/ui/FenceTime.h������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/FloatRect.h�����������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023015� 2../../include/ui/FloatRect.h������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/FrameStats.h����������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023371� 2../../include/ui/FrameStats.h�����������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Gralloc2.h������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 022361� 2../../include/ui/Gralloc2.h�������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/GraphicBuffer.h�������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 024465� 2../../include/ui/GraphicBuffer.h��������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/GraphicBufferAllocator.h����������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 030167� 2../../include/ui/GraphicBufferAllocator.h�����������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/GraphicBufferMapper.h�������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 026777� 2../../include/ui/GraphicBufferMapper.h��������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/GraphicTypes.h��������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 024253� 2../../include/ui/GraphicTypes.h���������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/HdrCapabilities.h�����������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 025325� 2../../include/ui/HdrCapabilities.h������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/PixelFormat.h���������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023733� 2../../include/ui/PixelFormat.h����������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Point.h���������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 021431� 2../../include/ui/Point.h����������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Rect.h����������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 021041� 2../../include/ui/Rect.h�����������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Region.h��������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 021715� 2../../include/ui/Region.h���������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Size.h����������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 021073� 2../../include/ui/Size.h�����������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/Transform.h�����������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 023175� 2../../include/ui/Transform.h������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include_vndk/ui/UiConfig.h������������������������������������������������������������������0100644 0000000 0000000 00000000000 13756501735 022455� 2../../include/ui/UiConfig.h�������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/��������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012334� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/Android.bp����������������������������������������������������������������������������0100644 0000000 0000000 00000004375 13756501735 014245� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// // 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. // cc_test { name: "Region_test", shared_libs: ["libui"], srcs: ["Region_test.cpp"], cflags: ["-Wall", "-Werror"], } cc_test { name: "colorspace_test", shared_libs: ["libui"], srcs: ["colorspace_test.cpp"], cflags: ["-Wall", "-Werror"], } cc_test { name: "GraphicBufferAllocator_test", header_libs: [ "libdvr_headers", "libnativewindow_headers", ], static_libs: [ "libgmock", ], shared_libs: [ "liblog", "libui", ], srcs: [ "GraphicBufferAllocator_test.cpp", "mock/MockGrallocAllocator.cpp", ], cflags: ["-Wall", "-Werror"], } cc_test { name: "GraphicBuffer_test", header_libs: [ "libdvr_headers", "libnativewindow_headers", ], shared_libs: [ "android.frameworks.bufferhub@1.0", "libcutils", "libhidlbase", "libhwbinder", "libui", "libutils", ], srcs: ["GraphicBuffer_test.cpp"], cflags: ["-Wall", "-Werror"], } cc_test { name: "BufferHub_test", header_libs: [ "libdvr_headers", "libnativewindow_headers", ], static_libs: [ "libgmock", ], shared_libs: [ "android.frameworks.bufferhub@1.0", "libcutils", "libhidlbase", "libhwbinder", "liblog", "libui", "libutils" ], srcs: [ "BufferHubBuffer_test.cpp", "BufferHubEventFd_test.cpp", "BufferHubMetadata_test.cpp", ], cflags: ["-Wall", "-Werror"], } cc_test { name: "Size_test", shared_libs: ["libui"], srcs: ["Size_test.cpp"], cflags: ["-Wall", "-Werror"], } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/BufferHubBuffer_test.cpp��������������������������������������������������������������0100644 0000000 0000000 00000040153 13756501735 017101� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #define LOG_TAG "BufferHubBufferTest" #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace { using ::android::BufferHubDefs::isAnyClientAcquired; using ::android::BufferHubDefs::isAnyClientGained; using ::android::BufferHubDefs::isAnyClientPosted; using ::android::BufferHubDefs::isClientAcquired; using ::android::BufferHubDefs::isClientGained; using ::android::BufferHubDefs::isClientPosted; using ::android::BufferHubDefs::isClientReleased; using ::android::BufferHubDefs::kMetadataHeaderSize; using ::android::frameworks::bufferhub::V1_0::IBufferHub; using ::testing::IsNull; using ::testing::NotNull; const int kWidth = 640; const int kHeight = 480; const int kLayerCount = 1; const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; const AHardwareBuffer_Desc kDesc = {kWidth, kHeight, kLayerCount, kFormat, kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL}; const size_t kUserMetadataSize = 1; class BufferHubBufferTest : public ::testing::Test { protected: void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); if (!BufferHubServiceRunning()) { // TODO(b/112940221): Enforce the test cross all devices once BufferHub lands in Android // R for all Android varieties. GTEST_SKIP() << "Skip test as the BufferHub service is not running."; } } bool BufferHubServiceRunning() { sp bufferhub = IBufferHub::getService(); return bufferhub.get() != nullptr; } }; bool cmpAHardwareBufferDesc(const AHardwareBuffer_Desc& desc, const AHardwareBuffer_Desc& other) { // Not comparing stride because it's unknown before allocation return desc.format == other.format && desc.height == other.height && desc.layers == other.layers && desc.usage == other.usage && desc.width == other.width; } class BufferHubBufferStateTransitionTest : public BufferHubBufferTest { protected: void SetUp() override { BufferHubBufferTest::SetUp(); if (IsSkipped()) { // If the base class' SetUp() stated the test should be skipped, we should short // circuit this sub-class' logic. return; } CreateTwoClientsOfABuffer(); } std::unique_ptr b1; uint32_t b1ClientMask = 0U; std::unique_ptr b2; uint32_t b2ClientMask = 0U; private: // Creates b1 and b2 as the clients of the same buffer for testing. void CreateTwoClientsOfABuffer(); }; void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() { b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); ASSERT_THAT(b1, NotNull()); b1ClientMask = b1->clientStateMask(); ASSERT_NE(b1ClientMask, 0U); sp token = b1->duplicate(); ASSERT_THAT(token, NotNull()); b2 = BufferHubBuffer::import(token); ASSERT_THAT(b2, NotNull()); b2ClientMask = b2->clientStateMask(); ASSERT_NE(b2ClientMask, 0U); ASSERT_NE(b2ClientMask, b1ClientMask); } TEST_F(BufferHubBufferTest, CreateBufferFails) { // Buffer Creation will fail: BLOB format requires height to be 1. auto b1 = BufferHubBuffer::create(kWidth, /*height=*/2, kLayerCount, /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize); EXPECT_THAT(b1, IsNull()); // Buffer Creation will fail: user metadata size too large. auto b2 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, /*userMetadataSize=*/std::numeric_limits::max()); EXPECT_THAT(b2, IsNull()); // Buffer Creation will fail: user metadata size too large. const size_t userMetadataSize = std::numeric_limits::max() - kMetadataHeaderSize; auto b3 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, userMetadataSize); EXPECT_THAT(b3, IsNull()); } TEST_F(BufferHubBufferTest, CreateBuffer) { auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); ASSERT_THAT(b1, NotNull()); EXPECT_TRUE(b1->isConnected()); EXPECT_TRUE(b1->isValid()); EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), kDesc)); EXPECT_EQ(b1->userMetadataSize(), kUserMetadataSize); } TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) { auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); ASSERT_THAT(b1, NotNull()); EXPECT_TRUE(b1->isValid()); sp token = b1->duplicate(); ASSERT_THAT(token, NotNull()); // The detached buffer should still be valid. EXPECT_TRUE(b1->isConnected()); EXPECT_TRUE(b1->isValid()); std::unique_ptr b2 = BufferHubBuffer::import(token); ASSERT_THAT(b2, NotNull()); EXPECT_TRUE(b2->isValid()); EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), b2->desc())); EXPECT_EQ(b1->userMetadataSize(), b2->userMetadataSize()); // These two buffer instances are based on the same physical buffer under the // hood, so they should share the same id. EXPECT_EQ(b1->id(), b2->id()); // We use clientStateMask() to tell those two instances apart. EXPECT_NE(b1->clientStateMask(), b2->clientStateMask()); // Both buffer instances should be in released state currently. EXPECT_TRUE(b1->isReleased()); EXPECT_TRUE(b2->isReleased()); // The event fd should behave like duped event fds. const BufferHubEventFd& eventFd1 = b1->eventFd(); ASSERT_GE(eventFd1.get(), 0); const BufferHubEventFd& eventFd2 = b2->eventFd(); ASSERT_GE(eventFd2.get(), 0); base::unique_fd epollFd(epoll_create(64)); ASSERT_GE(epollFd.get(), 0); // Add eventFd1 to epoll set, and signal eventFd2. epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno); std::array events; EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); eventFd2.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); eventFd2.signal(); eventFd2.clear(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubBufferTest, ImportFreedBuffer) { auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); ASSERT_THAT(b1, NotNull()); EXPECT_TRUE(b1->isValid()); sp token = b1->duplicate(); ASSERT_THAT(token, NotNull()); // Explicitly destroy b1. Backend buffer should be freed and token becomes invalid b1.reset(); std::unique_ptr b2 = BufferHubBuffer::import(token); // Import should fail with INVALID_TOKEN EXPECT_THAT(b2, IsNull()); } // nullptr must not crash the service TEST_F(BufferHubBufferTest, ImportNullToken) { auto b1 = BufferHubBuffer::import(nullptr); EXPECT_THAT(b1, IsNull()); } TEST_F(BufferHubBufferTest, ImportInvalidToken) { native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1); token->data[0] = 0; sp tokenHandle = NativeHandle::create(token, /*ownHandle=*/true); auto b1 = BufferHubBuffer::import(tokenHandle); EXPECT_THAT(b1, IsNull()); } TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) { ASSERT_TRUE(b1->isReleased()); // Successful gaining the buffer should change the buffer state bit of b1 to // gained state, other client state bits to released state. EXPECT_EQ(b1->gain(), 0); EXPECT_TRUE(isClientGained(b1->bufferState(), b1ClientMask)); } TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) { ASSERT_EQ(b1->gain(), 0); auto currentBufferState = b1->bufferState(); ASSERT_TRUE(isClientGained(currentBufferState, b1ClientMask)); // Gaining from gained state by the same client should not return error. EXPECT_EQ(b1->gain(), 0); // Gaining from gained state by another client should return error. EXPECT_EQ(b2->gain(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_EQ(b2->acquire(), 0); ASSERT_TRUE(isAnyClientAcquired(b1->bufferState())); // Gaining from acquired state should fail. EXPECT_EQ(b1->gain(), -EBUSY); EXPECT_EQ(b2->gain(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); // Gaining a buffer who has other posted client should succeed. EXPECT_EQ(b1->gain(), 0); } TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); // A posted client should be able to gain the buffer when there is no other clients in // acquired state. EXPECT_EQ(b2->gain(), 0); } TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask)); EXPECT_EQ(b2->post(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask)); EXPECT_EQ(b1->post(), 0); auto currentBufferState = b1->bufferState(); EXPECT_TRUE(isClientReleased(currentBufferState, b1ClientMask)); EXPECT_TRUE(isClientPosted(currentBufferState, b2ClientMask)); } TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); // Post from posted state should fail. EXPECT_EQ(b1->post(), -EBUSY); EXPECT_EQ(b2->post(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_EQ(b2->acquire(), 0); ASSERT_TRUE(isAnyClientAcquired(b1->bufferState())); // Posting from acquired state should fail. EXPECT_EQ(b1->post(), -EBUSY); EXPECT_EQ(b2->post(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) { ASSERT_TRUE(b1->isReleased()); // Posting from released state should fail. EXPECT_EQ(b1->post(), -EBUSY); EXPECT_EQ(b2->post(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask)); // Acquire from posted state should pass. EXPECT_EQ(b2->acquire(), 0); } TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask)); // Acquire from released state should fail, although there are other clients // in posted state. EXPECT_EQ(b1->acquire(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_EQ(b2->acquire(), 0); auto currentBufferState = b1->bufferState(); ASSERT_TRUE(isClientAcquired(currentBufferState, b2ClientMask)); // Acquiring from acquired state by the same client should not error out. EXPECT_EQ(b2->acquire(), 0); } TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) { ASSERT_TRUE(b1->isReleased()); // Acquiring form released state should fail. EXPECT_EQ(b1->acquire(), -EBUSY); EXPECT_EQ(b2->acquire(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_TRUE(isAnyClientGained(b1->bufferState())); // Acquiring from gained state should fail. EXPECT_EQ(b1->acquire(), -EBUSY); EXPECT_EQ(b2->acquire(), -EBUSY); } TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) { ASSERT_TRUE(b1->isReleased()); EXPECT_EQ(b1->release(), 0); } TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) { ASSERT_TRUE(b1->isReleased()); ASSERT_EQ(b1->gain(), 0); ASSERT_TRUE(isAnyClientGained(b1->bufferState())); EXPECT_EQ(b1->release(), 0); } TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); EXPECT_EQ(b2->release(), 0); } TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_EQ(b2->acquire(), 0); ASSERT_TRUE(isAnyClientAcquired(b1->bufferState())); EXPECT_EQ(b2->release(), 0); } TEST_F(BufferHubBufferStateTransitionTest, BasicUsage) { // 1 producer buffer and 1 consumer buffer initialised in testcase setup. // Test if this set of basic operation succeed: // Producer post three times to the consumer, and released by consumer. for (int i = 0; i < 3; ++i) { ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); ASSERT_EQ(b2->acquire(), 0); ASSERT_EQ(b2->release(), 0); } } TEST_F(BufferHubBufferTest, createNewConsumerAfterGain) { // Create a poducer buffer and gain. std::unique_ptr b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); ASSERT_THAT(b1, NotNull()); ASSERT_EQ(b1->gain(), 0); // Create a consumer of the buffer and test if the consumer can acquire the // buffer if producer posts. sp token = b1->duplicate(); ASSERT_THAT(token, NotNull()); std::unique_ptr b2 = BufferHubBuffer::import(token); ASSERT_THAT(b2, NotNull()); ASSERT_NE(b1->clientStateMask(), b2->clientStateMask()); ASSERT_EQ(b1->post(), 0); EXPECT_EQ(b2->acquire(), 0); } TEST_F(BufferHubBufferTest, createNewConsumerAfterPost) { // Create a poducer buffer and post. std::unique_ptr b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); ASSERT_EQ(b1->gain(), 0); ASSERT_EQ(b1->post(), 0); // Create a consumer of the buffer and test if the consumer can acquire the // buffer if producer posts. sp token = b1->duplicate(); ASSERT_THAT(token, NotNull()); std::unique_ptr b2 = BufferHubBuffer::import(token); ASSERT_THAT(b2, NotNull()); ASSERT_NE(b1->clientStateMask(), b2->clientStateMask()); EXPECT_EQ(b2->acquire(), 0); } } // namespace } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/BufferHubEventFd_test.cpp�������������������������������������������������������������0100644 0000000 0000000 00000035435 13756501735 017232� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #define LOG_TAG "BufferHubEventFdTest" #include #include #include #include #include #include #include #include #include #include namespace android { namespace { const int kTimeout = 100; const std::chrono::milliseconds kTimeoutMs(kTimeout); const int kTestRuns = 5; using ::testing::Contains; using BufferHubEventFdTest = ::testing::Test; } // namespace TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd(epoll_create(64)); ASSERT_GE(epollFd.get(), 0); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); std::array events; EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); eventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); // Check that it can receive consecutive signal. eventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); // Check that it can receive consecutive signal from a duplicated eventfd. BufferHubEventFd dupEventFd(dup(eventFd.get())); ASSERT_TRUE(dupEventFd.isValid()); dupEventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); dupEventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubEventFdTest, EventFd_testCreateEpollFdAndAddSignaledEventFd) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); eventFd.signal(); base::unique_fd epollFd(epoll_create(64)); ASSERT_GE(epollFd.get(), 0); // Make sure that the epoll set has not been signal yet. std::array events; ASSERT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); // Check that adding an signaled fd into this epoll set will trigger the epoll set. epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubEventFdTest, EventFd_testAddSignaledEventFdToEpollFd) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd(epoll_create(64)); ASSERT_GE(epollFd.get(), 0); eventFd.signal(); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); std::array events; EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromAEventFd) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd(epoll_create(64)); ASSERT_GE(epollFd.get(), 0); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); std::array events; for (int i = 0; i < kTestRuns; ++i) { eventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } } TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromADuplicatedEventFd) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd(epoll_create(64)); ASSERT_GE(epollFd.get(), 0); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); BufferHubEventFd dupEventFd(dup(eventFd.get())); ASSERT_TRUE(dupEventFd.isValid()); std::array events; for (int i = 0; i < kTestRuns; ++i) { dupEventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } } TEST_F(BufferHubEventFdTest, EventFd_testClear) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd(epoll_create(64)); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_GE(epollFd.get(), 0); ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); eventFd.signal(); eventFd.clear(); std::array events; EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd(epoll_create(64)); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_GE(epollFd.get(), 0); ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); // Technically, the dupliated eventFd and the original eventFd are pointing // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl // eventFd. BufferHubEventFd dupedEventFd(dup(eventFd.get())); ASSERT_GE(dupedEventFd.get(), 0); std::array events; EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); dupedEventFd.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); dupedEventFd.signal(); dupedEventFd.clear(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd1(epoll_create(64)); base::unique_fd epollFd2(epoll_create(64)); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_GE(epollFd1.get(), 0); ASSERT_GE(epollFd2.get(), 0); // Register the same eventFd to two EpollFds. ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); std::array events; EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); eventFd.signal(); EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); eventFd.signal(); EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); eventFd.clear(); EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); } TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) { BufferHubEventFd eventFd1; BufferHubEventFd eventFd2; ASSERT_TRUE(eventFd1.isValid()); ASSERT_TRUE(eventFd2.isValid()); base::unique_fd epollFd(epoll_create(64)); epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; ASSERT_GE(epollFd.get(), 0); ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); std::array events; EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); // Signal one by one. eventFd1.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(events[0].data.u32, e1.data.u32); eventFd2.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); EXPECT_EQ(events[0].data.u32, e2.data.u32); // Signal both. eventFd1.signal(); eventFd2.signal(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2); uint32_t u32s[] = {events[0].data.u32, events[1].data.u32}; EXPECT_THAT(u32s, Contains(e1.data.u32)); EXPECT_THAT(u32s, Contains(e2.data.u32)); // The epoll fd is edge triggered, so it only responds to the eventFd once. EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); eventFd1.signal(); eventFd2.signal(); eventFd2.clear(); EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); } TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) { BufferHubEventFd eventFd1; BufferHubEventFd eventFd2; ASSERT_TRUE(eventFd1.isValid()); ASSERT_TRUE(eventFd2.isValid()); base::unique_fd epollFd(epoll_create(64)); epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; ASSERT_GE(epollFd.get(), 0); ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); int countEvent1 = 0; int countEvent2 = 0; std::atomic stop{false}; std::mutex mx; std::condition_variable cv; std::thread pollingThread([&] { std::array events; while (true) { if (stop.load()) { break; } int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout); ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); std::lock_guard lock(mx); for (int i = 0; i < ret; i++) { if (events[i].data.u32 == e1.data.u32) { countEvent1++; cv.notify_one(); } else if (events[i].data.u32 == e2.data.u32) { countEvent2++; cv.notify_one(); } } } }); { std::unique_lock lock(mx); eventFd1.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; })); eventFd1.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; })); eventFd2.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; })); eventFd1.clear(); eventFd2.clear(); EXPECT_EQ(countEvent1, 2); EXPECT_EQ(countEvent2, 1); eventFd1.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; })); eventFd2.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; })); } stop.store(true); pollingThread.join(); } TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) { BufferHubEventFd eventFd; ASSERT_TRUE(eventFd.isValid()); base::unique_fd epollFd1(epoll_create(64)); base::unique_fd epollFd2(epoll_create(64)); epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; ASSERT_GE(epollFd1.get(), 0); ASSERT_GE(epollFd2.get(), 0); // Register the same eventFd to two EpollFds. ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); int countEpoll1 = 0; int countEpoll2 = 0; std::atomic stop{false}; std::mutex mx; std::condition_variable cv; std::thread pollingThread1([&] { std::array events; while (!stop.load()) { int ret = epoll_wait(epollFd1.get(), events.data(), events.size(), kTimeout); ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); if (ret > 0) { std::lock_guard lock(mx); countEpoll1++; cv.notify_one(); } } }); std::thread pollingThread2([&] { std::array events; while (!stop.load()) { int ret = epoll_wait(epollFd2.get(), events.data(), events.size(), kTimeout); ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); if (ret > 0) { std::lock_guard lock(mx); countEpoll2++; cv.notify_one(); } } }); { std::unique_lock lock(mx); eventFd.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 1; })); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 1; })); eventFd.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 2; })); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 2; })); eventFd.clear(); EXPECT_EQ(countEpoll1, 2); EXPECT_EQ(countEpoll2, 2); eventFd.signal(); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 3; })); EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 3; })); } stop.store(true); pollingThread1.join(); pollingThread2.join(); } } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/BufferHubMetadata_test.cpp������������������������������������������������������������0100644 0000000 0000000 00000006372 13756501735 017415� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 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. */ #include #include namespace android { namespace dvr { constexpr size_t kEmptyUserMetadataSize = 0; class BufferHubMetadataTest : public ::testing::Test {}; TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) { BufferHubMetadata m1 = BufferHubMetadata::create(std::numeric_limits::max()); EXPECT_FALSE(m1.isValid()); } TEST_F(BufferHubMetadataTest, Create_Success) { BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize); EXPECT_TRUE(m1.isValid()); EXPECT_NE(m1.metadataHeader(), nullptr); } TEST_F(BufferHubMetadataTest, Import_Success) { BufferHubMetadata m1 = BufferHubMetadata::create(kEmptyUserMetadataSize); EXPECT_TRUE(m1.isValid()); EXPECT_NE(m1.metadataHeader(), nullptr); unique_fd h2 = unique_fd(dup(m1.ashmemFd().get())); EXPECT_NE(h2.get(), -1); BufferHubMetadata m2 = BufferHubMetadata::import(std::move(h2)); EXPECT_EQ(h2.get(), -1); EXPECT_TRUE(m1.isValid()); BufferHubDefs::MetadataHeader* mh1 = m1.metadataHeader(); EXPECT_NE(mh1, nullptr); // Check if the newly allocated buffer is initialized in released state (i.e. // state equals to 0U). EXPECT_TRUE(mh1->bufferState.load() == 0U); EXPECT_TRUE(m2.isValid()); BufferHubDefs::MetadataHeader* mh2 = m2.metadataHeader(); EXPECT_NE(mh2, nullptr); // Check if the newly allocated buffer is initialized in released state (i.e. // state equals to 0U). EXPECT_TRUE(mh2->bufferState.load() == 0U); } TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) { BufferHubMetadata m1 = BufferHubMetadata::create(sizeof(int)); EXPECT_TRUE(m1.isValid()); EXPECT_NE(m1.metadataHeader(), nullptr); EXPECT_NE(m1.ashmemFd().get(), -1); EXPECT_EQ(m1.userMetadataSize(), sizeof(int)); BufferHubMetadata m2 = std::move(m1); // After the move, the metadata header (a raw pointer) should be reset in the older buffer. EXPECT_EQ(m1.metadataHeader(), nullptr); EXPECT_NE(m2.metadataHeader(), nullptr); EXPECT_EQ(m1.ashmemFd().get(), -1); EXPECT_NE(m2.ashmemFd().get(), -1); EXPECT_EQ(m1.userMetadataSize(), 0U); EXPECT_EQ(m2.userMetadataSize(), sizeof(int)); BufferHubMetadata m3{std::move(m2)}; // After the move, the metadata header (a raw pointer) should be reset in the older buffer. EXPECT_EQ(m2.metadataHeader(), nullptr); EXPECT_NE(m3.metadataHeader(), nullptr); EXPECT_EQ(m2.ashmemFd().get(), -1); EXPECT_NE(m3.ashmemFd().get(), -1); EXPECT_EQ(m2.userMetadataSize(), 0U); EXPECT_EQ(m3.userMetadataSize(), sizeof(int)); } } // namespace dvr } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/GraphicBufferAllocator_test.cpp�������������������������������������������������������0100644 0000000 0000000 00000010107 13756501735 020443� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #define LOG_TAG "GraphicBufferAllocatorTest" #include #include #include #include #include "mock/MockGrallocAllocator.h" #include #include namespace android { namespace { constexpr uint32_t kTestWidth = 1024; constexpr uint32_t kTestHeight = 1; constexpr uint32_t kTestLayerCount = 1; constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; } // namespace using ::testing::DoAll; using ::testing::Return; using ::testing::SetArgPointee; class TestableGraphicBufferAllocator : public GraphicBufferAllocator { public: TestableGraphicBufferAllocator() { mAllocator = std::make_unique(); } void setUpAllocateExpectations(status_t err, uint32_t stride) { std::cout << "Setting expected stride to " << stride << std::endl; EXPECT_CALL(*(reinterpret_cast(mAllocator.get())), allocate) .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err))); } std::unique_ptr& getAllocator() { return mAllocator; } }; class GraphicBufferAllocatorTest : public testing::Test { public: GraphicBufferAllocatorTest() : mAllocator() {} const TestableGraphicBufferAllocator& getAllocator() { return mAllocator; } protected: TestableGraphicBufferAllocator mAllocator; }; TEST_F(GraphicBufferAllocatorTest, AllocateNoError) { mAllocator.setUpAllocateExpectations(NO_ERROR, kTestWidth); android::PixelFormat format = PIXEL_FORMAT_RGBA_8888; uint32_t stride = 0; buffer_handle_t handle; status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage, &handle, &stride, 0, "GraphicBufferAllocatorTest"); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(kTestWidth, stride); } TEST_F(GraphicBufferAllocatorTest, AllocateZeroStride) { android::PixelFormat format = PIXEL_FORMAT_RGBA_8888; uint32_t expectedStride = 0; mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride); uint32_t stride = 0; buffer_handle_t handle; // a divide by zero would cause a crash status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage, &handle, &stride, 0, "GraphicBufferAllocatorTest"); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(expectedStride, stride); } TEST_F(GraphicBufferAllocatorTest, AllocateLargeStride) { uint32_t height = std::numeric_limits::max(); uint32_t bpp = 4; android::PixelFormat format = PIXEL_FORMAT_RGBA_8888; if (std::numeric_limits::max() / height / bpp >= std::numeric_limits::max()) { std::cout << "stride cannot cause overflow" << std::endl; GTEST_SUCCEED() << "stride cannot cause overflow"; return; } uint32_t width = std::numeric_limits::max() / height / bpp; uint32_t expectedStride = std::numeric_limits::max(); mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride); uint32_t stride = 0; buffer_handle_t handle; // an overflow would cause a crash status_t err = mAllocator.allocate(width, height, format, kTestLayerCount, kTestUsage, &handle, &stride, 0, "GraphicBufferAllocatorTest"); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(expectedStride, stride); } } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/GraphicBuffer_test.cpp����������������������������������������������������������������0100644 0000000 0000000 00000011702 13756501735 016604� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2018 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. */ #define LOG_TAG "GraphicBufferTest" #include #include #include namespace android { namespace { constexpr uint32_t kTestWidth = 1024; constexpr uint32_t kTestHeight = 1; constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB; constexpr uint32_t kTestLayerCount = 1; constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; } // namespace class GraphicBufferTest : public testing::Test {}; TEST_F(GraphicBufferTest, AllocateNoError) { PixelFormat format = PIXEL_FORMAT_RGBA_8888; sp gb(new GraphicBuffer(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage, std::string("test"))); ASSERT_EQ(NO_ERROR, gb->initCheck()); } TEST_F(GraphicBufferTest, AllocateBadDimensions) { PixelFormat format = PIXEL_FORMAT_RGBA_8888; uint32_t width, height; width = height = std::numeric_limits::max(); sp gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage, std::string("test"))); ASSERT_EQ(BAD_VALUE, gb->initCheck()); } TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) { std::unique_ptr b1 = BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, kTestUsage, /*userMetadataSize=*/0); ASSERT_NE(b1, nullptr); EXPECT_TRUE(b1->isValid()); sp gb(new GraphicBuffer(std::move(b1))); EXPECT_TRUE(gb->isBufferHubBuffer()); EXPECT_EQ(gb->getWidth(), kTestWidth); EXPECT_EQ(gb->getHeight(), kTestHeight); EXPECT_EQ(static_cast(gb->getPixelFormat()), kTestFormat); EXPECT_EQ(gb->getUsage(), kTestUsage); EXPECT_EQ(gb->getLayerCount(), kTestLayerCount); } TEST_F(GraphicBufferTest, InvalidBufferIdForNoneBufferHubBuffer) { sp gb( new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage)); EXPECT_FALSE(gb->isBufferHubBuffer()); EXPECT_EQ(gb->getBufferId(), -1); } TEST_F(GraphicBufferTest, BufferIdMatchesBufferHubBufferId) { std::unique_ptr b1 = BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, kTestUsage, /*userMetadataSize=*/0); EXPECT_NE(b1, nullptr); EXPECT_TRUE(b1->isValid()); int b1_id = b1->id(); EXPECT_GE(b1_id, 0); sp gb(new GraphicBuffer(std::move(b1))); EXPECT_TRUE(gb->isBufferHubBuffer()); EXPECT_EQ(gb->getBufferId(), b1_id); } TEST_F(GraphicBufferTest, flattenAndUnflatten) { std::unique_ptr b1 = BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, kTestUsage, /*userMetadataSize=*/0); ASSERT_NE(b1, nullptr); sp gb1(new GraphicBuffer(std::move(b1))); gb1->setGenerationNumber(42); size_t flattenedSize = gb1->getFlattenedSize(); EXPECT_EQ(flattenedSize, 48); size_t fdCount = gb1->getFdCount(); EXPECT_EQ(fdCount, 0); int data[flattenedSize]; int fds[0]; // Make copies of needed items since flatten modifies them. size_t flattenedSizeCopy = flattenedSize; size_t fdCountCopy = fdCount; void* dataStart = data; int* fdsStart = fds; status_t err = gb1->flatten(dataStart, flattenedSizeCopy, fdsStart, fdCountCopy); ASSERT_EQ(err, NO_ERROR); EXPECT_EQ(flattenedSizeCopy, 0); EXPECT_EQ(fdCountCopy, 0); size_t unflattenSize = flattenedSize; size_t unflattenFdCount = fdCount; const void* unflattenData = static_cast(dataStart); const int* unflattenFdData = static_cast(fdsStart); GraphicBuffer* gb2 = new GraphicBuffer(); err = gb2->unflatten(unflattenData, unflattenSize, unflattenFdData, unflattenFdCount); ASSERT_EQ(err, NO_ERROR); EXPECT_TRUE(gb2->isBufferHubBuffer()); EXPECT_EQ(gb2->getWidth(), kTestWidth); EXPECT_EQ(gb2->getHeight(), kTestHeight); EXPECT_EQ(static_cast(gb2->getPixelFormat()), kTestFormat); EXPECT_EQ(gb2->getUsage(), kTestUsage); EXPECT_EQ(gb2->getLayerCount(), kTestLayerCount); EXPECT_EQ(gb1->getBufferId(), gb2->getBufferId()); EXPECT_EQ(gb2->getGenerationNumber(), 42); } } // namespace android ��������������������������������������������������������������libs/ui/tests/Region_test.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000010022 13756501735 015312� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #define LOG_TAG "RegionTest" #include #include #include #include namespace android { class RegionTest : public testing::Test { protected: void checkVertTJunction(const Rect* lhs, const Rect* rhs) { EXPECT_FALSE((rhs->right > lhs->left && rhs->right < lhs->right) || (rhs->left > lhs->left && rhs->left < lhs->right)); } void verifyNoTJunctions(const Region& r) { for (const Rect* current = r.begin(); current < r.end(); current++) { for (const Rect* other = current - 1; other >= r.begin(); other--) { if (other->bottom < current->top) break; if (other->bottom != current->top) continue; checkVertTJunction(current, other); } for (const Rect* other = current + 1; other < r.end(); other++) { if (other->top > current->bottom) break; if (other->top != current->bottom) continue; checkVertTJunction(current, other); } } } void checkTJunctionFreeFromRegion(const Region& original, int expectedCount = -1) { Region modified = Region::createTJunctionFreeRegion(original); verifyNoTJunctions(modified); if (expectedCount != -1) { EXPECT_EQ(modified.end() - modified.begin(), expectedCount); } EXPECT_TRUE((original ^ modified).isEmpty()); } }; TEST_F(RegionTest, MinimalDivision_TJunction) { Region r; // | x | // |xxx| r.clear(); r.orSelf(Rect(1, 0, 2, 1)); r.orSelf(Rect(0, 1, 3, 2)); checkTJunctionFreeFromRegion(r, 4); // | x | // | | // |xxx| r.clear(); r.orSelf(Rect(1, 0, 2, 1)); r.orSelf(Rect(0, 2, 3, 3)); checkTJunctionFreeFromRegion(r, 2); } TEST_F(RegionTest, Trivial_TJunction) { Region r; checkTJunctionFreeFromRegion(r); r.orSelf(Rect(100, 100, 500, 500)); checkTJunctionFreeFromRegion(r); } TEST_F(RegionTest, Simple_TJunction) { Region r; // | x | // |xxxx| // |xxxx| // |xxxx| r.clear(); r.orSelf(Rect(1, 0, 2, 1)); r.orSelf(Rect(0, 1, 3, 3)); checkTJunctionFreeFromRegion(r); // | x | // |xx | // |xxx| r.clear(); r.orSelf(Rect(2,0,4,2)); r.orSelf(Rect(0,2,4,4)); r.orSelf(Rect(0,4,6,6)); checkTJunctionFreeFromRegion(r); // |x x| // |xxx| // |x x| r.clear(); r.orSelf(Rect(0,0,2,6)); r.orSelf(Rect(4,0,6,6)); r.orSelf(Rect(0,2,6,4)); checkTJunctionFreeFromRegion(r); // |xxx| // | x | // | x | r.clear(); r.orSelf(Rect(0,0,6,2)); r.orSelf(Rect(2,2,4,6)); checkTJunctionFreeFromRegion(r); } TEST_F(RegionTest, Bigger_TJunction) { Region r; // |xxxx | // | xxxx | // | xxxx | // | xxxx| for (int i = 0; i < 4; i++) { r.orSelf(Rect(i,i,i+4,i+1)); } checkTJunctionFreeFromRegion(r, 16); } #define ITER_MAX 1000 #define X_MAX 8 #define Y_MAX 8 TEST_F(RegionTest, Random_TJunction) { Region r; srandom(12345); for (int iter = 0; iter < ITER_MAX; iter++) { r.clear(); for (int i = 0; i < X_MAX; i++) { for (int j = 0; j < Y_MAX; j++) { if (random() % 2) { r.orSelf(Rect(i, j, i + 1, j + 1)); } } } checkTJunctionFreeFromRegion(r); } } }; // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/Size_test.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000010026 13756501735 015005� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2019 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. */ #define LOG_TAG "SizeTest" #include #include #include #include namespace android { namespace ui { TEST(SizeTest, BasicConstructionAndEqualityComparison) { Size s(123, 456); EXPECT_EQ(123, s.width); EXPECT_EQ(123, s.getWidth()); EXPECT_EQ(456, s.height); EXPECT_EQ(456, s.getHeight()); EXPECT_EQ(Size(123, 456), s); EXPECT_NE(Size(456, 123), s); } TEST(SizeTest, BasicLessThanComparison) { EXPECT_TRUE(Size(0, 1) < Size(2, 3)); EXPECT_FALSE(Size(2, 3) < Size(0, 1)); EXPECT_TRUE(Size(0, 3) < Size(2, 1)); EXPECT_FALSE(Size(2, 1) < Size(0, 3)); EXPECT_TRUE(Size(0, 1) < Size(0, 3)); EXPECT_FALSE(Size(0, 3) < Size(0, 1)); EXPECT_FALSE(Size(1, 1) < Size(1, 1)); } TEST(SizeTest, ValidAndEmpty) { { Size s; EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { Size s(-1, -1); EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { Size s(1, -1000); EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { Size s(-1000, 1); EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { Size s(-1000, -1000); EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { const auto& s = Size::INVALID; EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { Size s(123, 456); s.makeInvalid(); EXPECT_FALSE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } { Size s(0, 0); EXPECT_TRUE(s.isValid()); EXPECT_TRUE(s.isEmpty()); } { const auto& s = Size::EMPTY; EXPECT_TRUE(s.isValid()); EXPECT_TRUE(s.isEmpty()); } { Size s(123, 456); s.clear(); EXPECT_TRUE(s.isValid()); EXPECT_TRUE(s.isEmpty()); } { Size s(123, 456); EXPECT_TRUE(s.isValid()); EXPECT_FALSE(s.isEmpty()); } } TEST(SizeTest, Set) { { Size s; s.setWidth(0); EXPECT_EQ(Size(0, -1), s); } { Size s; s.setHeight(0); EXPECT_EQ(Size(-1, 0), s); } { Size s; s.set(123, 456); EXPECT_EQ(Size(123, 456), s); } } template void ClampTest(T input, U expected) { // The constructor, set(), setWidth() and setHeight() all allow arbitrary // conversions from other numeric types, and implement clamping if necessary. EXPECT_EQ(Size(expected, expected), Size(input, input)); { Size s; s.set(input, input); EXPECT_EQ(Size(expected, expected), s); } { Size s; s.setWidth(input); EXPECT_EQ(expected, s.width); } { Size s; s.setHeight(input); EXPECT_EQ(expected, s.height); } } TEST(SizeTest, Int8RangeIsNotClamped) { ClampTest(std::numeric_limits::max(), std::numeric_limits::max()); ClampTest(int8_t(0), int8_t(0)); ClampTest(std::numeric_limits::lowest(), std::numeric_limits::lowest()); } TEST(SizeTest, FloatRangeIsClamped) { ClampTest(std::numeric_limits::max(), std::numeric_limits::max()); ClampTest(float(0), int32_t(0)); ClampTest(std::numeric_limits::lowest(), std::numeric_limits::lowest()); } } // namespace ui } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/colorspace_test.cpp�������������������������������������������������������������������0100644 0000000 0000000 00000013152 13756501735 016230� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 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. */ #define LOG_TAG "ColorSpaceTest" #include #include #include #include namespace android { class ColorSpaceTest : public testing::Test { protected: }; TEST_F(ColorSpaceTest, XYZ) { mat3 sRGBToXYZ(transpose(mat3{ 0.412391f, 0.357584f, 0.180481f, 0.212639f, 0.715169f, 0.072192f, 0.019331f, 0.119195f, 0.950532f })); mat3 XYZtoSRGB(inverse(sRGBToXYZ)); ColorSpace sRGB("sRGB", sRGBToXYZ); EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ()); EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB()); } TEST_F(ColorSpaceTest, XYZPrimaries) { mat3 sRGBToXYZ(transpose(mat3{ 0.412391f, 0.357584f, 0.180481f, 0.212639f, 0.715169f, 0.072192f, 0.019331f, 0.119195f, 0.950532f })); ColorSpace sRGB("sRGB", sRGBToXYZ); EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f); EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f); EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f); EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f); EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f); EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f); } TEST_F(ColorSpaceTest, XYZWhitePoint) { mat3 sRGBToXYZ(transpose(mat3{ 0.412391f, 0.357584f, 0.180481f, 0.212639f, 0.715169f, 0.072192f, 0.019331f, 0.119195f, 0.950532f })); ColorSpace sRGB("sRGB", sRGBToXYZ); EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f); EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f); } TEST_F(ColorSpaceTest, XYZFromPrimaries) { mat3 sRGBToXYZ(transpose(mat3{ 0.412391f, 0.357584f, 0.180481f, 0.212639f, 0.715169f, 0.072192f, 0.019331f, 0.119195f, 0.950532f })); ColorSpace sRGB1("sRGB", sRGBToXYZ); ColorSpace sRGB2( "sRGB", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f} ); for (size_t i = 0; i < 3; i++) { for (size_t j= 0; j < 3; j++) { ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f); } } for (size_t i = 0; i < 3; i++) { for (size_t j= 0; j < 3; j++) { ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f); } } } TEST_F(ColorSpaceTest, TransferFunctions) { ColorSpace sRGB = ColorSpace::sRGB(); EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f); EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f); EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f); EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f); for (float v = 0.0f; v <= 0.5f; v += 1e-3f) { ASSERT_TRUE(v >= sRGB.getEOTF()(v)); ASSERT_TRUE(v <= sRGB.getOETF()(v)); } float previousEOTF = std::numeric_limits::lowest(); float previousOETF = std::numeric_limits::lowest(); for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v)); previousEOTF = sRGB.getEOTF()(v); ASSERT_TRUE(previousOETF < sRGB.getOETF()(v)); previousOETF = sRGB.getOETF()(v); } ColorSpace sRGB2( "sRGB", {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}}, {0.3127f, 0.3290f} // linear transfer functions ); for (float v = 0.0f; v <= 1.0f; v += 1e-3f) { ASSERT_EQ(v, sRGB2.getEOTF()(v)); ASSERT_EQ(v, sRGB2.getOETF()(v)); } } TEST_F(ColorSpaceTest, Clamping) { // Pick a color outside of sRGB float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0})); // The color will be clamped float3 sRGB(ColorSpace::sRGB().xyzToRGB(c)); EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0}); // The color will not be clamped float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c)); EXPECT_TRUE(extendedSRGB.g > 1.0f); } TEST_F(ColorSpaceTest, Connect) { // No chromatic adaptation auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB()) .transform({1.0f, 0.5f, 0.0f}); EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); // Test with chromatic adaptation r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB()) .transform({1.0f, 0.0f, 0.0f}); EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f}))); } TEST_F(ColorSpaceTest, LUT) { auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB()); EXPECT_TRUE(lut != nullptr); // {1.0f, 0.5f, 0.0f} auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16]; EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f}))); // {1.0f, 1.0f, 0.5f} r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f}))); // {1.0f, 1.0f, 1.0f} r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f}))); } }; // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/mock/���������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013265� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/mock/MockGrallocAllocator.cpp���������������������������������������������������������0100644 0000000 0000000 00000001524 13756501735 020026� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2019 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. */ #include "MockGrallocAllocator.h" namespace android { namespace mock { MockGrallocAllocator::MockGrallocAllocator() = default; MockGrallocAllocator::~MockGrallocAllocator() = default; } // namespace mock } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/mock/MockGrallocAllocator.h�����������������������������������������������������������0100644 0000000 0000000 00000002474 13756501735 017500� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2019 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. */ #pragma once #include #include namespace android { class GraphicBuffer; namespace mock { class MockGrallocAllocator : public GrallocAllocator { public: MockGrallocAllocator(); ~MockGrallocAllocator() override; MOCK_METHOD(bool, isLoaded, (), (const, override)); MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override)); MOCK_METHOD(status_t, allocate, (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride, buffer_handle_t* outBufferHandles), (const, override)); }; } // namespace mock } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tools/��������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012332� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tools/Android.bp����������������������������������������������������������������������������0100644 0000000 0000000 00000001626 13756501735 014237� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// // Copyright (C) 2017 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. // cc_defaults { name: "libui_tools_default", clang_cflags: [ "-Wall", "-Wextra", "-Werror", ], } cc_binary { name: "lutgen", cppflags: [ "-Wall", "-Wextra", "-Werror", ], shared_libs: ["libui"], srcs: ["lutgen.cpp"], } ����������������������������������������������������������������������������������������������������������libs/ui/tools/lutgen.cpp����������������������������������������������������������������������������0100644 0000000 0000000 00000015370 13756501735 014337� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright 2017 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. */ #include #include #include #include #include #include #include using namespace android; using namespace std; uint32_t gSize = 32; ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3(); ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB(); string gNameSrc = "DisplayP3"; string gNameDst = "extendedSRGB"; static void printHelp() { cout << "lutgen -d SIZE -s SOURCE -t TARGET " << endl; cout << endl; cout << "Generate a 3D LUT to convert between two color spaces." << endl; cout << endl; cout << "If ends in .inc, data is generated without the array declaration." << endl; cout << endl; cout << "Options:" << endl; cout << " --help, -h" << endl; cout << " print this message" << endl; cout << " --dimension=, -d" << endl; cout << " the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl; cout << " --source=COLORSPACE, -s" << endl; cout << " the source color space, see below for available names. DisplayP3 by default" << endl; cout << " --target=COLORSPACE, -t" << endl; cout << " the target color space, see below for available names. extendedSRGB by default" << endl; cout << endl; cout << "Colorspace names:" << endl; cout << " sRGB" << endl; cout << " linearSRGB" << endl; cout << " extendedSRGB" << endl; cout << " linearExtendedSRGB" << endl; cout << " NTSC" << endl; cout << " BT709" << endl; cout << " BT2020" << endl; cout << " AdobeRGB" << endl; cout << " ProPhotoRGB" << endl; cout << " DisplayP3" << endl; cout << " DCIP3" << endl; cout << " ACES" << endl; cout << " ACEScg" << endl; } static const ColorSpace findColorSpace(const string& name) { if (name == "linearSRGB") return ColorSpace::linearSRGB(); if (name == "extendedSRGB") return ColorSpace::extendedSRGB(); if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB(); if (name == "NTSC") return ColorSpace::NTSC(); if (name == "BT709") return ColorSpace::BT709(); if (name == "BT2020") return ColorSpace::BT2020(); if (name == "AdobeRGB") return ColorSpace::AdobeRGB(); if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB(); if (name == "DisplayP3") return ColorSpace::DisplayP3(); if (name == "DCIP3") return ColorSpace::DCIP3(); if (name == "ACES") return ColorSpace::ACES(); if (name == "ACEScg") return ColorSpace::ACEScg(); return ColorSpace::sRGB(); } static int handleCommandLineArgments(int argc, char* argv[]) { static constexpr const char* OPTSTR = "h:d:s:t:"; static const struct option OPTIONS[] = { { "help", no_argument, nullptr, 'h' }, { "dimension", required_argument, nullptr, 'd' }, { "source", required_argument, nullptr, 's' }, { "target", required_argument, nullptr, 't' }, { nullptr, 0, nullptr, 0 } // termination of the option list }; int opt; int index = 0; while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) { string arg(optarg ? optarg : ""); switch (opt) { default: case 'h': printHelp(); exit(0); break; case 'd': gSize = max(2, min(stoi(arg), 256)); break; case 's': gNameSrc = arg; gColorSpaceSrc = findColorSpace(arg); break; case 't': gNameDst = arg; gColorSpaceDst = findColorSpace(arg); break; } } return optind; } int main(int argc, char* argv[]) { int optionIndex = handleCommandLineArgments(argc, argv); int numArgs = argc - optionIndex; if (numArgs < 1) { printHelp(); return 1; } bool isInclude = false; string filename(argv[optionIndex]); size_t index = filename.find_last_of('.'); if (index != string::npos) { string extension(filename.substr(index + 1)); isInclude = extension == "inc"; } ofstream outputStream(filename, ios::trunc); if (outputStream.good()) { auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst); auto data = lut.get(); outputStream << "// generated with lutgen " << filename.c_str() << endl; outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl; outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl; string src(gNameSrc); string dst(gNameDst); if (!isInclude) { transform(src.begin(), src.end(), src.begin(), ::toupper); transform(dst.begin(), dst.end(), dst.begin(), ::toupper); outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl; outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {"; } else { outputStream << "// From " << src << " to " << dst << endl; } for (size_t z = 0; z < gSize; z++) { for (size_t y = 0; y < gSize; y++) { for (size_t x = 0; x < gSize; x++) { if (x % 4 == 0) outputStream << endl << " "; half3 rgb = half3(*data++); const uint16_t r = rgb.r.getBits(); const uint16_t g = rgb.g.getBits(); const uint16_t b = rgb.b.getBits(); outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", "; outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", "; outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", "; } } } if (!isInclude) { outputStream << endl << "}; // end LUT" << endl; } outputStream << endl; outputStream.flush(); outputStream.close(); } else { cerr << "Could not write to file: " << filename << endl; return 1; } return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vibrator/��������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012405� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vibrator/Android.bp����������������������������������������������������������������������������0100644 0000000 0000000 00000002316 13756501735 014307� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2018 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. cc_library_shared { name: "libvibrator", shared_libs: [ "libbinder", "liblog", "libutils", ], header_libs: [ "libaudio_system_headers", ], aidl: { include_dirs: ["frameworks/base/core/java"], local_include_dirs: ["include/"], export_aidl_headers: true, }, srcs: [ ":libvibrator_aidl", "*.cpp", ], cflags: [ "-Wall", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-variable", "-Wno-unused-parameter", ], export_include_dirs: ["include"], } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vibrator/ExternalVibration.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000004601 13756501735 016547� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) 2018 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. */ #include #include #include #include void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) { out->writeInt32(attrs.usage); out->writeInt32(attrs.content_type); out->writeInt32(attrs.source); out->writeInt32(attrs.flags); } void readAudioAttributes(audio_attributes_t* attrs, const android::Parcel* in) { attrs->usage = static_cast(in->readInt32()); attrs->content_type = static_cast(in->readInt32()); attrs->source = static_cast(in->readInt32()); attrs->flags = static_cast(in->readInt32()); } namespace android { namespace os { ExternalVibration::ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs, sp controller) : mUid(uid), mPkg(pkg), mAttrs(attrs), mController(controller) { } status_t ExternalVibration::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mUid); parcel->writeString16(String16(mPkg.c_str())); writeAudioAttributes(mAttrs, parcel); parcel->writeStrongBinder(IInterface::asBinder(mController)); parcel->writeStrongBinder(mToken); return OK; } status_t ExternalVibration::readFromParcel(const Parcel* parcel) { mUid = parcel->readInt32(); String8 pkgStr8 = String8(parcel->readString16()); mPkg = pkgStr8.c_str(); readAudioAttributes(&mAttrs, parcel); mController = IExternalVibrationController::asInterface(parcel->readStrongBinder()); mToken = parcel->readStrongBinder(); return OK; } inline bool ExternalVibration::operator==(const ExternalVibration& rhs) const { return mToken == rhs.mToken; } } // namespace os } // namespace android �������������������������������������������������������������������������������������������������������������������������������libs/vibrator/include/������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014030� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vibrator/include/vibrator/���������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015660� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vibrator/include/vibrator/ExternalVibration.h��������������������������������������������������0100644 0000000 0000000 00000003647 13756501735 021500� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * 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. */ #ifndef ANDROID_EXTERNAL_VIBRATION_H #define ANDROID_EXTERNAL_VIBRATION_H #include #include #include #include #include #include namespace android { namespace os { class ExternalVibration : public Parcelable, public virtual RefBase { public : ExternalVibration() = default; ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs, sp controller); virtual ~ExternalVibration() = default; ExternalVibration(const ExternalVibration&) = default; bool operator==(const ExternalVibration&) const; status_t writeToParcel(Parcel* parcel) const override; status_t readFromParcel(const Parcel* parcel) override; int32_t getUid() const { return mUid; } std::string getPackage() const { return mPkg; } audio_attributes_t getAudioAttributes() const { return mAttrs; } sp getController() { return mController; } private: int32_t mUid; std::string mPkg; audio_attributes_t mAttrs; sp mController; sp mToken = new BBinder(); }; } // namespace android } // namespace os #endif // ANDROID_EXTERNAL_VIBRATION_H �����������������������������������������������������������������������������������������libs/vr/��������������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 011204� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/.clang-format�������������������������������������������������������������������������������0100644 0000000 0000000 00000000231 13756501735 013550� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������BasedOnStyle: Google DerivePointerAlignment: false PointerAlignment: Left AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/Android.bp����������������������������������������������������������������������������������0100644 0000000 0000000 00000000027 13756501735 013103� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������subdirs = [ "*", ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/CPPLINT.cfg���������������������������������������������������������������������������������0100644 0000000 0000000 00000000153 13756501735 012772� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������set noparent filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/OWNERS��������������������������������������������������������������������������������������0100644 0000000 0000000 00000000076 13756501735 012144� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������hendrikw@google.com jwcai@google.com steventhomas@google.com ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/���������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014515� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/Android.bp�����������������������������������������������������������������0100644 0000000 0000000 00000001103 13756501735 016410� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������cc_library_static { name: "libbroadcastring", clang: true, cflags: [ "-Wall", "-Wextra", "-Werror", ], export_include_dirs: ["include"], shared_libs: [ "libbase", ], export_shared_lib_headers: [ "libbase", ], } cc_test { name: "broadcast_ring_tests", clang: true, cflags: [ "-Wall", "-Wextra", "-Werror", ], srcs: [ "broadcast_ring_test.cc", ], static_libs: [ "libbroadcastring", ], shared_libs: [ "libbase", ], } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/broadcast_ring_test.cc�����������������������������������������������������0100644 0000000 0000000 00000071454 13756501735 021054� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "libbroadcastring/broadcast_ring.h" #include #include #include // NOLINT #include #include namespace android { namespace dvr { namespace { template struct alignas(8) Aligned { char v[N]; }; template struct alignas(8) Sized { Sized() { Clear(); } explicit Sized(char c) { Fill(c); } char v[sizeof(Aligned)]; void Clear() { memset(v, 0, sizeof(v)); } void Fill(char c) { memset(v, c, sizeof(v)); } static Sized Pattern(uint8_t c) { Sized sized; for (size_t i = 0; i < sizeof(v); ++i) { sized.v[i] = static_cast(c + i); } return sized; } bool operator==(const Sized& right) const { static_assert(sizeof(*this) == sizeof(v), "Size mismatch"); return !memcmp(v, right.v, sizeof(v)); } template SmallerSized Truncate() const { SmallerSized val; static_assert(sizeof(val.v) <= sizeof(v), "Cannot truncate to larger size"); memcpy(val.v, v, sizeof(val.v)); return val; } }; char FillChar(int val) { return static_cast(val); } struct FakeMmap { explicit FakeMmap(size_t size) : size(size), data(new char[size]) {} size_t size; std::unique_ptr data; void* mmap() { return static_cast(data.get()); } }; template FakeMmap CreateRing(Ring* ring, uint32_t count) { FakeMmap mmap(Ring::MemorySize(count)); *ring = Ring::Create(mmap.mmap(), mmap.size, count); return mmap; } template struct Traits { using Record = RecordType; static constexpr bool kUseStaticRecordSize = StaticSize; static constexpr uint32_t kStaticRecordCount = StaticCount; static constexpr uint32_t kMaxReservedRecords = MaxReserved; static constexpr uint32_t kMinAvailableRecords = MinAvailable; static constexpr uint32_t kMinRecordCount = MaxReserved + MinAvailable; }; template struct TraitsDynamic : public Traits { using Ring = BroadcastRing; static uint32_t MinCount() { return MaxReserved + MinAvailable; } }; template struct TraitsStatic : public Traits { using Ring = BroadcastRing; static uint32_t MinCount() { return StaticCount; } }; using Dynamic_8_NxM = TraitsDynamic>; using Dynamic_16_NxM = TraitsDynamic>; using Dynamic_32_NxM = TraitsDynamic>; using Dynamic_32_32xM = TraitsDynamic, true>; using Dynamic_16_NxM_1plus0 = TraitsDynamic, false, 1, 0>; using Dynamic_16_NxM_1plus1 = TraitsDynamic, false, 1, 1>; using Dynamic_16_NxM_5plus11 = TraitsDynamic, false, 5, 11>; using Dynamic_256_NxM_1plus0 = TraitsDynamic, false, 1, 0>; using Static_8_8x1 = TraitsStatic, 1>; using Static_8_8x16 = TraitsStatic, 16>; using Static_16_16x8 = TraitsStatic, 8>; using Static_16_16x16 = TraitsStatic, 16>; using Static_16_16x32 = TraitsStatic, 32>; using Static_32_Nx8 = TraitsStatic, 8, false>; using TraitsList = ::testing::Types; } // namespace template class BroadcastRingTest : public ::testing::Test {}; TYPED_TEST_CASE(BroadcastRingTest, TraitsList); TYPED_TEST(BroadcastRingTest, Geometry) { using Record = typename TypeParam::Record; using Ring = typename TypeParam::Ring; Ring ring; auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); EXPECT_EQ(Ring::Traits::MinCount(), ring.record_count()); EXPECT_EQ(sizeof(Record), ring.record_size()); } TYPED_TEST(BroadcastRingTest, PutGet) { using Record = typename TypeParam::Record; using Ring = typename TypeParam::Ring; Ring ring; auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); const uint32_t oldest_sequence_at_start = ring.GetOldestSequence(); const uint32_t next_sequence_at_start = ring.GetNextSequence(); { uint32_t sequence = oldest_sequence_at_start; Record record; EXPECT_FALSE(ring.Get(&sequence, &record)); EXPECT_EQ(oldest_sequence_at_start, sequence); EXPECT_EQ(Record(), record); } const Record original_record(0x1a); ring.Put(original_record); { uint32_t sequence = next_sequence_at_start; Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(next_sequence_at_start, sequence); EXPECT_EQ(original_record, record); } { uint32_t sequence = next_sequence_at_start + 1; Record record; EXPECT_FALSE(ring.Get(&sequence, &record)); EXPECT_EQ(next_sequence_at_start + 1, sequence); EXPECT_EQ(Record(), record); } } TYPED_TEST(BroadcastRingTest, FillOnce) { using Record = typename TypeParam::Record; using Ring = typename TypeParam::Ring; Ring ring; auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); const uint32_t next_sequence_at_start = ring.GetNextSequence(); for (uint32_t i = 0; i < ring.record_count(); ++i) ring.Put(Record(FillChar(i))); for (uint32_t i = 0; i < ring.record_count(); ++i) { const uint32_t expected_sequence = next_sequence_at_start + i; const Record expected_record(FillChar(i)); { uint32_t sequence = ring.GetOldestSequence() + i; Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(expected_sequence, sequence); EXPECT_EQ(expected_record, record); } } { uint32_t sequence = ring.GetOldestSequence() + ring.record_count(); Record record; EXPECT_FALSE(ring.Get(&sequence, &record)); } } TYPED_TEST(BroadcastRingTest, FillTwice) { using Record = typename TypeParam::Record; using Ring = typename TypeParam::Ring; Ring ring; auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); const uint32_t next_sequence_at_start = ring.GetNextSequence(); for (uint32_t i = 0; i < 2 * ring.record_count(); ++i) { const Record newest_record(FillChar(i)); ring.Put(newest_record); const uint32_t newest_sequence = next_sequence_at_start + i; const uint32_t records_available = std::min(i + 1, ring.record_count()); const uint32_t oldest_sequence = newest_sequence - records_available + 1; EXPECT_EQ(newest_sequence, ring.GetNewestSequence()); EXPECT_EQ(oldest_sequence, ring.GetOldestSequence()); EXPECT_EQ(newest_sequence + 1, ring.GetNextSequence()); for (uint32_t j = 0; j < records_available; ++j) { const uint32_t sequence_jth_newest = newest_sequence - j; const Record record_jth_newest(FillChar(i - j)); { uint32_t sequence = sequence_jth_newest; Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(sequence_jth_newest, sequence); EXPECT_EQ(record_jth_newest, record); } { uint32_t sequence = sequence_jth_newest; Record record; EXPECT_TRUE(ring.GetNewest(&sequence, &record)); EXPECT_EQ(newest_sequence, sequence); EXPECT_EQ(newest_record, record); } } const Record oldest_record( FillChar(i + (oldest_sequence - newest_sequence))); const uint32_t sequence_0th_overwritten = oldest_sequence - 1; const uint32_t sequence_0th_future = newest_sequence + 1; const uint32_t sequence_1st_future = newest_sequence + 2; { uint32_t sequence = sequence_0th_overwritten; Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(oldest_sequence, sequence); EXPECT_EQ(oldest_record, record); } { uint32_t sequence = sequence_0th_overwritten; Record record; EXPECT_TRUE(ring.GetNewest(&sequence, &record)); EXPECT_EQ(newest_sequence, sequence); EXPECT_EQ(newest_record, record); } { uint32_t sequence = sequence_0th_future; Record record; EXPECT_FALSE(ring.Get(&sequence, &record)); EXPECT_EQ(sequence_0th_future, sequence); EXPECT_EQ(Record(), record); } { uint32_t sequence = sequence_0th_future; Record record; EXPECT_FALSE(ring.GetNewest(&sequence, &record)); EXPECT_EQ(sequence_0th_future, sequence); EXPECT_EQ(Record(), record); } { uint32_t sequence = sequence_1st_future; Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(oldest_sequence, sequence); EXPECT_EQ(oldest_record, record); } { uint32_t sequence = sequence_1st_future; Record record; EXPECT_TRUE(ring.GetNewest(&sequence, &record)); EXPECT_EQ(newest_sequence, sequence); EXPECT_EQ(newest_record, record); } } } TYPED_TEST(BroadcastRingTest, Import) { using Record = typename TypeParam::Record; using Ring = typename TypeParam::Ring; Ring ring; auto mmap = CreateRing(&ring, Ring::Traits::MinCount()); const uint32_t sequence_0 = ring.GetNextSequence(); const uint32_t sequence_1 = ring.GetNextSequence() + 1; const Record record_0 = Record::Pattern(0x00); const Record record_1 = Record::Pattern(0x80); ring.Put(record_0); ring.Put(record_1); { Ring imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = Ring::Import(mmap.mmap(), mmap.size); EXPECT_TRUE(import_ok); EXPECT_EQ(ring.record_size(), imported_ring.record_size()); EXPECT_EQ(ring.record_count(), imported_ring.record_count()); if (ring.record_count() != 1) { uint32_t sequence = sequence_0; Record imported_record; EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); EXPECT_EQ(sequence_0, sequence); EXPECT_EQ(record_0, imported_record); } { uint32_t sequence = sequence_1; Record imported_record; EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); EXPECT_EQ(sequence_1, sequence); EXPECT_EQ(record_1, imported_record); } } } TEST(BroadcastRingTest, ShouldFailImportIfStaticSizeMismatch) { using OriginalRing = typename Static_16_16x16::Ring; using RecordSizeMismatchRing = typename Static_8_8x16::Ring; using RecordCountMismatchRing = typename Static_16_16x8::Ring; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); { using ImportedRing = RecordSizeMismatchRing; ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_FALSE(import_ok); auto mmap_imported = CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); EXPECT_NE(original_ring.record_size(), imported_ring.record_size()); EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); } { using ImportedRing = RecordCountMismatchRing; ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_FALSE(import_ok); auto mmap_imported = CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); EXPECT_NE(original_ring.record_count(), imported_ring.record_count()); } } TEST(BroadcastRingTest, ShouldFailImportIfDynamicSizeGrows) { using OriginalRing = typename Dynamic_8_NxM::Ring; using RecordSizeGrowsRing = typename Dynamic_16_NxM::Ring; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); { using ImportedRing = RecordSizeGrowsRing; ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_FALSE(import_ok); auto mmap_imported = CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); EXPECT_LT(original_ring.record_size(), imported_ring.record_size()); EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); } } TEST(BroadcastRingTest, ShouldFailImportIfCountTooSmall) { using OriginalRing = typename Dynamic_16_NxM_1plus0::Ring; using MinCountRing = typename Dynamic_16_NxM_1plus1::Ring; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); { using ImportedRing = MinCountRing; ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_FALSE(import_ok); auto mmap_imported = CreateRing(&imported_ring, ImportedRing::Traits::MinCount()); EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); EXPECT_LT(original_ring.record_count(), imported_ring.record_count()); } } TEST(BroadcastRingTest, ShouldFailImportIfMmapTooSmall) { using OriginalRing = typename Dynamic_16_NxM::Ring; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); { using ImportedRing = OriginalRing; ImportedRing imported_ring; bool import_ok; const size_t kMinSize = ImportedRing::MemorySize(original_ring.record_count()); std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), 0); EXPECT_FALSE(import_ok); std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), kMinSize - 1); EXPECT_FALSE(import_ok); std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), kMinSize); EXPECT_TRUE(import_ok); EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); } } TEST(BroadcastRingTest, ShouldImportIfDynamicSizeShrinks) { using OriginalRing = typename Dynamic_16_NxM::Ring; using RecordSizeShrinksRing = typename Dynamic_8_NxM::Ring; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount()); using OriginalRecord = typename OriginalRing::Record; const uint32_t original_sequence_0 = original_ring.GetNextSequence(); const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1; const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00); const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80); original_ring.Put(original_record_0); original_ring.Put(original_record_1); { using ImportedRing = RecordSizeShrinksRing; using ImportedRecord = typename ImportedRing::Record; ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_TRUE(import_ok); EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); EXPECT_GT(sizeof(OriginalRecord), sizeof(ImportedRecord)); { uint32_t sequence = original_sequence_0; ImportedRecord shrunk_record; EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record)); EXPECT_EQ(original_sequence_0, sequence); EXPECT_EQ(original_record_0.Truncate(), shrunk_record); } { uint32_t sequence = original_sequence_1; ImportedRecord shrunk_record; EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record)); EXPECT_EQ(original_sequence_1, sequence); EXPECT_EQ(original_record_1.Truncate(), shrunk_record); } } } TEST(BroadcastRingTest, ShouldImportIfCompatibleDynamicToStatic) { using OriginalRing = typename Dynamic_16_NxM::Ring; using ImportedRing = typename Static_16_16x16::Ring; using OriginalRecord = typename OriginalRing::Record; using ImportedRecord = typename ImportedRing::Record; using StaticRing = ImportedRing; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount()); const uint32_t original_sequence_0 = original_ring.GetNextSequence(); const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1; const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00); const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80); original_ring.Put(original_record_0); original_ring.Put(original_record_1); { ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_TRUE(import_ok); EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); { uint32_t sequence = original_sequence_0; ImportedRecord imported_record; EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); EXPECT_EQ(original_sequence_0, sequence); EXPECT_EQ(original_record_0, imported_record); } { uint32_t sequence = original_sequence_1; ImportedRecord imported_record; EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); EXPECT_EQ(original_sequence_1, sequence); EXPECT_EQ(original_record_1, imported_record); } } } TEST(BroadcastRingTest, ShouldImportIfCompatibleStaticToDynamic) { using OriginalRing = typename Static_16_16x16::Ring; using ImportedRing = typename Dynamic_16_NxM::Ring; using OriginalRecord = typename OriginalRing::Record; using ImportedRecord = typename ImportedRing::Record; using StaticRing = OriginalRing; OriginalRing original_ring; auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount()); const uint32_t original_sequence_0 = original_ring.GetNextSequence(); const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1; const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00); const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80); original_ring.Put(original_record_0); original_ring.Put(original_record_1); { ImportedRing imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), mmap.size); EXPECT_TRUE(import_ok); EXPECT_EQ(original_ring.record_size(), imported_ring.record_size()); EXPECT_EQ(original_ring.record_count(), imported_ring.record_count()); { uint32_t sequence = original_sequence_0; ImportedRecord imported_record; EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); EXPECT_EQ(original_sequence_0, sequence); EXPECT_EQ(original_record_0, imported_record); } { uint32_t sequence = original_sequence_1; ImportedRecord imported_record; EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record)); EXPECT_EQ(original_sequence_1, sequence); EXPECT_EQ(original_record_1, imported_record); } } } TEST(BroadcastRingTest, ShouldImportIfReadonlyMmap) { using Ring = Dynamic_32_NxM::Ring; using Record = Ring::Record; uint32_t record_count = Ring::Traits::MinCount(); size_t ring_size = Ring::MemorySize(record_count); size_t page_size = sysconf(_SC_PAGESIZE); size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); ASSERT_GE(mmap_size, ring_size); void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, mmap_base); Ring ring = Ring::Create(mmap_base, mmap_size, record_count); for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i))); ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ)); { Ring imported_ring; bool import_ok; std::tie(imported_ring, import_ok) = Ring::Import(mmap_base, mmap_size); EXPECT_TRUE(import_ok); EXPECT_EQ(ring.record_size(), imported_ring.record_size()); EXPECT_EQ(ring.record_count(), imported_ring.record_count()); uint32_t oldest_sequence = imported_ring.GetOldestSequence(); for (uint32_t i = 0; i < record_count; ++i) { uint32_t sequence = oldest_sequence + i; Record record; EXPECT_TRUE(imported_ring.Get(&sequence, &record)); EXPECT_EQ(Record(FillChar(i)), record); } } ASSERT_EQ(0, munmap(mmap_base, mmap_size)); } TEST(BroadcastRingTest, ShouldDieIfPutReadonlyMmap) { using Ring = Dynamic_32_NxM::Ring; using Record = Ring::Record; uint32_t record_count = Ring::Traits::MinCount(); size_t ring_size = Ring::MemorySize(record_count); size_t page_size = sysconf(_SC_PAGESIZE); size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); ASSERT_GE(mmap_size, ring_size); void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, mmap_base); Ring ring = Ring::Create(mmap_base, mmap_size, record_count); for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i))); ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ)); EXPECT_DEATH_IF_SUPPORTED({ ring.Put(Record(7)); }, ""); ASSERT_EQ(0, munmap(mmap_base, mmap_size)); } TEST(BroadcastRingTest, ShouldDieIfCreationMmapTooSmall) { using Ring = Dynamic_32_NxM::Ring; using Record = Ring::Record; uint32_t record_count = Ring::Traits::MinCount(); size_t ring_size = Ring::MemorySize(record_count); FakeMmap mmap(ring_size); EXPECT_DEATH_IF_SUPPORTED({ Ring ring = Ring::Create(mmap.mmap(), ring_size - 1, record_count); }, ""); Ring ring = Ring::Create(mmap.mmap(), ring_size, record_count); ring.Put(Record(3)); { uint32_t sequence = ring.GetNewestSequence(); Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(Record(3), record); } } TEST(BroadcastRingTest, ShouldDieIfCreationMmapMisaligned) { using Ring = Static_8_8x1::Ring; using Record = Ring::Record; constexpr int kAlign = Ring::mmap_alignment(); constexpr int kMisalign = kAlign / 2; size_t ring_size = Ring::MemorySize(); std::unique_ptr buf(new char[ring_size + kMisalign]); EXPECT_DEATH_IF_SUPPORTED( { Ring ring = Ring::Create(buf.get() + kMisalign, ring_size); }, ""); Ring ring = Ring::Create(buf.get(), ring_size); ring.Put(Record(3)); { uint32_t sequence = ring.GetNewestSequence(); Record record; EXPECT_TRUE(ring.Get(&sequence, &record)); EXPECT_EQ(Record(3), record); } } template std::unique_ptr CopyTask(std::atomic* quit, void* in_base, size_t in_size, void* out_base, size_t out_size) { return std::unique_ptr( new std::thread([quit, in_base, in_size, out_base, out_size]() { using Record = typename Ring::Record; bool import_ok; Ring in_ring; Ring out_ring; std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size); ASSERT_TRUE(import_ok); std::tie(out_ring, import_ok) = Ring::Import(out_base, out_size); ASSERT_TRUE(import_ok); uint32_t sequence = in_ring.GetOldestSequence(); while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) { Record record; if (in_ring.Get(&sequence, &record)) { out_ring.Put(record); sequence++; } } })); } TEST(BroadcastRingTest, ThreadedCopySingle) { using Ring = Dynamic_32_NxM::Ring; using Record = Ring::Record; Ring in_ring; auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount()); Ring out_ring; auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount()); std::atomic quit(false); std::unique_ptr copy_task = CopyTask( &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size); const Record out_record(0x1c); out_ring.Put(out_record); uint32_t in_sequence = in_ring.GetOldestSequence(); Record in_record; while (!in_ring.Get(&in_sequence, &in_record)) { // Do nothing. } EXPECT_EQ(out_record, in_record); std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); copy_task->join(); } TEST(BroadcastRingTest, ThreadedCopyLossless) { using Ring = Dynamic_32_NxM::Ring; using Record = Ring::Record; Ring in_ring; auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount()); Ring out_ring; auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount()); std::atomic quit(false); std::unique_ptr copy_task = CopyTask( &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size); constexpr uint32_t kRecordsToProcess = 10000; uint32_t out_records = 0; uint32_t in_records = 0; uint32_t in_sequence = in_ring.GetNextSequence(); while (out_records < kRecordsToProcess || in_records < kRecordsToProcess) { if (out_records < kRecordsToProcess && out_records - in_records < out_ring.record_count()) { const Record out_record(FillChar(out_records)); out_ring.Put(out_record); out_records++; } Record in_record; while (in_ring.Get(&in_sequence, &in_record)) { EXPECT_EQ(Record(FillChar(in_records)), in_record); in_records++; in_sequence++; } } EXPECT_EQ(kRecordsToProcess, out_records); EXPECT_EQ(kRecordsToProcess, in_records); std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); copy_task->join(); } TEST(BroadcastRingTest, ThreadedCopyLossy) { using Ring = Dynamic_32_NxM::Ring; using Record = Ring::Record; Ring in_ring; auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount()); Ring out_ring; auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount()); std::atomic quit(false); std::unique_ptr copy_task = CopyTask( &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size); constexpr uint32_t kRecordsToProcess = 100000; uint32_t out_records = 0; uint32_t in_records = 0; uint32_t in_sequence = in_ring.GetNextSequence(); while (out_records < kRecordsToProcess) { const Record out_record(FillChar(out_records)); out_ring.Put(out_record); out_records++; Record in_record; if (in_ring.GetNewest(&in_sequence, &in_record)) { EXPECT_EQ(Record(in_record.v[0]), in_record); in_records++; in_sequence++; } } EXPECT_EQ(kRecordsToProcess, out_records); EXPECT_GE(kRecordsToProcess, in_records); std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); copy_task->join(); } template std::unique_ptr CheckFillTask(std::atomic* quit, void* in_base, size_t in_size) { return std::unique_ptr( new std::thread([quit, in_base, in_size]() { using Record = typename Ring::Record; bool import_ok; Ring in_ring; std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size); ASSERT_TRUE(import_ok); uint32_t sequence = in_ring.GetOldestSequence(); while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) { Record record; if (in_ring.Get(&sequence, &record)) { ASSERT_EQ(Record(record.v[0]), record); sequence++; } } })); } template void ThreadedOverwriteTorture() { using Record = typename Ring::Record; // Maximize overwrites by having few records. const int kMinRecordCount = 1; const int kMaxRecordCount = 4; for (int count = kMinRecordCount; count <= kMaxRecordCount; count *= 2) { Ring out_ring; auto out_mmap = CreateRing(&out_ring, count); std::atomic quit(false); std::unique_ptr check_task = CheckFillTask(&quit, out_mmap.mmap(), out_mmap.size); constexpr int kIterations = 10000; for (int i = 0; i < kIterations; ++i) { const Record record(FillChar(i)); out_ring.Put(record); } std::atomic_store_explicit(&quit, true, std::memory_order_relaxed); check_task->join(); } } TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) { ThreadedOverwriteTorture(); } TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) { ThreadedOverwriteTorture(); } } // namespace dvr } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/include/�������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016140� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/include/libbroadcastring/��������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 021451� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h����������������������������������0100644 0000000 0000000 00000062620 13756501735 024606� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BROADCAST_RING_H_ #define ANDROID_DVR_BROADCAST_RING_H_ #include #include #include #include #include #include #include #include #include "android-base/logging.h" #if ATOMIC_LONG_LOCK_FREE != 2 || ATOMIC_INT_LOCK_FREE != 2 #error "This file requires lock free atomic uint32_t and long" #endif namespace android { namespace dvr { struct DefaultRingTraits { // Set this to false to allow compatibly expanding the record size. static constexpr bool kUseStaticRecordSize = false; // Set this to a nonzero value to fix the number of records in the ring. static constexpr uint32_t kStaticRecordCount = 0; // Set this to the max number of records that can be written simultaneously. static constexpr uint32_t kMaxReservedRecords = 1; // Set this to the min number of records that must be readable. static constexpr uint32_t kMinAvailableRecords = 1; }; // Nonblocking ring suitable for concurrent single-writer, multi-reader access. // // Readers never block the writer and thus this is a nondeterministically lossy // transport in the absence of external synchronization. Don't use this as a // transport when deterministic behavior is required. // // Readers may have a read-only mapping; each reader's state is a single local // sequence number. // // The implementation takes care to avoid data races on record access. // Inconsistent data can only be returned if at least 2^32 records are written // during the read-side critical section. // // In addition, both readers and the writer are careful to avoid accesses // outside the bounds of the mmap area passed in during initialization even if // there is a misbehaving or malicious task with write access to the mmap area. // // When dynamic record size is enabled, readers use the record size in the ring // header when indexing the ring, so that it is possible to extend the record // type without breaking the read-side ABI. // // Avoid calling Put() in a tight loop; there should be significantly more time // between successive puts than it takes to read one record from memory to // ensure Get() completes quickly. This requirement should not be difficult to // achieve for most practical uses; 4kB puts at 10,000Hz is well below the // scaling limit on current mobile chips. // // Example Writer Usage: // // using Record = MyRecordType; // using Ring = BroadcastRing; // // uint32_t record_count = kMyDesiredCount; // uint32_t ring_size = Ring::MemorySize(record_count); // // size_t page_size = sysconf(_SC_PAGESIZE); // uint32_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); // // // Allocate & map via your preferred mechanism, e.g. // int fd = open("/dev/shm/ring_test", O_CREAT|O_RDWR|O_CLOEXEC, 0600); // CHECK(fd >= 0); // CHECK(!ftruncate(fd, ring_size)); // void *mmap_base = mmap(nullptr, mmap_size, PROT_READ|PROT_WRITE, // MAP_SHARED, fd, 0); // CHECK(mmap_base != MAP_FAILED); // close(fd); // // Ring ring = Ring::Create(mmap_base, mmap_size, record_count); // // while (!done) // ring.Put(BuildNextRecordBlocking()); // // CHECK(!munmap(mmap_base, mmap_size)); // // Example Reader Usage: // // using Record = MyRecordType; // using Ring = BroadcastRing; // // // Map via your preferred mechanism, e.g. // int fd = open("/dev/shm/ring_test", O_RDONLY|O_CLOEXEC); // CHECK(fd >= 0); // struct stat st; // CHECK(!fstat(fd, &st)); // size_t mmap_size = st.st_size; // void *mmap_base = mmap(nullptr, mmap_size, PROT_READ, // MAP_SHARED, fd, 0); // CHECK(mmap_base != MAP_FAILED); // close(fd); // // Ring ring; // bool import_ok; // std::tie(ring, import_ok) = Ring::Import(mmap_base, mmap_size); // CHECK(import_ok); // // uint32_t sequence; // // // Choose starting point (using "0" is unpredictable but not dangerous) // sequence = ring.GetOldestSequence(); // The oldest available // sequence = ring.GetNewestSequence(); // The newest available // sequence = ring.GetNextSequence(); // The next one produced // // while (!done) { // Record record; // // if (you_want_to_process_all_available_records) { // while (ring.Get(&sequence, &record)) { // ProcessRecord(sequence, record); // sequence++; // } // } else if (you_want_to_skip_to_the_newest_record) { // if (ring.GetNewest(&sequence, &record)) { // ProcessRecord(sequence, record); // sequence++; // } // } // // DoSomethingExpensiveOrBlocking(); // } // // CHECK(!munmap(mmap_base, mmap_size)); // template class BroadcastRing { public: using Record = RecordType; struct Traits : public BaseTraits { // Must have enough space for writers, plus enough space for readers. static constexpr int kMinRecordCount = BaseTraits::kMaxReservedRecords + BaseTraits::kMinAvailableRecords; // Count of zero means dynamic, non-zero means static. static constexpr bool kUseStaticRecordCount = (BaseTraits::kStaticRecordCount != 0); // If both record size and count are static then the overall size is too. static constexpr bool kIsStaticSize = BaseTraits::kUseStaticRecordSize && kUseStaticRecordCount; }; static constexpr bool IsPowerOfTwo(uint32_t size) { return (size & (size - 1)) == 0; } // Sanity check the options provided in Traits. static_assert(Traits::kMinRecordCount >= 1, "Min record count too small"); static_assert(!Traits::kUseStaticRecordCount || Traits::kStaticRecordCount >= Traits::kMinRecordCount, "Static record count is too small"); static_assert(!Traits::kStaticRecordCount || IsPowerOfTwo(Traits::kStaticRecordCount), "Static record count is not a power of two"); static_assert(std::is_standard_layout::value, "Record type must be standard layout"); BroadcastRing() {} // Creates a new ring at |mmap| with |record_count| records. // // There must be at least |MemorySize(record_count)| bytes of space already // allocated at |mmap|. The ring does not take ownership. static BroadcastRing Create(void* mmap, size_t mmap_size, uint32_t record_count) { BroadcastRing ring(mmap); CHECK(ring.ValidateGeometry(mmap_size, sizeof(Record), record_count)); ring.InitializeHeader(sizeof(Record), record_count); return ring; } // Creates a new ring at |mmap|. // // There must be at least |MemorySize()| bytes of space already allocated at // |mmap|. The ring does not take ownership. static BroadcastRing Create(void* mmap, size_t mmap_size) { return Create(mmap, mmap_size, Traits::kUseStaticRecordCount ? Traits::kStaticRecordCount : BroadcastRing::GetRecordCount(mmap_size)); } // Imports an existing ring at |mmap|. // // Import may fail if the ring parameters in the mmap header are not sensible. // In this case the returned boolean is false; make sure to check this value. static std::tuple Import(void* mmap, size_t mmap_size) { BroadcastRing ring(mmap); uint32_t record_size = 0; uint32_t record_count = 0; if (mmap_size >= sizeof(Header)) { record_size = std::atomic_load_explicit(&ring.header_mmap()->record_size, std::memory_order_relaxed); record_count = std::atomic_load_explicit( &ring.header_mmap()->record_count, std::memory_order_relaxed); } bool ok = ring.ValidateGeometry(mmap_size, record_size, record_count); return std::make_tuple(ring, ok); } ~BroadcastRing() {} // Calculates the space necessary for a ring of size |record_count|. // // Use this function for dynamically sized rings. static constexpr size_t MemorySize(uint32_t record_count) { return sizeof(Header) + sizeof(Record) * record_count; } // Calculates the space necessary for a statically sized ring. // // Use this function for statically sized rings. static constexpr size_t MemorySize() { static_assert( Traits::kUseStaticRecordCount, "Wrong MemorySize() function called for dynamic record count"); return MemorySize(Traits::kStaticRecordCount); } static uint32_t NextPowerOf2(uint32_t n) { if (n == 0) return 0; n -= 1; n |= n >> 16; n |= n >> 8; n |= n >> 4; n |= n >> 2; n |= n >> 1; return n + 1; } // Gets the biggest power of 2 record count that can fit into this mmap. // // The header size has been taken into account. static uint32_t GetRecordCount(size_t mmap_size) { if (mmap_size <= sizeof(Header)) { return 0; } uint32_t count = static_cast((mmap_size - sizeof(Header)) / sizeof(Record)); return IsPowerOfTwo(count) ? count : (NextPowerOf2(count) / 2); } // Writes a record to the ring. // // The oldest record is overwritten unless the ring is not already full. void Put(const Record& record) { const int kRecordCount = 1; Reserve(kRecordCount); Geometry geometry = GetGeometry(); PutRecordInternal(&record, record_mmap_writer(geometry.tail_index)); Publish(kRecordCount); } // Gets sequence number of the oldest currently available record. uint32_t GetOldestSequence() const { return std::atomic_load_explicit(&header_mmap()->head, std::memory_order_relaxed); } // Gets sequence number of the first future record. // // If the returned value is passed to Get() and there is no concurrent Put(), // Get() will return false. uint32_t GetNextSequence() const { return std::atomic_load_explicit(&header_mmap()->tail, std::memory_order_relaxed); } // Gets sequence number of the newest currently available record. uint32_t GetNewestSequence() const { return GetNextSequence() - 1; } // Copies the oldest available record with sequence at least |*sequence| to // |record|. // // Returns false if there is no recent enough record available. // // Updates |*sequence| with the sequence number of the record returned. To get // the following record, increment this number by one. // // This function synchronizes with two other operations: // // (1) Load-Acquire of |tail| // // Together with the store-release in Publish(), this load-acquire // ensures each store to a record in PutRecordInternal() happens-before // any corresponding load in GetRecordInternal(). // // i.e. the stores for the records with sequence numbers < |tail| have // completed from our perspective // // (2) Acquire Fence between record access & final load of |head| // // Together with the release fence in Reserve(), this ensures that if // GetRecordInternal() loads a value stored in some execution of // PutRecordInternal(), then the store of |head| in the Reserve() that // preceeded it happens-before our final load of |head|. // // i.e. if we read a record with sequence number >= |final_head| then // no later store to that record has completed from our perspective bool Get(uint32_t* sequence /*inout*/, Record* record /*out*/) const { for (;;) { uint32_t tail = std::atomic_load_explicit(&header_mmap()->tail, std::memory_order_acquire); uint32_t head = std::atomic_load_explicit(&header_mmap()->head, std::memory_order_relaxed); if (tail - head > record_count()) continue; // Concurrent modification; re-try. if (*sequence - head > tail - head) *sequence = head; // Out of window, skip forward to first available. if (*sequence == tail) return false; // No new records available. Geometry geometry = CalculateGeometry(record_count(), record_size(), *sequence, tail); // Compute address explicitly in case record_size > sizeof(Record). RecordStorage* record_storage = record_mmap_reader(geometry.head_index); GetRecordInternal(record_storage, record); // NB: It is not sufficient to change this to a load-acquire of |head|. std::atomic_thread_fence(std::memory_order_acquire); uint32_t final_head = std::atomic_load_explicit( &header_mmap()->head, std::memory_order_relaxed); if (final_head - head > *sequence - head) continue; // Concurrent modification; re-try. // Note: Combining the above 4 comparisons gives: // 0 <= final_head - head <= sequence - head < tail - head <= record_count // // We can also write this as: // head <=* final_head <=* sequence <* tail <=* head + record_count // // where <* orders by difference from head: x <* y if x - head < y - head. // This agrees with the order of sequence updates during "put" operations. return true; } } // Copies the newest available record with sequence at least |*sequence| to // |record|. // // Returns false if there is no recent enough record available. // // Updates |*sequence| with the sequence number of the record returned. To get // the following record, increment this number by one. bool GetNewest(uint32_t* sequence, Record* record) const { uint32_t newest_sequence = GetNewestSequence(); if (*sequence == newest_sequence + 1) return false; *sequence = newest_sequence; return Get(sequence, record); } // Returns true if this instance has been created or imported. bool is_valid() const { return !!data_.mmap; } uint32_t record_count() const { return record_count_internal(); } uint32_t record_size() const { return record_size_internal(); } static constexpr uint32_t mmap_alignment() { return alignof(Mmap); } private: struct Header { // Record size for reading out of the ring. Writers always write the full // length; readers may need to read a prefix of each record. std::atomic record_size; // Number of records in the ring. std::atomic record_count; // Readable region is [head % record_count, tail % record_count). // // The region in [tail % record_count, head % record_count) was either never // populated or is being updated. // // These are sequences numbers, not indexes - indexes should be computed // with a modulus. // // To ensure consistency: // // (1) Writes advance |head| past any updated records before writing to // them, and advance |tail| after they are written. // (2) Readers check |tail| before reading data and |head| after, // making sure to discard any data that was written to concurrently. std::atomic head; std::atomic tail; }; // Store using the standard word size. using StorageType = long; // NOLINT // Always require 8 byte alignment so that the same record sizes are legal on // 32 and 64 bit builds. static constexpr size_t kRecordAlignment = 8; static_assert(kRecordAlignment % sizeof(StorageType) == 0, "Bad record alignment"); struct RecordStorage { // This is accessed with relaxed atomics to prevent data races on the // contained data, which would be undefined behavior. std::atomic data[sizeof(Record) / sizeof(StorageType)]; }; static_assert(sizeof(StorageType) * std::extent() == sizeof(Record), "Record length must be a multiple of sizeof(StorageType)"); struct Geometry { // Static geometry. uint32_t record_count; uint32_t record_size; // Copy of atomic sequence counts. uint32_t head; uint32_t tail; // First index of readable region. uint32_t head_index; // First index of writable region. uint32_t tail_index; // Number of records in readable region. uint32_t count; // Number of records in writable region. uint32_t space; }; // Mmap area layout. // // Readers should not index directly into |records| as this is not valid when // dynamic record sizes are used; use record_mmap_reader() instead. struct Mmap { Header header; RecordStorage records[]; }; static_assert(std::is_standard_layout::value, "Mmap must be standard layout"); static_assert(sizeof(std::atomic) == sizeof(uint32_t), "Lockless atomics contain extra state"); static_assert(sizeof(std::atomic) == sizeof(StorageType), "Lockless atomics contain extra state"); explicit BroadcastRing(void* mmap) { CHECK_EQ(0U, reinterpret_cast(mmap) % alignof(Mmap)); data_.mmap = reinterpret_cast(mmap); } // Initializes the mmap area header for a new ring. void InitializeHeader(uint32_t record_size, uint32_t record_count) { constexpr uint32_t kInitialSequence = -256; // Force an early wrap. std::atomic_store_explicit(&header_mmap()->record_size, record_size, std::memory_order_relaxed); std::atomic_store_explicit(&header_mmap()->record_count, record_count, std::memory_order_relaxed); std::atomic_store_explicit(&header_mmap()->head, kInitialSequence, std::memory_order_relaxed); std::atomic_store_explicit(&header_mmap()->tail, kInitialSequence, std::memory_order_relaxed); } // Validates ring geometry. // // Ring geometry is validated carefully on import and then cached. This allows // us to avoid out-of-range accesses even if the parameters in the header are // later changed. bool ValidateGeometry(size_t mmap_size, uint32_t header_record_size, uint32_t header_record_count) { set_record_size(header_record_size); set_record_count(header_record_count); if (record_size() != header_record_size) return false; if (record_count() != header_record_count) return false; if (record_count() < Traits::kMinRecordCount) return false; if (record_size() < sizeof(Record)) return false; if (record_size() % kRecordAlignment != 0) return false; if (!IsPowerOfTwo(record_count())) return false; size_t memory_size = record_count() * record_size(); if (memory_size / record_size() != record_count()) return false; if (memory_size + sizeof(Header) < memory_size) return false; if (memory_size + sizeof(Header) > mmap_size) return false; return true; } // Copies a record into the ring. // // This is done with relaxed atomics because otherwise it is racy according to // the C++ memory model. This is very low overhead once optimized. static inline void PutRecordInternal(const Record* in, RecordStorage* out) { StorageType data[sizeof(Record) / sizeof(StorageType)]; memcpy(data, in, sizeof(*in)); for (size_t i = 0; i < std::extent(); ++i) { std::atomic_store_explicit(&out->data[i], data[i], std::memory_order_relaxed); } } // Copies a record out of the ring. // // This is done with relaxed atomics because otherwise it is racy according to // the C++ memory model. This is very low overhead once optimized. static inline void GetRecordInternal(RecordStorage* in, Record* out) { StorageType data[sizeof(Record) / sizeof(StorageType)]; for (size_t i = 0; i < std::extent(); ++i) { data[i] = std::atomic_load_explicit(&in->data[i], std::memory_order_relaxed); } memcpy(out, &data, sizeof(*out)); } // Converts a record's sequence number into a storage index. static uint32_t SequenceToIndex(uint32_t sequence, uint32_t record_count) { return sequence & (record_count - 1); } // Computes readable & writable ranges from ring parameters. static Geometry CalculateGeometry(uint32_t record_count, uint32_t record_size, uint32_t head, uint32_t tail) { Geometry geometry; geometry.record_count = record_count; geometry.record_size = record_size; DCHECK_EQ(0U, geometry.record_size % kRecordAlignment); geometry.head = head; geometry.tail = tail; geometry.head_index = SequenceToIndex(head, record_count); geometry.tail_index = SequenceToIndex(tail, record_count); geometry.count = geometry.tail - geometry.head; DCHECK_LE(geometry.count, record_count); geometry.space = geometry.record_count - geometry.count; return geometry; } // Gets the current ring readable & writable regions. // // This this is always safe from the writing thread since it is the only // thread allowed to update the header. Geometry GetGeometry() const { return CalculateGeometry( record_count(), record_size(), std::atomic_load_explicit(&header_mmap()->head, std::memory_order_relaxed), std::atomic_load_explicit(&header_mmap()->tail, std::memory_order_relaxed)); } // Makes space for at least |reserve_count| records. // // There is nothing to prevent overwriting records that have concurrent // readers. We do however ensure that this situation can be detected: the // fence ensures the |head| update will be the first update seen by readers, // and readers check this value after reading and discard data that may have // been concurrently modified. void Reserve(uint32_t reserve_count) { Geometry geometry = GetGeometry(); DCHECK_LE(reserve_count, Traits::kMaxReservedRecords); uint32_t needed = (geometry.space >= reserve_count ? 0 : reserve_count - geometry.space); std::atomic_store_explicit(&header_mmap()->head, geometry.head + needed, std::memory_order_relaxed); // NB: It is not sufficient to change this to a store-release of |head|. std::atomic_thread_fence(std::memory_order_release); } // Makes |publish_count| records visible to readers. // // Space must have been reserved by a previous call to Reserve(). void Publish(uint32_t publish_count) { Geometry geometry = GetGeometry(); DCHECK_LE(publish_count, geometry.space); std::atomic_store_explicit(&header_mmap()->tail, geometry.tail + publish_count, std::memory_order_release); } // Helpers to compute addresses in mmap area. Mmap* mmap() const { return data_.mmap; } Header* header_mmap() const { return &data_.mmap->header; } RecordStorage* record_mmap_writer(uint32_t index) const { DCHECK_EQ(sizeof(Record), record_size()); return &data_.mmap->records[index]; } RecordStorage* record_mmap_reader(uint32_t index) const { if (Traits::kUseStaticRecordSize) { return &data_.mmap->records[index]; } else { // Calculate the location of a record in the ring without assuming that // sizeof(Record) == record_size. return reinterpret_cast( reinterpret_cast(data_.mmap->records) + index * record_size()); } } // The following horrifying template gunk enables us to store just the mmap // base pointer for compile-time statically sized rings. Dynamically sized // rings also store the validated copy of the record size & count. // // This boils down to: use a compile time constant if available, and otherwise // load the value that was validated on import from a member variable. template typename std::enable_if::type record_size_internal() const { return sizeof(Record); } template typename std::enable_if::type record_size_internal() const { return data_.record_size; } template typename std::enable_if::type set_record_size( uint32_t /*record_size*/) {} template typename std::enable_if::type set_record_size( uint32_t record_size) { data_.record_size = record_size; } template typename std::enable_if::type record_count_internal() const { return Traits::kStaticRecordCount; } template typename std::enable_if::type record_count_internal() const { return data_.record_count; } template typename std::enable_if::type set_record_count(uint32_t /*record_count*/) const {} template typename std::enable_if::type set_record_count(uint32_t record_count) { data_.record_count = record_count; } // Data we need to store for statically sized rings. struct DataStaticSize { Mmap* mmap = nullptr; }; // Data we need to store for dynamically sized rings. struct DataDynamicSize { Mmap* mmap = nullptr; // These are cached to make sure misbehaving writers cannot cause // out-of-bounds memory accesses by updating the values in the mmap header. uint32_t record_size = 0; uint32_t record_count = 0; }; using DataStaticOrDynamic = typename std::conditional::type; DataStaticOrDynamic data_; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BROADCAST_RING_H_ ����������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013643� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/Android.bp���������������������������������������������������������������������0100644 0000000 0000000 00000003407 13756501735 015547� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. cc_library_headers { name: "libbufferhub_headers", export_include_dirs: ["include"], vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor. } sourceFiles = [ "buffer_hub_base.cpp", "buffer_hub_rpc.cpp", "consumer_buffer.cpp", "ion_buffer.cpp", "producer_buffer.cpp", ] sharedLibraries = [ "libbase", "libcutils", "libhardware", "liblog", "libui", "libutils", "libnativewindow", "libpdx_default_transport", ] headerLibraries = [ "libbufferhub_headers", "libdvr_headers", "libnativebase_headers", ] cc_library { srcs: sourceFiles, cflags: [ "-DLOG_TAG=\"libbufferhub\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", "-Wall", "-Werror", ], shared_libs: sharedLibraries, header_libs: headerLibraries, name: "libbufferhub", export_header_lib_headers: [ "libbufferhub_headers", "libnativebase_headers", ], } cc_test { srcs: ["buffer_hub-test.cpp"], static_libs: ["libbufferhub"], shared_libs: sharedLibraries, header_libs: headerLibraries, name: "buffer_hub-test", } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/buffer_hub-test.cpp������������������������������������������������������������0100644 0000000 0000000 00000077424 13756501735 017446� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #include #include namespace { #define RETRY_EINTR(fnc_call) \ ([&]() -> decltype(fnc_call) { \ decltype(fnc_call) result; \ do { \ result = (fnc_call); \ } while (result == -1 && errno == EINTR); \ return result; \ })() using android::BufferHubDefs::isAnyClientAcquired; using android::BufferHubDefs::isAnyClientGained; using android::BufferHubDefs::isAnyClientPosted; using android::BufferHubDefs::isClientAcquired; using android::BufferHubDefs::isClientPosted; using android::BufferHubDefs::isClientReleased; using android::BufferHubDefs::kFirstClientBitMask; using android::dvr::ConsumerBuffer; using android::dvr::ProducerBuffer; using android::pdx::LocalHandle; using android::pdx::Status; using LibBufferHubTest = ::testing::Test; const int kWidth = 640; const int kHeight = 480; const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; // Maximum number of consumers for the buffer that only has one producer in the // test. const size_t kMaxConsumerCount = android::BufferHubDefs::kMaxNumberOfClients - 1; const int kPollTimeoutMs = 100; // Helper function to poll the eventfd in BufferHubBase. template int PollBufferEvent(const std::unique_ptr& buffer, int timeout_ms = kPollTimeoutMs) { pollfd p = {buffer->event_fd(), POLLIN, 0}; return poll(&p, 1, timeout_ms); } } // namespace TEST_F(LibBufferHubTest, TestBasicUsage) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c1 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); // Check that consumers can spawn other consumers. std::unique_ptr c2 = ConsumerBuffer::Import(c1->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); // Checks the state masks of client p, c1 and c2. EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask); EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1); EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2); // Initial state: producer not available, consumers not available. EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2))); EXPECT_EQ(0, p->GainAsync()); EXPECT_EQ(0, p->Post(LocalHandle())); // New state: producer not available, consumers available. EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c2))); LocalHandle fence; EXPECT_EQ(0, c1->Acquire(&fence)); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(c2))); EXPECT_EQ(0, c2->Acquire(&fence)); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2))); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(0, c1->Release(LocalHandle())); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(0, c2->Discard()); EXPECT_EQ(1, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(0, p->Gain(&fence)); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2))); } TEST_F(LibBufferHubTest, TestEpoll) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)}; ASSERT_TRUE(epoll_fd.IsValid()); epoll_event event; std::array events; auto event_sources = p->GetEventSources(); ASSERT_LT(event_sources.size(), events.size()); for (const auto& event_source : event_sources) { event = {.events = event_source.event_mask | EPOLLET, .data = {.fd = p->event_fd()}}; ASSERT_EQ(0, epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_source.event_fd, &event)); } event_sources = c->GetEventSources(); ASSERT_LT(event_sources.size(), events.size()); for (const auto& event_source : event_sources) { event = {.events = event_source.event_mask | EPOLLET, .data = {.fd = c->event_fd()}}; ASSERT_EQ(0, epoll_ctl(epoll_fd.Get(), EPOLL_CTL_ADD, event_source.event_fd, &event)); } // No events should be signaled initially. ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0)); // Gain and post the producer and check for consumer signal. EXPECT_EQ(0, p->GainAsync()); EXPECT_EQ(0, p->Post({})); ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); ASSERT_TRUE(events[0].events & EPOLLIN); ASSERT_EQ(c->event_fd(), events[0].data.fd); // Save the event bits to translate later. event = events[0]; // Check for events again. Edge-triggered mode should prevent any. EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); // Translate the events. auto event_status = c->GetEventMask(event.events); ASSERT_TRUE(event_status); ASSERT_TRUE(event_status.get() & EPOLLIN); // Check for events again. Edge-triggered mode should prevent any. EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); } TEST_F(LibBufferHubTest, TestStateMask) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); // It's ok to create up to kMaxConsumerCount consumer buffers. uint32_t client_state_masks = p->client_state_mask(); std::array, kMaxConsumerCount> cs; for (size_t i = 0; i < kMaxConsumerCount; i++) { cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // Expect all buffers have unique state mask. EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U); client_state_masks |= cs[i]->client_state_mask(); } EXPECT_EQ(client_state_masks, ~0U); // The 64th creation will fail with out-of-memory error. auto state = p->CreateConsumer(); EXPECT_EQ(state.error(), E2BIG); // Release any consumer should allow us to re-create. for (size_t i = 0; i < kMaxConsumerCount; i++) { client_state_masks &= ~cs[i]->client_state_mask(); cs[i] = nullptr; cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // The released state mask will be reused. EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U); client_state_masks |= cs[i]->client_state_mask(); } } TEST_F(LibBufferHubTest, TestStateTransitions) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); LocalHandle fence; EXPECT_EQ(0, p->GainAsync()); // Acquire in gained state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence)); // Post in gained state should succeed. EXPECT_EQ(0, p->Post(LocalHandle())); // Post and gain in posted state should fail. EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Acquire in posted state should succeed. EXPECT_EQ(0, c->Acquire(&fence)); // Acquire, post, and gain in acquired state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence)); EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Release in acquired state should succeed. EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); // Acquire and post in released state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence)); EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); // Gain in released state should succeed. EXPECT_EQ(0, p->Gain(&fence)); // Acquire in gained state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; EXPECT_EQ(0, p->GainAsync()); // Acquire in gained state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_FALSE(invalid_fence.IsValid()); // Post in gained state should succeed. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_EQ(p->buffer_state(), c->buffer_state()); EXPECT_TRUE(isAnyClientPosted(p->buffer_state())); // Post and gain in posted state should fail. EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence)); EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); // Acquire in posted state should succeed. EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c))); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(p->buffer_state(), c->buffer_state()); EXPECT_TRUE(isAnyClientAcquired(p->buffer_state())); // Acquire, post, and gain in acquired state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence)); EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); // Release in acquired state should succeed. EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence)); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(p->buffer_state(), c->buffer_state()); EXPECT_TRUE(p->is_released()); // Acquire and post in released state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence)); // Gain in released state should succeed. EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(p->buffer_state(), c->buffer_state()); EXPECT_TRUE(isAnyClientGained(p->buffer_state())); // Acquire and gain in gained state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); } TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); ASSERT_EQ(0, p->GainAsync()); ASSERT_EQ(0, p->GainAsync()); } TEST_F(LibBufferHubTest, TestGainPostedBuffer) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); ASSERT_EQ(0, p->GainAsync()); ASSERT_EQ(0, p->Post(LocalHandle())); ASSERT_TRUE(isAnyClientPosted(p->buffer_state())); // Gain in posted state should only succeed with gain_posted_buffer = true. LocalHandle invalid_fence; EXPECT_EQ(-EBUSY, p->Gain(&invalid_fence, false)); EXPECT_EQ(0, p->Gain(&invalid_fence, true)); } TEST_F(LibBufferHubTest, TestGainPostedBufferAsync) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); ASSERT_EQ(0, p->GainAsync()); ASSERT_EQ(0, p->Post(LocalHandle())); ASSERT_TRUE(isAnyClientPosted(p->buffer_state())); // GainAsync in posted state should only succeed with gain_posted_buffer // equals true. DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence, false)); EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true)); } TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); ASSERT_EQ(0, p->GainAsync()); ASSERT_EQ(0, p->Post(LocalHandle())); // Producer state bit is in released state after post, other clients shall be // in posted state although there is no consumer of this buffer yet. ASSERT_TRUE(isClientReleased(p->buffer_state(), p->client_state_mask())); ASSERT_TRUE(p->is_released()); ASSERT_TRUE(isAnyClientPosted(p->buffer_state())); // Gain in released state should succeed. LocalHandle invalid_fence; EXPECT_EQ(0, p->Gain(&invalid_fence, false)); } TEST_F(LibBufferHubTest, TestMaxConsumers) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); uint32_t producer_state_mask = p->client_state_mask(); std::array, kMaxConsumerCount> cs; for (size_t i = 0; i < kMaxConsumerCount; ++i) { cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); EXPECT_TRUE(cs[i]->is_released()); EXPECT_NE(producer_state_mask, cs[i]->client_state_mask()); } EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the producer should trigger all consumers to be available. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_TRUE(isClientReleased(p->buffer_state(), p->client_state_mask())); for (size_t i = 0; i < kMaxConsumerCount; ++i) { EXPECT_TRUE( isClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask())); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(cs[i]))); EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence)); EXPECT_TRUE( isClientAcquired(p->buffer_state(), cs[i]->client_state_mask())); } // All consumers have to release before the buffer is considered to be // released. for (size_t i = 0; i < kMaxConsumerCount; i++) { EXPECT_FALSE(p->is_released()); EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence)); } EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_TRUE(p->is_released()); // Buffer state cross all clients must be consistent. for (size_t i = 0; i < kMaxConsumerCount; i++) { EXPECT_EQ(p->buffer_state(), cs[i]->buffer_state()); } } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); EXPECT_TRUE(isAnyClientGained(p->buffer_state())); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_TRUE(isAnyClientGained(c->buffer_state())); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the gained buffer should signal already created consumer. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_TRUE(isAnyClientPosted(p->buffer_state())); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c))); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_TRUE(isAnyClientAcquired(c->buffer_state())); } TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); EXPECT_TRUE(isAnyClientGained(p->buffer_state())); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the gained buffer before any consumer gets created. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_TRUE(p->is_released()); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(p))); // Newly created consumer will be signalled for the posted buffer although it // is created after producer posting. std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_TRUE(isClientPosted(c->buffer_state(), c->client_state_mask())); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c1 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post, acquire, and release the buffer.. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(0, c1->AcquireAsync(&metadata, &invalid_fence)); EXPECT_EQ(0, c1->ReleaseAsync(&metadata, invalid_fence)); // Note that the next PDX call is on the producer channel, which may be // executed before Release impulse gets executed by bufferhubd. Thus, here we // need to wait until the releasd is confirmed before creating another // consumer. EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_TRUE(p->is_released()); // Create another consumer immediately after the release, should not make the // buffer un-released. std::unique_ptr c2 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); EXPECT_TRUE(p->is_released()); EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence)); EXPECT_TRUE(isAnyClientGained(p->buffer_state())); } TEST_F(LibBufferHubTest, TestWithCustomMetadata) { struct Metadata { int64_t field1; int64_t field2; }; std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); Metadata m = {1, 3}; EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata))); EXPECT_LE(0, RETRY_EINTR(PollBufferEvent(c))); LocalHandle fence; Metadata m2 = {}; EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2))); EXPECT_EQ(m.field1, m2.field1); EXPECT_EQ(m.field2, m2.field2); EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p, /*timeout_ms=*/0))); } TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { struct Metadata { int64_t field1; int64_t field2; }; struct OverSizedMetadata { int64_t field1; int64_t field2; int64_t field3; }; std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); // It is illegal to post metadata larger than originally requested during // buffer allocation. OverSizedMetadata evil_meta = {}; EXPECT_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata))); EXPECT_GE(0, RETRY_EINTR(PollBufferEvent(c))); // It is ok to post metadata smaller than originally requested during // buffer allocation. EXPECT_EQ(0, p->Post(LocalHandle())); } TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { struct Metadata { int64_t field1; int64_t field2; }; struct OverSizedMetadata { int64_t field1; int64_t field2; int64_t field3; }; std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); Metadata m = {1, 3}; EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m))); LocalHandle fence; int64_t sequence; OverSizedMetadata e; // It is illegal to acquire metadata larger than originally requested during // buffer allocation. EXPECT_NE(0, c->Acquire(&fence, &e, sizeof(e))); // It is ok to acquire metadata smaller than originally requested during // buffer allocation. EXPECT_EQ(0, c->Acquire(&fence, &sequence, sizeof(sequence))); EXPECT_EQ(m.field1, sequence); } TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); int64_t sequence = 3; EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence))); LocalHandle fence; EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestWithNoMeta) { std::unique_ptr p = ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); LocalHandle fence; EXPECT_EQ(0, p->Post(LocalHandle())); EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) { std::unique_ptr p = ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); int64_t sequence = 3; EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence))); } namespace { int PollFd(int fd, int timeout_ms) { pollfd p = {fd, POLLIN, 0}; return poll(&p, 1, timeout_ms); } } // namespace TEST_F(LibBufferHubTest, TestAcquireFence) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata meta; LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); // Post with unsignaled fence. EXPECT_EQ(0, p->PostAsync(&meta, f1)); // Should acquire a valid fence. LocalHandle f2; EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c))); EXPECT_EQ(0, c->AcquireAsync(&meta, &f2)); EXPECT_TRUE(f2.IsValid()); // The original fence and acquired fence should have different fd number. EXPECT_NE(f1.Get(), f2.Get()); EXPECT_GE(0, PollFd(f2.Get(), 0)); // Signal the original fence will trigger the new fence. eventfd_write(f1.Get(), 1); // Now the original FD has been signaled. EXPECT_LT(0, PollFd(f2.Get(), kPollTimeoutMs)); // Release the consumer with an invalid fence. EXPECT_EQ(0, c->ReleaseAsync(&meta, LocalHandle())); // Should gain an invalid fence. LocalHandle f3; EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(0, p->GainAsync(&meta, &f3)); EXPECT_FALSE(f3.IsValid()); // Post with a signaled fence. EXPECT_EQ(0, p->PostAsync(&meta, f1)); // Should acquire a valid fence and it's already signalled. LocalHandle f4; EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c))); EXPECT_EQ(0, c->AcquireAsync(&meta, &f4)); EXPECT_TRUE(f4.IsValid()); EXPECT_LT(0, PollFd(f4.Get(), kPollTimeoutMs)); // Release with an unsignalled fence and signal it immediately after release // without producer gainning. LocalHandle f5(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); EXPECT_EQ(0, c->ReleaseAsync(&meta, f5)); eventfd_write(f5.Get(), 1); // Should gain a valid fence, which is already signaled. LocalHandle f6; EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); EXPECT_EQ(0, p->GainAsync(&meta, &f6)); EXPECT_TRUE(f6.IsValid()); EXPECT_LT(0, PollFd(f6.Get(), kPollTimeoutMs)); } TEST_F(LibBufferHubTest, TestOrphanedAcquire) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c1 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); const uint32_t client_state_mask1 = c1->client_state_mask(); EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata meta; EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); LocalHandle fence; EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1))); EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence)); // Destroy the consumer who has acquired but not released the buffer. c1 = nullptr; // The buffer is now available for the producer to gain. EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); // Newly added consumer is not able to acquire the buffer. std::unique_ptr c2 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); const uint32_t client_state_mask2 = c2->client_state_mask(); EXPECT_NE(client_state_mask1, client_state_mask2); EXPECT_EQ(0, RETRY_EINTR(PollBufferEvent(c2))); EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence)); // Producer should be able to gain. EXPECT_EQ(0, p->GainAsync(&meta, &fence, false)); } TEST_F(LibBufferHubTest, TestAcquireLastPosted) { std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c1 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); const uint32_t client_state_mask1 = c1->client_state_mask(); EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata meta; EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c1))); // c2 is created when the buffer is in posted state. buffer state for c1 is // posted. Thus, c2 should be automatically set to posted and able to acquire. std::unique_ptr c2 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); const uint32_t client_state_mask2 = c2->client_state_mask(); EXPECT_NE(client_state_mask1, client_state_mask2); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c2))); LocalHandle invalid_fence; EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence)); EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence)); // c3 is created when the buffer is in acquired state. buffer state for c1 and // c2 are acquired. Thus, c3 should be automatically set to posted and able to // acquire. std::unique_ptr c3 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c3.get() != nullptr); const uint32_t client_state_mask3 = c3->client_state_mask(); EXPECT_NE(client_state_mask1, client_state_mask3); EXPECT_NE(client_state_mask2, client_state_mask3); EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(c3))); EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence)); // Releasing c2 and c3 in normal ways. EXPECT_EQ(0, c2->Release(LocalHandle())); EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle())); // Destroy the c1 who has not released the buffer. c1 = nullptr; // The buffer is now available for the producer to gain. EXPECT_LT(0, RETRY_EINTR(PollBufferEvent(p))); // C4 is created in released state. Thus, it cannot gain the just posted // buffer. std::unique_ptr c4 = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c4.get() != nullptr); const uint32_t client_state_mask4 = c4->client_state_mask(); EXPECT_NE(client_state_mask3, client_state_mask4); EXPECT_GE(0, RETRY_EINTR(PollBufferEvent(c3))); EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence)); // Producer should be able to gain. EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence)); } TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { // TODO(b/112338294) rewrite test after migration return; /* std::unique_ptr p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); std::unique_ptr c = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(p.get() != nullptr); ASSERT_TRUE(c.get() != nullptr); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; int p_id = p->id(); // Detach in posted state should fail. EXPECT_EQ(0, p->GainAsync()); EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_GT(RETRY_EINTR(PollBufferEvent(c)), 0); auto s1 = p->Detach(); EXPECT_FALSE(s1); // Detach in acquired state should fail. EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); s1 = p->Detach(); EXPECT_FALSE(s1); // Detach in released state should fail. EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence)); EXPECT_GT(RETRY_EINTR(PollBufferEvent(p)), 0); s1 = p->Detach(); EXPECT_FALSE(s1); // Detach in gained state should succeed. EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence)); s1 = p->Detach(); EXPECT_TRUE(s1); LocalChannelHandle handle = s1.take(); EXPECT_TRUE(handle.valid()); // Both producer and consumer should have hangup. EXPECT_GT(RETRY_EINTR(PollBufferEvent(p)), 0); auto s2 = p->GetEventMask(POLLHUP); EXPECT_TRUE(s2); EXPECT_EQ(s2.get(), POLLHUP); EXPECT_GT(RETRY_EINTR(PollBufferEvent(c)), 0); s2 = p->GetEventMask(POLLHUP); EXPECT_TRUE(s2); EXPECT_EQ(s2.get(), POLLHUP); auto s3 = p->CreateConsumer(); EXPECT_FALSE(s3); // Note that here the expected error code is EOPNOTSUPP as the socket towards // ProducerChannel has been teared down. EXPECT_EQ(s3.error(), EOPNOTSUPP); s3 = c->CreateConsumer(); EXPECT_FALSE(s3); // Note that here the expected error code is EPIPE returned from // ConsumerChannel::HandleMessage as the socket is still open but the producer // is gone. EXPECT_EQ(s3.error(), EPIPE); // Detached buffer handle can be use to construct a new BufferHubBuffer // object. auto d = BufferHubBuffer::Import(std::move(handle)); EXPECT_FALSE(handle.valid()); EXPECT_TRUE(d->IsConnected()); EXPECT_TRUE(d->IsValid()); EXPECT_EQ(d->id(), p_id); */ } TEST_F(LibBufferHubTest, TestDetach) { // TODO(b/112338294) rewrite test after migration return; /* std::unique_ptr p1 = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p1.get() != nullptr); int p1_id = p1->id(); // Detached the producer from gained state. EXPECT_EQ(0, p1->GainAsync()); auto status_or_handle = p1->Detach(); EXPECT_TRUE(status_or_handle.ok()); LocalChannelHandle h1 = status_or_handle.take(); EXPECT_TRUE(h1.valid()); // Detached buffer handle can be use to construct a new BufferHubBuffer // object. auto b1 = BufferHubBuffer::Import(std::move(h1)); EXPECT_FALSE(h1.valid()); EXPECT_TRUE(b1->IsValid()); int b1_id = b1->id(); EXPECT_EQ(b1_id, p1_id); */ } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/buffer_hub_base.cpp������������������������������������������������������������0100644 0000000 0000000 00000016706 13756501735 017457� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; using android::pdx::default_transport::ClientChannel; using android::pdx::default_transport::ClientChannelFactory; namespace android { namespace dvr { BufferHubBase::BufferHubBase(LocalChannelHandle channel_handle) : Client{pdx::default_transport::ClientChannel::Create( std::move(channel_handle))}, id_(-1), cid_(-1) {} BufferHubBase::BufferHubBase(const std::string& endpoint_path) : Client{pdx::default_transport::ClientChannelFactory::Create( endpoint_path)}, id_(-1), cid_(-1) {} BufferHubBase::~BufferHubBase() { // buffer_state and fence_state are not reset here. They will be used to // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method. if (metadata_header_ != nullptr) { metadata_buffer_.Unlock(); } } Status BufferHubBase::CreateConsumer() { Status status = InvokeRemoteMethod(); ALOGE_IF(!status, "BufferHub::CreateConsumer: Failed to create consumer channel: %s", status.GetErrorMessage().c_str()); return status; } int BufferHubBase::ImportBuffer() { ATRACE_NAME("BufferHubBase::ImportBuffer"); Status> status = InvokeRemoteMethod(); if (!status) { ALOGE("BufferHubBase::ImportBuffer: Failed to get buffer: %s", status.GetErrorMessage().c_str()); return -status.error(); } else if (status.get().id() < 0) { ALOGE("BufferHubBase::ImportBuffer: Received an invalid id!"); return -EIO; } auto buffer_desc = status.take(); // Stash the buffer id to replace the value in id_. const int new_id = buffer_desc.id(); // Import the buffer. IonBuffer ion_buffer; ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d.", buffer_desc.id()); if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) return ret; // Import the metadata. IonBuffer metadata_buffer; if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { ALOGE("Failed to import metadata buffer, error=%d", ret); return ret; } size_t metadata_buf_size = metadata_buffer.width(); if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { ALOGE("BufferHubBase::ImportBuffer: metadata buffer too small: %zu", metadata_buf_size); return -ENOMEM; } // If all imports succee, replace the previous buffer and id. buffer_ = std::move(ion_buffer); metadata_buffer_ = std::move(metadata_buffer); metadata_buf_size_ = metadata_buf_size; user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; void* metadata_ptr = nullptr; if (const int ret = metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, /*y=*/0, metadata_buf_size_, /*height=*/1, &metadata_ptr)) { ALOGE("BufferHubBase::ImportBuffer: Failed to lock metadata."); return ret; } // Set up shared fences. shared_acquire_fence_ = buffer_desc.take_acquire_fence(); shared_release_fence_ = buffer_desc.take_release_fence(); if (!shared_acquire_fence_ || !shared_release_fence_) { ALOGE("BufferHubBase::ImportBuffer: Failed to import shared fences."); return -EIO; } metadata_header_ = reinterpret_cast(metadata_ptr); if (user_metadata_size_) { user_metadata_ptr_ = reinterpret_cast(reinterpret_cast(metadata_ptr) + BufferHubDefs::kMetadataHeaderSize); } else { user_metadata_ptr_ = nullptr; } id_ = new_id; cid_ = buffer_desc.buffer_cid(); client_state_mask_ = buffer_desc.client_state_mask(); // Note that here the buffer_state, fence_state and active_clients_bit_mask // are mapped from shared memory as an atomic object. The std::atomic's // constructor will not be called so that the original value stored in the // memory region will be preserved. buffer_state_ = &metadata_header_->bufferState; ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d, buffer_state=%" PRIx32 ".", id(), buffer_state_->load(std::memory_order_acquire)); fence_state_ = &metadata_header_->fenceState; ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d, fence_state=%" PRIx32 ".", id(), fence_state_->load(std::memory_order_acquire)); active_clients_bit_mask_ = &metadata_header_->activeClientsBitMask; ALOGD_IF( TRACE, "BufferHubBase::ImportBuffer: id=%d, active_clients_bit_mask=%" PRIx32 ".", id(), active_clients_bit_mask_->load(std::memory_order_acquire)); return 0; } int BufferHubBase::CheckMetadata(size_t user_metadata_size) const { if (user_metadata_size && !user_metadata_ptr_) { ALOGE("BufferHubBase::CheckMetadata: doesn't support custom metadata."); return -EINVAL; } if (user_metadata_size > user_metadata_size_) { ALOGE("BufferHubBase::CheckMetadata: too big: %zu, maximum: %zu.", user_metadata_size, user_metadata_size_); return -E2BIG; } return 0; } int BufferHubBase::UpdateSharedFence(const LocalHandle& new_fence, const LocalHandle& shared_fence) { if (pending_fence_fd_.Get() != new_fence.Get()) { // First, replace the old fd if there was already one. Skipping if the new // one is the same as the old. if (pending_fence_fd_.IsValid()) { const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, pending_fence_fd_.Get(), nullptr); ALOGW_IF(ret, "BufferHubBase::UpdateSharedFence: failed to remove old fence " "fd from epoll set, error: %s.", strerror(errno)); } if (new_fence.IsValid()) { // If ready fence is valid, we put that into the epoll set. epoll_event event; event.events = EPOLLIN; event.data.u32 = client_state_mask(); pending_fence_fd_ = new_fence.Duplicate(); if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), &event) < 0) { const int error = errno; ALOGE( "BufferHubBase::UpdateSharedFence: failed to add new fence fd " "into epoll set, error: %s.", strerror(error)); return -error; } // Set bit in fence state to indicate that there is a fence from this // producer or consumer. fence_state_->fetch_or(client_state_mask()); } else { // Unset bit in fence state to indicate that there is no fence, so that // when consumer to acquire or producer to acquire, it knows no need to // check fence for this buffer. fence_state_->fetch_and(~client_state_mask()); } } return 0; } int BufferHubBase::Lock(int usage, int x, int y, int width, int height, void** address) { return buffer_.Lock(usage, x, y, width, height, address); } int BufferHubBase::Unlock() { return buffer_.Unlock(); } int BufferHubBase::GetBlobReadWritePointer(size_t size, void** addr) { int width = static_cast(size); int height = 1; int ret = Lock(usage(), 0, 0, width, height, addr); if (ret == 0) Unlock(); return ret; } } // namespace dvr } // namespace android ����������������������������������������������������������libs/vr/libbufferhub/buffer_hub_rpc.cpp�������������������������������������������������������������0100644 0000000 0000000 00000000256 13756501735 017322� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/bufferhub_rpc.h" namespace android { namespace dvr { constexpr char BufferHubRPC::kClientPath[]; } // namespace dvr } // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/consumer_buffer.cpp������������������������������������������������������������0100644 0000000 0000000 00000017243 13756501735 017537� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; namespace android { namespace dvr { ConsumerBuffer::ConsumerBuffer(LocalChannelHandle channel) : BASE(std::move(channel)) { const int ret = ImportBuffer(); if (ret < 0) { ALOGE("ConsumerBuffer::ConsumerBuffer: Failed to import buffer: %s", strerror(-ret)); Close(ret); } } std::unique_ptr ConsumerBuffer::Import( LocalChannelHandle channel) { ATRACE_NAME("ConsumerBuffer::Import"); ALOGD_IF(TRACE, "ConsumerBuffer::Import: channel=%d", channel.value()); return ConsumerBuffer::Create(std::move(channel)); } std::unique_ptr ConsumerBuffer::Import( Status status) { return Import(status ? status.take() : LocalChannelHandle{nullptr, -status.error()}); } int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { if (!out_meta) return -EINVAL; // The buffer can be acquired iff the buffer state for this client is posted. uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); if (!BufferHubDefs::isClientPosted(current_buffer_state, client_state_mask())) { ALOGE( "%s: Failed to acquire the buffer. The buffer is not posted, id=%d " "state=%" PRIx32 " client_state_mask=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state, client_state_mask()); return -EBUSY; } // Change the buffer state for this consumer from posted to acquired. uint32_t updated_buffer_state = current_buffer_state ^ client_state_mask(); while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { ALOGD( "%s Failed to acquire the buffer. Current buffer state was changed to " "%" PRIx32 " when trying to acquire the buffer and modify the buffer state to " "%" PRIx32 ". About to try again if the buffer is still posted.", __FUNCTION__, current_buffer_state, updated_buffer_state); if (!BufferHubDefs::isClientPosted(current_buffer_state, client_state_mask())) { ALOGE( "%s: Failed to acquire the buffer. The buffer is no longer posted, " "id=%d state=%" PRIx32 " client_state_mask=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state, client_state_mask()); return -EBUSY; } // The failure of compare_exchange_weak updates current_buffer_state. updated_buffer_state = current_buffer_state ^ client_state_mask(); } // Copy the canonical metadata. void* metadata_ptr = reinterpret_cast(&metadata_header_->metadata); memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); // Fill in the user_metadata_ptr in address space of the local process. if (out_meta->user_metadata_size) { out_meta->user_metadata_ptr = reinterpret_cast(user_metadata_ptr_); } else { out_meta->user_metadata_ptr = 0; } uint32_t fence_state = fence_state_->load(std::memory_order_acquire); // If there is an acquire fence from producer, we need to return it. // The producer state bit mask is kFirstClientBitMask for now. if (fence_state & BufferHubDefs::kFirstClientBitMask) { *out_fence = shared_acquire_fence_.Duplicate(); } return 0; } int ConsumerBuffer::Acquire(LocalHandle* ready_fence) { return Acquire(ready_fence, nullptr, 0); } int ConsumerBuffer::Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size) { ATRACE_NAME("ConsumerBuffer::Acquire"); if (const int error = CheckMetadata(user_metadata_size)) return error; DvrNativeBufferMetadata canonical_meta; if (const int error = LocalAcquire(&canonical_meta, ready_fence)) return error; if (meta && user_metadata_size) { void* metadata_src = reinterpret_cast(canonical_meta.user_metadata_ptr); if (metadata_src) { memcpy(meta, metadata_src, user_metadata_size); } else { ALOGW("ConsumerBuffer::Acquire: no user-defined metadata."); } } auto status = InvokeRemoteMethod(); if (!status) return -status.error(); return 0; } int ConsumerBuffer::AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { ATRACE_NAME("ConsumerBuffer::AcquireAsync"); if (const int error = LocalAcquire(out_meta, out_fence)) return error; auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); if (!status) return -status.error(); return 0; } int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence) { if (const int error = CheckMetadata(meta->user_metadata_size)) return error; // Set the buffer state of this client to released if it is not already in // released state. uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); if (BufferHubDefs::isClientReleased(current_buffer_state, client_state_mask())) { return 0; } uint32_t updated_buffer_state = current_buffer_state & (~client_state_mask()); while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { ALOGD( "%s: Failed to release the buffer. Current buffer state was changed to " "%" PRIx32 " when trying to release the buffer and modify the buffer state to " "%" PRIx32 ". About to try again.", __FUNCTION__, current_buffer_state, updated_buffer_state); // The failure of compare_exchange_weak updates current_buffer_state. updated_buffer_state = current_buffer_state & (~client_state_mask()); } // On release, only the user requested metadata is copied back into the shared // memory for metadata. Since there are multiple consumers, it doesn't make // sense to send the canonical metadata back to the producer. However, one of // the consumer can still choose to write up to user_metadata_size bytes of // data into user_metadata_ptr. if (meta->user_metadata_ptr && meta->user_metadata_size) { void* metadata_src = reinterpret_cast(meta->user_metadata_ptr); memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); } // Send out the release fence through the shared epoll fd. Note that during // releasing the producer is not expected to be polling on the fence. if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) return error; return 0; } int ConsumerBuffer::Release(const LocalHandle& release_fence) { ATRACE_NAME("ConsumerBuffer::Release"); DvrNativeBufferMetadata meta; if (const int error = LocalRelease(&meta, release_fence)) return error; return ReturnStatusOrError(InvokeRemoteMethod( BorrowedFence(release_fence.Borrow()))); } int ConsumerBuffer::ReleaseAsync() { DvrNativeBufferMetadata meta; return ReleaseAsync(&meta, LocalHandle()); } int ConsumerBuffer::ReleaseAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence) { ATRACE_NAME("ConsumerBuffer::ReleaseAsync"); if (const int error = LocalRelease(meta, release_fence)) return error; return ReturnStatusOrError( SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); } int ConsumerBuffer::Discard() { return Release(LocalHandle()); } } // namespace dvr } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/�����������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015266� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/���������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016740� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/�����������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 017533� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h������������������������������������������0100644 0000000 0000000 00000014244 13756501735 023007� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_BASE_H_ #define ANDROID_DVR_BUFFER_HUB_BASE_H_ #include #include namespace android { namespace dvr { // Base class of two types of BufferHub clients: dvr::ProducerBuffer and // dvr::ConsumerBuffer. class BufferHubBase : public pdx::Client { public: using LocalHandle = pdx::LocalHandle; using LocalChannelHandle = pdx::LocalChannelHandle; template using Status = pdx::Status; // Create a new consumer channel that is attached to the producer. Returns // a file descriptor for the new channel or a negative error code. Status CreateConsumer(); // Gets a blob buffer that was created with ProducerBuffer::CreateBlob. // Locking and Unlocking is handled internally. There's no need to Unlock // after calling this method. int GetBlobReadWritePointer(size_t size, void** addr); // Returns a dup'd file descriptor for accessing the blob shared memory. The // caller takes ownership of the file descriptor and must close it or pass on // ownership. Some GPU API extensions can take file descriptors to bind shared // memory gralloc buffers to GPU buffer objects. LocalHandle GetBlobFd() const { // Current GPU vendor puts the buffer allocation in one FD. If we change GPU // vendors and this is the wrong fd, late-latching and EDS will very clearly // stop working and we will need to correct this. The alternative is to use // a GL context in the pose service to allocate this buffer or to use the // ION API directly instead of gralloc. return LocalHandle(dup(native_handle()->data[0])); } using Client::event_fd; Status GetEventMask(int events) { if (auto* client_channel = GetChannel()) { return client_channel->GetEventMask(events); } else { return pdx::ErrorStatus(EINVAL); } } std::vector GetEventSources() const { if (auto* client_channel = GetChannel()) { return client_channel->GetEventSources(); } else { return {}; } } native_handle_t* native_handle() const { return const_cast(buffer_.handle()); } IonBuffer* buffer() { return &buffer_; } const IonBuffer* buffer() const { return &buffer_; } // Gets ID of the buffer client. All BufferHub clients derived from the same // buffer in bufferhubd share the same buffer id. int id() const { return id_; } // Gets the channel id of the buffer client. Each BufferHub client has its // system unique channel id. int cid() const { return cid_; } // Returns the buffer buffer state. uint32_t buffer_state() { return buffer_state_->load(std::memory_order_acquire); }; // Returns whether the buffer is already released by all current clients. bool is_released() { return (buffer_state() & active_clients_bit_mask_->load(std::memory_order_acquire)) == 0; } // A state mask which is unique to a buffer hub client among all its siblings // sharing the same concrete graphic buffer. uint32_t client_state_mask() const { return client_state_mask_; } // The following methods return settings of the first buffer. Currently, // it is only possible to create multi-buffer BufferHubBases with the same // settings. uint32_t width() const { return buffer_.width(); } uint32_t height() const { return buffer_.height(); } uint32_t stride() const { return buffer_.stride(); } uint32_t format() const { return buffer_.format(); } uint32_t usage() const { return buffer_.usage(); } uint32_t layer_count() const { return buffer_.layer_count(); } uint64_t GetQueueIndex() const { return metadata_header_->queueIndex; } void SetQueueIndex(uint64_t index) { metadata_header_->queueIndex = index; } protected: explicit BufferHubBase(LocalChannelHandle channel); explicit BufferHubBase(const std::string& endpoint_path); virtual ~BufferHubBase(); // Initialization helper. int ImportBuffer(); // Check invalid metadata operation. Returns 0 if requested metadata is valid. int CheckMetadata(size_t user_metadata_size) const; // Send out the new fence by updating the shared fence (shared_release_fence // for producer and shared_acquire_fence for consumer). Note that during this // should only be used in LocalPost() or LocalRelease, and the shared fence // shouldn't be poll'ed by the other end. int UpdateSharedFence(const LocalHandle& new_fence, const LocalHandle& shared_fence); // Locks the area specified by (x, y, width, height) for a specific usage. If // the usage is software then |addr| will be updated to point to the address // of the buffer in virtual memory. The caller should only access/modify the // pixels in the specified area. anything else is undefined behavior. int Lock(int usage, int x, int y, int width, int height, void** addr); // Must be called after Lock() when the caller has finished changing the // buffer. int Unlock(); // IonBuffer that is shared between bufferhubd, producer, and consumers. size_t metadata_buf_size_{0}; size_t user_metadata_size_{0}; BufferHubDefs::MetadataHeader* metadata_header_ = nullptr; void* user_metadata_ptr_ = nullptr; std::atomic* buffer_state_ = nullptr; std::atomic* fence_state_ = nullptr; std::atomic* active_clients_bit_mask_ = nullptr; LocalHandle shared_acquire_fence_; LocalHandle shared_release_fence_; // A local fence fd that holds the ownership of the fence fd on Post (for // producer) and Release (for consumer). LocalHandle pending_fence_fd_; private: BufferHubBase(const BufferHubBase&) = delete; void operator=(const BufferHubBase&) = delete; // Global id for the buffer that is consistent across processes. It is meant // for logging and debugging purposes only and should not be used for lookup // or any other functional purpose as a security precaution. int id_; // Channel id. int cid_; // Client bit mask which indicates the locations of this client object in the // buffer_state_. uint32_t client_state_mask_{0U}; IonBuffer buffer_; IonBuffer metadata_buffer_; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BUFFER_HUB_BASE_H_ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h������������������������������������������0100644 0000000 0000000 00000013423 13756501735 023014� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_DEFS_H_ #define ANDROID_DVR_BUFFER_HUB_DEFS_H_ #include #include #include #include #include #include #include #include namespace android { namespace dvr { namespace BufferHubDefs { static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB; static constexpr uint32_t kMetadataUsage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; // See more details in libs/ui/include/ui/BufferHubDefs.h static constexpr int kMaxNumberOfClients = android::BufferHubDefs::kMaxNumberOfClients; static constexpr uint32_t kLowbitsMask = android::BufferHubDefs::kLowbitsMask; static constexpr uint32_t kHighBitsMask = android::BufferHubDefs::kHighBitsMask; static constexpr uint32_t kFirstClientBitMask = android::BufferHubDefs::kFirstClientBitMask; static inline bool isAnyClientGained(uint32_t state) { return android::BufferHubDefs::isAnyClientGained(state); } static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) { return android::BufferHubDefs::isClientGained(state, client_bit_mask); } static inline bool isAnyClientPosted(uint32_t state) { return android::BufferHubDefs::isAnyClientPosted(state); } static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) { return android::BufferHubDefs::isClientPosted(state, client_bit_mask); } static inline bool isAnyClientAcquired(uint32_t state) { return android::BufferHubDefs::isAnyClientAcquired(state); } static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) { return android::BufferHubDefs::isClientAcquired(state, client_bit_mask); } static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) { return android::BufferHubDefs::isClientReleased(state, client_bit_mask); } // Returns the next available buffer client's client_state_masks. // @params union_bits. Union of all existing clients' client_state_masks. static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) { return android::BufferHubDefs::findNextAvailableClientStateMask(union_bits); } using MetadataHeader = android::BufferHubDefs::MetadataHeader; static constexpr size_t kMetadataHeaderSize = android::BufferHubDefs::kMetadataHeaderSize; } // namespace BufferHubDefs template class BufferTraits { public: BufferTraits() = default; BufferTraits(const native_handle_t* buffer_handle, const FileHandleType& metadata_handle, int id, uint32_t client_state_mask, uint64_t metadata_size, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, uint32_t stride, const FileHandleType& acquire_fence_fd, const FileHandleType& release_fence_fd) : id_(id), client_state_mask_(client_state_mask), metadata_size_(metadata_size), width_(width), height_(height), layer_count_(layer_count), format_(format), usage_(usage), stride_(stride), buffer_handle_(buffer_handle), metadata_handle_(metadata_handle.Borrow()), acquire_fence_fd_(acquire_fence_fd.Borrow()), release_fence_fd_(release_fence_fd.Borrow()) {} BufferTraits(BufferTraits&& other) = default; BufferTraits& operator=(BufferTraits&& other) = default; // ID of the buffer client. All BufferHubBuffer clients derived from the same // buffer in bufferhubd share the same buffer id. int id() const { return id_; } // State mask of the buffer client. Each BufferHubBuffer client backed by the // same buffer channel has uniqued state bit among its siblings. For a // producer buffer the bit must be kFirstClientBitMask; for a consumer the bit // must be one of the kConsumerStateMask. uint32_t client_state_mask() const { return client_state_mask_; } uint64_t metadata_size() const { return metadata_size_; } uint32_t width() { return width_; } uint32_t height() { return height_; } uint32_t layer_count() { return layer_count_; } uint32_t format() { return format_; } uint64_t usage() { return usage_; } uint32_t stride() { return stride_; } const NativeHandleWrapper& buffer_handle() const { return buffer_handle_; } NativeHandleWrapper take_buffer_handle() { return std::move(buffer_handle_); } FileHandleType take_metadata_handle() { return std::move(metadata_handle_); } FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } FileHandleType take_release_fence() { return std::move(release_fence_fd_); } private: // BufferHub specific traits. int id_ = -1; uint32_t client_state_mask_; uint64_t metadata_size_; // Traits for a GraphicBuffer. uint32_t width_; uint32_t height_; uint32_t layer_count_; uint32_t format_; uint64_t usage_; uint32_t stride_; // Native handle for the graphic buffer. NativeHandleWrapper buffer_handle_; // File handle of an ashmem that holds buffer metadata. FileHandleType metadata_handle_; // Pamameters for shared fences. FileHandleType acquire_fence_fd_; FileHandleType release_fence_fd_; PDX_SERIALIZABLE_MEMBERS(BufferTraits, id_, client_state_mask_, metadata_size_, stride_, width_, height_, layer_count_, format_, usage_, buffer_handle_, metadata_handle_, acquire_fence_fd_, release_fence_fd_); BufferTraits(const BufferTraits&) = delete; void operator=(const BufferTraits&) = delete; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BUFFER_HUB_DEFS_H_ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h��������������������������������������������0100644 0000000 0000000 00000031622 13756501735 022521� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_ #define ANDROID_DVR_BUFFERHUB_RPC_H_ #include "buffer_hub_defs.h" #include #include #include #include #include #include #include namespace android { namespace dvr { template class NativeBufferHandle { public: NativeBufferHandle() { Clear(); } NativeBufferHandle(const IonBuffer& buffer, int id) : id_(id), stride_(buffer.stride()), width_(buffer.width()), height_(buffer.height()), layer_count_(buffer.layer_count()), format_(buffer.format()), usage_(buffer.usage()) { // Populate the fd and int vectors: native_handle->data[] is an array of fds // followed by an array of opaque ints. const int fd_count = buffer.handle()->numFds; const int int_count = buffer.handle()->numInts; for (int i = 0; i < fd_count; i++) { fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i])); } for (int i = 0; i < int_count; i++) { opaque_ints_.push_back(buffer.handle()->data[fd_count + i]); } } NativeBufferHandle(NativeBufferHandle&& other) noexcept = default; NativeBufferHandle& operator=(NativeBufferHandle&& other) noexcept = default; // Imports the native handle into the given IonBuffer instance. int Import(IonBuffer* buffer) { // This is annoying, but we need to convert the vector of FileHandles into a // vector of ints for the Import API. std::vector fd_ints; for (const auto& fd : fds_) fd_ints.push_back(fd.Get()); const int ret = buffer->Import(fd_ints.data(), fd_ints.size(), opaque_ints_.data(), opaque_ints_.size(), width_, height_, layer_count_, stride_, format_, usage_); if (ret < 0) return ret; // Import succeeded, release the file handles which are now owned by the // IonBuffer and clear members. for (auto& fd : fds_) fd.Release(); opaque_ints_.clear(); Clear(); return 0; } int id() const { return id_; } size_t IntCount() const { return opaque_ints_.size(); } size_t FdCount() const { return fds_.size(); } private: int id_; uint32_t stride_; uint32_t width_; uint32_t height_; uint32_t layer_count_; uint32_t format_; uint64_t usage_; std::vector opaque_ints_; std::vector fds_; void Clear() { id_ = -1; stride_ = width_ = height_ = format_ = usage_ = 0; } PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle, id_, stride_, width_, height_, layer_count_, format_, usage_, opaque_ints_, fds_); NativeBufferHandle(const NativeBufferHandle&) = delete; void operator=(const NativeBufferHandle&) = delete; }; template class BufferDescription { public: BufferDescription() = default; BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id, int buffer_cid, uint32_t client_state_mask, const FileHandleType& acquire_fence_fd, const FileHandleType& release_fence_fd) : id_(id), buffer_cid_(buffer_cid), client_state_mask_(client_state_mask), buffer_(buffer, id), metadata_(metadata, id), acquire_fence_fd_(acquire_fence_fd.Borrow()), release_fence_fd_(release_fence_fd.Borrow()) {} BufferDescription(BufferDescription&& other) noexcept = default; BufferDescription& operator=(BufferDescription&& other) noexcept = default; // ID of the buffer client. All BufferHub clients derived from the same buffer // in bufferhubd share the same buffer id. int id() const { return id_; } // Channel ID of the buffer client. Each BufferHub client has its system // unique channel id. int buffer_cid() const { return buffer_cid_; } // State mask of the buffer client. Each BufferHub client backed by the // same buffer channel has uniqued state bit among its siblings. uint32_t client_state_mask() const { return client_state_mask_; } FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } FileHandleType take_release_fence() { return std::move(release_fence_fd_); } int ImportBuffer(IonBuffer* buffer) { return buffer_.Import(buffer); } int ImportMetadata(IonBuffer* metadata) { return metadata_.Import(metadata); } private: int id_{-1}; int buffer_cid_{-1}; uint32_t client_state_mask_{0U}; // Two IonBuffers: one for the graphic buffer and one for metadata. NativeBufferHandle buffer_; NativeBufferHandle metadata_; // Pamameters for shared fences. FileHandleType acquire_fence_fd_; FileHandleType release_fence_fd_; PDX_SERIALIZABLE_MEMBERS(BufferDescription, id_, buffer_cid_, client_state_mask_, buffer_, metadata_, acquire_fence_fd_, release_fence_fd_); BufferDescription(const BufferDescription&) = delete; void operator=(const BufferDescription&) = delete; }; using BorrowedNativeBufferHandle = NativeBufferHandle; using LocalNativeBufferHandle = NativeBufferHandle; template class FenceHandle { public: FenceHandle() = default; explicit FenceHandle(int fence) : fence_{fence} {} explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {} FenceHandle(FenceHandle&&) noexcept = default; FenceHandle& operator=(FenceHandle&&) noexcept = default; explicit operator bool() const { return fence_.IsValid(); } const FileHandleType& get() const { fence_; } FileHandleType&& take() { return std::move(fence_); } int get_fd() const { return fence_.Get(); } void close() { fence_.Close(); } FenceHandle borrow() const { return FenceHandle(fence_.Borrow()); } private: FileHandleType fence_; PDX_SERIALIZABLE_MEMBERS(FenceHandle, fence_); FenceHandle(const FenceHandle&) = delete; void operator=(const FenceHandle&) = delete; }; using LocalFence = FenceHandle; using BorrowedFence = FenceHandle; struct ProducerQueueConfig { // Whether the buffer queue is operating in Async mode. // From GVR's perspective of view, this means a buffer can be acquired // asynchronously by the compositor. // From Android Surface's perspective of view, this is equivalent to // IGraphicBufferProducer's async mode. When in async mode, a producer // will never block even if consumer is running slow. bool is_async; // Default buffer width that is set during ProducerQueue's creation. uint32_t default_width; // Default buffer height that is set during ProducerQueue's creation. uint32_t default_height; // Default buffer format that is set during ProducerQueue's creation. uint32_t default_format; // Size of the meta data associated with all the buffers allocated from the // queue. size_t user_metadata_size; private: PDX_SERIALIZABLE_MEMBERS(ProducerQueueConfig, is_async, default_width, default_height, default_format, user_metadata_size); }; class ProducerQueueConfigBuilder { public: // Build a ProducerQueueConfig object. ProducerQueueConfig Build() { return {is_async_, default_width_, default_height_, default_format_, user_metadata_size_}; } ProducerQueueConfigBuilder& SetIsAsync(bool is_async) { is_async_ = is_async; return *this; } ProducerQueueConfigBuilder& SetDefaultWidth(uint32_t width) { default_width_ = width; return *this; } ProducerQueueConfigBuilder& SetDefaultHeight(uint32_t height) { default_height_ = height; return *this; } ProducerQueueConfigBuilder& SetDefaultFormat(uint32_t format) { default_format_ = format; return *this; } template ProducerQueueConfigBuilder& SetMetadata() { user_metadata_size_ = sizeof(Meta); return *this; } ProducerQueueConfigBuilder& SetMetadataSize(size_t user_metadata_size) { user_metadata_size_ = user_metadata_size; return *this; } private: bool is_async_{false}; uint32_t default_width_{1}; uint32_t default_height_{1}; uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888 size_t user_metadata_size_{0}; }; // Explicit specializations of ProducerQueueConfigBuilder::Build for void // metadata type. template <> inline ProducerQueueConfigBuilder& ProducerQueueConfigBuilder::SetMetadata() { user_metadata_size_ = 0; return *this; } struct QueueInfo { ProducerQueueConfig producer_config; int id; private: PDX_SERIALIZABLE_MEMBERS(QueueInfo, producer_config, id); }; struct UsagePolicy { uint64_t usage_set_mask{0}; uint64_t usage_clear_mask{0}; uint64_t usage_deny_set_mask{0}; uint64_t usage_deny_clear_mask{0}; private: PDX_SERIALIZABLE_MEMBERS(UsagePolicy, usage_set_mask, usage_clear_mask, usage_deny_set_mask, usage_deny_clear_mask); }; // BufferHub Service RPC interface. Defines the endpoints, op codes, and method // type signatures supported by bufferhubd. struct BufferHubRPC { // Service path. static constexpr char kClientPath[] = "system/buffer_hub/client"; // |BufferHubQueue| will keep track of at most this value of buffers. // Attempts at runtime to increase the number of buffers past this // will fail. Note that the value is in sync with |android::BufferQueue|, so // that slot id can be shared between |android::dvr::BufferHubQueueProducer| // and |android::BufferQueueProducer| which both implements the same // interface: |android::IGraphicBufferProducer|. static constexpr size_t kMaxQueueCapacity = android::BufferQueueDefs::NUM_BUFFER_SLOTS; // Op codes. enum { kOpCreateBuffer = 0, kOpGetBuffer, kOpNewConsumer, kOpProducerPost, kOpProducerGain, kOpConsumerAcquire, kOpConsumerRelease, kOpConsumerBufferDetach, kOpCreateProducerQueue, kOpCreateConsumerQueue, kOpGetQueueInfo, kOpProducerQueueAllocateBuffers, kOpProducerQueueInsertBuffer, kOpProducerQueueRemoveBuffer, kOpConsumerQueueImportBuffers, // TODO(b/77153033): Separate all those RPC operations into subclasses. }; // Aliases. using LocalChannelHandle = pdx::LocalChannelHandle; using LocalHandle = pdx::LocalHandle; using Void = pdx::rpc::Void; // Methods. PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer, void(uint32_t width, uint32_t height, uint32_t format, uint64_t usage, size_t user_metadata_size)); PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer, BufferDescription(Void)); PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void)); PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost, void(LocalFence acquire_fence)); PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void)); PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void)); PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease, void(LocalFence release_fence)); // Detaches a ConsumerBuffer from an existing producer/consumer set. Can only // be called when the consumer is the only consumer and it has exclusive // access to the buffer (i.e. in the acquired'ed state). On the successful // return of the IPC call, a new DetachedBufferChannel handle will be returned // and all existing producer and consumer channels will be closed. Further // IPCs towards those channels will return error. PDX_REMOTE_METHOD(ConsumerBufferDetach, kOpConsumerBufferDetach, LocalChannelHandle(Void)); // Buffer Queue Methods. PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue, QueueInfo(const ProducerQueueConfig& producer_config, const UsagePolicy& usage_policy)); PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue, LocalChannelHandle(bool silent_queue)); PDX_REMOTE_METHOD(GetQueueInfo, kOpGetQueueInfo, QueueInfo(Void)); PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers, kOpProducerQueueAllocateBuffers, std::vector>( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count)); PDX_REMOTE_METHOD(ProducerQueueInsertBuffer, kOpProducerQueueInsertBuffer, size_t(int buffer_cid)); PDX_REMOTE_METHOD(ProducerQueueRemoveBuffer, kOpProducerQueueRemoveBuffer, void(size_t slot)); PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers, std::vector>(Void)); }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BUFFERHUB_RPC_H_ ��������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h������������������������������������������0100644 0000000 0000000 00000005750 13756501735 023074� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_CONSUMER_BUFFER_H_ #define ANDROID_DVR_CONSUMER_BUFFER_H_ #include namespace android { namespace dvr { // This is a connection to a producer buffer, which can be located in another // application. When that buffer is Post()ed, this fd will be signaled and // Acquire allows read access. The user is responsible for making sure that // Acquire is called with the correct metadata structure. The only guarantee the // API currently provides is that an Acquire() with metadata of the wrong size // will fail. class ConsumerBuffer : public pdx::ClientBase { public: // This call assumes ownership of |fd|. static std::unique_ptr Import(LocalChannelHandle channel); static std::unique_ptr Import( Status status); // Attempt to retrieve a post event from buffer hub. If successful, // |ready_fence| will be set to a fence to wait on until the buffer is ready. // This call will only succeed after the fd is signalled. This call may be // performed as an alternative to the Acquire() with metadata. In such cases // the metadata is not read. // // This returns zero or negative unix error code. int Acquire(LocalHandle* ready_fence); // Attempt to retrieve a post event from buffer hub. If successful, // |ready_fence| is set to a fence signaling that the contents of the buffer // are available. This call will only succeed if the buffer is in the posted // state. // Returns zero on success, or a negative errno code otherwise. int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size); // Asynchronously acquires a bufer. int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); // Releases the buffer from any buffer state. If the fence is valid the fence // determines the buffer usage, otherwise the buffer is released immediately. // This returns zero or a negative unix error code. int Release(const LocalHandle& release_fence); int ReleaseAsync(); // Asynchronously releases a buffer. Similar to the synchronous version above, // except that it does not wait for BufferHub to reply with success or error. // The fence and metadata are passed to consumer via shared fd and shared // memory. int ReleaseAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence); // May be called after or instead of Acquire to indicate that the consumer // does not need to access the buffer this cycle. This returns zero or a // negative unix error code. int Discard(); private: friend BASE; explicit ConsumerBuffer(LocalChannelHandle channel); // Local state transition helpers. int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); int LocalRelease(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence); }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_CONSUMER_BUFFER_H_ ������������������������libs/vr/libbufferhub/include/private/dvr/ion_buffer.h�����������������������������������������������0100644 0000000 0000000 00000007637 13756501735 022034� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_ION_BUFFER_H_ #define ANDROID_DVR_ION_BUFFER_H_ #include #include #include namespace android { namespace dvr { // IonBuffer is an abstraction of Ion/Gralloc buffers. class IonBuffer { public: IonBuffer(); IonBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage); IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t stride, uint32_t format, uint64_t usage); IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage); ~IonBuffer(); IonBuffer(IonBuffer&& other) noexcept; IonBuffer& operator=(IonBuffer&& other) noexcept; // Returns check this IonBuffer holds a valid Gralloc buffer. bool IsValid() const { return buffer_ && buffer_->initCheck() == OK; } // Frees the underlying native handle and leaves the instance initialized to // empty. void FreeHandle(); // Allocates a new native handle with the given parameters, freeing the // previous native handle if necessary. Returns 0 on success or a negative // errno code otherwise. If allocation fails the previous native handle is // left intact. int Alloc(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage); // Resets the underlying native handle and parameters, freeing the previous // native handle if necessary. void Reset(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage); // Like Reset but also registers the native handle, which is necessary for // native handles received over IPC. Returns 0 on success or a negative errno // code otherwise. If import fails the previous native handle is left intact. int Import(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage); // Like Reset but imports a native handle from raw fd and int arrays. Returns // 0 on success or a negative errno code otherwise. If import fails the // previous native handle is left intact. int Import(const int* fd_array, int fd_count, const int* int_array, int int_count, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage); // Duplicates the native handle underlying |other| and then imports it. This // is useful for creating multiple, independent views of the same Ion/Gralloc // buffer. Returns 0 on success or a negative errno code otherwise. If // duplication or import fail the previous native handle is left intact. int Duplicate(const IonBuffer* other); int Lock(uint32_t usage, int x, int y, int width, int height, void** address); int LockYUV(uint32_t usage, int x, int y, int width, int height, struct android_ycbcr* yuv); int Unlock(); sp& buffer() { return buffer_; } const sp& buffer() const { return buffer_; } buffer_handle_t handle() const { return buffer_.get() ? buffer_->handle : nullptr; } uint32_t width() const { return buffer_.get() ? buffer_->getWidth() : 0; } uint32_t height() const { return buffer_.get() ? buffer_->getHeight() : 0; } uint32_t layer_count() const { return buffer_.get() ? buffer_->getLayerCount() : 0; } uint32_t stride() const { return buffer_.get() ? buffer_->getStride() : 0; } uint32_t format() const { return buffer_.get() ? buffer_->getPixelFormat() : 0; } uint64_t usage() const { return buffer_.get() ? static_cast(buffer_->getUsage()) : 0; } private: sp buffer_; IonBuffer(const IonBuffer&) = delete; void operator=(const IonBuffer&) = delete; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_ION_BUFFER_H_ �������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h������������������������������������0100644 0000000 0000000 00000005656 13756501735 024256� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ #define ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ #include #include #include #include namespace android { namespace dvr { // A PDX-friendly wrapper to maintain the life cycle of a native_handle_t // object. // // See https://source.android.com/devices/architecture/hidl/types#handle_t for // more information about native_handle_t. template class NativeHandleWrapper { public: NativeHandleWrapper() = default; NativeHandleWrapper(NativeHandleWrapper&& other) = default; NativeHandleWrapper& operator=(NativeHandleWrapper&& other) = default; // Create a new NativeHandleWrapper by duplicating the handle. explicit NativeHandleWrapper(const native_handle_t* handle) { const int fd_count = handle->numFds; const int int_count = handle->numInts; // Populate the fd and int vectors: native_handle->data[] is an array of fds // followed by an array of opaque ints. for (int i = 0; i < fd_count; i++) { fds_.emplace_back(FileHandleType::AsDuplicate(handle->data[i])); } for (int i = 0; i < int_count; i++) { ints_.push_back(handle->data[fd_count + i]); } } size_t int_count() const { return ints_.size(); } size_t fd_count() const { return fds_.size(); } bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; } // Duplicate a native handle from the wrapper. native_handle_t* DuplicateHandle() const { if (!IsValid()) { return nullptr; } // numFds + numInts ints. std::vector fds; for (const auto& fd : fds_) { if (!fd.IsValid()) { return nullptr; } fds.emplace_back(fd.Duplicate()); } return FromFdsAndInts(std::move(fds), ints_); } // Takes the native handle out of the wrapper. native_handle_t* TakeHandle() { if (!IsValid()) { return nullptr; } return FromFdsAndInts(std::move(fds_), std::move(ints_)); } private: NativeHandleWrapper(const NativeHandleWrapper&) = delete; void operator=(const NativeHandleWrapper&) = delete; static native_handle_t* FromFdsAndInts(std::vector fds, std::vector ints) { native_handle_t* handle = native_handle_create(fds.size(), ints.size()); if (!handle) { ALOGE("NativeHandleWrapper::TakeHandle: Failed to create new handle."); return nullptr; } // numFds + numInts ints. for (int i = 0; i < handle->numFds; i++) { handle->data[i] = fds[i].Release(); } memcpy(&handle->data[handle->numFds], ints.data(), sizeof(int) * handle->numInts); return handle; } std::vector ints_; std::vector fds_; PDX_SERIALIZABLE_MEMBERS(NativeHandleWrapper, ints_, fds_); }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ ����������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/producer_buffer.h������������������������������������������0100644 0000000 0000000 00000011227 13756501735 023060� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_PRODUCER_BUFFER_H_ #define ANDROID_DVR_PRODUCER_BUFFER_H_ #include namespace android { namespace dvr { // This represents a writable buffer. Calling Post notifies all clients and // makes the buffer read-only. Call Gain to acquire write access. A buffer // may have many consumers. // // The user of ProducerBuffer is responsible with making sure that the Post() is // done with the correct metadata type and size. The user is also responsible // for making sure that remote ends (ConsumerBuffers) are also using the correct // metadata when acquiring the buffer. The API guarantees that a Post() with a // metadata of wrong size will fail. However, it currently does not do any // type checking. // The API also assumes that metadata is a serializable type (plain old data). class ProducerBuffer : public pdx::ClientBase { public: // Imports a bufferhub producer channel, assuming ownership of its handle. static std::unique_ptr Import(LocalChannelHandle channel); static std::unique_ptr Import( Status status); // Asynchronously posts a buffer. The fence and metadata are passed to // consumer via shared fd and shared memory. int PostAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence); // Post this buffer, passing |ready_fence| to the consumers. The bytes in // |meta| are passed unaltered to the consumers. The producer must not modify // the buffer until it is re-gained. // This returns zero or a negative unix error code. int Post(const LocalHandle& ready_fence, const void* meta, size_t user_metadata_size); int Post(const LocalHandle& ready_fence) { return Post(ready_fence, nullptr, 0); } // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it // must be waited on before using the buffer. If it is not valid then the // buffer is free for immediate use. This call will succeed if the buffer // is in the released state, or in posted state and gain_posted_buffer is // true. // // @param release_fence output fence. // @param gain_posted_buffer whether to gain posted buffer or not. // @return This returns zero or a negative unix error code. int Gain(LocalHandle* release_fence, bool gain_posted_buffer = false); // Asynchronously marks a released buffer as gained. This method is similar to // the synchronous version above, except that it does not wait for BufferHub // to acknowledge success or failure. Because of the asynchronous nature of // the underlying message, no error is returned if this method is called when // the buffer is in an incorrect state. Returns zero if sending the message // succeeded, or a negative errno code if local error check fails. // TODO(b/112007999): gain_posted_buffer true is only used to prevent // libdvrtracking from starving when there are non-responding clients. This // gain_posted_buffer param can be removed once libdvrtracking start to use // the new AHardwareBuffer API. int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, bool gain_posted_buffer = false); int GainAsync(); // Detaches a ProducerBuffer from an existing producer/consumer set. Can only // be called when a producer buffer has exclusive access to the buffer (i.e. // in the gain'ed state). On the successful return of the IPC call, a new // LocalChannelHandle representing a detached buffer will be returned and all // existing producer and consumer channels will be closed. Further IPCs // towards those channels will return error. Status Detach(); private: friend BASE; // Constructors are automatically exposed through ProducerBuffer::Create(...) // static template methods inherited from ClientBase, which take the same // arguments as the constructors. // Constructs a buffer with the given geometry and parameters. ProducerBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage, size_t metadata_size = 0); // Constructs a blob (flat) buffer with the given usage flags. ProducerBuffer(uint64_t usage, size_t size); // Imports the given file handle to a producer channel, taking ownership. explicit ProducerBuffer(LocalChannelHandle channel); // Local state transition helpers. int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, bool gain_posted_buffer = false); int LocalPost(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence); }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_PRODUCER_BUFFER_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/ion_buffer.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000016753 13756501735 016476� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include namespace { constexpr uint32_t kDefaultGraphicBufferLayerCount = 1; } // anonymous namespace namespace android { namespace dvr { IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0) {} IonBuffer::IonBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage) : IonBuffer() { Alloc(width, height, kDefaultGraphicBufferLayerCount, format, usage); } IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t stride, uint32_t format, uint64_t usage) : IonBuffer(handle, width, height, kDefaultGraphicBufferLayerCount, stride, format, usage) {} IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage) : buffer_(nullptr) { ALOGD_IF(TRACE, "IonBuffer::IonBuffer: handle=%p width=%u height=%u layer_count=%u " "stride=%u format=%u usage=%" PRIx64, handle, width, height, layer_count, stride, format, usage); if (handle != 0) { Import(handle, width, height, layer_count, stride, format, usage); } } IonBuffer::~IonBuffer() { ALOGD_IF(TRACE, "IonBuffer::~IonBuffer: handle=%p width=%u height=%u stride=%u " "format=%u usage=%" PRIx64, handle(), width(), height(), stride(), format(), usage()); FreeHandle(); } IonBuffer::IonBuffer(IonBuffer&& other) noexcept : IonBuffer() { *this = std::move(other); } IonBuffer& IonBuffer::operator=(IonBuffer&& other) noexcept { ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle(), other.handle()); if (this != &other) { buffer_ = other.buffer_; other.FreeHandle(); } return *this; } void IonBuffer::FreeHandle() { if (buffer_.get()) { // GraphicBuffer unregisters and cleans up the handle if needed buffer_ = nullptr; } } int IonBuffer::Alloc(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage) { ALOGD_IF(TRACE, "IonBuffer::Alloc: width=%u height=%u layer_count=%u format=%u " "usage=%" PRIx64, width, height, layer_count, format, usage); sp buffer = new GraphicBuffer(width, height, format, layer_count, usage); if (buffer->initCheck() != OK) { ALOGE("IonBuffer::Aloc: Failed to allocate buffer"); return -EINVAL; } else { buffer_ = buffer; return 0; } } void IonBuffer::Reset(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage) { ALOGD_IF(TRACE, "IonBuffer::Reset: handle=%p width=%u height=%u layer_count=%u " "stride=%u format=%u usage=%" PRIx64, handle, width, height, layer_count, stride, format, usage); Import(handle, width, height, layer_count, stride, format, usage); } int IonBuffer::Import(buffer_handle_t handle, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage) { ATRACE_NAME("IonBuffer::Import1"); ALOGD_IF(TRACE, "IonBuffer::Import: handle=%p width=%u height=%u layer_count=%u " "stride=%u format=%u usage=%" PRIx64, handle, width, height, layer_count, stride, format, usage); FreeHandle(); sp buffer = new GraphicBuffer(handle, GraphicBuffer::TAKE_UNREGISTERED_HANDLE, width, height, format, layer_count, usage, stride); if (buffer->initCheck() != OK) { ALOGE("IonBuffer::Import: Failed to import buffer"); return -EINVAL; } else { buffer_ = buffer; return 0; } } int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array, int int_count, uint32_t width, uint32_t height, uint32_t layer_count, uint32_t stride, uint32_t format, uint64_t usage) { ATRACE_NAME("IonBuffer::Import2"); ALOGD_IF(TRACE, "IonBuffer::Import: fd_count=%d int_count=%d width=%u height=%u " "layer_count=%u stride=%u format=%u usage=%" PRIx64, fd_count, int_count, width, height, layer_count, stride, format, usage); if (fd_count < 0 || int_count < 0) { ALOGE("IonBuffer::Import: invalid arguments."); return -EINVAL; } native_handle_t* handle = native_handle_create(fd_count, int_count); if (!handle) { ALOGE("IonBuffer::Import: failed to create new native handle."); return -ENOMEM; } // Copy fd_array into the first part of handle->data and int_array right // after it. memcpy(handle->data, fd_array, sizeof(int) * fd_count); memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count); const int ret = Import(handle, width, height, layer_count, stride, format, usage); if (ret < 0) { ALOGE("IonBuffer::Import: failed to import raw native handle: %s", strerror(-ret)); native_handle_close(handle); native_handle_delete(handle); } return ret; } int IonBuffer::Duplicate(const IonBuffer* other) { if (!other->handle()) return -EINVAL; const int fd_count = other->handle()->numFds; const int int_count = other->handle()->numInts; if (fd_count < 0 || int_count < 0) return -EINVAL; native_handle_t* handle = native_handle_create(fd_count, int_count); if (!handle) { ALOGE("IonBuffer::Duplicate: Failed to create new native handle."); return -ENOMEM; } // Duplicate the file descriptors from the other native handle. for (int i = 0; i < fd_count; i++) handle->data[i] = dup(other->handle()->data[i]); // Copy the ints after the file descriptors. memcpy(handle->data + fd_count, other->handle()->data + fd_count, sizeof(int) * int_count); const int ret = Import(handle, other->width(), other->height(), other->layer_count(), other->stride(), other->format(), other->usage()); if (ret < 0) { ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s", strerror(-ret)); native_handle_close(handle); native_handle_delete(handle); } return ret; } int IonBuffer::Lock(uint32_t usage, int x, int y, int width, int height, void** address) { ATRACE_NAME("IonBuffer::Lock"); ALOGD_IF(TRACE, "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d " "address=%p", handle(), usage, x, y, width, height, address); status_t err = buffer_->lock(usage, Rect(x, y, x + width, y + height), address); if (err != OK) return -EINVAL; else return 0; } int IonBuffer::LockYUV(uint32_t usage, int x, int y, int width, int height, struct android_ycbcr* yuv) { ATRACE_NAME("IonBuffer::LockYUV"); ALOGD_IF(TRACE, "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d", handle(), usage, x, y, width, height); status_t err = buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv); if (err != OK) return -EINVAL; else return 0; } int IonBuffer::Unlock() { ATRACE_NAME("IonBuffer::Unlock"); ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle()); status_t err = buffer_->unlock(); if (err != OK) return -EINVAL; else return 0; } } // namespace dvr } // namespace android ���������������������libs/vr/libbufferhub/producer_buffer.cpp������������������������������������������������������������0100644 0000000 0000000 00000026537 13756501735 017535� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; namespace android { namespace dvr { ProducerBuffer::ProducerBuffer(uint32_t width, uint32_t height, uint32_t format, uint64_t usage, size_t user_metadata_size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("ProducerBuffer::ProducerBuffer"); ALOGD_IF(TRACE, "ProducerBuffer::ProducerBuffer: fd=%d width=%u height=%u format=%u " "usage=%" PRIx64 " user_metadata_size=%zu", event_fd(), width, height, format, usage, user_metadata_size); auto status = InvokeRemoteMethod( width, height, format, usage, user_metadata_size); if (!status) { ALOGE( "ProducerBuffer::ProducerBuffer: Failed to create producer buffer: %s", status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", strerror(-ret)); Close(ret); } } ProducerBuffer::ProducerBuffer(uint64_t usage, size_t size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("ProducerBuffer::ProducerBuffer"); ALOGD_IF(TRACE, "ProducerBuffer::ProducerBuffer: usage=%" PRIx64 " size=%zu", usage, size); const int width = static_cast(size); const int height = 1; const int format = HAL_PIXEL_FORMAT_BLOB; const size_t user_metadata_size = 0; auto status = InvokeRemoteMethod( width, height, format, usage, user_metadata_size); if (!status) { ALOGE("ProducerBuffer::ProducerBuffer: Failed to create blob: %s", status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", strerror(-ret)); Close(ret); } } ProducerBuffer::ProducerBuffer(LocalChannelHandle channel) : BASE(std::move(channel)) { const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", strerror(-ret)); Close(ret); } } int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence) { if (const int error = CheckMetadata(meta->user_metadata_size)) return error; // The buffer can be posted iff the buffer state for this client is gained. uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); if (!BufferHubDefs::isClientGained(current_buffer_state, client_state_mask())) { ALOGE("%s: not gained, id=%d state=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state); return -EBUSY; } // Set the producer client buffer state to released, that of all other clients // (both existing and non-existing clients) to posted. uint32_t updated_buffer_state = (~client_state_mask()) & BufferHubDefs::kHighBitsMask; while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { ALOGD( "%s: Failed to post the buffer. Current buffer state was changed to " "%" PRIx32 " when trying to post the buffer and modify the buffer state to " "%" PRIx32 ". About to try again if the buffer is still gained by this client.", __FUNCTION__, current_buffer_state, updated_buffer_state); if (!BufferHubDefs::isClientGained(current_buffer_state, client_state_mask())) { ALOGE( "%s: Failed to post the buffer. The buffer is no longer gained, " "id=%d state=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state); return -EBUSY; } } // Copy the canonical metadata. void* metadata_ptr = reinterpret_cast(&metadata_header_->metadata); memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); // Copy extra user requested metadata. if (meta->user_metadata_ptr && meta->user_metadata_size) { void* metadata_src = reinterpret_cast(meta->user_metadata_ptr); memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); } // Send out the acquire fence through the shared epoll fd. Note that during // posting no consumer is not expected to be polling on the fence. if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) return error; return 0; } int ProducerBuffer::Post(const LocalHandle& ready_fence, const void* meta, size_t user_metadata_size) { ATRACE_NAME("ProducerBuffer::Post"); // Populate cononical metadata for posting. DvrNativeBufferMetadata canonical_meta; canonical_meta.user_metadata_ptr = reinterpret_cast(meta); canonical_meta.user_metadata_size = user_metadata_size; if (const int error = LocalPost(&canonical_meta, ready_fence)) return error; return ReturnStatusOrError(InvokeRemoteMethod( BorrowedFence(ready_fence.Borrow()))); } int ProducerBuffer::PostAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence) { ATRACE_NAME("ProducerBuffer::PostAsync"); if (const int error = LocalPost(meta, ready_fence)) return error; return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); } int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, bool gain_posted_buffer) { if (!out_meta) return -EINVAL; uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state); if (BufferHubDefs::isClientGained(current_buffer_state, client_state_mask())) { ALOGV("%s: already gained id=%d.", __FUNCTION__, id()); return 0; } if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) || BufferHubDefs::isAnyClientGained(current_buffer_state) || (BufferHubDefs::isAnyClientPosted( current_buffer_state & active_clients_bit_mask_->load(std::memory_order_acquire)) && !gain_posted_buffer)) { ALOGE("%s: not released id=%d state=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state); return -EBUSY; } // Change the buffer state to gained state. uint32_t updated_buffer_state = client_state_mask(); while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { ALOGD( "%s: Failed to gain the buffer. Current buffer state was changed to " "%" PRIx32 " when trying to gain the buffer and modify the buffer state to " "%" PRIx32 ". About to try again if the buffer is still not read by other " "clients.", __FUNCTION__, current_buffer_state, updated_buffer_state); if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) || BufferHubDefs::isAnyClientGained(current_buffer_state) || (BufferHubDefs::isAnyClientPosted( current_buffer_state & active_clients_bit_mask_->load(std::memory_order_acquire)) && !gain_posted_buffer)) { ALOGE( "%s: Failed to gain the buffer. The buffer is no longer released. " "id=%d state=%" PRIx32 ".", __FUNCTION__, id(), current_buffer_state); return -EBUSY; } } // Canonical metadata is undefined on Gain. Except for user_metadata and // release_fence_mask. Fill in the user_metadata_ptr in address space of the // local process. if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { out_meta->user_metadata_size = metadata_header_->metadata.user_metadata_size; out_meta->user_metadata_ptr = reinterpret_cast(user_metadata_ptr_); } else { out_meta->user_metadata_size = 0; out_meta->user_metadata_ptr = 0; } uint32_t current_fence_state = fence_state_->load(std::memory_order_acquire); uint32_t current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire); // If there are release fence(s) from consumer(s), we need to return it to the // consumer(s). // TODO(b/112007999) add an atomic variable in metadata header in shared // memory to indicate which client is the last producer of the buffer. // Currently, assume the first client is the only producer to the buffer. if (current_fence_state & current_active_clients_bit_mask & (~BufferHubDefs::kFirstClientBitMask)) { *out_fence = shared_release_fence_.Duplicate(); out_meta->release_fence_mask = current_fence_state & current_active_clients_bit_mask & (~BufferHubDefs::kFirstClientBitMask); } return 0; } int ProducerBuffer::Gain(LocalHandle* release_fence, bool gain_posted_buffer) { ATRACE_NAME("ProducerBuffer::Gain"); DvrNativeBufferMetadata meta; if (const int error = LocalGain(&meta, release_fence, gain_posted_buffer)) return error; auto status = InvokeRemoteMethod(); if (!status) return -status.error(); return 0; } int ProducerBuffer::GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* release_fence, bool gain_posted_buffer) { ATRACE_NAME("ProducerBuffer::GainAsync"); if (const int error = LocalGain(out_meta, release_fence, gain_posted_buffer)) return error; return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); } int ProducerBuffer::GainAsync() { DvrNativeBufferMetadata meta; LocalHandle fence; return GainAsync(&meta, &fence); } std::unique_ptr ProducerBuffer::Import( LocalChannelHandle channel) { ALOGD_IF(TRACE, "ProducerBuffer::Import: channel=%d", channel.value()); return ProducerBuffer::Create(std::move(channel)); } std::unique_ptr ProducerBuffer::Import( Status status) { return Import(status ? status.take() : LocalChannelHandle{nullptr, -status.error()}); } Status ProducerBuffer::Detach() { // TODO(b/112338294) remove after migrate producer buffer to binder ALOGW("ProducerBuffer::Detach: not supported operation during migration"); return {}; // TODO(b/112338294) Keep here for reference. Remove it after new logic is // written. /* uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire); if (!BufferHubDefs::isClientGained( buffer_state, BufferHubDefs::kFirstClientStateMask)) { // Can only detach a ProducerBuffer when it's in gained state. ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx32 ") is not in gained state.", id(), buffer_state); return {}; } Status status = InvokeRemoteMethod(); ALOGE_IF(!status, "ProducerBuffer::Detach: Failed to detach buffer (id=%d): %s.", id(), status.GetErrorMessage().c_str()); return status; */ } } // namespace dvr } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/��������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014710� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/Android.bp����������������������������������������������������������������0100644 0000000 0000000 00000003052 13756501735 016610� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. sourceFiles = [ "buffer_hub_queue_client.cpp", "buffer_hub_queue_parcelable.cpp", ] includeFiles = [ "include", ] staticLibraries = [ "libbufferhub", ] sharedLibraries = [ "libbase", "libbinder", "libcutils", "libhardware", "liblog", "libui", "libutils", "libpdx_default_transport", ] headerLibraries = [ "libdvr_headers", "libnativebase_headers", ] cc_library_shared { name: "libbufferhubqueue", cflags: [ "-DLOG_TAG=\"libbufferhubqueue\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", "-Wall", "-Werror", "-Wno-format", "-Wno-unused-parameter", "-Wno-unused-variable", ], srcs: sourceFiles, export_include_dirs: includeFiles, export_static_lib_headers: staticLibraries, static_libs: staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, } subdirs = ["benchmarks", "tests"] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/benchmarks/���������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 017025� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/benchmarks/Android.bp�����������������������������������������������������0100644 0000000 0000000 00000001045 13756501735 020725� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� cc_benchmark { srcs: ["buffer_transport_benchmark.cpp"], shared_libs: [ "libbase", "libbinder", "libcutils", "libdvr.google", "libgui", "liblog", "libhardware", "libui", "libutils", "libnativewindow", "libbufferhubqueue", "libpdx_default_transport", ], cflags: [ "-DLOG_TAG=\"buffer_transport_benchmark\"", "-DTRACE=0", "-O2", "-Wall", "-Werror", ], name: "buffer_transport_benchmark", } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp���������������������������������0100644 0000000 0000000 00000043624 13756501735 025136� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Use ALWAYS at the tag level. Control is performed manually during command // line processing. #ifdef ATRACE_TAG #undef ATRACE_TAG #endif #define ATRACE_TAG ATRACE_TAG_ALWAYS using namespace android; using ::benchmark::State; static const String16 kBinderService = String16("bufferTransport"); static const uint32_t kBufferWidth = 100; static const uint32_t kBufferHeight = 1; static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; static const uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; static const uint32_t kBufferLayer = 1; static const int kMaxAcquiredImages = 1; static const int kQueueDepth = 2; // We are double buffering for this test. static const size_t kMaxQueueCounts = 128; static const int kInvalidFence = -1; enum BufferTransportServiceCode { CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION, }; // A binder services that minics a compositor that consumes buffers. It provides // one Binder interface to create a new Surface for buffer producer to write // into; while itself will carry out no-op buffer consuming by acquiring then // releasing the buffer immediately. class BufferTransportService : public BBinder { public: BufferTransportService() = default; ~BufferTransportService() = default; virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { (void)flags; (void)data; switch (code) { case CREATE_BUFFER_QUEUE: { auto new_queue = std::make_shared(this); reply->writeStrongBinder( IGraphicBufferProducer::asBinder(new_queue->producer)); buffer_queues_.push_back(new_queue); return OK; } default: return UNKNOWN_TRANSACTION; }; } private: struct FrameListener : public ConsumerBase::FrameAvailableListener { public: FrameListener(BufferTransportService* /*service*/, sp buffer_item_consumer) : buffer_item_consumer_(buffer_item_consumer) {} void onFrameAvailable(const BufferItem& /*item*/) override { BufferItem buffer; status_t ret = 0; { ATRACE_NAME("AcquireBuffer"); ret = buffer_item_consumer_->acquireBuffer(&buffer, /*presentWhen=*/0, /*waitForFence=*/false); } if (ret != OK) { LOG(ERROR) << "Failed to acquire next buffer."; return; } { ATRACE_NAME("ReleaseBuffer"); ret = buffer_item_consumer_->releaseBuffer(buffer); } if (ret != OK) { LOG(ERROR) << "Failed to release buffer."; return; } } private: sp buffer_item_consumer_; }; struct BufferQueueHolder { explicit BufferQueueHolder(BufferTransportService* service) { BufferQueue::createBufferQueue(&producer, &consumer); sp buffer_item_consumer = new BufferItemConsumer(consumer, kBufferUsage, kMaxAcquiredImages, /*controlledByApp=*/true); buffer_item_consumer->setName(String8("BinderBufferTransport")); frame_listener_ = new FrameListener(service, buffer_item_consumer); buffer_item_consumer->setFrameAvailableListener(frame_listener_); } sp producer; sp consumer; private: sp frame_listener_; }; std::vector> buffer_queues_; }; // A virtual interfaces that abstracts the common BufferQueue operations, so // that the test suite can use the same test case to drive different types of // transport backends. class BufferTransport { public: virtual ~BufferTransport() {} virtual int Start() = 0; virtual sp CreateSurface() = 0; }; // Binder-based buffer transport backend. // // On Start() a new process will be swapned to run a Binder server that // actually consumes the buffer. // On CreateSurface() a new Binder BufferQueue will be created, which the // service holds the concrete binder node of the IGraphicBufferProducer while // sending the binder proxy to the client. In another word, the producer side // operations are carried out process while the consumer side operations are // carried out within the BufferTransportService's own process. class BinderBufferTransport : public BufferTransport { public: BinderBufferTransport() {} int Start() override { sp sm = defaultServiceManager(); service_ = sm->getService(kBinderService); if (service_ == nullptr) { LOG(ERROR) << "Failed to get the benchmark service."; return -EIO; } LOG(INFO) << "Binder server is ready for client."; return 0; } sp CreateSurface() override { Parcel data; Parcel reply; int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply); if (error != OK) { LOG(ERROR) << "Failed to get buffer queue over binder."; return nullptr; } sp binder; error = reply.readNullableStrongBinder(&binder); if (error != OK) { LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder."; return nullptr; } auto producer = interface_cast(binder); if (producer == nullptr) { LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder."; return nullptr; } sp surface = new Surface(producer, /*controlledByApp=*/true); // Set buffer dimension. ANativeWindow* window = static_cast(surface.get()); ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight, kBufferFormat); return surface; } private: sp service_; }; class DvrApi { public: DvrApi() { handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL); CHECK(handle_); auto dvr_get_api = reinterpret_cast(dlsym(handle_, "dvrGetApi")); int ret = dvr_get_api(&api_, sizeof(api_), /*version=*/1); CHECK(ret == 0); } ~DvrApi() { dlclose(handle_); } const DvrApi_v1& Api() { return api_; } private: void* handle_ = nullptr; DvrApi_v1 api_; }; // BufferHub/PDX-based buffer transport. // // On Start() a new thread will be swapned to run an epoll polling thread which // minics the behavior of a compositor. Similar to Binder-based backend, the // buffer available handler is also a no-op: Buffer gets acquired and released // immediately. // On CreateSurface() a pair of dvr::ProducerQueue and dvr::ConsumerQueue will // be created. The epoll thread holds on the consumer queue and dequeues buffer // from it; while the producer queue will be wrapped in a Surface and returned // to test suite. class BufferHubTransport : public BufferTransport { public: virtual ~BufferHubTransport() { stopped_.store(true); if (reader_thread_.joinable()) { reader_thread_.join(); } } int Start() override { int ret = epoll_fd_.Create(); if (ret < 0) { LOG(ERROR) << "Failed to create epoll fd: %s", strerror(-ret); return -1; } // Create the reader thread. reader_thread_ = std::thread([this]() { int ret = dvr_.Api().PerformanceSetSchedulerPolicy(0, "graphics"); if (ret < 0) { LOG(ERROR) << "Failed to set scheduler policy, ret=" << ret; return; } stopped_.store(false); LOG(INFO) << "Reader Thread Running..."; while (!stopped_.load()) { std::array events; // Don't sleep forever so that we will have a chance to wake up. const int ret = epoll_fd_.Wait(events.data(), events.size(), /*timeout=*/100); if (ret < 0) { LOG(ERROR) << "Error polling consumer queues."; continue; } if (ret == 0) { continue; } const int num_events = ret; for (int i = 0; i < num_events; i++) { uint32_t index = events[i].data.u32; dvr_.Api().ReadBufferQueueHandleEvents( buffer_queues_[index]->GetReadQueue()); } } LOG(INFO) << "Reader Thread Exiting..."; }); return 0; } sp CreateSurface() override { auto new_queue = std::make_shared(); if (!new_queue->IsReady()) { LOG(ERROR) << "Failed to create BufferHub-based BufferQueue."; return nullptr; } // Set buffer dimension. ANativeWindow_setBuffersGeometry(new_queue->GetSurface(), kBufferWidth, kBufferHeight, kBufferFormat); // Use the next position as buffer_queue index. uint32_t index = buffer_queues_.size(); epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u32 = index}}; int queue_fd = dvr_.Api().ReadBufferQueueGetEventFd(new_queue->GetReadQueue()); const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, queue_fd, &event); if (ret < 0) { LOG(ERROR) << "Failed to track consumer queue: " << strerror(-ret) << ", consumer queue fd: " << queue_fd; return nullptr; } buffer_queues_.push_back(new_queue); ANativeWindow_acquire(new_queue->GetSurface()); return static_cast(new_queue->GetSurface()); } private: struct BufferQueueHolder { BufferQueueHolder() { int ret = 0; ret = dvr_.Api().WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kBufferLayer, kBufferUsage, 0, sizeof(DvrNativeBufferMetadata), &write_queue_); if (ret < 0) { LOG(ERROR) << "Failed to create write buffer queue, ret=" << ret; return; } ret = dvr_.Api().WriteBufferQueueCreateReadQueue(write_queue_, &read_queue_); if (ret < 0) { LOG(ERROR) << "Failed to create read buffer queue, ret=" << ret; return; } ret = dvr_.Api().ReadBufferQueueSetBufferAvailableCallback( read_queue_, BufferAvailableCallback, this); if (ret < 0) { LOG(ERROR) << "Failed to create buffer available callback, ret=" << ret; return; } ret = dvr_.Api().WriteBufferQueueGetANativeWindow(write_queue_, &surface_); if (ret < 0) { LOG(ERROR) << "Failed to create surface, ret=" << ret; return; } } static void BufferAvailableCallback(void* context) { BufferQueueHolder* thiz = static_cast(context); thiz->HandleBufferAvailable(); } DvrReadBufferQueue* GetReadQueue() { return read_queue_; } ANativeWindow* GetSurface() { return surface_; } bool IsReady() { return write_queue_ != nullptr && read_queue_ != nullptr && surface_ != nullptr; } void HandleBufferAvailable() { int ret = 0; DvrNativeBufferMetadata meta; DvrReadBuffer* buffer = nullptr; DvrNativeBufferMetadata metadata; int acquire_fence = kInvalidFence; { ATRACE_NAME("AcquireBuffer"); ret = dvr_.Api().ReadBufferQueueAcquireBuffer( read_queue_, 0, &buffer, &metadata, &acquire_fence); } if (ret < 0) { LOG(ERROR) << "Failed to acquire consumer buffer, error: " << ret; return; } if (buffer != nullptr) { ATRACE_NAME("ReleaseBuffer"); ret = dvr_.Api().ReadBufferQueueReleaseBuffer(read_queue_, buffer, &meta, kInvalidFence); } if (ret < 0) { LOG(ERROR) << "Failed to release consumer buffer, error: " << ret; } } private: DvrWriteBufferQueue* write_queue_ = nullptr; DvrReadBufferQueue* read_queue_ = nullptr; ANativeWindow* surface_ = nullptr; }; static DvrApi dvr_; std::atomic stopped_; std::thread reader_thread_; dvr::EpollFileDescriptor epoll_fd_; std::vector> buffer_queues_; }; DvrApi BufferHubTransport::dvr_ = {}; enum TransportType { kBinderBufferTransport, kBufferHubTransport, }; // Main test suite, which supports two transport backend: 1) BinderBufferQueue, // 2) BufferHubQueue. The test case drives the producer end of both transport // backend by queuing buffers into the buffer queue by using ANativeWindow API. class BufferTransportBenchmark : public ::benchmark::Fixture { public: void SetUp(State& state) override { if (state.thread_index == 0) { const int transport = state.range(0); switch (transport) { case kBinderBufferTransport: transport_.reset(new BinderBufferTransport); break; case kBufferHubTransport: transport_.reset(new BufferHubTransport); break; default: CHECK(false) << "Unknown test case."; break; } CHECK(transport_); const int ret = transport_->Start(); CHECK_EQ(ret, 0); LOG(INFO) << "Transport backend running, transport=" << transport << "."; // Create surfaces for each thread. surfaces_.resize(state.threads); for (int i = 0; i < state.threads; i++) { // Common setup every thread needs. surfaces_[i] = transport_->CreateSurface(); CHECK(surfaces_[i]); LOG(INFO) << "Surface initialized on thread " << i << "."; } } } void TearDown(State& state) override { if (state.thread_index == 0) { surfaces_.clear(); transport_.reset(); LOG(INFO) << "Tear down benchmark."; } } protected: std::unique_ptr transport_; std::vector> surfaces_; }; BENCHMARK_DEFINE_F(BufferTransportBenchmark, Producers)(State& state) { ANativeWindow* window = nullptr; ANativeWindow_Buffer buffer; int32_t error = 0; double total_gain_buffer_us = 0; double total_post_buffer_us = 0; int iterations = 0; while (state.KeepRunning()) { if (window == nullptr) { CHECK(surfaces_[state.thread_index]); window = static_cast(surfaces_[state.thread_index].get()); // Lock buffers a couple time from the queue, so that we have the buffer // allocated. for (int i = 0; i < kQueueDepth; i++) { error = ANativeWindow_lock(window, &buffer, /*inOutDirtyBounds=*/nullptr); CHECK_EQ(error, 0); error = ANativeWindow_unlockAndPost(window); CHECK_EQ(error, 0); } } { ATRACE_NAME("GainBuffer"); auto t1 = std::chrono::high_resolution_clock::now(); error = ANativeWindow_lock(window, &buffer, /*inOutDirtyBounds=*/nullptr); auto t2 = std::chrono::high_resolution_clock::now(); std::chrono::duration delta_us = t2 - t1; total_gain_buffer_us += delta_us.count(); } CHECK_EQ(error, 0); { ATRACE_NAME("PostBuffer"); auto t1 = std::chrono::high_resolution_clock::now(); error = ANativeWindow_unlockAndPost(window); auto t2 = std::chrono::high_resolution_clock::now(); std::chrono::duration delta_us = t2 - t1; total_post_buffer_us += delta_us.count(); } CHECK_EQ(error, 0); iterations++; } state.counters["gain_buffer_us"] = ::benchmark::Counter( total_gain_buffer_us / iterations, ::benchmark::Counter::kAvgThreads); state.counters["post_buffer_us"] = ::benchmark::Counter( total_post_buffer_us / iterations, ::benchmark::Counter::kAvgThreads); state.counters["producer_us"] = ::benchmark::Counter( (total_gain_buffer_us + total_post_buffer_us) / iterations, ::benchmark::Counter::kAvgThreads); } BENCHMARK_REGISTER_F(BufferTransportBenchmark, Producers) ->Unit(::benchmark::kMicrosecond) ->Ranges({{kBinderBufferTransport, kBufferHubTransport}}) ->ThreadRange(1, 32); static void runBinderServer() { ProcessState::self()->setThreadPoolMaxThreadCount(0); ProcessState::self()->startThreadPool(); sp sm = defaultServiceManager(); sp service = new BufferTransportService; sm->addService(kBinderService, service, false); LOG(INFO) << "Binder server running..."; while (true) { int stat, retval; retval = wait(&stat); if (retval == -1 && errno == ECHILD) { break; } } LOG(INFO) << "Service Exiting..."; } // To run binder-based benchmark, use: // adb shell buffer_transport_benchmark \ // --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/0/" // // To run bufferhub-based benchmark, use: // adb shell buffer_transport_benchmark \ // --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/1/" int main(int argc, char** argv) { bool tracing_enabled = false; // Parse arguments in addition to "--benchmark_filter" paramters. for (int i = 1; i < argc; i++) { if (std::string(argv[i]) == "--help") { std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl; std::cout << "\t--trace: Enable systrace logging." << std::endl; return 0; } if (std::string(argv[i]) == "--trace") { tracing_enabled = true; continue; } } // Setup ATRACE/systrace based on command line. atrace_setup(); atrace_set_tracing_enabled(tracing_enabled); pid_t pid = fork(); if (pid == 0) { // Child, i.e. the client side. ProcessState::self()->startThreadPool(); ::benchmark::Initialize(&argc, argv); ::benchmark::RunSpecifiedBenchmarks(); } else { LOG(INFO) << "Benchmark process pid: " << pid; runBinderServer(); } } ������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp�����������������������������������������������0100644 0000000 0000000 00000064633 13756501735 022276� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/buffer_hub_queue_client.h" #include #include #include #include #include #include #include #include #include #define RETRY_EINTR(fnc_call) \ ([&]() -> decltype(fnc_call) { \ decltype(fnc_call) result; \ do { \ result = (fnc_call); \ } while (result == -1 && errno == EINTR); \ return result; \ })() using android::pdx::ErrorStatus; using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; namespace android { namespace dvr { namespace { std::pair Unstuff(uint64_t value) { return {static_cast(value >> 32), static_cast(value & ((1ull << 32) - 1))}; } uint64_t Stuff(int32_t a, int32_t b) { const uint32_t ua = static_cast(a); const uint32_t ub = static_cast(b); return (static_cast(ua) << 32) | static_cast(ub); } } // anonymous namespace BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle) : Client{pdx::default_transport::ClientChannel::Create( std::move(channel_handle))} { Initialize(); } BufferHubQueue::BufferHubQueue(const std::string& endpoint_path) : Client{ pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} { Initialize(); } void BufferHubQueue::Initialize() { int ret = epoll_fd_.Create(); if (ret < 0) { ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s", strerror(-ret)); return; } epoll_event event = { .events = EPOLLIN | EPOLLET, .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}}; ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); if (ret < 0) { ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__, strerror(-ret)); } } Status BufferHubQueue::ImportQueue() { auto status = InvokeRemoteMethod(); if (!status) { ALOGE("%s: Failed to import queue: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return ErrorStatus(status.error()); } else { SetupQueue(status.get()); return {}; } } void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) { is_async_ = queue_info.producer_config.is_async; default_width_ = queue_info.producer_config.default_width; default_height_ = queue_info.producer_config.default_height; default_format_ = queue_info.producer_config.default_format; user_metadata_size_ = queue_info.producer_config.user_metadata_size; id_ = queue_info.id; } std::unique_ptr BufferHubQueue::CreateConsumerQueue() { if (auto status = CreateConsumerQueueHandle(/*silent*/ false)) return std::unique_ptr(new ConsumerQueue(status.take())); else return nullptr; } std::unique_ptr BufferHubQueue::CreateSilentConsumerQueue() { if (auto status = CreateConsumerQueueHandle(/*silent*/ true)) return std::unique_ptr(new ConsumerQueue(status.take())); else return nullptr; } Status BufferHubQueue::CreateConsumerQueueHandle( bool silent) { auto status = InvokeRemoteMethod(silent); if (!status) { ALOGE( "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: " "%s", status.GetErrorMessage().c_str()); return ErrorStatus(status.error()); } return status; } pdx::Status BufferHubQueue::CreateConsumerQueueParcelable(bool silent) { auto status = CreateConsumerQueueHandle(silent); if (!status) return status.error_status(); // A temporary consumer queue client to pull its channel parcelable. auto consumer_queue = std::unique_ptr(new ConsumerQueue(status.take())); ConsumerQueueParcelable queue_parcelable( consumer_queue->GetChannel()->TakeChannelParcelable()); if (!queue_parcelable.IsValid()) { ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__); return ErrorStatus(EINVAL); } return {std::move(queue_parcelable)}; } bool BufferHubQueue::WaitForBuffers(int timeout) { ATRACE_NAME("BufferHubQueue::WaitForBuffers"); std::array events; // Loop at least once to check for hangups. do { ALOGD_IF( TRACE, "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu", id(), count(), capacity()); // If there is already a buffer then just check for hangup without waiting. const int ret = epoll_fd_.Wait(events.data(), events.size(), count() == 0 ? timeout : 0); if (ret == 0) { ALOGI_IF(TRACE, "BufferHubQueue::WaitForBuffers: No events before timeout: " "queue_id=%d", id()); return count() != 0; } if (ret < 0 && ret != -EINTR) { ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret)); return false; } const int num_events = ret; // A BufferQueue's epoll fd tracks N+1 events, where there are N events, // one for each buffer in the queue, and one extra event for the queue // client itself. for (int i = 0; i < num_events; i++) { int32_t event_fd; int32_t index; std::tie(event_fd, index) = Unstuff(events[i].data.u64); PDX_TRACE_FORMAT( "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;" "slot=%d|", id(), num_events, i, event_fd, index); ALOGD_IF(TRACE, "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d", i, event_fd, index); if (is_buffer_event_index(index)) { HandleBufferEvent(static_cast(index), event_fd, events[i].events); } else if (is_queue_event_index(index)) { HandleQueueEvent(events[i].events); } else { ALOGW( "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d " "index=%d", event_fd, index); } } } while (count() == 0 && capacity() > 0 && !hung_up()); return count() != 0; } Status BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd, int poll_events) { ATRACE_NAME("BufferHubQueue::HandleBufferEvent"); if (!buffers_[slot]) { ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot); return ErrorStatus(ENOENT); } auto status = buffers_[slot]->GetEventMask(poll_events); if (!status) { ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s", status.GetErrorMessage().c_str()); return status.error_status(); } const int events = status.get(); PDX_TRACE_FORMAT( "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;" "events=%d|", id(), buffers_[slot]->id(), slot, event_fd, poll_events, events); if (events & EPOLLIN) { return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()}); } else if (events & EPOLLHUP) { ALOGW( "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu " "event_fd=%d buffer_id=%d", slot, buffers_[slot]->event_fd(), buffers_[slot]->id()); return RemoveBuffer(slot); } else { ALOGW( "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll " "events=%d", slot, events); } return {}; } Status BufferHubQueue::HandleQueueEvent(int poll_event) { ATRACE_NAME("BufferHubQueue::HandleQueueEvent"); auto status = GetEventMask(poll_event); if (!status) { ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s", status.GetErrorMessage().c_str()); return status.error_status(); } const int events = status.get(); if (events & EPOLLIN) { // Note that after buffer imports, if |count()| still returns 0, epoll // wait will be tried again to acquire the newly imported buffer. auto buffer_status = OnBufferAllocated(); if (!buffer_status) { ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, buffer_status.GetErrorMessage().c_str()); } } else if (events & EPOLLHUP) { ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__); hung_up_ = true; } else { ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events); } return {}; } Status BufferHubQueue::AddBuffer( const std::shared_ptr& buffer, size_t slot) { ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(), slot); if (is_full()) { ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_); return ErrorStatus(E2BIG); } if (buffers_[slot]) { // Replace the buffer if the slot is occupied. This could happen when the // producer side replaced the slot with a newly allocated buffer. Remove the // buffer before setting up with the new one. auto remove_status = RemoveBuffer(slot); if (!remove_status) return remove_status.error_status(); } for (const auto& event_source : buffer->GetEventSources()) { epoll_event event = {.events = event_source.event_mask | EPOLLET, .data = {.u64 = Stuff(buffer->event_fd(), slot)}}; const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event); if (ret < 0) { ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__, strerror(-ret)); return ErrorStatus(-ret); } } buffers_[slot] = buffer; capacity_++; return {}; } Status BufferHubQueue::RemoveBuffer(size_t slot) { ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot); if (buffers_[slot]) { for (const auto& event_source : buffers_[slot]->GetEventSources()) { const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr); if (ret < 0) { ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__, strerror(-ret)); return ErrorStatus(-ret); } } // Trigger OnBufferRemoved callback if registered. if (on_buffer_removed_) on_buffer_removed_(buffers_[slot]); buffers_[slot] = nullptr; capacity_--; } return {}; } Status BufferHubQueue::Enqueue(Entry entry) { if (!is_full()) { // Find and remove the enqueued buffer from unavailable_buffers_slot if // exist. auto enqueued_buffer_iter = std::find_if( unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(), [&entry](size_t slot) -> bool { return slot == entry.slot; }); if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) { unavailable_buffers_slot_.erase(enqueued_buffer_iter); } available_buffers_.push(std::move(entry)); // Trigger OnBufferAvailable callback if registered. if (on_buffer_available_) on_buffer_available_(); return {}; } else { ALOGE("%s: Buffer queue is full!", __FUNCTION__); return ErrorStatus(E2BIG); } } Status> BufferHubQueue::Dequeue(int timeout, size_t* slot) { ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout); PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count()); if (count() == 0) { if (!WaitForBuffers(timeout)) return ErrorStatus(ETIMEDOUT); } auto& entry = available_buffers_.top(); PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), entry.slot); std::shared_ptr buffer = std::move(entry.buffer); *slot = entry.slot; available_buffers_.pop(); unavailable_buffers_slot_.push_back(*slot); return {std::move(buffer)}; } void BufferHubQueue::SetBufferAvailableCallback( BufferAvailableCallback callback) { on_buffer_available_ = callback; } void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) { on_buffer_removed_ = callback; } pdx::Status BufferHubQueue::FreeAllBuffers() { // Clear all available buffers. while (!available_buffers_.empty()) available_buffers_.pop(); pdx::Status last_error; // No error. // Clear all buffers this producer queue is tracking. for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { if (buffers_[slot] != nullptr) { auto status = RemoveBuffer(slot); if (!status) { ALOGE( "ProducerQueue::FreeAllBuffers: Failed to remove buffer at " "slot=%zu.", slot); last_error = status.error_status(); } } } return last_error; } ProducerQueue::ProducerQueue(LocalChannelHandle handle) : BASE(std::move(handle)) { auto status = ImportQueue(); if (!status) { ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s", status.GetErrorMessage().c_str()); Close(-status.error()); } } ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage) : BASE(BufferHubRPC::kClientPath) { auto status = InvokeRemoteMethod(config, usage); if (!status) { ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s", status.GetErrorMessage().c_str()); Close(-status.error()); return; } SetupQueue(status.get()); } Status> ProducerQueue::AllocateBuffers( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count) { if (buffer_count == 0) { return {std::vector()}; } if (capacity() + buffer_count > kMaxQueueCapacity) { ALOGE( "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot " "allocate %zu more buffer(s).", capacity(), buffer_count); return ErrorStatus(E2BIG); } Status>> status = InvokeRemoteMethod( width, height, layer_count, format, usage, buffer_count); if (!status) { ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s", status.GetErrorMessage().c_str()); return status.error_status(); } auto buffer_handle_slots = status.take(); LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count, "BufferHubRPC::ProducerQueueAllocateBuffers should " "return %zu buffer handle(s), but returned %zu instead.", buffer_count, buffer_handle_slots.size()); std::vector buffer_slots; buffer_slots.reserve(buffer_count); // Bookkeeping for each buffer. for (auto& hs : buffer_handle_slots) { auto& buffer_handle = hs.first; size_t buffer_slot = hs.second; // Note that import might (though very unlikely) fail. If so, buffer_handle // will be closed and included in returned buffer_slots. if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)), buffer_slot)) { ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu", buffer_slot); buffer_slots.push_back(buffer_slot); } } if (buffer_slots.size() != buffer_count) { // Error out if the count of imported buffer(s) is not correct. ALOGE( "ProducerQueue::AllocateBuffers: requested to import %zu " "buffers, but actually imported %zu buffers.", buffer_count, buffer_slots.size()); return ErrorStatus(ENOMEM); } return {std::move(buffer_slots)}; } Status ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage) { // We only allocate one buffer at a time. constexpr size_t buffer_count = 1; auto status = AllocateBuffers(width, height, layer_count, format, usage, buffer_count); if (!status) { ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s", status.GetErrorMessage().c_str()); return status.error_status(); } return {status.get()[0]}; } Status ProducerQueue::AddBuffer( const std::shared_ptr& buffer, size_t slot) { ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", id(), buffer->id(), slot); // For producer buffer, we need to enqueue the newly added buffer // immediately. Producer queue starts with all buffers in available state. auto status = BufferHubQueue::AddBuffer(buffer, slot); if (!status) return status; return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); } Status ProducerQueue::InsertBuffer( const std::shared_ptr& buffer) { if (buffer == nullptr || !BufferHubDefs::isClientGained(buffer->buffer_state(), buffer->client_state_mask())) { ALOGE( "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in " "gained state."); return ErrorStatus(EINVAL); } auto status_or_slot = InvokeRemoteMethod( buffer->cid()); if (!status_or_slot) { ALOGE( "ProducerQueue::InsertBuffer: Failed to insert producer buffer: " "buffer_cid=%d, error: %s.", buffer->cid(), status_or_slot.GetErrorMessage().c_str()); return status_or_slot.error_status(); } size_t slot = status_or_slot.get(); // Note that we are calling AddBuffer() from the base class to explicitly // avoid Enqueue() the ProducerBuffer. auto status = BufferHubQueue::AddBuffer(buffer, slot); if (!status) { ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.", status.GetErrorMessage().c_str()); return status.error_status(); } return {slot}; } Status ProducerQueue::RemoveBuffer(size_t slot) { auto status = InvokeRemoteMethod(slot); if (!status) { ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return status.error_status(); } return BufferHubQueue::RemoveBuffer(slot); } Status> ProducerQueue::Dequeue( int timeout, size_t* slot, LocalHandle* release_fence) { DvrNativeBufferMetadata canonical_meta; return Dequeue(timeout, slot, &canonical_meta, release_fence); } pdx::Status> ProducerQueue::Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, pdx::LocalHandle* release_fence, bool gain_posted_buffer) { ATRACE_NAME("ProducerQueue::Dequeue"); if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { ALOGE("%s: Invalid parameter.", __FUNCTION__); return ErrorStatus(EINVAL); } std::shared_ptr buffer; Status> dequeue_status = BufferHubQueue::Dequeue(timeout, slot); if (dequeue_status.ok()) { buffer = std::static_pointer_cast(dequeue_status.take()); } else { if (gain_posted_buffer) { Status> dequeue_unacquired_status = ProducerQueue::DequeueUnacquiredBuffer(slot); if (!dequeue_unacquired_status.ok()) { ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__, dequeue_unacquired_status.error()); return dequeue_unacquired_status.error_status(); } buffer = dequeue_unacquired_status.take(); } else { return dequeue_status.error_status(); } } const int ret = buffer->GainAsync(out_meta, release_fence, gain_posted_buffer); if (ret < 0 && ret != -EALREADY) return ErrorStatus(-ret); return {std::move(buffer)}; } Status> ProducerQueue::DequeueUnacquiredBuffer( size_t* slot) { if (unavailable_buffers_slot_.size() < 1) { ALOGE( "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in " "acquired state if exist.", __FUNCTION__); return ErrorStatus(ENOMEM); } // Find the first buffer that is not in acquired state from // unavailable_buffers_slot_. for (auto iter = unavailable_buffers_slot_.begin(); iter != unavailable_buffers_slot_.end(); iter++) { std::shared_ptr buffer = ProducerQueue::GetBuffer(*iter); if (buffer == nullptr) { ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__, static_cast(*slot)); return ErrorStatus(EIO); } if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) { *slot = *iter; unavailable_buffers_slot_.erase(iter); unavailable_buffers_slot_.push_back(*slot); ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d", __FUNCTION__, static_cast(*slot)); return {std::move(buffer)}; } } ALOGE( "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.", __FUNCTION__); return ErrorStatus(EBUSY); } pdx::Status ProducerQueue::TakeAsParcelable() { if (capacity() != 0) { ALOGE( "%s: producer queue can only be taken out as a parcelable when empty. " "Current queue capacity: %zu", __FUNCTION__, capacity()); return ErrorStatus(EINVAL); } std::unique_ptr channel = TakeChannel(); ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable()); // Here the queue parcelable is returned and holds the underlying system // resources backing the queue; while the original client channel of this // producer queue is destroyed in place so that this client can no longer // provide producer operations. return {std::move(queue_parcelable)}; } /*static */ std::unique_ptr ConsumerQueue::Import( LocalChannelHandle handle) { return std::unique_ptr(new ConsumerQueue(std::move(handle))); } ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) : BufferHubQueue(std::move(handle)) { auto status = ImportQueue(); if (!status) { ALOGE("%s: Failed to import queue: %s", __FUNCTION__, status.GetErrorMessage().c_str()); Close(-status.error()); } auto import_status = ImportBuffers(); if (import_status) { ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get()); } else { ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, import_status.GetErrorMessage().c_str()); } } Status ConsumerQueue::ImportBuffers() { auto status = InvokeRemoteMethod(); if (!status) { if (status.error() == EBADR) { ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__); return {0}; } else { ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return status.error_status(); } } int ret; Status last_error; size_t imported_buffers_count = 0; auto buffer_handle_slots = status.take(); for (auto& buffer_handle_slot : buffer_handle_slots) { ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__, buffer_handle_slot.first.value()); std::unique_ptr consumer_buffer = ConsumerBuffer::Import(std::move(buffer_handle_slot.first)); if (!consumer_buffer) { ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__, buffer_handle_slot.second); last_error = ErrorStatus(EPIPE); continue; } auto add_status = AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second); if (!add_status) { ALOGE("%s: Failed to add buffer: %s", __FUNCTION__, add_status.GetErrorMessage().c_str()); last_error = add_status; } else { imported_buffers_count++; } } if (imported_buffers_count > 0) return {imported_buffers_count}; else return last_error.error_status(); } Status ConsumerQueue::AddBuffer( const std::shared_ptr& buffer, size_t slot) { ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(), buffer->id(), slot); return BufferHubQueue::AddBuffer(buffer, slot); } Status> ConsumerQueue::Dequeue( int timeout, size_t* slot, void* meta, size_t user_metadata_size, LocalHandle* acquire_fence) { if (user_metadata_size != user_metadata_size_) { ALOGE( "%s: Metadata size (%zu) for the dequeuing buffer does not match " "metadata size (%zu) for the queue.", __FUNCTION__, user_metadata_size, user_metadata_size_); return ErrorStatus(EINVAL); } DvrNativeBufferMetadata canonical_meta; auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence); if (!status) return status.error_status(); if (meta && user_metadata_size) { void* metadata_src = reinterpret_cast(canonical_meta.user_metadata_ptr); if (metadata_src) { memcpy(meta, metadata_src, user_metadata_size); } else { ALOGW("%s: no user-defined metadata.", __FUNCTION__); } } return status; } Status> ConsumerQueue::Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, pdx::LocalHandle* acquire_fence) { ATRACE_NAME("ConsumerQueue::Dequeue"); if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) { ALOGE("%s: Invalid parameter.", __FUNCTION__); return ErrorStatus(EINVAL); } auto status = BufferHubQueue::Dequeue(timeout, slot); if (!status) return status.error_status(); auto buffer = std::static_pointer_cast(status.take()); const int ret = buffer->AcquireAsync(out_meta, acquire_fence); if (ret < 0) return ErrorStatus(-ret); return {std::move(buffer)}; } Status ConsumerQueue::OnBufferAllocated() { ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id()); auto status = ImportBuffers(); if (!status) { ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return ErrorStatus(status.error()); } else if (status.get() == 0) { ALOGW("%s: No new buffers allocated!", __FUNCTION__); return ErrorStatus(ENOBUFS); } else { ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__, status.get()); return {}; } } } // namespace dvr } // namespace android �����������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp�������������������������������������������0100644 0000000 0000000 00000004651 13756501735 023104� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/buffer_hub_queue_parcelable.h" #include #include namespace android { namespace dvr { template bool BufferHubQueueParcelable::IsValid() const { return !!channel_parcelable_ && channel_parcelable_->IsValid(); } template pdx::LocalChannelHandle BufferHubQueueParcelable::TakeChannelHandle() { if (!IsValid()) { ALOGE( "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel."); return {}; // Returns an empty channel handle. } // Take channel handle out of the parcelable and reset the parcelable. pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle(); // Now channel_parcelable_ should already be invalid, but reset it to release // the invalid parcelable object from unique_ptr. channel_parcelable_ = nullptr; return handle; } template status_t BufferHubQueueParcelable::writeToParcel(Parcel* parcel) const { if (!IsValid()) { ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel."); return -EINVAL; } status_t res = parcel->writeUint32(Magic); if (res != OK) { ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic."); return res; } return channel_parcelable_->writeToParcel(parcel); } template status_t BufferHubQueueParcelable::readFromParcel(const Parcel* parcel) { if (IsValid()) { ALOGE( "BufferHubQueueParcelable::readFromParcel: This parcelable object has " "been initialized already."); return -EINVAL; } uint32_t out_magic = 0; status_t res = OK; res = parcel->readUint32(&out_magic); if (res != OK) return res; if (out_magic != Magic) { ALOGE( "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, " "epxected: 0x%x", out_magic, Magic); return -EINVAL; } // (Re)Alocate channel parcelable object. channel_parcelable_ = std::make_unique(); return channel_parcelable_->readFromParcel(parcel); } template class BufferHubQueueParcelable< BufferHubQueueParcelableMagic::Producer>; template class BufferHubQueueParcelable< BufferHubQueueParcelableMagic::Consumer>; } // namespace dvr } // namespace android ���������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016333� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/����������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020005� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/dvr/������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020600� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h�����������������������������0100644 0000000 0000000 00000046553 13756501735 025634� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ #define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ #include #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Weverything" #endif // The following headers are included without checking every warning. // TODO(b/72172820): Remove the workaround once we have enforced -Weverything // in these headers and their dependencies. #include #include #include #include #include #include #include #if defined(__clang__) #pragma clang diagnostic pop #endif #include #include #include namespace android { namespace dvr { class ConsumerQueue; // |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are // automatically re-requeued when released by the remote side. class BufferHubQueue : public pdx::Client { public: using BufferAvailableCallback = std::function; using BufferRemovedCallback = std::function&)>; virtual ~BufferHubQueue() {} // Creates a new consumer queue that is attached to the producer. Returns // a new consumer queue client or nullptr on failure. std::unique_ptr CreateConsumerQueue(); // Creates a new consumer queue that is attached to the producer. This queue // sets each of its imported consumer buffers to the ignored state to avoid // participation in lifecycle events. std::unique_ptr CreateSilentConsumerQueue(); // Returns whether the buffer queue is in async mode. bool is_async() const { return is_async_; } // Returns the default buffer width of this buffer queue. uint32_t default_width() const { return default_width_; } // Returns the default buffer height of this buffer queue. uint32_t default_height() const { return default_height_; } // Returns the default buffer format of this buffer queue. uint32_t default_format() const { return default_format_; } // Creates a new consumer in handle form for immediate transport over RPC. pdx::Status CreateConsumerQueueHandle( bool silent = false); // Creates a new consumer in parcelable form for immediate transport over // Binder. pdx::Status CreateConsumerQueueParcelable( bool silent = false); // Returns the number of buffers avaiable for dequeue. size_t count() const { return available_buffers_.size(); } // Returns the total number of buffers that the queue is tracking. size_t capacity() const { return capacity_; } // Returns the size of metadata structure associated with this queue. size_t metadata_size() const { return user_metadata_size_; } // Returns whether the buffer queue is full. bool is_full() const { return available_buffers_.size() >= kMaxQueueCapacity; } // Returns whether the buffer queue is connected to bufferhubd. bool is_connected() const { return !!GetChannel(); } int GetBufferId(size_t slot) const { return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id() : -1; } std::shared_ptr GetBuffer(size_t slot) const { return buffers_[slot]; } pdx::Status GetEventMask(int events) { if (auto* client_channel = GetChannel()) { return client_channel->GetEventMask(events); } else { return pdx::ErrorStatus(EINVAL); } } // Returns an fd that signals pending queue events using // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be // called to handle pending queue events. int queue_fd() const { return epoll_fd_.Get(); } // Handles any pending events, returning available buffers to the queue and // reaping disconnected buffers. Returns true if successful, false if an error // occurred. bool HandleQueueEvents() { return WaitForBuffers(0); } // Set buffer event callbacks, which are std::function wrappers. The caller is // responsible for ensuring the validity of these callbacks' callable targets. void SetBufferAvailableCallback(BufferAvailableCallback callback); void SetBufferRemovedCallback(BufferRemovedCallback callback); // The queue tracks at most this many buffers. static constexpr size_t kMaxQueueCapacity = android::BufferQueueDefs::NUM_BUFFER_SLOTS; static constexpr int kNoTimeOut = -1; int id() const { return id_; } bool hung_up() const { return hung_up_; } protected: explicit BufferHubQueue(pdx::LocalChannelHandle channel); explicit BufferHubQueue(const std::string& endpoint_path); // Imports the queue parameters by querying BufferHub for the parameters for // this channel. pdx::Status ImportQueue(); // Sets up the queue with the given parameters. void SetupQueue(const QueueInfo& queue_info); // Register a buffer for management by the queue. Used by subclasses to add a // buffer to internal bookkeeping. pdx::Status AddBuffer(const std::shared_ptr& buffer, size_t slot); // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only // to deregister a buffer for epoll and internal bookkeeping. virtual pdx::Status RemoveBuffer(size_t slot); // Free all buffers that belongs to this queue. Can only be called from // producer side. virtual pdx::Status FreeAllBuffers(); // Dequeue a buffer from the free queue, blocking until one is available. The // timeout argument specifies the number of milliseconds that |Dequeue()| will // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely, // while specifying a timeout equal to zero cause Dequeue() to return // immediately, even if no buffers are available. pdx::Status> Dequeue(int timeout, size_t* slot); // Waits for buffers to become available and adds them to the available queue. bool WaitForBuffers(int timeout); pdx::Status HandleBufferEvent(size_t slot, int event_fd, int poll_events); pdx::Status HandleQueueEvent(int poll_events); // Entry in the priority queue of available buffers that stores related // per-buffer data. struct Entry { Entry() : slot(0) {} Entry(const std::shared_ptr& in_buffer, size_t in_slot, uint64_t in_index) : buffer(in_buffer), slot(in_slot), index(in_index) {} Entry(const std::shared_ptr& in_buffer, std::unique_ptr in_metadata, pdx::LocalHandle in_fence, size_t in_slot) : buffer(in_buffer), metadata(std::move(in_metadata)), fence(std::move(in_fence)), slot(in_slot) {} Entry(Entry&&) = default; Entry& operator=(Entry&&) = default; std::shared_ptr buffer; std::unique_ptr metadata; pdx::LocalHandle fence; size_t slot; uint64_t index; }; struct EntryComparator { bool operator()(const Entry& lhs, const Entry& rhs) { return lhs.index > rhs.index; } }; // Enqueues a buffer to the available list (Gained for producer or Acquireed // for consumer). pdx::Status Enqueue(Entry entry); // Called when a buffer is allocated remotely. virtual pdx::Status OnBufferAllocated() { return {}; } // Size of the metadata that buffers in this queue cary. size_t user_metadata_size_{0}; // Buffers and related data that are available for dequeue. std::priority_queue, EntryComparator> available_buffers_; // Slot of the buffers that are not available for normal dequeue. For example, // the slot of posted or acquired buffers in the perspective of a producer. std::vector unavailable_buffers_slot_; private: void Initialize(); // Special epoll data field indicating that the epoll event refers to the // queue. static constexpr int64_t kEpollQueueEventIndex = -1; static constexpr size_t kMaxEvents = 128; // The u64 data field of an epoll event is interpreted as int64_t: // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific // element of |buffers_| as a direct index; static bool is_buffer_event_index(int64_t index) { return index >= 0 && index < static_cast(BufferHubQueue::kMaxQueueCapacity); } // When |index| == kEpollQueueEventIndex it refers to the queue itself. static bool is_queue_event_index(int64_t index) { return index == BufferHubQueue::kEpollQueueEventIndex; } // Whether the buffer queue is operating in Async mode. // From GVR's perspective of view, this means a buffer can be acquired // asynchronously by the compositor. // From Android Surface's perspective of view, this is equivalent to // IGraphicBufferProducer's async mode. When in async mode, a producer // will never block even if consumer is running slow. bool is_async_{false}; // Default buffer width that is set during ProducerQueue's creation. uint32_t default_width_{1}; // Default buffer height that is set during ProducerQueue's creation. uint32_t default_height_{1}; // Default buffer format that is set during ProducerQueue's creation. uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888 // Tracks the buffers belonging to this queue. Buffers are stored according to // "slot" in this vector. Each slot is a logical id of the buffer within this // queue regardless of its queue position or presence in the ring buffer. std::array, kMaxQueueCapacity> buffers_; // Keeps track with how many buffers have been added into the queue. size_t capacity_{0}; // Epoll fd used to manage buffer events. EpollFileDescriptor epoll_fd_; // Flag indicating that the other side hung up. For ProducerQueues this // triggers when BufferHub dies or explicitly closes the queue channel. For // ConsumerQueues this can either mean the same or that the ProducerQueue on // the other end hung up. bool hung_up_{false}; // Global id for the queue that is consistent across processes. int id_{-1}; // Buffer event callbacks BufferAvailableCallback on_buffer_available_; BufferRemovedCallback on_buffer_removed_; BufferHubQueue(const BufferHubQueue&) = delete; void operator=(BufferHubQueue&) = delete; }; class ProducerQueue : public pdx::ClientBase { public: // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits // in |usage_clear_mask| will be automatically masked off. Note that // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer // allocation through this producer queue shall not have any of the usage bits // in |usage_deny_set_mask| set. Allocation calls violating this will be // rejected. All buffer allocation through this producer queue must have all // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating // this will be rejected. Note that |usage_deny_set_mask| and // |usage_deny_clear_mask| shall not conflict with each other. Such // configuration will be treated as invalid input on creation. static std::unique_ptr Create( const ProducerQueueConfig& config, const UsagePolicy& usage) { return BASE::Create(config, usage); } // Import a ProducerQueue from a channel handle. static std::unique_ptr Import(pdx::LocalChannelHandle handle) { return BASE::Create(std::move(handle)); } // Get a producer buffer. Note that the method doesn't check whether the // buffer slot has a valid buffer that has been allocated already. When no // buffer has been imported before it returns nullptr; otherwise it returns // a shared pointer to a ProducerBuffer. std::shared_ptr GetBuffer(size_t slot) const { return std::static_pointer_cast( BufferHubQueue::GetBuffer(slot)); } // Batch allocate buffers. Once allocated, producer buffers are automatically // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED // state). Upon success, returns a list of slots for each buffer allocated. pdx::Status> AllocateBuffers( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count); // Allocate producer buffer to populate the queue. Once allocated, a producer // buffer is automatically enqueue'd into the ProducerQueue and available to // use (i.e. in GAINED state). Upon success, returns the slot number for the // buffer allocated. pdx::Status AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage); // Add a producer buffer to populate the queue. Once added, a producer buffer // is available to use (i.e. in GAINED state). pdx::Status AddBuffer(const std::shared_ptr& buffer, size_t slot); // Inserts a ProducerBuffer into the queue. On success, the method returns the // |slot| number where the new buffer gets inserted. Note that the buffer // being inserted should be in Gain'ed state prior to the call and it's // considered as already Dequeued when the function returns. pdx::Status InsertBuffer( const std::shared_ptr& buffer); // Remove producer buffer from the queue. pdx::Status RemoveBuffer(size_t slot) override; // Free all buffers on this producer queue. pdx::Status FreeAllBuffers() override { return BufferHubQueue::FreeAllBuffers(); } // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, // and caller should call Post() once it's done writing to release the buffer // to the consumer side. // @return a buffer in gained state, which was originally in released state. pdx::Status> Dequeue( int timeout, size_t* slot, pdx::LocalHandle* release_fence); // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, // and caller should call Post() once it's done writing to release the buffer // to the consumer side. // // @param timeout to dequeue a buffer. // @param slot is the slot of the output ProducerBuffer. // @param release_fence for gaining a buffer. // @param out_meta metadata of the output buffer. // @param gain_posted_buffer whether to gain posted buffer if no released // buffer is available to gain. // @return a buffer in gained state, which was originally in released state if // gain_posted_buffer is false, or in posted/released state if // gain_posted_buffer is true. // TODO(b/112007999): gain_posted_buffer true is only used to prevent // libdvrtracking from starving when there are non-responding clients. This // gain_posted_buffer param can be removed once libdvrtracking start to use // the new AHardwareBuffer API. pdx::Status> Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, pdx::LocalHandle* release_fence, bool gain_posted_buffer = false); // Enqueues a producer buffer in the queue. pdx::Status Enqueue(const std::shared_ptr& buffer, size_t slot, uint64_t index) { return BufferHubQueue::Enqueue({buffer, slot, index}); } // Takes out the current producer queue as a binder parcelable object. Note // that the queue must be empty to be exportable. After successful export, the // producer queue client should no longer be used. pdx::Status TakeAsParcelable(); private: friend BASE; // Constructors are automatically exposed through ProducerQueue::Create(...) // static template methods inherited from ClientBase, which take the same // arguments as the constructors. explicit ProducerQueue(pdx::LocalChannelHandle handle); ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage); // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, // and caller should call Post() once it's done writing to release the buffer // to the consumer side. // // @param slot the slot of the returned buffer. // @return a buffer in gained state, which was originally in posted state or // released state. pdx::Status> DequeueUnacquiredBuffer( size_t* slot); }; class ConsumerQueue : public BufferHubQueue { public: // Get a consumer buffer. Note that the method doesn't check whether the // buffer slot has a valid buffer that has been imported already. When no // buffer has been imported before it returns nullptr; otherwise returns a // shared pointer to a ConsumerBuffer. std::shared_ptr GetBuffer(size_t slot) const { return std::static_pointer_cast( BufferHubQueue::GetBuffer(slot)); } // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls // whether or not buffers are set to be ignored when imported. This may be // used to avoid participation in the buffer lifecycle by a consumer queue // that is only used to spawn other consumer queues, such as in an // intermediate service. static std::unique_ptr Import(pdx::LocalChannelHandle handle); // Import newly created buffers from the service side. // Returns number of buffers successfully imported or an error. pdx::Status ImportBuffers(); // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed // mode, and caller should call Releasse() once it's done writing to release // the buffer to the producer side. |meta| is passed along from BufferHub, // The user of ProducerBuffer is responsible with making sure that the // Dequeue() is done with the corect metadata type and size with those used // when the buffer is orignally created. template pdx::Status> Dequeue( int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) { return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence); } pdx::Status> Dequeue( int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) { return Dequeue(timeout, slot, nullptr, 0, acquire_fence); } pdx::Status> Dequeue( int timeout, size_t* slot, void* meta, size_t user_metadata_size, pdx::LocalHandle* acquire_fence); pdx::Status> Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, pdx::LocalHandle* acquire_fence); private: friend BufferHubQueue; explicit ConsumerQueue(pdx::LocalChannelHandle handle); // Add a consumer buffer to populate the queue. Once added, a consumer buffer // is NOT available to use until the producer side |Post| it. |WaitForBuffers| // will catch the |Post| and |Acquire| the buffer to make it available for // consumer. pdx::Status AddBuffer(const std::shared_ptr& buffer, size_t slot); pdx::Status OnBufferAllocated() override; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h�������������������������0100644 0000000 0000000 00000005106 13756501735 026435� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ #define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Weverything" #endif // The following headers are included without checking every warning. // TODO(b/72172820): Remove the workaround once we have enforced -Weverything // in these headers and their dependencies. #include #if defined(__clang__) #pragma clang diagnostic pop #endif namespace android { namespace dvr { enum BufferHubQueueParcelableMagic : uint32_t { Producer = 0x62687170, // 'bhqp' Consumer = 0x62687163, // 'bhqc' }; template class BufferHubQueueParcelable : public Parcelable { public: BufferHubQueueParcelable() = default; BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default; BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept { channel_parcelable_ = std::move(other.channel_parcelable_); return *this; } // Constructs an parcelable contains the channel parcelable. explicit BufferHubQueueParcelable( std::unique_ptr channel_parcelable) : channel_parcelable_(std::move(channel_parcelable)) {} BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete; void operator=(const BufferHubQueueParcelable&) = delete; bool IsValid() const; // Returns a channel handle constructed from this parcelable object and takes // the ownership of all resources from the parcelable object. pdx::LocalChannelHandle TakeChannelHandle(); // Serializes the queue parcelable into the given parcel. Note that no system // resources are getting duplicated, nor did the parcel takes ownership of the // queue parcelable. Thus, the parcelable object must remain valid for the // lifetime of the parcel. status_t writeToParcel(Parcel* parcel) const override; // Deserialize the queue parcelable from the given parcel. Note that system // resources are duplicated from the parcel into the queue parcelable. Returns // error if the targeting parcelable object is already valid. status_t readFromParcel(const Parcel* parcel) override; private: std::unique_ptr channel_parcelable_; }; using ProducerQueueParcelable = BufferHubQueueParcelable; using ConsumerQueueParcelable = BufferHubQueueParcelable; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h�������������������������������0100644 0000000 0000000 00000002553 13756501735 025323� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ #define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ #include #include #include namespace android { namespace dvr { class EpollFileDescriptor { public: static const int CTL_ADD = EPOLL_CTL_ADD; static const int CTL_MOD = EPOLL_CTL_MOD; static const int CTL_DEL = EPOLL_CTL_DEL; EpollFileDescriptor() : fd_(-1) {} // Constructs an EpollFileDescriptor from an integer file descriptor and // takes ownership. explicit EpollFileDescriptor(int fd) : fd_(fd) {} bool IsValid() const { return fd_.get() >= 0; } int Create() { if (IsValid()) { ALOGW("epoll fd has already been created."); return -EALREADY; } fd_.reset(epoll_create1(EPOLL_CLOEXEC)); if (fd_.get() < 0) return -errno; else return 0; } int Control(int op, int target_fd, epoll_event* ev) { if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0) return -errno; else return 0; } int Wait(epoll_event* events, int maxevents, int timeout) { int ret = epoll_wait(fd_.get(), events, maxevents, timeout); if (ret < 0) return -errno; else return ret; } int Get() const { return fd_.get(); } private: base::unique_fd fd_; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/��������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016052� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/Android.bp����������������������������������������������������������0100644 0000000 0000000 00000002154 13756501735 017754� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� header_libraries = [ "libdvr_headers", ] shared_libraries = [ "libbase", "libbinder", "libbufferhubqueue", "libcutils", "libgui", "liblog", "libhardware", "libui", "libutils", "libnativewindow", "libpdx_default_transport", ] static_libraries = [ "libchrome", "libdvrcommon", "libperformance", ] cc_test { srcs: ["buffer_hub_queue-test.cpp"], header_libs: header_libraries, static_libs: static_libraries, shared_libs: shared_libraries, cflags: [ "-DLOG_TAG=\"buffer_hub_queue-test\"", "-DTRACE=0", "-O0", "-g", "-Wall", "-Werror", "-Wno-error=sign-compare", // to fix later ], name: "buffer_hub_queue-test", } cc_test { srcs: ["buffer_hub_queue_producer-test.cpp"], header_libs: header_libraries, static_libs: static_libraries, shared_libs: shared_libraries, cflags: [ "-DLOG_TAG=\"buffer_hub_queue_producer-test\"", "-DTRACE=0", "-O0", "-g", "-Wall", "-Werror", ], name: "buffer_hub_queue_producer-test", } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp�������������������������������������������0100644 0000000 0000000 00000113046 13756501735 023050� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #include #include // Enable/disable debug logging. #define TRACE 0 namespace android { namespace dvr { using pdx::LocalChannelHandle; using pdx::LocalHandle; namespace { constexpr uint32_t kBufferWidth = 100; constexpr uint32_t kBufferHeight = 1; constexpr uint32_t kBufferLayerCount = 1; constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB; constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY; constexpr int kTimeoutMs = 100; constexpr int kNoTimeout = 0; class BufferHubQueueTest : public ::testing::Test { public: bool CreateProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage) { producer_queue_ = ProducerQueue::Create(config, usage); return producer_queue_ != nullptr; } bool CreateConsumerQueue() { if (producer_queue_) { consumer_queue_ = producer_queue_->CreateConsumerQueue(); return consumer_queue_ != nullptr; } else { return false; } } bool CreateQueues(const ProducerQueueConfig& config, const UsagePolicy& usage) { return CreateProducerQueue(config, usage) && CreateConsumerQueue(); } void AllocateBuffer(size_t* slot_out = nullptr) { // Create producer buffer. auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage); ASSERT_TRUE(status.ok()); size_t slot = status.take(); if (slot_out) *slot_out = slot; } bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) { pollfd pfd{queue->queue_fd(), POLLIN, 0}; int ret; do { ret = poll(&pfd, 1, timeout_ms); } while (ret == -1 && errno == EINTR); if (ret < 0) { ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(), strerror(errno)); return false; } else if (ret == 0) { return false; } return queue->HandleQueueEvents(); } protected: ProducerQueueConfigBuilder config_builder_; std::unique_ptr producer_queue_; std::unique_ptr consumer_queue_; }; TEST_F(BufferHubQueueTest, TestDequeue) { const int64_t nb_dequeue_times = 16; ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); // Allocate only one buffer. AllocateBuffer(); // But dequeue multiple times. for (int64_t i = 0; i < nb_dequeue_times; i++) { size_t slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; // Producer gains a buffer. auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // Producer posts the buffer. mi.index = i; EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); // Consumer acquires a buffer. auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); EXPECT_EQ(mi.index, i); EXPECT_EQ(mo.index, i); // Consumer releases the buffer. EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); } } TEST_F(BufferHubQueueTest, TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) { ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); // Allocate 3 buffers to use. const size_t test_queue_capacity = 3; for (int64_t i = 0; i < test_queue_capacity; i++) { AllocateBuffer(); } EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); size_t producer_slot, consumer_slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; // Producer posts 2 buffers and remember their posted sequence. std::deque posted_slots; for (int64_t i = 0; i < 2; i++) { auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // Producer should not be gaining posted buffer when there are still // available buffers to gain. auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), producer_slot); EXPECT_EQ(found_iter, posted_slots.end()); posted_slots.push_back(producer_slot); // Producer posts the buffer. mi.index = i; EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); } // Consumer acquires one buffer. auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence); EXPECT_TRUE(c1_status.ok()); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); // Consumer should get the oldest posted buffer. No checks here. // posted_slots[0] should be in acquired state now. EXPECT_EQ(mo.index, 0); // Consumer releases the buffer. EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); // posted_slots[0] should be in released state now. // Producer gain and post 2 buffers. for (int64_t i = 0; i < 2; i++) { auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // The gained buffer should be the one in released state or the one haven't // been use. EXPECT_NE(posted_slots[1], producer_slot); mi.index = i + 2; EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); } // Producer gains a buffer. auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // The gained buffer should be the oldest posted buffer. EXPECT_EQ(posted_slots[1], producer_slot); // Producer posts the buffer. mi.index = 4; EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); } TEST_F(BufferHubQueueTest, TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) { ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); // Allocate 4 buffers to use. const size_t test_queue_capacity = 4; for (int64_t i = 0; i < test_queue_capacity; i++) { AllocateBuffer(); } EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); // Post all allowed buffers and remember their posted sequence. std::deque posted_slots; for (int64_t i = 0; i < test_queue_capacity; i++) { size_t slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; // Producer gains a buffer. auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // Producer should not be gaining posted buffer when there are still // available buffers to gain. auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot); EXPECT_EQ(found_iter, posted_slots.end()); posted_slots.push_back(slot); // Producer posts the buffer. mi.index = i; EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); } // Gain posted buffers in sequence. const int64_t nb_dequeue_all_times = 2; for (int j = 0; j < nb_dequeue_all_times; ++j) { for (int i = 0; i < test_queue_capacity; ++i) { size_t slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; // Producer gains a buffer. auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // The gained buffer should be the oldest posted buffer. EXPECT_EQ(posted_slots[i], slot); // Producer posts the buffer. mi.index = i + test_queue_capacity * (j + 1); EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); } } } TEST_F(BufferHubQueueTest, TestProducerConsumer) { const size_t kBufferCount = 16; size_t slot; DvrNativeBufferMetadata mi, mo; LocalHandle fence; ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); for (size_t i = 0; i < kBufferCount; i++) { AllocateBuffer(); // Producer queue has all the available buffers on initialize. ASSERT_EQ(producer_queue_->count(), i + 1); ASSERT_EQ(producer_queue_->capacity(), i + 1); // Consumer queue has no avaiable buffer on initialize. ASSERT_EQ(consumer_queue_->count(), 0U); // Consumer queue does not import buffers until a dequeue is issued. ASSERT_EQ(consumer_queue_->capacity(), i); // Dequeue returns timeout since no buffer is ready to consumer, but // this implicitly triggers buffer import and bump up |capacity|. auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence); ASSERT_FALSE(status.ok()); ASSERT_EQ(ETIMEDOUT, status.error()); ASSERT_EQ(consumer_queue_->capacity(), i + 1); } // Use eventfd as a stand-in for a fence. LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); for (size_t i = 0; i < kBufferCount; i++) { // First time there is no buffer available to dequeue. auto consumer_status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence); ASSERT_FALSE(consumer_status.ok()); ASSERT_EQ(consumer_status.error(), ETIMEDOUT); // Make sure Producer buffer is POSTED so that it's ready to Accquire // in the consumer's Dequeue() function. auto producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(producer_status.ok()); auto producer = producer_status.take(); ASSERT_NE(nullptr, producer); mi.index = static_cast(i); ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0); // Second time the just the POSTED buffer should be dequeued. consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(consumer_status.ok()); EXPECT_TRUE(fence.IsValid()); auto consumer = consumer_status.take(); ASSERT_NE(nullptr, consumer); ASSERT_EQ(mi.index, mo.index); } } TEST_F(BufferHubQueueTest, TestInsertBuffer) { ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); consumer_queue_ = producer_queue_->CreateConsumerQueue(); ASSERT_TRUE(consumer_queue_ != nullptr); EXPECT_EQ(producer_queue_->capacity(), 0); EXPECT_EQ(consumer_queue_->capacity(), 0); std::shared_ptr p1 = ProducerBuffer::Create( kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0); ASSERT_TRUE(p1 != nullptr); ASSERT_EQ(p1->GainAsync(), 0); // Inserting a posted buffer will fail. DvrNativeBufferMetadata meta; EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0); auto status_or_slot = producer_queue_->InsertBuffer(p1); EXPECT_FALSE(status_or_slot.ok()); EXPECT_EQ(status_or_slot.error(), EINVAL); // Inserting a gained buffer will succeed. std::shared_ptr p2 = ProducerBuffer::Create( kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage); ASSERT_EQ(p2->GainAsync(), 0); ASSERT_TRUE(p2 != nullptr); status_or_slot = producer_queue_->InsertBuffer(p2); EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage(); // This is the first buffer inserted, should take slot 0. size_t slot = status_or_slot.get(); EXPECT_EQ(slot, 0); // Wait and expect the consumer to kick up the newly inserted buffer. WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); EXPECT_EQ(consumer_queue_->capacity(), 1ULL); } TEST_F(BufferHubQueueTest, TestRemoveBuffer) { ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); DvrNativeBufferMetadata mo; // Allocate buffers. const size_t kBufferCount = 4u; for (size_t i = 0; i < kBufferCount; i++) { AllocateBuffer(); } ASSERT_EQ(kBufferCount, producer_queue_->count()); ASSERT_EQ(kBufferCount, producer_queue_->capacity()); consumer_queue_ = producer_queue_->CreateConsumerQueue(); ASSERT_NE(nullptr, consumer_queue_); // Check that buffers are correctly imported on construction. EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); EXPECT_EQ(0u, consumer_queue_->count()); // Dequeue all the buffers and keep track of them in an array. This prevents // the producer queue ring buffer ref counts from interfering with the tests. struct Entry { std::shared_ptr buffer; LocalHandle fence; size_t slot; }; std::array buffers; for (size_t i = 0; i < kBufferCount; i++) { Entry* entry = &buffers[i]; auto producer_status = producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence); ASSERT_TRUE(producer_status.ok()); entry->buffer = producer_status.take(); ASSERT_NE(nullptr, entry->buffer); } // Remove a buffer and make sure both queues reflect the change. ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot)); EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); // As long as the removed buffer is still alive the consumer queue won't know // its gone. EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); // Release the removed buffer. buffers[0].buffer = nullptr; // Now the consumer queue should know it's gone. EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs)); ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity()); // Allocate a new buffer. This should take the first empty slot. size_t slot; AllocateBuffer(&slot); ALOGE_IF(TRACE, "ALLOCATE %zu", slot); EXPECT_EQ(buffers[0].slot, slot); EXPECT_EQ(kBufferCount, producer_queue_->capacity()); // The consumer queue should pick up the new buffer. EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity()); EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); // Remove and allocate a buffer. ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot)); EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); buffers[1].buffer = nullptr; AllocateBuffer(&slot); ALOGE_IF(TRACE, "ALLOCATE %zu", slot); EXPECT_EQ(buffers[1].slot, slot); EXPECT_EQ(kBufferCount, producer_queue_->capacity()); // The consumer queue should pick up the new buffer but the count shouldn't // change. EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); // Remove and allocate a buffer, but don't free the buffer right away. ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot)); EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity()); AllocateBuffer(&slot); ALOGE_IF(TRACE, "ALLOCATE %zu", slot); EXPECT_EQ(buffers[2].slot, slot); EXPECT_EQ(kBufferCount, producer_queue_->capacity()); EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); // Release the producer buffer to trigger a POLLHUP event for an already // removed buffer. buffers[2].buffer = nullptr; EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); EXPECT_FALSE(consumer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); } TEST_F(BufferHubQueueTest, TestMultipleConsumers) { // ProducerConfigureBuilder doesn't set Metadata{size}, which means there // is no metadata associated with this BufferQueue's buffer. ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); // Allocate buffers. const size_t kBufferCount = 4u; for (size_t i = 0; i < kBufferCount; i++) { AllocateBuffer(); } ASSERT_EQ(kBufferCount, producer_queue_->count()); // Build a silent consumer queue to test multi-consumer queue features. auto silent_queue = producer_queue_->CreateSilentConsumerQueue(); ASSERT_NE(nullptr, silent_queue); // Check that silent queue doesn't import buffers on creation. EXPECT_EQ(silent_queue->capacity(), 0U); // Dequeue and post a buffer. size_t slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; auto producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(producer_status.ok()); auto producer_buffer = producer_status.take(); ASSERT_NE(producer_buffer, nullptr); EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0); // After post, check the number of remaining available buffers. EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); // Currently we expect no buffer to be available prior to calling // WaitForBuffers/HandleQueueEvents. // TODO(eieio): Note this behavior may change in the future. EXPECT_EQ(silent_queue->count(), 0U); EXPECT_FALSE(silent_queue->HandleQueueEvents()); EXPECT_EQ(silent_queue->count(), 0U); // Build a new consumer queue to test multi-consumer queue features. consumer_queue_ = silent_queue->CreateConsumerQueue(); ASSERT_NE(consumer_queue_, nullptr); // Check that buffers are correctly imported on construction. EXPECT_EQ(consumer_queue_->capacity(), kBufferCount); // Buffers are only imported, but their availability is not checked until // first call to Dequeue(). EXPECT_EQ(consumer_queue_->count(), 0U); // Reclaim released/ignored buffers. EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); usleep(10000); WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs); EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); // Post another buffer. producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(producer_buffer, nullptr); EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0); // Verify that the consumer queue receives it. size_t consumer_queue_count = consumer_queue_->count(); WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); EXPECT_GT(consumer_queue_->count(), consumer_queue_count); // Save the current consumer queue buffer count to compare after the dequeue. consumer_queue_count = consumer_queue_->count(); // Dequeue and acquire/release (discard) buffers on the consumer end. auto consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(consumer_status.ok()); auto consumer_buffer = consumer_status.take(); ASSERT_NE(consumer_buffer, nullptr); consumer_buffer->Discard(); // Buffer should be returned to the producer queue without being handled by // the silent consumer queue. EXPECT_LT(consumer_queue_->count(), consumer_queue_count); EXPECT_EQ(producer_queue_->count(), kBufferCount - 2); WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs); EXPECT_EQ(producer_queue_->count(), kBufferCount - 1); } struct TestUserMetadata { char a; int32_t b; int64_t c; }; constexpr uint64_t kUserMetadataSize = static_cast(sizeof(TestUserMetadata)); TEST_F(BufferHubQueueTest, TestUserMetadata) { ASSERT_TRUE(CreateQueues( config_builder_.SetMetadata().Build(), UsagePolicy{})); AllocateBuffer(); std::vector user_metadata_list = { {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}}; for (auto user_metadata : user_metadata_list) { size_t slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // TODO(b/69469185): Test against metadata from consumer once we implement // release metadata properly. // EXPECT_EQ(mo.user_metadata_ptr, 0U); // EXPECT_EQ(mo.user_metadata_size, 0U); mi.user_metadata_size = kUserMetadataSize; mi.user_metadata_ptr = reinterpret_cast(&user_metadata); EXPECT_EQ(p1->PostAsync(&mi, {}), 0); auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize); auto out_user_metadata = reinterpret_cast(mo.user_metadata_ptr); EXPECT_EQ(user_metadata.a, out_user_metadata->a); EXPECT_EQ(user_metadata.b, out_user_metadata->b); EXPECT_EQ(user_metadata.c, out_user_metadata->c); // When release, empty metadata is also legit. mi.user_metadata_size = 0U; mi.user_metadata_ptr = 0U; c1->ReleaseAsync(&mi, {}); } } TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) { ASSERT_TRUE(CreateQueues( config_builder_.SetMetadata().Build(), UsagePolicy{})); AllocateBuffer(); TestUserMetadata user_metadata; size_t slot; LocalHandle fence; DvrNativeBufferMetadata mi, mo; auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); EXPECT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // Post with mismatched user metadata size will fail. But the producer buffer // itself should stay untouched. mi.user_metadata_ptr = reinterpret_cast(&user_metadata); mi.user_metadata_size = kUserMetadataSize + 1; EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG); // Post with the exact same user metdata size can success. mi.user_metadata_ptr = reinterpret_cast(&user_metadata); mi.user_metadata_size = kUserMetadataSize; EXPECT_EQ(p1->PostAsync(&mi, {}), 0); } TEST_F(BufferHubQueueTest, TestEnqueue) { ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); AllocateBuffer(); size_t slot; LocalHandle fence; DvrNativeBufferMetadata mo; auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); producer_queue_->Enqueue(p1, slot, 0ULL); auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_FALSE(c1_status.ok()); } TEST_F(BufferHubQueueTest, TestAllocateBuffer) { ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); size_t ps1; AllocateBuffer(); LocalHandle fence; DvrNativeBufferMetadata mi, mo; auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(p1, nullptr); // producer queue is exhausted size_t ps2; auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence); ASSERT_FALSE(p2_status.ok()); ASSERT_EQ(ETIMEDOUT, p2_status.error()); // dynamically add buffer. AllocateBuffer(); ASSERT_EQ(producer_queue_->count(), 1U); ASSERT_EQ(producer_queue_->capacity(), 2U); // now we can dequeue again p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence); ASSERT_TRUE(p2_status.ok()); auto p2 = p2_status.take(); ASSERT_NE(p2, nullptr); ASSERT_EQ(producer_queue_->count(), 0U); // p1 and p2 should have different slot number ASSERT_NE(ps1, ps2); // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers| // are called. So far consumer_queue_ should be empty. ASSERT_EQ(consumer_queue_->count(), 0U); int64_t seq = 1; mi.index = seq; ASSERT_EQ(p1->PostAsync(&mi, {}), 0); size_t cs1, cs2; auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence); ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); ASSERT_EQ(consumer_queue_->count(), 0U); ASSERT_EQ(consumer_queue_->capacity(), 2U); ASSERT_EQ(cs1, ps1); ASSERT_EQ(p2->PostAsync(&mi, {}), 0); auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence); ASSERT_TRUE(c2_status.ok()); auto c2 = c2_status.take(); ASSERT_NE(c2, nullptr); ASSERT_EQ(cs2, ps2); } TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) { ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); ASSERT_EQ(producer_queue_->capacity(), 0); auto status = producer_queue_->AllocateBuffers( kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage, /*buffer_count=*/2); ASSERT_TRUE(status.ok()); std::vector buffer_slots = status.take(); ASSERT_EQ(buffer_slots.size(), 2); ASSERT_EQ(producer_queue_->capacity(), 2); } TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) { ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); ASSERT_EQ(producer_queue_->capacity(), 0); auto status = producer_queue_->AllocateBuffers( kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage, /*buffer_count=*/0); ASSERT_TRUE(status.ok()); std::vector buffer_slots = status.take(); ASSERT_EQ(buffer_slots.size(), 0); ASSERT_EQ(producer_queue_->capacity(), 0); } TEST_F(BufferHubQueueTest, TestUsageSetMask) { const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE( CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0})); // When allocation, leave out |set_mask| from usage bits on purpose. auto status = producer_queue_->AllocateBuffer( kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage & ~set_mask); ASSERT_TRUE(status.ok()); LocalHandle fence; size_t slot; DvrNativeBufferMetadata mo; auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_EQ(p1->usage() & set_mask, set_mask); } TEST_F(BufferHubQueueTest, TestUsageClearMask) { const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE( CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0})); // When allocation, add |clear_mask| into usage bits on purpose. auto status = producer_queue_->AllocateBuffer( kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage | clear_mask); ASSERT_TRUE(status.ok()); LocalHandle fence; size_t slot; DvrNativeBufferMetadata mo; auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_EQ(p1->usage() & clear_mask, 0U); } TEST_F(BufferHubQueueTest, TestUsageDenySetMask) { const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{0, 0, deny_set_mask, 0})); // Now that |deny_set_mask| is illegal, allocation without those bits should // be able to succeed. auto status = producer_queue_->AllocateBuffer( kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage & ~deny_set_mask); ASSERT_TRUE(status.ok()); // While allocation with those bits should fail. status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage | deny_set_mask); ASSERT_FALSE(status.ok()); ASSERT_EQ(EINVAL, status.error()); } TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) { const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{0, 0, 0, deny_clear_mask})); // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are // mandatory), allocation with those bits should be able to succeed. auto status = producer_queue_->AllocateBuffer( kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage | deny_clear_mask); ASSERT_TRUE(status.ok()); // While allocation without those bits should fail. status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage & ~deny_clear_mask); ASSERT_FALSE(status.ok()); ASSERT_EQ(EINVAL, status.error()); } TEST_F(BufferHubQueueTest, TestQueueInfo) { static const bool kIsAsync = true; ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync) .SetDefaultWidth(kBufferWidth) .SetDefaultHeight(kBufferHeight) .SetDefaultFormat(kBufferFormat) .Build(), UsagePolicy{})); EXPECT_EQ(producer_queue_->default_width(), kBufferWidth); EXPECT_EQ(producer_queue_->default_height(), kBufferHeight); EXPECT_EQ(producer_queue_->default_format(), kBufferFormat); EXPECT_EQ(producer_queue_->is_async(), kIsAsync); EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth); EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight); EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat); EXPECT_EQ(consumer_queue_->is_async(), kIsAsync); } TEST_F(BufferHubQueueTest, TestFreeAllBuffers) { constexpr size_t kBufferCount = 2; #define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers) \ EXPECT_EQ(consumer_queue_->count(), 0U); \ EXPECT_EQ(consumer_queue_->capacity(), 0U); \ EXPECT_EQ(producer_queue_->count(), 0U); \ EXPECT_EQ(producer_queue_->capacity(), 0U); \ for (size_t i = 0; i < num_buffers; i++) { \ AllocateBuffer(); \ } \ EXPECT_EQ(producer_queue_->count(), num_buffers); \ EXPECT_EQ(producer_queue_->capacity(), num_buffers); size_t slot; LocalHandle fence; pdx::Status status; pdx::Status> consumer_status; pdx::Status> producer_status; std::shared_ptr consumer_buffer; std::shared_ptr producer_buffer; DvrNativeBufferMetadata mi, mo; ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); // Free all buffers when buffers are avaible for dequeue. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); status = producer_queue_->FreeAllBuffers(); EXPECT_TRUE(status.ok()); // Free all buffers when one buffer is dequeued. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(producer_status.ok()); status = producer_queue_->FreeAllBuffers(); EXPECT_TRUE(status.ok()); // Free all buffers when all buffers are dequeued. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); for (size_t i = 0; i < kBufferCount; i++) { producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(producer_status.ok()); } status = producer_queue_->FreeAllBuffers(); EXPECT_TRUE(status.ok()); // Free all buffers when one buffer is posted. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence)); status = producer_queue_->FreeAllBuffers(); EXPECT_TRUE(status.ok()); // Free all buffers when all buffers are posted. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); for (size_t i = 0; i < kBufferCount; i++) { producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(producer_buffer, nullptr); ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); } status = producer_queue_->FreeAllBuffers(); EXPECT_TRUE(status.ok()); // Free all buffers when all buffers are acquired. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); for (size_t i = 0; i < kBufferCount; i++) { producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(producer_buffer, nullptr); ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage(); } status = producer_queue_->FreeAllBuffers(); EXPECT_TRUE(status.ok()); // In addition to FreeAllBuffers() from the queue, it is also required to // delete all references to the ProducerBuffer (i.e. the PDX client). producer_buffer = nullptr; // Crank consumer queue events to pickup EPOLLHUP events on the queue. consumer_queue_->HandleQueueEvents(); // One last check. CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount); #undef CHECK_NO_BUFFER_THEN_ALLOCATE } TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) { ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); // Allocate only one buffer. AllocateBuffer(); // Export should fail as the queue is not empty. auto status = producer_queue_->TakeAsParcelable(); EXPECT_FALSE(status.ok()); } TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); auto s1 = producer_queue_->TakeAsParcelable(); EXPECT_TRUE(s1.ok()); ProducerQueueParcelable output_parcelable = s1.take(); EXPECT_TRUE(output_parcelable.IsValid()); Parcel parcel; status_t res; res = output_parcelable.writeToParcel(&parcel); EXPECT_EQ(res, OK); // After written into parcelable, the output_parcelable is still valid has // keeps the producer channel alive. EXPECT_TRUE(output_parcelable.IsValid()); // Creating producer buffer should fail. auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, kBufferUsage); ASSERT_FALSE(s2.ok()); // Reset the data position so that we can read back from the same parcel // without doing actually Binder IPC. parcel.setDataPosition(0); producer_queue_ = nullptr; // Recreate the producer queue from the parcel. ProducerQueueParcelable input_parcelable; EXPECT_FALSE(input_parcelable.IsValid()); res = input_parcelable.readFromParcel(&parcel); EXPECT_EQ(res, OK); EXPECT_TRUE(input_parcelable.IsValid()); EXPECT_EQ(producer_queue_, nullptr); producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle()); EXPECT_FALSE(input_parcelable.IsValid()); ASSERT_NE(producer_queue_, nullptr); // Newly created queue from the parcel can allocate buffer, post buffer to // consumer. EXPECT_NO_FATAL_FAILURE(AllocateBuffer()); EXPECT_EQ(producer_queue_->count(), 1U); EXPECT_EQ(producer_queue_->capacity(), 1U); size_t slot; DvrNativeBufferMetadata producer_meta; DvrNativeBufferMetadata consumer_meta; LocalHandle fence; auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence); EXPECT_TRUE(s3.ok()); std::shared_ptr p1 = s3.take(); ASSERT_NE(p1, nullptr); producer_meta.timestamp = 42; EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0); // Make sure the buffer can be dequeued from consumer side. auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage(); EXPECT_EQ(consumer_queue_->capacity(), 1U); auto consumer = s4.take(); ASSERT_NE(consumer, nullptr); EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp); } TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); auto s1 = producer_queue_->CreateConsumerQueueParcelable(); EXPECT_TRUE(s1.ok()); ConsumerQueueParcelable output_parcelable = s1.take(); EXPECT_TRUE(output_parcelable.IsValid()); // Write to a Parcel new object. Parcel parcel; status_t res; res = output_parcelable.writeToParcel(&parcel); // Reset the data position so that we can read back from the same parcel // without doing actually Binder IPC. parcel.setDataPosition(0); // No consumer queue created yet. EXPECT_EQ(consumer_queue_, nullptr); // If the parcel contains a consumer queue, read into a // ProducerQueueParcelable should fail. ProducerQueueParcelable wrongly_typed_parcelable; EXPECT_FALSE(wrongly_typed_parcelable.IsValid()); res = wrongly_typed_parcelable.readFromParcel(&parcel); EXPECT_EQ(res, -EINVAL); parcel.setDataPosition(0); // Create the consumer queue from the parcel. ConsumerQueueParcelable input_parcelable; EXPECT_FALSE(input_parcelable.IsValid()); res = input_parcelable.readFromParcel(&parcel); EXPECT_EQ(res, OK); EXPECT_TRUE(input_parcelable.IsValid()); consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle()); EXPECT_FALSE(input_parcelable.IsValid()); ASSERT_NE(consumer_queue_, nullptr); EXPECT_NO_FATAL_FAILURE(AllocateBuffer()); EXPECT_EQ(producer_queue_->count(), 1U); EXPECT_EQ(producer_queue_->capacity(), 1U); size_t slot; DvrNativeBufferMetadata producer_meta; DvrNativeBufferMetadata consumer_meta; LocalHandle fence; auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence); EXPECT_TRUE(s2.ok()); std::shared_ptr p1 = s2.take(); ASSERT_NE(p1, nullptr); producer_meta.timestamp = 42; EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0); // Make sure the buffer can be dequeued from consumer side. auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage(); EXPECT_EQ(consumer_queue_->capacity(), 1U); auto consumer = s3.take(); ASSERT_NE(consumer, nullptr); EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp); } } // namespace } // namespace dvr } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp����������������������������������0100644 0000000 0000000 00000051443 13756501735 024755� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include namespace android { namespace dvr { using pdx::LocalHandle; namespace { // Default dimensions before setDefaultBufferSize is called by the consumer. constexpr uint32_t kDefaultWidth = 1; constexpr uint32_t kDefaultHeight = 1; // Default format before setDefaultBufferFormat is called by the consumer. constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888; constexpr int kDefaultConsumerUsageBits = 0; // Default transform hint before setTransformHint is called by the consumer. constexpr uint32_t kDefaultTransformHint = 0; constexpr int kTestApi = NATIVE_WINDOW_API_CPU; constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL; constexpr int kTestApiInvalid = 0xDEADBEEF; constexpr int kTestProducerUsageBits = 0; constexpr bool kTestControlledByApp = true; // Builder pattern to slightly vary *almost* correct input // -- avoids copying and pasting struct QueueBufferInputBuilder { IGraphicBufferProducer::QueueBufferInput build() { return IGraphicBufferProducer::QueueBufferInput( mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode, mTransform, mFence); } QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { this->mTimestamp = timestamp; return *this; } QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) { this->mIsAutoTimestamp = isAutoTimestamp; return *this; } QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) { this->mDataSpace = dataSpace; return *this; } QueueBufferInputBuilder& setCrop(Rect crop) { this->mCrop = crop; return *this; } QueueBufferInputBuilder& setScalingMode(int scalingMode) { this->mScalingMode = scalingMode; return *this; } QueueBufferInputBuilder& setTransform(uint32_t transform) { this->mTransform = transform; return *this; } QueueBufferInputBuilder& setFence(sp fence) { this->mFence = fence; return *this; } private: int64_t mTimestamp{1384888611}; bool mIsAutoTimestamp{false}; android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN}; Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)}; int mScalingMode{0}; uint32_t mTransform{0}; sp mFence{Fence::NO_FENCE}; }; // This is a test that covers our implementation of bufferhubqueue-based // IGraphicBufferProducer. class BufferHubQueueProducerTest : public ::testing::Test { protected: virtual void SetUp() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); auto config = ProducerQueueConfigBuilder().Build(); auto queue = ProducerQueue::Create(config, UsagePolicy{}); ASSERT_TRUE(queue != nullptr); mProducer = BufferHubProducer::Create(std::move(queue)); ASSERT_TRUE(mProducer != nullptr); mSurface = new Surface(mProducer, true); ASSERT_TRUE(mSurface != nullptr); } // Connect to a producer in a 'correct' fashion. void ConnectProducer() { IGraphicBufferProducer::QueueBufferOutput output; // Can connect the first time. ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp, &output)); } // Dequeue a buffer in a 'correct' fashion. // Precondition: Producer is connected. void DequeueBuffer(int* outSlot) { sp fence; ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence)); } void DequeueBuffer(int* outSlot, sp* outFence) { ASSERT_NE(nullptr, outSlot); ASSERT_NE(nullptr, outFence); int ret = mProducer->dequeueBuffer( outSlot, outFence, kDefaultWidth, kDefaultHeight, kDefaultFormat, kTestProducerUsageBits, nullptr, nullptr); // BUFFER_NEEDS_REALLOCATION can be either on or off. ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret); // Slot number should be in boundary. ASSERT_LE(0, *outSlot); ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot); } // Create a generic "valid" input for queueBuffer // -- uses the default buffer format, width, etc. static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() { return QueueBufferInputBuilder().build(); } const sp kDummyListener{new DummyProducerListener}; sp mProducer; sp mSurface; }; TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) { IGraphicBufferProducer::QueueBufferOutput output; // NULL output returns BAD_VALUE EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp, nullptr)); // Invalid API returns bad value EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid, kTestControlledByApp, &output)); } TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // Can't connect when there is already a producer connected. IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp, &output)); } TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); } TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // Must disconnect with same API number EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther)); // API must not be out of range EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid)); } TEST_F(BufferHubQueueProducerTest, Query_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int32_t value = -1; EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); EXPECT_EQ(kDefaultWidth, static_cast(value)); EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); EXPECT_EQ(kDefaultHeight, static_cast(value)); EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); EXPECT_EQ(kDefaultFormat, value); EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); EXPECT_LE(0, value); EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value); EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); EXPECT_EQ(kDefaultConsumerUsageBits, value); } TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // One past the end of the last 'query' enum value. Update this if we add more // enums. const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1; int value; // What was out of range EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value)); EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value)); EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value)); // Some enums from window.h are 'invalid' EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value)); EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value)); EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value)); EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value)); EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); // Value was NULL EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL)); } TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) { int slot = -1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); // Request the buffer (pre-requisite for queueing) sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; // Queue the buffer back into the BQ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); EXPECT_EQ(kDefaultWidth, output.width); EXPECT_EQ(kDefaultHeight, output.height); EXPECT_EQ(kDefaultTransformHint, output.transformHint); // BufferHubQueue delivers buffers to consumer immediately. EXPECT_EQ(0u, output.numPendingBuffers); // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to // be a SurfaceFlinger specific optimization. EXPECT_EQ(0u, output.nextFrameNumber); // Buffer was not in the dequeued state EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); } // Test invalid slot number TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output)); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output)); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS, input, &output)); } // Slot was not in the dequeued state (all slots start out in Free state) TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output)); } // Slot was enqueued without requesting a buffer TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) { int slot = -1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); } // Test when fence was NULL TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) { int slot = -1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); sp nullFence = NULL; IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); } // Test scaling mode was invalid TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) { int slot = -1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setScalingMode(-1).build(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); } // Test crop rect is out of bounds of the buffer dimensions TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) { int slot = -1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder() .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1)) .build(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output)); } TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) { int slot = -1; sp fence; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); // Should be able to cancel buffer after a dequeue. EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); } TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { return; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false; ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers)) << "bufferCount: " << minBuffers; // Should now be able to dequeue up to minBuffers times // Should now be able to dequeue up to maxBuffers times int slot = -1; for (int i = 0; i < minBuffers; ++i) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); } ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers)); // queue the first buffer to enable max dequeued buffer count checking IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); sp fence; for (int i = 0; i < maxBuffers; ++i) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); } // Cancel a buffer, so we can decrease the buffer count ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence)); // Should now be able to decrease the max dequeued count by 1 ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); } TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false; // Buffer count was out of range EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0)) << "bufferCount: " << 0; EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1)) << "bufferCount: " << maxBuffers + 1; // Set max dequeue count to 2 ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2)); // Dequeue 2 buffers int slot = -1; sp fence; for (int i = 0; i < 2; i++) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, kDefaultHeight, kDefaultFormat, kTestProducerUsageBits, nullptr, nullptr))) << "slot: " << slot; } // Client has too many buffers dequeued EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1)) << "bufferCount: " << minBuffers; } TEST_F(BufferHubQueueProducerTest, DisconnectedProducerReturnsError_dequeueBuffer) { int slot = -1; sp fence; ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth, kDefaultHeight, kDefaultFormat, kTestProducerUsageBits, nullptr, nullptr)); } TEST_F(BufferHubQueueProducerTest, DisconnectedProducerReturnsError_requestBuffer) { int slot = -1; sp buffer; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); // Shouldn't be able to request buffer after disconnect. ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer)); } TEST_F(BufferHubQueueProducerTest, DisconnectedProducerReturnsError_queueBuffer) { int slot = -1; sp buffer; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; // Shouldn't be able to queue buffer after disconnect. ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output)); } TEST_F(BufferHubQueueProducerTest, DisconnectedProducerReturnsError_cancelBuffer) { int slot = -1; sp buffer; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // Shouldn't be able to cancel buffer after disconnect. ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE)); } TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) { int slot = -1; sp buffer; IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_NO_FATAL_FAILURE(ConnectProducer()); constexpr int maxDequeuedBuffers = 1; int minUndequeuedBuffers; EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); EXPECT_EQ(OK, mProducer->setAsyncMode(false)); EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers)); int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers; // Dequeue, request, and queue all buffers. for (int i = 0; i < maxCapacity; i++) { EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); } // Disconnect then reconnect. EXPECT_EQ(OK, mProducer->disconnect(kTestApi)); EXPECT_NO_FATAL_FAILURE(ConnectProducer()); // Dequeue, request, and queue all buffers. for (int i = 0; i < maxCapacity; i++) { EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); } EXPECT_EQ(OK, mProducer->disconnect(kTestApi)); } TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { // Connected producer cannot be taken out as a parcelable. EXPECT_NO_FATAL_FAILURE(ConnectProducer()); ProducerQueueParcelable producer_parcelable; EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE); // Create a valid dummy producer parcelable. auto dummy_channel_parcelable = std::make_unique( LocalHandle(0), LocalHandle(0), LocalHandle(0)); EXPECT_TRUE(dummy_channel_parcelable->IsValid()); ProducerQueueParcelable dummy_producer_parcelable( std::move(dummy_channel_parcelable)); EXPECT_TRUE(dummy_producer_parcelable.IsValid()); // Disconnect producer can be taken out, but only to an invalid parcelable. ASSERT_EQ(mProducer->disconnect(kTestApi), OK); EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE); EXPECT_FALSE(producer_parcelable.IsValid()); EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK); EXPECT_TRUE(producer_parcelable.IsValid()); // Should still be able to query buffer dimension after disconnect. int32_t value = -1; EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); EXPECT_EQ(static_cast(value), kDefaultWidth); EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK); EXPECT_EQ(static_cast(value), kDefaultHeight); EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK); EXPECT_EQ(value, kDefaultFormat); // But connect to API will fail. IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp, &output), BAD_VALUE); // Create a new producer from the parcelable and connect to kTestApi should // succeed. sp new_producer = BufferHubProducer::Create(std::move(producer_parcelable)); ASSERT_TRUE(new_producer != nullptr); EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi, kTestControlledByApp, &output), OK); } } // namespace } // namespace dvr } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/���������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013340� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/Android.bp�����������������������������������������������������������������������0100644 0000000 0000000 00000003264 13756501735 015245� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. sourceFiles = [ "display_client.cpp", "display_manager_client.cpp", "display_protocol.cpp", "shared_buffer_helpers.cpp", "vsync_service.cpp", ] localIncludeFiles = [ "include", ] sharedLibraries = [ "libbase", "libbinder", "libbufferhubqueue", "libcutils", "liblog", "libutils", "libui", "libgui", "libhardware", "libsync", "libnativewindow", "libpdx_default_transport", ] staticLibraries = [ "libdvrcommon", "libbroadcastring", ] headerLibraries = [ "vulkan_headers", "libdvr_headers", ] cc_library { srcs: sourceFiles, cflags: ["-DLOG_TAG=\"libdisplay\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", "-Wall", "-Werror", ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ], export_include_dirs: localIncludeFiles, shared_libs: sharedLibraries, static_libs: staticLibraries, header_libs: headerLibraries, export_header_lib_headers: headerLibraries, name: "libdisplay", } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/display_client.cpp���������������������������������������������������������������0100644 0000000 0000000 00000017752 13756501735 017060� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/display_client.h" #include #include #include #include #include #include #include using android::pdx::ErrorStatus; using android::pdx::LocalHandle; using android::pdx::LocalChannelHandle; using android::pdx::Status; using android::pdx::Transaction; using android::pdx::rpc::IfAnyOf; namespace android { namespace dvr { namespace display { Surface::Surface(LocalChannelHandle channel_handle, int* error) : BASE{pdx::default_transport::ClientChannel::Create( std::move(channel_handle))} { auto status = InvokeRemoteMethod(); if (!status) { ALOGE("Surface::Surface: Failed to get surface info: %s", status.GetErrorMessage().c_str()); Close(status.error()); if (error) *error = status.error(); } surface_id_ = status.get().surface_id; z_order_ = status.get().z_order; visible_ = status.get().visible; } Surface::Surface(const SurfaceAttributes& attributes, int* error) : BASE{pdx::default_transport::ClientChannelFactory::Create( DisplayProtocol::kClientPath), kInfiniteTimeout} { auto status = InvokeRemoteMethod(attributes); if (!status) { ALOGE("Surface::Surface: Failed to create display surface: %s", status.GetErrorMessage().c_str()); Close(status.error()); if (error) *error = status.error(); } surface_id_ = status.get().surface_id; z_order_ = status.get().z_order; visible_ = status.get().visible; } Status Surface::SetVisible(bool visible) { return SetAttributes( {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}}); } Status Surface::SetZOrder(int z_order) { return SetAttributes( {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}}); } Status Surface::SetAttributes(const SurfaceAttributes& attributes) { auto status = InvokeRemoteMethod(attributes); if (!status) { ALOGE( "Surface::SetAttributes: Failed to set display surface " "attributes: %s", status.GetErrorMessage().c_str()); return status.error_status(); } // Set the local cached copies of the attributes we care about from the full // set of attributes sent to the display service. for (const auto& attribute : attributes) { const auto& key = attribute.first; const auto* variant = &attribute.second; bool invalid_value = false; switch (key) { case SurfaceAttribute::Visible: invalid_value = !IfAnyOf::Get(variant, &visible_); break; case SurfaceAttribute::ZOrder: invalid_value = !IfAnyOf::Get(variant, &z_order_); break; } if (invalid_value) { ALOGW( "Surface::SetAttributes: Failed to set display surface " "attribute %d because of incompatible type: %d", key, variant->index()); } } return {}; } Status> Surface::CreateQueue( uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) { ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue."); auto status = InvokeRemoteMethod( ProducerQueueConfigBuilder() .SetDefaultWidth(width) .SetDefaultHeight(height) .SetDefaultFormat(format) .SetMetadataSize(metadata_size) .Build()); if (!status) { ALOGE("Surface::CreateQueue: Failed to create queue: %s", status.GetErrorMessage().c_str()); return status.error_status(); } auto producer_queue = ProducerQueue::Import(status.take()); if (!producer_queue) { ALOGE("Surface::CreateQueue: Failed to import producer queue!"); return ErrorStatus(ENOMEM); } return {std::move(producer_queue)}; } Status> Surface::CreateQueue( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t capacity, size_t metadata_size) { ALOGD_IF(TRACE, "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u " "usage=%" PRIx64 " capacity=%zu", width, height, layer_count, format, usage, capacity); auto status = CreateQueue(width, height, format, metadata_size); if (!status) return status.error_status(); auto producer_queue = status.take(); ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity); auto allocate_status = producer_queue->AllocateBuffers( width, height, layer_count, format, usage, capacity); if (!allocate_status) { ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s", producer_queue->id(), allocate_status.GetErrorMessage().c_str()); return allocate_status.error_status(); } return {std::move(producer_queue)}; } DisplayClient::DisplayClient(int* error) : BASE(pdx::default_transport::ClientChannelFactory::Create( DisplayProtocol::kClientPath), kInfiniteTimeout) { if (error) *error = Client::error(); } Status DisplayClient::GetDisplayMetrics() { return InvokeRemoteMethod(); } Status DisplayClient::GetConfigurationData( ConfigFileType config_type) { auto status = InvokeRemoteMethod(config_type); if (!status && status.error() != ENOENT) { ALOGE( "DisplayClient::GetConfigurationData: Unable to get" "configuration data. Error: %s", status.GetErrorMessage().c_str()); } return status; } Status> DisplayClient::CreateSurface( const SurfaceAttributes& attributes) { int error; if (auto client = Surface::Create(attributes, &error)) return {std::move(client)}; else return ErrorStatus(error); } pdx::Status> DisplayClient::SetupGlobalBuffer( DvrGlobalBufferKey key, size_t size, uint64_t usage) { auto status = InvokeRemoteMethod(key, size, usage); if (!status) { ALOGE( "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer " "%s", status.GetErrorMessage().c_str()); return status.error_status(); } auto ion_buffer = std::make_unique(); auto native_buffer_handle = status.take(); const int ret = native_buffer_handle.Import(ion_buffer.get()); if (ret < 0) { ALOGE( "DisplayClient::GetGlobalBuffer: Failed to import global buffer: " "key=%d; error=%s", key, strerror(-ret)); return ErrorStatus(-ret); } return {std::move(ion_buffer)}; } pdx::Status DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) { auto status = InvokeRemoteMethod(key); if (!status) { ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s", status.GetErrorMessage().c_str()); } return status; } Status> DisplayClient::GetGlobalBuffer( DvrGlobalBufferKey key) { auto status = InvokeRemoteMethod(key); if (!status) { ALOGE( "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; " "error=%s", key, status.GetErrorMessage().c_str()); return status.error_status(); } auto ion_buffer = std::make_unique(); auto native_buffer_handle = status.take(); const int ret = native_buffer_handle.Import(ion_buffer.get()); if (ret < 0) { ALOGE( "DisplayClient::GetGlobalBuffer: Failed to import global buffer: " "key=%d; error=%s", key, strerror(-ret)); return ErrorStatus(-ret); } return {std::move(ion_buffer)}; } Status DisplayClient::IsVrAppRunning() { return InvokeRemoteMethod(); } } // namespace display } // namespace dvr } // namespace android ����������������������libs/vr/libdisplay/display_manager_client.cpp�������������������������������������������������������0100644 0000000 0000000 00000003003 13756501735 020532� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/display_manager_client.h" #include #include #include #include using android::pdx::ErrorStatus; using android::pdx::LocalChannelHandle; using android::pdx::Transaction; namespace android { namespace dvr { namespace display { DisplayManagerClient::DisplayManagerClient() : BASE(pdx::default_transport::ClientChannelFactory::Create( DisplayManagerProtocol::kClientPath)) {} DisplayManagerClient::~DisplayManagerClient() {} pdx::Status> DisplayManagerClient::GetSurfaceState() { auto status = InvokeRemoteMethod(); if (!status) { ALOGE( "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s", status.GetErrorMessage().c_str()); } return status; } pdx::Status> DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) { auto status = InvokeRemoteMethod( surface_id, queue_id); if (!status) { ALOGE( "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for " "surface_id=%d queue_id=%d: %s", surface_id, queue_id, status.GetErrorMessage().c_str()); return status.error_status(); } return {ConsumerQueue::Import(status.take())}; } } // namespace display } // namespace dvr } // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/display_protocol.cpp�������������������������������������������������������������0100644 0000000 0000000 00000000503 13756501735 017425� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/display_protocol.h" namespace android { namespace dvr { namespace display { constexpr char DisplayProtocol::kClientPath[]; constexpr char DisplayManagerProtocol::kClientPath[]; constexpr char VSyncProtocol::kClientPath[]; } // namespace display } // namespace dvr } // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014763� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/CPPLINT.cfg��������������������������������������������������������������0100644 0000000 0000000 00000000033 13756501735 016546� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������filter=-build/header_guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/�����������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016435� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/�������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 017230� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/display_client.h���������������������������������������������0100644 0000000 0000000 00000006717 13756501735 022414� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_ #define ANDROID_DVR_DISPLAY_CLIENT_H_ #include #include #include #include #include #include namespace android { namespace dvr { namespace display { class Surface : public pdx::ClientBase { public: // Utility named constructor. This can be removed once ClientBase::Create is // refactored to return Status types. static pdx::Status> CreateSurface( const SurfaceAttributes& attributes) { int error; pdx::Status> status; if (auto surface = Create(attributes, &error)) status.SetValue(std::move(surface)); else status.SetError(error); return status; } int surface_id() const { return surface_id_; } int z_order() const { return z_order_; } bool visible() const { return visible_; } pdx::Status SetVisible(bool visible); pdx::Status SetZOrder(int z_order); pdx::Status SetAttributes(const SurfaceAttributes& attributes); // Creates an empty queue. pdx::Status> CreateQueue(uint32_t width, uint32_t height, uint32_t format, size_t metadata_size); // Creates a queue and populates it with |capacity| buffers of the specified // parameters. pdx::Status> CreateQueue(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t capacity, size_t metadata_size); private: friend BASE; int surface_id_ = -1; int z_order_ = 0; bool visible_ = false; // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create() // returns Status. explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr); explicit Surface(pdx::LocalChannelHandle channel_handle, int* error = nullptr); Surface(const Surface&) = delete; void operator=(const Surface&) = delete; }; class DisplayClient : public pdx::ClientBase { public: pdx::Status GetDisplayMetrics(); pdx::Status GetConfigurationData(ConfigFileType config_type); pdx::Status> SetupGlobalBuffer( DvrGlobalBufferKey key, size_t size, uint64_t usage); pdx::Status DeleteGlobalBuffer(DvrGlobalBufferKey key); pdx::Status> GetGlobalBuffer( DvrGlobalBufferKey key); pdx::Status> CreateSurface( const SurfaceAttributes& attributes); // Temporary query for current VR status. Will be removed later. pdx::Status IsVrAppRunning(); private: friend BASE; explicit DisplayClient(int* error = nullptr); DisplayClient(const DisplayClient&) = delete; void operator=(const DisplayClient&) = delete; }; } // namespace display } // namespace dvr } // namespace android #endif // ANDROID_DVR_DISPLAY_CLIENT_H_ �������������������������������������������������libs/vr/libdisplay/include/private/dvr/display_manager_client.h�������������������������������������0100644 0000000 0000000 00000002223 13756501735 024072� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ #define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ #include #include #include #include #include namespace android { namespace dvr { class IonBuffer; class ConsumerQueue; namespace display { class DisplayManagerClient : public pdx::ClientBase { public: ~DisplayManagerClient() override; pdx::Status> GetSurfaceState(); pdx::Status> GetSurfaceQueue(int surface_id, int queue_id); using Client::event_fd; pdx::Status GetEventMask(int events) { if (auto* client_channel = GetChannel()) return client_channel->GetEventMask(events); else return pdx::ErrorStatus(EINVAL); } private: friend BASE; DisplayManagerClient(); DisplayManagerClient(const DisplayManagerClient&) = delete; void operator=(const DisplayManagerClient&) = delete; }; } // namespace display } // namespace dvr } // namespace android #endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/display_protocol.h�������������������������������������������0100644 0000000 0000000 00000021460 13756501735 022767� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_ #define ANDROID_DVR_DISPLAY_PROTOCOL_H_ #include #include #include #include #include #include #include #include #include #include // RPC protocol definitions for DVR display services (VrFlinger). namespace android { namespace dvr { namespace display { // Native display metrics. struct Metrics { // Basic display properties. uint32_t display_width; uint32_t display_height; uint32_t display_x_dpi; uint32_t display_y_dpi; uint32_t vsync_period_ns; // HMD metrics. // TODO(eieio): Determine how these fields should be populated. On phones // these values are determined at runtime by VrCore based on which headset the // phone is in. On dedicated hardware this needs to come from somewhere else. // Perhaps these should be moved to a separate structure that is returned by a // separate runtime call. uint32_t distorted_width; uint32_t distorted_height; uint32_t hmd_ipd_mm; float inter_lens_distance_m; std::array left_fov_lrbt; std::array right_fov_lrbt; private: PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height, display_x_dpi, display_y_dpi, vsync_period_ns, distorted_width, distorted_height, hmd_ipd_mm, inter_lens_distance_m, left_fov_lrbt, right_fov_lrbt); }; // Serializable base type for enum structs. Enum structs are easier to use than // enum classes, especially for bitmasks. This base type provides common // utilities for flags types. template class Flags { public: using Base = Flags; using Type = Integer; // NOLINTNEXTLINE(google-explicit-constructor) Flags(const Integer& value) : value_{value} {} Flags(const Flags&) = default; Flags& operator=(const Flags&) = default; Integer value() const { return value_; } // NOLINTNEXTLINE(google-explicit-constructor) operator Integer() const { return value_; } bool IsSet(Integer bits) const { return (value_ & bits) == bits; } bool IsClear(Integer bits) const { return (value_ & bits) == 0; } void Set(Integer bits) { value_ |= bits; } void Clear(Integer bits) { value_ &= ~bits; } Integer operator|(Integer bits) const { return value_ | bits; } Integer operator&(Integer bits) const { return value_ & bits; } Flags& operator|=(Integer bits) { value_ |= bits; return *this; } Flags& operator&=(Integer bits) { value_ &= bits; return *this; } private: Integer value_; PDX_SERIALIZABLE_MEMBERS(Flags, value_); }; // Flags indicating what changed since last update. struct SurfaceUpdateFlags : public Flags { enum : Type { None = DVR_SURFACE_UPDATE_FLAGS_NONE, NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE, BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED, VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED, AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED, }; SurfaceUpdateFlags() : Base{None} {} using Base::Base; }; // Surface attribute key/value types. using SurfaceAttributeKey = int32_t; using SurfaceAttributeValue = pdx::rpc::Variant, std::array, std::array, std::array, std::array>; // Defined surface attribute keys. struct SurfaceAttribute : public Flags { enum : Type { // Keys in the negative integer space are interpreted by VrFlinger for // direct surfaces. Direct = DVR_SURFACE_ATTRIBUTE_DIRECT, ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER, Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE, // Invalid key. May be used to terminate C style lists in public API code. Invalid = DVR_SURFACE_ATTRIBUTE_INVALID, // Positive keys are interpreted by the compositor only. FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY, }; SurfaceAttribute() : Base{Invalid} {} using Base::Base; }; // Collection of surface attribute key/value pairs. using SurfaceAttributes = std::map; struct SurfaceState { int32_t surface_id; int32_t process_id; int32_t user_id; SurfaceAttributes surface_attributes; SurfaceUpdateFlags update_flags; std::vector queue_ids; // Convenience accessors. bool GetVisible() const { bool bool_value = false; GetAttribute(SurfaceAttribute::Visible, &bool_value, ValidTypes{}); return bool_value; } int GetZOrder() const { int int_value = 0; GetAttribute(SurfaceAttribute::ZOrder, &int_value, ValidTypes{}); return int_value; } private: template struct ValidTypes {}; template bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value, ValidTypes) const { auto search = surface_attributes.find(key); if (search != surface_attributes.end()) return pdx::rpc::IfAnyOf::Get(&search->second, out_value); else return false; } PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id, surface_attributes, update_flags, queue_ids); }; struct SurfaceInfo { int surface_id; bool visible; int z_order; private: PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order); }; enum class ConfigFileType : uint32_t { kLensMetrics, kDeviceMetrics, kDeviceConfiguration }; struct DisplayProtocol { // Service path. static constexpr char kClientPath[] = "system/vr/display/client"; // Op codes. enum { kOpGetMetrics = 0, kOpGetConfigurationData, kOpSetupGlobalBuffer, kOpDeleteGlobalBuffer, kOpGetGlobalBuffer, kOpIsVrAppRunning, kOpCreateSurface, kOpGetSurfaceInfo, kOpCreateQueue, kOpSetAttributes, }; // Aliases. using LocalChannelHandle = pdx::LocalChannelHandle; using Void = pdx::rpc::Void; // Methods. PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void)); PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData, std::string(ConfigFileType config_type)); PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer, LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size, uint64_t usage)); PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer, void(DvrGlobalBufferKey key)); PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer, LocalNativeBufferHandle(DvrGlobalBufferKey key)); PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void)); PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface, SurfaceInfo(const SurfaceAttributes& attributes)); PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void)); PDX_REMOTE_METHOD( CreateQueue, kOpCreateQueue, LocalChannelHandle(const ProducerQueueConfig& producer_config)); PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes, void(const SurfaceAttributes& attributes)); }; struct DisplayManagerProtocol { // Service path. static constexpr char kClientPath[] = "system/vr/display/manager"; // Op codes. enum { kOpGetSurfaceState = 0, kOpGetSurfaceQueue, }; // Aliases. using LocalChannelHandle = pdx::LocalChannelHandle; using Void = pdx::rpc::Void; // Methods. PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState, std::vector(Void)); PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue, LocalChannelHandle(int surface_id, int queue_id)); }; struct VSyncSchedInfo { int64_t vsync_period_ns; int64_t timestamp_ns; uint32_t next_vsync_count; private: PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns, next_vsync_count); }; struct VSyncProtocol { // Service path. static constexpr char kClientPath[] = "system/vr/display/vsync"; // Op codes. enum { kOpWait = 0, kOpAck, kOpGetLastTimestamp, kOpGetSchedInfo, kOpAcknowledge, }; // Aliases. using Void = pdx::rpc::Void; using Timestamp = int64_t; // Methods. PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void)); PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void)); PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void)); PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void)); }; } // namespace display } // namespace dvr } // namespace android #endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h��������������������������������������0100644 0000000 0000000 00000010537 13756501735 023725� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ #define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ #include #include #include #include namespace android { namespace dvr { // The buffer usage type for mapped shared buffers. enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY }; // Holds the memory for the mapped shared buffer. Unlocks and releases the // underlying IonBuffer in destructor. class CPUMappedBuffer { public: // This constructor will create a display client and get the buffer from it. CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode); // If you already have the IonBuffer, use this. It will take ownership. CPUMappedBuffer(std::unique_ptr buffer, CPUUsageMode mode); // Use this if you do not want to take ownership. CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode); ~CPUMappedBuffer(); // Getters. size_t Size() const { return size_; } void* Address() const { return address_; } bool IsMapped() const { return Address() != nullptr; } // Attempt mapping this buffer to the CPU addressable space. // This will create a display client and see if the buffer exists. // If the buffer has not been setup yet, you will need to try again later. void TryMapping(); protected: // The memory area if we managed to map it. size_t size_ = 0; void* address_ = nullptr; // If we are polling the display client, the buffer key here. DvrGlobalBufferKey buffer_key_; // If we just own the IonBuffer outright, it's here. std::unique_ptr owned_buffer_ = nullptr; // The last time we connected to the display service. int64_t last_display_service_connection_ns_ = 0; // If we do not own the IonBuffer, it's here IonBuffer* buffer_ = nullptr; // The usage mode. CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN; }; // Represents a broadcast ring inside a mapped shared memory buffer. // If has the same set of constructors as CPUMappedBuffer. // The template argument is the concrete BroadcastRing class that this buffer // holds. template class CPUMappedBroadcastRing : public CPUMappedBuffer { public: CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode) : CPUMappedBuffer(key, mode) {} CPUMappedBroadcastRing(std::unique_ptr buffer, CPUUsageMode mode) : CPUMappedBuffer(std::move(buffer), mode) {} CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode) : CPUMappedBuffer(buffer, mode) {} // Helper function for publishing records in the ring. void Publish(const typename RingType::Record& record) { assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) || (usage_mode_ == CPUUsageMode::WRITE_RARELY)); auto ring = Ring(); if (ring) { ring->Put(record); } } // Helper function for getting records from the ring. // Returns true if we were able to retrieve the latest. bool GetNewest(typename RingType::Record* record) { assert((usage_mode_ == CPUUsageMode::READ_OFTEN) || (usage_mode_ == CPUUsageMode::READ_RARELY)); auto ring = Ring(); if (ring) { return ring->GetNewest(&sequence_, record); } return false; } // Try obtaining the ring. If the named buffer has not been created yet, it // will return nullptr. RingType* Ring() { // No ring created yet? if (ring_ == nullptr) { // Not mapped the memory yet? if (IsMapped() == false) { TryMapping(); } // If have the memory mapped, allocate the ring. if (IsMapped()) { switch (usage_mode_) { case CPUUsageMode::READ_OFTEN: case CPUUsageMode::READ_RARELY: { RingType ring; bool import_ok; std::tie(ring, import_ok) = RingType::Import(address_, size_); if (import_ok) { ring_ = std::make_unique(ring); } } break; case CPUUsageMode::WRITE_OFTEN: case CPUUsageMode::WRITE_RARELY: ring_ = std::make_unique(RingType::Create(address_, size_)); break; } } } return ring_.get(); } protected: std::unique_ptr ring_ = nullptr; uint32_t sequence_ = 0; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/vsync_service.h����������������������������������������������0100644 0000000 0000000 00000004061 13756501735 022261� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_VSYNC_SERVICE_H_ #define ANDROID_DVR_VSYNC_SERVICE_H_ #include namespace android { namespace dvr { class IVsyncCallback : public IInterface { public: DECLARE_META_INTERFACE(VsyncCallback) enum { ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION }; virtual status_t onVsync(int64_t vsync_timestamp) = 0; }; class BnVsyncCallback : public BnInterface { public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // Register a callback with IVsyncService to be notified of vsync events and // timestamps. There's also a shared memory vsync buffer defined in // dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared // memory buffer that make it preferable in certain situations: // // 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService // is always available as long as surface flinger is running. // // 2. IVsyncService will make a binder callback when a vsync event occurs. This // allows the client to not write code to implement periodic "get the latest // vsync" calls, which is necessary with the vsync shared memory buffer. // // 3. The IVsyncService provides the real vsync timestamp reported by hardware // composer, whereas the vsync shared memory buffer only has predicted vsync // times. class IVsyncService : public IInterface { public: DECLARE_META_INTERFACE(VsyncService) static const char* GetServiceName() { return "vrflinger_vsync"; } enum { REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, UNREGISTER_CALLBACK }; virtual status_t registerCallback(const sp callback) = 0; virtual status_t unregisterCallback(const sp callback) = 0; }; class BnVsyncService : public BnInterface { public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_VSYNC_SERVICE_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/shared_buffer_helpers.cpp��������������������������������������������������������0100644 0000000 0000000 00000005450 13756501735 020366� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include namespace android { namespace dvr { namespace { // We will not poll the display service for buffers more frequently than this. constexpr size_t kDisplayServiceTriesPerSecond = 2; } // namespace CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode) : buffer_key_(key), usage_mode_(mode) { TryMapping(); } CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr buffer, CPUUsageMode mode) : owned_buffer_(std::move(buffer)), buffer_(owned_buffer_.get()), usage_mode_(mode) { TryMapping(); } CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode) : buffer_(buffer), usage_mode_(mode) { TryMapping(); } CPUMappedBuffer::~CPUMappedBuffer() { if (IsMapped()) { buffer_->Unlock(); } } void CPUMappedBuffer::TryMapping() { // Do we have an IonBuffer for this shared memory object? if (buffer_ == nullptr) { // Has it been too long since we last connected to the display service? const auto current_time_ns = GetSystemClockNs(); if ((current_time_ns - last_display_service_connection_ns_) < (1e9 / kDisplayServiceTriesPerSecond)) { // Early exit. return; } last_display_service_connection_ns_ = current_time_ns; // Create a display client and get the buffer. auto display_client = display::DisplayClient::Create(); if (display_client) { auto get_result = display_client->GetGlobalBuffer(buffer_key_); if (get_result.ok()) { owned_buffer_ = get_result.take(); buffer_ = owned_buffer_.get(); } else { // The buffer has not been created yet. This is OK, we will keep // retrying. } } else { ALOGE("Unable to create display client for shared buffer access"); } } if (buffer_) { auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK & ~GRALLOC_USAGE_SW_WRITE_MASK; // Figure out the usage bits. switch (usage_mode_) { case CPUUsageMode::READ_OFTEN: usage |= GRALLOC_USAGE_SW_READ_OFTEN; break; case CPUUsageMode::READ_RARELY: usage |= GRALLOC_USAGE_SW_READ_RARELY; break; case CPUUsageMode::WRITE_OFTEN: usage |= GRALLOC_USAGE_SW_WRITE_OFTEN; break; case CPUUsageMode::WRITE_RARELY: usage |= GRALLOC_USAGE_SW_WRITE_RARELY; break; } int width = static_cast(buffer_->width()); int height = 1; const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_); if (ret < 0 || !address_) { ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_); buffer_->Unlock(); } else { size_ = width; } } } } // namespace dvr } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/system/��������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014664� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/system/CPPLINT.cfg���������������������������������������������������������������0100644 0000000 0000000 00000000033 13756501735 016447� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������filter=-build/header_guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/vsync_service.cpp����������������������������������������������������������������0100644 0000000 0000000 00000010527 13756501735 016730� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/vsync_service.h" #include #include namespace android { namespace dvr { status_t BnVsyncCallback::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { case ON_VSYNC: { CHECK_INTERFACE(IVsyncCallback, data, reply); int64_t vsync_timestamp = 0; status_t result = data.readInt64(&vsync_timestamp); if (result != OK) { ALOGE("onVsync failed to readInt64: %d", result); return result; } onVsync(vsync_timestamp); return OK; } default: { return BBinder::onTransact(code, data, reply, flags); } } } class BpVsyncCallback : public BpInterface { public: explicit BpVsyncCallback(const sp& impl) : BpInterface(impl) {} virtual ~BpVsyncCallback() {} virtual status_t onVsync(int64_t vsync_timestamp) { Parcel data, reply; status_t result = data.writeInterfaceToken( IVsyncCallback::getInterfaceDescriptor()); if (result != OK) { ALOGE("onVsync failed to writeInterfaceToken: %d", result); return result; } result = data.writeInt64(vsync_timestamp); if (result != OK) { ALOGE("onVsync failed to writeInt64: %d", result); return result; } result = remote()->transact( BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY); if (result != OK) { ALOGE("onVsync failed to transact: %d", result); return result; } return result; } }; IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback"); status_t BnVsyncService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { case REGISTER_CALLBACK: { CHECK_INTERFACE(IVsyncService, data, reply); sp callback; status_t result = data.readStrongBinder(&callback); if (result != OK) { ALOGE("registerCallback failed to readStrongBinder: %d", result); return result; } registerCallback(interface_cast(callback)); return OK; } case UNREGISTER_CALLBACK: { CHECK_INTERFACE(IVsyncService, data, reply); sp callback; status_t result = data.readStrongBinder(&callback); if (result != OK) { ALOGE("unregisterCallback failed to readStrongBinder: %d", result); return result; } unregisterCallback(interface_cast(callback)); return OK; } default: { return BBinder::onTransact(code, data, reply, flags); } } } class BpVsyncService : public BpInterface { public: explicit BpVsyncService(const sp& impl) : BpInterface(impl) {} virtual ~BpVsyncService() {} virtual status_t registerCallback(const sp callback) { Parcel data, reply; status_t result = data.writeInterfaceToken( IVsyncService::getInterfaceDescriptor()); if (result != OK) { ALOGE("registerCallback failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(IInterface::asBinder(callback)); if (result != OK) { ALOGE("registerCallback failed to writeStrongBinder: %d", result); return result; } result = remote()->transact( BnVsyncService::REGISTER_CALLBACK, data, &reply); if (result != OK) { ALOGE("registerCallback failed to transact: %d", result); return result; } return result; } virtual status_t unregisterCallback(const sp callback) { Parcel data, reply; status_t result = data.writeInterfaceToken( IVsyncService::getInterfaceDescriptor()); if (result != OK) { ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(IInterface::asBinder(callback)); if (result != OK) { ALOGE("unregisterCallback failed to writeStrongBinder: %d", result); return result; } result = remote()->transact( BnVsyncService::UNREGISTER_CALLBACK, data, &reply); if (result != OK) { ALOGE("unregisterCallback failed to transact: %d", result); return result; } return result; } }; IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService"); } // namespace dvr } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/�������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012466� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/Android.bp���������������������������������������������������������������������������0100644 0000000 0000000 00000004706 13756501735 014375� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2017 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. cc_library_headers { name: "libdvr_headers", export_include_dirs: ["include"], vendor_available: true, } cc_library_headers { name: "libdvr_private_headers", export_include_dirs: ["."], vendor_available: false, } cflags = [ "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"libdvr\"", "-DTRACE=0", "-Wall", "-Werror", ] srcs = [ "dvr_api.cpp", "dvr_buffer.cpp", "dvr_buffer_queue.cpp", "dvr_configuration_data.cpp", "dvr_display_manager.cpp", "dvr_hardware_composer_client.cpp", "dvr_performance.cpp", "dvr_pose.cpp", "dvr_surface.cpp", "dvr_tracking.cpp", ] static_libs = [ "libbroadcastring", "libvrsensor", "libdisplay", "libvirtualtouchpadclient", "libvr_hwc-impl", "libvr_hwc-binder", "libgrallocusage", "libperformance", ] shared_libs = [ "android.hardware.graphics.bufferqueue@1.0", "android.hidl.token@1.0-utils", "libbase", "libbufferhubqueue", "libbinder", "liblog", "libcutils", "libutils", "libnativewindow", "libgui", "libui", "libpdx_default_transport", ] cc_library_shared { name: "libdvr.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], export_header_lib_headers: ["libdvr_headers"], srcs: srcs, static_libs: static_libs, shared_libs: shared_libs, version_script: "exported_apis.lds", } // Also build a static libdvr for linking into tests. The linker script // restricting function access in the shared lib makes it inconvenient to use in // test code. cc_library_static { name: "libdvr_static.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], export_header_lib_headers: ["libdvr_headers"], srcs: srcs, static_libs: static_libs, shared_libs: shared_libs, } subdirs = [ "tests", ] ����������������������������������������������������������libs/vr/libdvr/dvr_api.cpp��������������������������������������������������������������������������0100644 0000000 0000000 00000004367 13756501735 014625� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_api.h" #include #include #include // Headers from libdvr #include #include #include #include #include #include #include #include // Headers not yet moved into libdvr. // TODO(jwcai) Move these once their callers are moved into Google3. #include #include #include extern "C" { int dvrGetApi(void* api, size_t struct_size, int version) { ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size, version); if (version == 1) { // New entry points are added at the end. If the caller's struct and // this library have different sizes, we define the entry points in common. // The caller is expected to handle unset entry points if necessary. size_t clamped_struct_size = std::min(struct_size, sizeof(DvrApi_v1)); DvrApi_v1* dvr_api = static_cast(api); // Defines an API entry for V1 (no version suffix). #define DVR_V1_API_ENTRY(name) \ do { \ if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \ clamped_struct_size) { \ dvr_api->name = dvr##name; \ } \ } while (0) #define DVR_V1_API_ENTRY_DEPRECATED(name) \ do { \ if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \ clamped_struct_size) { \ dvr_api->name = nullptr; \ } \ } while (0) #include "include/dvr/dvr_api_entries.h" // Undefine macro definitions to play nice with Google3 style rules. #undef DVR_V1_API_ENTRY #undef DVR_V1_API_ENTRY_DEPRECATED return 0; } ALOGE("dvrGetApi: Unknown API version=%d", version); return -EINVAL; } } // extern "C" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_buffer.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000006564 13756501735 015326� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_buffer.h" #include #include #include #include #include #include "dvr_internal.h" using namespace android; namespace android { namespace dvr { DvrBuffer* CreateDvrBufferFromIonBuffer( const std::shared_ptr& ion_buffer) { if (!ion_buffer) return nullptr; return new DvrBuffer{std::move(ion_buffer)}; } } // namespace dvr } // namespace android namespace { int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer, AHardwareBuffer** hardware_buffer) { if (!hardware_buffer || !graphic_buffer) { return -EINVAL; } *hardware_buffer = reinterpret_cast(graphic_buffer); AHardwareBuffer_acquire(*hardware_buffer); return 0; } } // anonymous namespace extern "C" { void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) { if (write_buffer != nullptr) { ALOGW_IF( write_buffer->slot != -1, "dvrWriteBufferDestroy: Destroying a buffer associated with a valid " "buffer queue slot. This may indicate possible leaks, buffer_id=%d.", dvrWriteBufferGetId(write_buffer)); delete write_buffer; } } int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) { return write_buffer && write_buffer->write_buffer; } int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) { if (!write_buffer || !write_buffer->write_buffer) return -EINVAL; return write_buffer->write_buffer->id(); } int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer, AHardwareBuffer** hardware_buffer) { if (!write_buffer || !write_buffer->write_buffer) return -EINVAL; return ConvertToAHardwareBuffer( write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer); } void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { if (read_buffer != nullptr) { ALOGW_IF( read_buffer->slot != -1, "dvrReadBufferDestroy: Destroying a buffer associated with a valid " "buffer queue slot. This may indicate possible leaks, buffer_id=%d.", dvrReadBufferGetId(read_buffer)); delete read_buffer; } } int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) { return read_buffer && read_buffer->read_buffer; } int dvrReadBufferGetId(DvrReadBuffer* read_buffer) { if (!read_buffer || !read_buffer->read_buffer) return -EINVAL; return read_buffer->read_buffer->id(); } int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer, AHardwareBuffer** hardware_buffer) { if (!read_buffer || !read_buffer->read_buffer) return -EINVAL; return ConvertToAHardwareBuffer( read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer); } void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; } int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer, AHardwareBuffer** hardware_buffer) { if (!buffer || !buffer->buffer || !hardware_buffer) { return -EINVAL; } return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(), hardware_buffer); } // Retrieve the shared buffer layout version defined in dvr_shared_buffers.h. int dvrBufferGlobalLayoutVersionGet() { return android::dvr::kSharedBufferLayoutVersion; } } // extern "C" ��������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_buffer_queue.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000046752 13756501735 016535� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_api.h" #include "include/dvr/dvr_buffer_queue.h" #include #include #include "dvr_internal.h" #include "dvr_buffer_queue_internal.h" using namespace android; using android::dvr::BufferHubBase; using android::dvr::ConsumerBuffer; using android::dvr::ConsumerQueue; using android::dvr::ProducerBuffer; using android::dvr::ProducerQueue; using android::dvr::ProducerQueueConfigBuilder; using android::dvr::UsagePolicy; extern "C" { DvrWriteBufferQueue::DvrWriteBufferQueue( const std::shared_ptr& producer_queue) : producer_queue_(producer_queue), width_(producer_queue->default_width()), height_(producer_queue->default_height()), format_(producer_queue->default_format()) {} int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) { if (native_window_ == nullptr) { // Lazy creation of |native_window|, as not everyone is using // DvrWriteBufferQueue as an external surface. sp gbp = BufferHubProducer::Create(producer_queue_); native_window_ = new Surface(gbp, true); } *out_window = static_cast(native_window_.get()); return 0; } int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { std::unique_ptr consumer_queue = producer_queue_->CreateConsumerQueue(); if (consumer_queue == nullptr) { ALOGE( "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue " "from producer queue: queue_id=%d.", producer_queue_->id()); return -ENOMEM; } *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue)); return 0; } int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd) { DvrNativeBufferMetadata meta; DvrWriteBuffer* buffer = nullptr; int fence_fd = -1; if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd)) return ret; if (!buffer) return -ENOMEM; write_buffers_[buffer->slot].reset(buffer); write_buffer->write_buffer = std::move(buffer->write_buffer); *out_fence_fd = fence_fd; return 0; } int DvrWriteBufferQueue::GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { size_t slot; pdx::LocalHandle release_fence; // Need to retry N+1 times, where N is total number of buffers in the queue. // As in the worst case, we will dequeue all N buffers and reallocate them, on // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension. size_t max_retries = 1 + producer_queue_->capacity(); size_t retry = 0; for (; retry < max_retries; retry++) { auto buffer_status = producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence); if (!buffer_status) { ALOGE_IF(buffer_status.error() != ETIMEDOUT, "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } if (write_buffers_[slot] == nullptr) { // Lazy initialization of a write_buffers_ slot. Note that a slot will // only be dynamically allocated once during the entire cycle life of a // queue. write_buffers_[slot] = std::make_unique(); write_buffers_[slot]->slot = slot; } LOG_ALWAYS_FATAL_IF( write_buffers_[slot]->write_buffer, "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot); write_buffers_[slot]->write_buffer = std::move(buffer_status.take()); const auto& producer_buffer = write_buffers_[slot]->write_buffer; if (!producer_buffer) return -ENOMEM; if (width_ == producer_buffer->width() && height_ == producer_buffer->height() && format_ == producer_buffer->format()) { // Producer queue returns a buffer matches the current request. break; } // Needs reallocation. Note that if there are already multiple available // buffers in the queue, the next one returned from |queue_->Dequeue| may // still have the old buffer dimension or format. Retry up to N+1 times or // until we dequeued a buffer with new configuration. ALOGD_IF(TRACE, "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu " "(w=%u, h=%u, fmt=%u) is different from the buffer returned " "(w=%u, h=%u, fmt=%u). Need re-allocation.", slot, width_, height_, format_, producer_buffer->width(), producer_buffer->height(), producer_buffer->format()); // Currently, we are not storing |layer_count| and |usage| in queue // configuration. Copy those setup from the last buffer dequeued before we // remove it. uint32_t old_layer_count = producer_buffer->layer_count(); uint64_t old_usage = producer_buffer->usage(); // Allocate a new producer buffer with new buffer configs. Note that if // there are already multiple available buffers in the queue, the next one // returned from |queue_->Dequeue| may still have the old buffer dimension // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until // we dequeued a buffer with new configuration. auto remove_status = producer_queue_->RemoveBuffer(slot); if (!remove_status) { ALOGE("DvrWriteBufferQueue::Dequeue: Failed to remove buffer: %s", remove_status.GetErrorMessage().c_str()); return -remove_status.error(); } // Make sure that the previously allocated buffer is dereferenced from // write_buffers_ array. write_buffers_[slot]->write_buffer = nullptr; auto allocate_status = producer_queue_->AllocateBuffer( width_, height_, old_layer_count, format_, old_usage); if (!allocate_status) { ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s", allocate_status.GetErrorMessage().c_str()); return -allocate_status.error(); } } if (retry >= max_retries) { ALOGE( "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after " "resizing."); return -ENOMEM; } *out_write_buffer = write_buffers_[slot].release(); *out_fence_fd = release_fence.Release(); return 0; } int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd) { // Some basic sanity checks before we put the buffer back into a slot. size_t slot = static_cast(write_buffer->slot); LOG_FATAL_IF( (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()), "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); if (write_buffers_[slot] != nullptr) { ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot); return -EINVAL; } if (write_buffer->write_buffer == nullptr) { ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer."); return -EINVAL; } if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) { ALOGE( "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not " "belong to this buffer queue. Posting buffer: id=%d, buffer in " "queue: id=%d", write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot)); return -EINVAL; } write_buffer->write_buffer->SetQueueIndex(next_post_index_++); pdx::LocalHandle fence(ready_fence_fd); const int ret = write_buffer->write_buffer->PostAsync(meta, fence); if (ret < 0) { ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d", ret); return ret; } // Put the DvrWriteBuffer pointer back into its slot for reuse. write_buffers_[slot].reset(write_buffer); // It's import to reset the write buffer client now. It should stay invalid // until next GainBuffer on the same slot. write_buffers_[slot]->write_buffer = nullptr; return 0; } int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) { if (width == 0 || height == 0) { ALOGE( "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, " "h=%u.", width, height); return -EINVAL; } width_ = width; height_ = height; return 0; } int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** out_write_queue) { if (!out_write_queue) return -EINVAL; auto config_builder = ProducerQueueConfigBuilder() .SetDefaultWidth(width) .SetDefaultHeight(height) .SetDefaultFormat(format) .SetMetadataSize(metadata_size); std::unique_ptr producer_queue = ProducerQueue::Create(config_builder.Build(), UsagePolicy{}); if (!producer_queue) { ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue."); return -ENOMEM; } auto status = producer_queue->AllocateBuffers(width, height, layer_count, format, usage, capacity); if (!status.ok()) { ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers."); return -ENOMEM; } *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue)); return 0; } void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) { delete write_queue; } ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) { if (!write_queue) return -EINVAL; return write_queue->capacity(); } int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) { if (!write_queue) return -EINVAL; return write_queue->id(); } int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue, ANativeWindow** out_window) { if (!write_queue || !out_window) return -EINVAL; return write_queue->GetNativeWindow(out_window); } int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue) { if (!write_queue || !out_read_queue) return -EINVAL; return write_queue->CreateReadQueue(out_read_queue); } int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd) return -EINVAL; return write_queue->GainBuffer(timeout, out_write_buffer, out_meta, out_fence_fd); } int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd) { if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta) return -EINVAL; return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd); } int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height) { if (!write_queue) return -EINVAL; return write_queue->ResizeBuffer(width, height); } // ReadBufferQueue DvrReadBufferQueue::DvrReadBufferQueue( const std::shared_ptr& consumer_queue) : consumer_queue_(consumer_queue) {} int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) { std::unique_ptr consumer_queue = consumer_queue_->CreateConsumerQueue(); if (consumer_queue == nullptr) { ALOGE( "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue " "from producer queue: queue_id=%d.", consumer_queue_->id()); return -ENOMEM; } *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue)); return 0; } int DvrReadBufferQueue::AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { size_t slot; pdx::LocalHandle acquire_fence; auto buffer_status = consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence); if (!buffer_status) { ALOGE_IF(buffer_status.error() != ETIMEDOUT, "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } if (read_buffers_[slot] == nullptr) { // Lazy initialization of a read_buffers_ slot. Note that a slot will only // be dynamically allocated once during the entire cycle life of a queue. read_buffers_[slot] = std::make_unique(); read_buffers_[slot]->slot = slot; } LOG_FATAL_IF( read_buffers_[slot]->read_buffer, "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot); read_buffers_[slot]->read_buffer = std::move(buffer_status.take()); *out_read_buffer = read_buffers_[slot].release(); *out_fence_fd = acquire_fence.Release(); return 0; } int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd) { // Some basic sanity checks before we put the buffer back into a slot. size_t slot = static_cast(read_buffer->slot); LOG_FATAL_IF( (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()), "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); if (read_buffers_[slot] != nullptr) { ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot); return -EINVAL; } if (read_buffer->read_buffer == nullptr) { ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer."); return -EINVAL; } if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) { if (consumer_queue_->GetBufferId(slot) > 0) { ALOGE( "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not " "belong to this queue (queue_id=%d): attempting to release buffer " "(buffer_id=%d) at slot %d which holds a different buffer " "(buffer_id=%d).", consumer_queue_->id(), read_buffer->read_buffer->id(), static_cast(slot), consumer_queue_->GetBufferId(slot)); } else { ALOGI( "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not " "belong to this queue (queue_id=%d): attempting to release buffer " "(buffer_id=%d) at slot %d which is empty.", consumer_queue_->id(), read_buffer->read_buffer->id(), static_cast(slot)); } } pdx::LocalHandle fence(release_fence_fd); int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence); if (ret < 0) { ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d", ret); return ret; } // Put the DvrReadBuffer pointer back into its slot for reuse. read_buffers_[slot].reset(read_buffer); // It's import to reset the read buffer client now. It should stay invalid // until next AcquireBuffer on the same slot. read_buffers_[slot]->read_buffer = nullptr; return 0; } void DvrReadBufferQueue::SetBufferAvailableCallback( DvrReadBufferQueueBufferAvailableCallback callback, void* context) { if (callback == nullptr) { consumer_queue_->SetBufferAvailableCallback(nullptr); } else { consumer_queue_->SetBufferAvailableCallback( [callback, context]() { callback(context); }); } } void DvrReadBufferQueue::SetBufferRemovedCallback( DvrReadBufferQueueBufferRemovedCallback callback, void* context) { if (callback == nullptr) { consumer_queue_->SetBufferRemovedCallback(nullptr); } else { consumer_queue_->SetBufferRemovedCallback( [callback, context](const std::shared_ptr& buffer) { // When buffer is removed from the queue, the slot is already invalid. auto read_buffer = std::make_unique(); read_buffer->read_buffer = std::static_pointer_cast(buffer); callback(read_buffer.release(), context); }); } } int DvrReadBufferQueue::HandleEvents() { // TODO(jwcai) Probably should change HandleQueueEvents to return Status. consumer_queue_->HandleQueueEvents(); return 0; } void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) { delete read_queue; } ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->capacity(); } int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->id(); } int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->event_fd(); } int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue) { if (!read_queue || !out_read_queue) return -EINVAL; return read_queue->CreateReadQueue(out_read_queue); } int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd, void* out_meta, size_t meta_size_bytes) { if (!read_queue || !read_buffer || !out_fence_fd) return -EINVAL; if (meta_size_bytes != 0 && !out_meta) return -EINVAL; return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta, meta_size_bytes); } int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd) { if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd) return -EINVAL; return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta, out_fence_fd); } int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd) { if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta) return -EINVAL; return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd); } int dvrReadBufferQueueSetBufferAvailableCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferAvailableCallback callback, void* context) { if (!read_queue) return -EINVAL; read_queue->SetBufferAvailableCallback(callback, context); return 0; } int dvrReadBufferQueueSetBufferRemovedCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferRemovedCallback callback, void* context) { if (!read_queue) return -EINVAL; read_queue->SetBufferRemovedCallback(callback, context); return 0; } int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue) { if (!read_queue) return -EINVAL; return read_queue->HandleEvents(); } } // extern "C" ����������������������libs/vr/libdvr/dvr_buffer_queue_internal.h����������������������������������������������������������0100644 0000000 0000000 00000007041 13756501735 020062� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ #define ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ #include #include #include #include #include #include "dvr_internal.h" struct ANativeWindow; typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata; typedef struct DvrReadBuffer DvrReadBuffer; typedef struct DvrReadBufferQueue DvrReadBufferQueue; typedef struct DvrWriteBuffer DvrWriteBuffer; typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context); typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer, void* context); struct DvrWriteBufferQueue { using BufferHubQueue = android::dvr::BufferHubQueue; using ProducerQueue = android::dvr::ProducerQueue; // Create a concrete object for DvrWriteBufferQueue. // // @param producer_queue The BufferHub's ProducerQueue that is used to back // this DvrWriteBufferQueue, must not be NULL. explicit DvrWriteBufferQueue( const std::shared_ptr& producer_queue); int id() const { return producer_queue_->id(); } uint32_t width() const { return width_; }; uint32_t height() const { return height_; }; uint32_t format() const { return format_; }; size_t capacity() const { return producer_queue_->capacity(); } const std::shared_ptr& producer_queue() const { return producer_queue_; } int GetNativeWindow(ANativeWindow** out_window); int CreateReadQueue(DvrReadBufferQueue** out_read_queue); int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd); int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd); int PostBuffer(DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd); int ResizeBuffer(uint32_t width, uint32_t height); private: std::shared_ptr producer_queue_; std::array, BufferHubQueue::kMaxQueueCapacity> write_buffers_; int64_t next_post_index_ = 0; uint32_t width_; uint32_t height_; uint32_t format_; android::sp native_window_; }; struct DvrReadBufferQueue { using BufferHubQueue = android::dvr::BufferHubQueue; using ConsumerQueue = android::dvr::ConsumerQueue; explicit DvrReadBufferQueue( const std::shared_ptr& consumer_queue); int id() const { return consumer_queue_->id(); } int event_fd() const { return consumer_queue_->queue_fd(); } size_t capacity() const { return consumer_queue_->capacity(); } int CreateReadQueue(DvrReadBufferQueue** out_read_queue); int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd, void* out_meta, size_t user_metadata_size); int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd); int ReleaseBuffer(DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd); void SetBufferAvailableCallback( DvrReadBufferQueueBufferAvailableCallback callback, void* context); void SetBufferRemovedCallback( DvrReadBufferQueueBufferRemovedCallback callback, void* context); int HandleEvents(); private: std::shared_ptr consumer_queue_; std::array, BufferHubQueue::kMaxQueueCapacity> read_buffers_; }; #endif // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_configuration_data.cpp�����������������������������������������������������������0100644 0000000 0000000 00000001743 13756501735 017707� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_configuration_data.h" #include using android::dvr::display::ConfigFileType; using android::dvr::display::DisplayClient; extern "C" { int dvrConfigurationDataGet(int config_type, uint8_t** data, size_t* data_size) { if (!data || !data_size) { return -EINVAL; } auto client = DisplayClient::Create(); if (!client) { ALOGE("dvrGetGlobalBuffer: Failed to create display client!"); return -ECOMM; } ConfigFileType config_file_type = static_cast(config_type); auto config_data_status = client->GetConfigurationData(config_file_type); if (!config_data_status) { return -config_data_status.error(); } *data_size = config_data_status.get().size(); *data = new uint8_t[*data_size]; std::copy_n(config_data_status.get().begin(), *data_size, *data); return 0; } void dvrConfigurationDataDestroy(uint8_t* data) { delete[] data; } } // extern "C" �����������������������������libs/vr/libdvr/dvr_display_manager.cpp��������������������������������������������������������������0100644 0000000 0000000 00000021136 13756501735 017204� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_display_manager.h" #include #include #include #include #include #include #include "dvr_internal.h" #include "dvr_buffer_queue_internal.h" using android::dvr::ConsumerBuffer; using android::dvr::display::DisplayManagerClient; using android::dvr::display::SurfaceAttribute; using android::dvr::display::SurfaceAttributes; using android::dvr::display::SurfaceState; using android::pdx::rpc::EmptyVariant; namespace { // Extracts type and value from the attribute Variant and writes them into the // respective fields of DvrSurfaceAttribute. struct AttributeVisitor { DvrSurfaceAttribute* attribute; void operator()(int32_t value) { attribute->value.int32_value = value; attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32; } void operator()(int64_t value) { attribute->value.int64_value = value; attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64; } void operator()(bool value) { attribute->value.bool_value = value; attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL; } void operator()(float value) { attribute->value.float_value = value; attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT; } void operator()(const std::array& value) { std::copy(value.cbegin(), value.cend(), attribute->value.float2_value); attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2; } void operator()(const std::array& value) { std::copy(value.cbegin(), value.cend(), attribute->value.float3_value); attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3; } void operator()(const std::array& value) { std::copy(value.cbegin(), value.cend(), attribute->value.float4_value); attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4; } void operator()(const std::array& value) { std::copy(value.cbegin(), value.cend(), attribute->value.float8_value); attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8; } void operator()(const std::array& value) { std::copy(value.cbegin(), value.cend(), attribute->value.float16_value); attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16; } void operator()(EmptyVariant) { attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE; } }; size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes, DvrSurfaceAttribute* attributes, size_t max_count) { size_t count = 0; for (const auto& attribute : surface_attributes) { if (count >= max_count) break; // Copy the key and extract the Variant value using a visitor. attributes[count].key = attribute.first; attribute.second.Visit(AttributeVisitor{&attributes[count]}); count++; } return count; } } // anonymous namespace extern "C" { struct DvrDisplayManager { std::unique_ptr client; }; struct DvrSurfaceState { std::vector state; }; int dvrDisplayManagerCreate(DvrDisplayManager** client_out) { if (!client_out) return -EINVAL; auto client = DisplayManagerClient::Create(); if (!client) { ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!"); return -EIO; } *client_out = new DvrDisplayManager{std::move(client)}; return 0; } void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; } int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) { if (!client) return -EINVAL; return client->client->event_fd(); } int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client, int in_events, int* out_events) { if (!client || !out_events) return -EINVAL; auto status = client->client->GetEventMask(in_events); if (!status) return -status.error(); *out_events = status.get(); return 0; } int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client, DvrSurfaceState* state) { if (!client || !state) return -EINVAL; auto status = client->client->GetSurfaceState(); if (!status) return -status.error(); state->state = status.take(); return 0; } int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client, int surface_id, int queue_id, DvrReadBufferQueue** queue_out) { if (!client || !queue_out) return -EINVAL; auto status = client->client->GetSurfaceQueue(surface_id, queue_id); if (!status) { ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s", status.GetErrorMessage().c_str()); return -status.error(); } *queue_out = new DvrReadBufferQueue(status.take()); return 0; } int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) { if (!surface_state_out) return -EINVAL; *surface_state_out = new DvrSurfaceState{}; return 0; } void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) { delete surface_state; } int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state, size_t* count_out) { if (!surface_state) return -EINVAL; *count_out = surface_state->state.size(); return 0; } int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state, size_t surface_index, DvrSurfaceUpdateFlags* flags_out) { if (!surface_state || surface_index >= surface_state->state.size()) return -EINVAL; *flags_out = surface_state->state[surface_index].update_flags; return 0; } int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state, size_t surface_index, int* surface_id_out) { if (!surface_state || surface_index >= surface_state->state.size()) return -EINVAL; *surface_id_out = surface_state->state[surface_index].surface_id; return 0; } int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state, size_t surface_index, int* process_id_out) { if (!surface_state || surface_index >= surface_state->state.size()) return -EINVAL; *process_id_out = surface_state->state[surface_index].process_id; return 0; } int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out) { if (!surface_state || surface_index >= surface_state->state.size()) return -EINVAL; *count_out = surface_state->state[surface_index].queue_ids.size(); return 0; } ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state, size_t surface_index, int* queue_ids, size_t max_count) { if (!surface_state || surface_index >= surface_state->state.size()) return -EINVAL; size_t i; const auto& state = surface_state->state[surface_index]; for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) { queue_ids[i] = state.queue_ids[i]; } return i; } int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state, size_t surface_index, int* z_order_out) { if (!surface_state || surface_index >= surface_state->state.size() || !z_order_out) { return -EINVAL; } *z_order_out = surface_state->state[surface_index].GetZOrder(); return 0; } int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state, size_t surface_index, bool* visible_out) { if (!surface_state || surface_index >= surface_state->state.size() || !visible_out) { return -EINVAL; } *visible_out = surface_state->state[surface_index].GetVisible(); return 0; } int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out) { if (!surface_state || surface_index >= surface_state->state.size() || !count_out) { return -EINVAL; } *count_out = surface_state->state[surface_index].surface_attributes.size(); return 0; } ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state, size_t surface_index, DvrSurfaceAttribute* attributes, size_t max_count) { if (!surface_state || surface_index >= surface_state->state.size() || !attributes) { return -EINVAL; } return ConvertSurfaceAttributes( surface_state->state[surface_index].surface_attributes, attributes, max_count); } } // extern "C" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_hardware_composer_client.cpp�����������������������������������������������������0100644 0000000 0000000 00000021222 13756501735 021103� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_hardware_composer_client.h" #include #include #include #include #include #include #include #include struct DvrHwcFrame { android::dvr::ComposerView::Frame frame; }; namespace { class HwcCallback : public android::dvr::BnVrComposerCallback { public: using CallbackFunction = std::function; explicit HwcCallback(const CallbackFunction& callback); ~HwcCallback() override; // Reset the callback. This needs to be done early to avoid use after free // accesses from binder thread callbacks. void Shutdown(); std::unique_ptr DequeueFrame(); private: // android::dvr::BnVrComposerCallback: android::binder::Status onNewFrame( const android::dvr::ParcelableComposerFrame& frame, android::dvr::ParcelableUniqueFd* fence) override; // Protects the |callback_| from uses from multiple threads. During shutdown // there may be in-flight frame update events. In those cases the callback // access needs to be protected otherwise binder threads may access an invalid // callback. std::mutex mutex_; CallbackFunction callback_; HwcCallback(const HwcCallback&) = delete; void operator=(const HwcCallback&) = delete; }; HwcCallback::HwcCallback(const CallbackFunction& callback) : callback_(callback) {} HwcCallback::~HwcCallback() {} void HwcCallback::Shutdown() { std::lock_guard guard(mutex_); callback_ = nullptr; } android::binder::Status HwcCallback::onNewFrame( const android::dvr::ParcelableComposerFrame& frame, android::dvr::ParcelableUniqueFd* fence) { std::lock_guard guard(mutex_); if (!callback_) { fence->set_fence(android::base::unique_fd()); return android::binder::Status::ok(); } std::unique_ptr dvr_frame(new DvrHwcFrame()); dvr_frame->frame = frame.frame(); fence->set_fence(android::base::unique_fd(callback_(dvr_frame.release()))); return android::binder::Status::ok(); } } // namespace struct DvrHwcClient { android::sp composer; android::sp callback; }; DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) { std::unique_ptr client(new DvrHwcClient()); android::sp sm(android::defaultServiceManager()); client->composer = android::interface_cast( sm->getService(android::dvr::IVrComposer::SERVICE_NAME())); if (!client->composer.get()) return nullptr; client->callback = new HwcCallback(std::bind(callback, data, std::placeholders::_1)); android::binder::Status status = client->composer->registerObserver( client->callback); if (!status.isOk()) return nullptr; return client.release(); } void dvrHwcClientDestroy(DvrHwcClient* client) { client->composer->clearObserver(); // NOTE: Deleting DvrHwcClient* isn't enough since DvrHwcClient::callback is a // shared pointer that could be referenced from a binder thread. But the // client callback isn't valid past this calls so that needs to be reset. client->callback->Shutdown(); delete client; } void dvrHwcFrameDestroy(DvrHwcFrame* frame) { delete frame; } DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) { return frame->frame.display_id; } int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) { return frame->frame.display_width; } int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) { return frame->frame.display_height; } bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) { return frame->frame.removed; } size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) { return frame->frame.layers.size(); } uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) { return static_cast(frame->frame.active_config); } uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) { return static_cast(frame->frame.color_mode); } void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix, int32_t* out_hint) { *out_hint = frame->frame.color_transform_hint; memcpy(out_matrix, frame->frame.color_transform, sizeof(frame->frame.color_transform)); } uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) { return static_cast(frame->frame.power_mode); } uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) { return static_cast(frame->frame.vsync_enabled); } DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].id; } AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame, size_t layer_index) { AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer( frame->frame.layers[layer_index].buffer.get()); AHardwareBuffer_acquire(buffer); return buffer; } int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].fence->dup(); } DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame, size_t layer_index) { return DvrHwcRecti{ frame->frame.layers[layer_index].display_frame.left, frame->frame.layers[layer_index].display_frame.top, frame->frame.layers[layer_index].display_frame.right, frame->frame.layers[layer_index].display_frame.bottom, }; } DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) { return DvrHwcRectf{ frame->frame.layers[layer_index].crop.left, frame->frame.layers[layer_index].crop.top, frame->frame.layers[layer_index].crop.right, frame->frame.layers[layer_index].crop.bottom, }; } DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame, size_t layer_index) { return static_cast( frame->frame.layers[layer_index].blend_mode); } float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].alpha; } uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].type; } uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].app_id; } uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].z_order; } void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index, int32_t* out_x, int32_t* out_y) { *out_x = frame->frame.layers[layer_index].cursor_x; *out_y = frame->frame.layers[layer_index].cursor_y; } uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].transform; } uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].dataspace; } uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) { const auto& color = frame->frame.layers[layer_index].color; return color.r | (static_cast(color.g) << 8) | (static_cast(color.b) << 16) | (static_cast(color.a) << 24); } uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].visible_regions.size(); } DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame, size_t layer_index, size_t index) { return DvrHwcRecti{ frame->frame.layers[layer_index].visible_regions[index].left, frame->frame.layers[layer_index].visible_regions[index].top, frame->frame.layers[layer_index].visible_regions[index].right, frame->frame.layers[layer_index].visible_regions[index].bottom, }; } uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame, size_t layer_index) { return frame->frame.layers[layer_index].damaged_regions.size(); } DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame, size_t layer_index, size_t index) { return DvrHwcRecti{ frame->frame.layers[layer_index].damaged_regions[index].left, frame->frame.layers[layer_index].damaged_regions[index].top, frame->frame.layers[layer_index].damaged_regions[index].right, frame->frame.layers[layer_index].damaged_regions[index].bottom, }; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_internal.h�����������������������������������������������������������������������0100644 0000000 0000000 00000002341 13756501735 015323� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_INTERNAL_H_ #define ANDROID_DVR_INTERNAL_H_ #include #include extern "C" { typedef struct DvrBuffer DvrBuffer; typedef struct DvrReadBuffer DvrReadBuffer; typedef struct DvrWriteBuffer DvrWriteBuffer; } // extern "C" namespace android { namespace dvr { class IonBuffer; DvrBuffer* CreateDvrBufferFromIonBuffer( const std::shared_ptr& ion_buffer); } // namespace dvr } // namespace android extern "C" { struct DvrWriteBuffer { // The slot nubmer of the buffer, a valid slot number must be in the range of // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for // DvrWriteBuffer acquired from a DvrWriteBufferQueue. int32_t slot = -1; std::shared_ptr write_buffer; }; struct DvrReadBuffer { // The slot nubmer of the buffer, a valid slot number must be in the range of // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for // DvrReadBuffer acquired from a DvrReadBufferQueue. int32_t slot = -1; std::shared_ptr read_buffer; }; struct DvrBuffer { std::shared_ptr buffer; }; } // extern "C" #endif // ANDROID_DVR_INTERNAL_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_performance.cpp������������������������������������������������������������������0100644 0000000 0000000 00000000671 13756501735 016347� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_performance.h" #include using android::dvr::PerformanceClient; extern "C" { int dvrPerformanceSetSchedulerPolicy(pid_t task_id, const char* scheduler_policy) { int error; if (auto client = PerformanceClient::Create(&error)) return client->SetSchedulerPolicy(task_id, scheduler_policy); else return error; } } // extern "C" �����������������������������������������������������������������������libs/vr/libdvr/dvr_pose.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000001644 13756501735 015015� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_pose.h" #include #include #include #include "dvr_buffer_queue_internal.h" using android::dvr::ConsumerQueue; int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type, DvrReadBufferQueue** queue_out) { if (!client || !queue_out) return -EINVAL; ConsumerQueue* consumer_queue; int status = android::dvr::dvrPoseClientGetDataReaderHandle(client, data_type, &consumer_queue); if (status != 0) { ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status); return status; } std::shared_ptr consumer_queue_ptr{consumer_queue}; *queue_out = new DvrReadBufferQueue(consumer_queue_ptr); return 0; } ��������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_surface.cpp����������������������������������������������������������������������0100644 0000000 0000000 00000021130 13756501735 015467� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_surface.h" #include #include #include #include #include "dvr_buffer_queue_internal.h" #include "dvr_internal.h" using android::AHardwareBuffer_convertToGrallocUsageBits; using android::dvr::display::DisplayClient; using android::dvr::display::Surface; using android::dvr::display::SurfaceAttributes; using android::dvr::display::SurfaceAttributeValue; using android::pdx::rpc::EmptyVariant; namespace { // Sets the Variant |destination| to the target std::array type and copies the C // array into it. Unsupported std::array configurations will fail to compile. template void ArrayCopy(SurfaceAttributeValue* destination, const T (&source)[N]) { using ArrayType = std::array; *destination = ArrayType{}; std::copy(std::begin(source), std::end(source), std::get(*destination).begin()); } bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes, size_t attribute_count, SurfaceAttributes* surface_attributes, size_t* error_index) { for (size_t i = 0; i < attribute_count; i++) { SurfaceAttributeValue value; switch (attributes[i].value.type) { case DVR_SURFACE_ATTRIBUTE_TYPE_INT32: value = attributes[i].value.int32_value; break; case DVR_SURFACE_ATTRIBUTE_TYPE_INT64: value = attributes[i].value.int64_value; break; case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL: // bool_value is defined in an extern "C" block, which makes it look // like an int to C++. Use a cast to assign the correct type to the // Variant type SurfaceAttributeValue. value = static_cast(attributes[i].value.bool_value); break; case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT: value = attributes[i].value.float_value; break; case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2: ArrayCopy(&value, attributes[i].value.float2_value); break; case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3: ArrayCopy(&value, attributes[i].value.float3_value); break; case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4: ArrayCopy(&value, attributes[i].value.float4_value); break; case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8: ArrayCopy(&value, attributes[i].value.float8_value); break; case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16: ArrayCopy(&value, attributes[i].value.float16_value); break; case DVR_SURFACE_ATTRIBUTE_TYPE_NONE: value = EmptyVariant{}; break; default: *error_index = i; return false; } surface_attributes->emplace(attributes[i].key, value); } return true; } } // anonymous namespace extern "C" { struct DvrSurface { std::unique_ptr surface; }; int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes, size_t attribute_count, DvrSurface** out_surface) { if (out_surface == nullptr) { ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface); return -EINVAL; } size_t error_index; SurfaceAttributes surface_attributes; if (!ConvertSurfaceAttributes(attributes, attribute_count, &surface_attributes, &error_index)) { ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64, attributes[error_index].value.type); return -EINVAL; } auto status = Surface::CreateSurface(surface_attributes); if (!status) { ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s", status.GetErrorMessage().c_str()); return -status.error(); } *out_surface = new DvrSurface{status.take()}; return 0; } void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; } int dvrSurfaceGetId(DvrSurface* surface) { return surface->surface->surface_id(); } int dvrSurfaceSetAttributes(DvrSurface* surface, const DvrSurfaceAttribute* attributes, size_t attribute_count) { if (surface == nullptr || attributes == nullptr) { ALOGE( "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p " "attribute_count=%zu", surface, attributes, attribute_count); return -EINVAL; } size_t error_index; SurfaceAttributes surface_attributes; if (!ConvertSurfaceAttributes(attributes, attribute_count, &surface_attributes, &error_index)) { ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64, attributes[error_index].value.type); return -EINVAL; } auto status = surface->surface->SetAttributes(surface_attributes); if (!status) { ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s", status.GetErrorMessage().c_str()); return -status.error(); } return 0; } int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** out_writer) { if (surface == nullptr || out_writer == nullptr) { ALOGE( "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, " "out_writer=%p.", surface, out_writer); return -EINVAL; } auto status = surface->surface->CreateQueue( width, height, layer_count, format, usage, capacity, metadata_size); if (!status) { ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s", status.GetErrorMessage().c_str()); return -status.error(); } *out_writer = new DvrWriteBufferQueue(status.take()); return 0; } int dvrSetupGlobalBuffer(DvrGlobalBufferKey key, size_t size, uint64_t usage, DvrBuffer** buffer_out) { if (!buffer_out) return -EINVAL; int error; auto client = DisplayClient::Create(&error); if (!client) { ALOGE("dvrSetupGlobalBuffer: Failed to create display client: %s", strerror(-error)); return error; } uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage); auto buffer_status = client->SetupGlobalBuffer(key, size, gralloc_usage); if (!buffer_status) { ALOGE("dvrSetupGlobalBuffer: Failed to setup global buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take()); return 0; } int dvrDeleteGlobalBuffer(DvrGlobalBufferKey key) { int error; auto client = DisplayClient::Create(&error); if (!client) { ALOGE("dvrDeleteGlobalBuffer: Failed to create display client: %s", strerror(-error)); return error; } auto buffer_status = client->DeleteGlobalBuffer(key); if (!buffer_status) { ALOGE("dvrDeleteGlobalBuffer: Failed to delete named buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } return 0; } int dvrGetGlobalBuffer(DvrGlobalBufferKey key, DvrBuffer** out_buffer) { if (!out_buffer) return -EINVAL; int error; auto client = DisplayClient::Create(&error); if (!client) { ALOGE("dvrGetGlobalBuffer: Failed to create display client: %s", strerror(-error)); return error; } auto status = client->GetGlobalBuffer(key); if (!status) { return -status.error(); } *out_buffer = CreateDvrBufferFromIonBuffer(status.take()); return 0; } int dvrGetNativeDisplayMetrics(size_t sizeof_metrics, DvrNativeDisplayMetrics* metrics) { ALOGE_IF(sizeof_metrics != sizeof(DvrNativeDisplayMetrics), "dvrGetNativeDisplayMetrics: metrics struct mismatch, your dvr api " "header is out of date."); auto client = DisplayClient::Create(); if (!client) { ALOGE("dvrGetNativeDisplayMetrics: Failed to create display client!"); return -ECOMM; } if (metrics == nullptr) { ALOGE("dvrGetNativeDisplayMetrics: output metrics buffer must be non-null"); return -EINVAL; } auto status = client->GetDisplayMetrics(); if (!status) { return -status.error(); } if (sizeof_metrics >= 20) { metrics->display_width = status.get().display_width; metrics->display_height = status.get().display_height; metrics->display_x_dpi = status.get().display_x_dpi; metrics->display_y_dpi = status.get().display_y_dpi; metrics->vsync_period_ns = status.get().vsync_period_ns; } return 0; } } // extern "C" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_tracking.cpp���������������������������������������������������������������������0100644 0000000 0000000 00000004661 13756501735 015653� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_tracking.h" #include #include #if !DVR_TRACKING_IMPLEMENTED extern "C" { // This file provides the stub implementation of dvrTrackingXXX APIs. On // platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the // build file. int dvrTrackingCameraCreate(DvrTrackingCamera**) { ALOGE("dvrTrackingCameraCreate is not implemented."); return -ENOSYS; } void dvrTrackingCameraDestroy(DvrTrackingCamera*) { ALOGE("dvrTrackingCameraDestroy is not implemented."); } int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) { ALOGE("dvrTrackingCameraCreate is not implemented."); return -ENOSYS; } int dvrTrackingCameraStop(DvrTrackingCamera*) { ALOGE("dvrTrackingCameraCreate is not implemented."); return -ENOSYS; } int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) { ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); return -ENOSYS; } void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) { ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented."); } int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*, DvrTrackingFeatureCallback, void*) { ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); return -ENOSYS; } int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) { ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); return -ENOSYS; } int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*, DvrReadBuffer*, const DvrTrackingBufferMetadata*, bool*) { ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented."); return -ENOSYS; } int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) { ALOGE("dvrTrackingSensorsCreate is not implemented."); return -ENOSYS; } void dvrTrackingSensorsDestroy(DvrTrackingSensors*) { ALOGE("dvrTrackingSensorsDestroy is not implemented."); } int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback, void*) { ALOGE("dvrTrackingStart is not implemented."); return -ENOSYS; } int dvrTrackingSensorsStop(DvrTrackingSensors*) { ALOGE("dvrTrackingStop is not implemented."); return -ENOSYS; } } // extern "C" #endif // DVR_TRACKING_IMPLEMENTED �������������������������������������������������������������������������������libs/vr/libdvr/exported_apis.lds��������������������������������������������������������������������0100644 0000000 0000000 00000000175 13756501735 016040� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������{ global: # Whitelist the function to load the dvr api. dvrGetApi; local: # Hide everything else. *; }; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/�����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014111� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/CPPLINT.cfg������������������������������������������������������������������0100644 0000000 0000000 00000000033 13756501735 015674� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������filter=-build/header_guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014704� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_api.h����������������������������������������������������������������0100644 0000000 0000000 00000060427 13756501735 016507� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_API_H_ #define ANDROID_DVR_API_H_ #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #ifdef __GNUC__ #define ALIGNED_DVR_STRUCT(x) __attribute__((packed, aligned(x))) #else #define ALIGNED_DVR_STRUCT(x) #endif typedef struct ANativeWindow ANativeWindow; typedef struct DvrPoseAsync DvrPoseAsync; typedef uint64_t DvrSurfaceUpdateFlags; typedef struct DvrDisplayManager DvrDisplayManager; typedef struct DvrSurfaceState DvrSurfaceState; typedef struct DvrPoseClient DvrPoseClient; typedef struct DvrPoseDataCaptureRequest DvrPoseDataCaptureRequest; typedef struct DvrVSyncClient DvrVSyncClient; typedef struct DvrVirtualTouchpad DvrVirtualTouchpad; typedef struct DvrBuffer DvrBuffer; typedef struct DvrWriteBuffer DvrWriteBuffer; typedef struct DvrReadBuffer DvrReadBuffer; typedef struct AHardwareBuffer AHardwareBuffer; typedef struct DvrReadBufferQueue DvrReadBufferQueue; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata; typedef struct DvrSurface DvrSurface; typedef uint64_t DvrSurfaceAttributeType; typedef int32_t DvrSurfaceAttributeKey; typedef int32_t DvrGlobalBufferKey; typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue; typedef struct DvrSurfaceAttribute DvrSurfaceAttribute; typedef struct DvrReadBuffer DvrReadBuffer; typedef struct DvrTrackingCamera DvrTrackingCamera; typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor; typedef struct DvrTrackingSensors DvrTrackingSensors; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; // Note: To avoid breaking others during active development, only modify this // struct by appending elements to the end. // If you do feel we should to re-arrange or remove elements, please make a // note of it, and wait until we're about to finalize for an API release to do // so. typedef struct DvrNativeDisplayMetrics { uint32_t display_width; uint32_t display_height; uint32_t display_x_dpi; uint32_t display_y_dpi; uint32_t vsync_period_ns; } DvrNativeDisplayMetrics; // native_handle contains the fds for the underlying ION allocations inside // the gralloc buffer. This is needed temporarily while GPU vendors work on // better support for AHardwareBuffer via glBindSharedBuffer APIs. See // b/37207909. For now we can declare the native_handle struct where it is // used for GPU late latching. See cutils/native_handle.h for the struct layout. struct native_handle; // Device metrics data type enums. enum { // Request the device lens metrics protobuf. This matches cardboard protos. DVR_CONFIGURATION_DATA_LENS_METRICS = 0, // Request the device metrics protobuf. DVR_CONFIGURATION_DATA_DEVICE_METRICS = 1, // Request the per device configuration data file. DVR_CONFIGURATION_DATA_DEVICE_CONFIG = 2, }; // dvr_display_manager.h typedef int (*DvrDisplayManagerCreatePtr)(DvrDisplayManager** client_out); typedef void (*DvrDisplayManagerDestroyPtr)(DvrDisplayManager* client); typedef int (*DvrDisplayManagerGetEventFdPtr)(DvrDisplayManager* client); typedef int (*DvrDisplayManagerTranslateEpollEventMaskPtr)( DvrDisplayManager* client, int in_events, int* out_events); typedef int (*DvrDisplayManagerGetSurfaceStatePtr)( DvrDisplayManager* client, DvrSurfaceState* surface_state); typedef int (*DvrDisplayManagerGetReadBufferQueuePtr)( DvrDisplayManager* client, int surface_id, int queue_id, DvrReadBufferQueue** queue_out); typedef int (*DvrConfigurationDataGetPtr)(int config_type, uint8_t** data, size_t* data_size); typedef void (*DvrConfigurationDataDestroyPtr)(uint8_t* data); typedef int (*DvrSurfaceStateCreatePtr)(DvrSurfaceState** surface_state); typedef void (*DvrSurfaceStateDestroyPtr)(DvrSurfaceState* surface_state); typedef int (*DvrSurfaceStateGetSurfaceCountPtr)(DvrSurfaceState* surface_state, size_t* count_out); typedef int (*DvrSurfaceStateGetUpdateFlagsPtr)( DvrSurfaceState* surface_state, size_t surface_index, DvrSurfaceUpdateFlags* flags_out); typedef int (*DvrSurfaceStateGetSurfaceIdPtr)(DvrSurfaceState* surface_state, size_t surface_index, int* surface_id_out); typedef int (*DvrSurfaceStateGetProcessIdPtr)(DvrSurfaceState* surface_state, size_t surface_index, int* process_id_out); typedef int (*DvrSurfaceStateGetQueueCountPtr)(DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out); typedef ssize_t (*DvrSurfaceStateGetQueueIdsPtr)(DvrSurfaceState* surface_state, size_t surface_index, int* queue_ids, size_t max_count); typedef int (*DvrSurfaceStateGetZOrderPtr)(DvrSurfaceState* surface_state, size_t surface_index, int* z_order_out); typedef int (*DvrSurfaceStateGetVisiblePtr)(DvrSurfaceState* surface_state, size_t surface_index, bool* visible_out); typedef int (*DvrSurfaceStateGetAttributeCountPtr)( DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out); typedef ssize_t (*DvrSurfaceStateGetAttributesPtr)( DvrSurfaceState* surface_state, size_t surface_index, DvrSurfaceAttribute* attributes, size_t max_attribute_count); // dvr_buffer.h typedef void (*DvrWriteBufferCreateEmptyPtr)(DvrWriteBuffer** write_buffer_out); typedef void (*DvrWriteBufferDestroyPtr)(DvrWriteBuffer* write_buffer); typedef int (*DvrWriteBufferIsValidPtr)(DvrWriteBuffer* write_buffer); typedef int (*DvrWriteBufferClearPtr)(DvrWriteBuffer* write_buffer); typedef int (*DvrWriteBufferGetIdPtr)(DvrWriteBuffer* write_buffer); typedef int (*DvrWriteBufferGetAHardwareBufferPtr)( DvrWriteBuffer* write_buffer, AHardwareBuffer** hardware_buffer); typedef int (*DvrWriteBufferPostPtr)(DvrWriteBuffer* write_buffer, int ready_fence_fd, const void* meta, size_t meta_size_bytes); typedef int (*DvrWriteBufferGainPtr)(DvrWriteBuffer* write_buffer, int* release_fence_fd); typedef int (*DvrWriteBufferGainAsyncPtr)(DvrWriteBuffer* write_buffer); typedef const struct native_handle* (*DvrWriteBufferGetNativeHandlePtr)( DvrWriteBuffer* write_buffer); typedef void (*DvrReadBufferCreateEmptyPtr)(DvrReadBuffer** read_buffer_out); typedef void (*DvrReadBufferDestroyPtr)(DvrReadBuffer* read_buffer); typedef int (*DvrReadBufferIsValidPtr)(DvrReadBuffer* read_buffer); typedef int (*DvrReadBufferClearPtr)(DvrReadBuffer* read_buffer); typedef int (*DvrReadBufferGetIdPtr)(DvrReadBuffer* read_buffer); typedef int (*DvrReadBufferGetAHardwareBufferPtr)( DvrReadBuffer* read_buffer, AHardwareBuffer** hardware_buffer); typedef int (*DvrReadBufferAcquirePtr)(DvrReadBuffer* read_buffer, int* ready_fence_fd, void* meta, size_t meta_size_bytes); typedef int (*DvrReadBufferReleasePtr)(DvrReadBuffer* read_buffer, int release_fence_fd); typedef int (*DvrReadBufferReleaseAsyncPtr)(DvrReadBuffer* read_buffer); typedef const struct native_handle* (*DvrReadBufferGetNativeHandlePtr)( DvrReadBuffer* read_buffer); typedef void (*DvrBufferDestroyPtr)(DvrBuffer* buffer); typedef int (*DvrBufferGetAHardwareBufferPtr)( DvrBuffer* buffer, AHardwareBuffer** hardware_buffer); typedef int (*DvrBufferGlobalLayoutVersionGetPtr)(); typedef const struct native_handle* (*DvrBufferGetNativeHandlePtr)( DvrBuffer* buffer); // dvr_buffer_queue.h typedef int (*DvrWriteBufferQueueCreatePtr)(uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** queue_out); typedef void (*DvrWriteBufferQueueDestroyPtr)(DvrWriteBufferQueue* write_queue); typedef ssize_t (*DvrWriteBufferQueueGetCapacityPtr)( DvrWriteBufferQueue* write_queue); typedef int (*DvrWriteBufferQueueGetIdPtr)(DvrWriteBufferQueue* write_queue); typedef int (*DvrWriteBufferQueueGetExternalSurfacePtr)( DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); typedef int (*DvrWriteBufferQueueGetANativeWindowPtr)( DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); typedef int (*DvrWriteBufferQueueCreateReadQueuePtr)( DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue); typedef int (*DvrWriteBufferQueueDequeuePtr)(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer* out_buffer, int* out_fence_fd); typedef int (*DvrWriteBufferQueueGainBufferPtr)( DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd); typedef int (*DvrWriteBufferQueuePostBufferPtr)( DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd); typedef int (*DvrWriteBufferQueueResizeBufferPtr)( DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height); typedef void (*DvrReadBufferQueueDestroyPtr)(DvrReadBufferQueue* read_queue); typedef ssize_t (*DvrReadBufferQueueGetCapacityPtr)( DvrReadBufferQueue* read_queue); typedef int (*DvrReadBufferQueueGetIdPtr)(DvrReadBufferQueue* read_queue); typedef int (*DvrReadBufferQueueGetEventFdPtr)(DvrReadBufferQueue* read_queue); typedef int (*DvrReadBufferQueueCreateReadQueuePtr)( DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue); typedef int (*DvrReadBufferQueueDequeuePtr)(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer* out_buffer, int* out_fence_fd, void* out_meta, size_t meta_size_bytes); typedef int (*DvrReadBufferQueueAcquireBufferPtr)( DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd); typedef int (*DvrReadBufferQueueReleaseBufferPtr)( DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd); typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context); typedef int (*DvrReadBufferQueueSetBufferAvailableCallbackPtr)( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferAvailableCallback callback, void* context); typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer, void* context); typedef int (*DvrReadBufferQueueSetBufferRemovedCallbackPtr)( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferRemovedCallback callback, void* context); typedef int (*DvrReadBufferQueueHandleEventsPtr)( DvrReadBufferQueue* read_queue); // dvr_surface.h typedef int (*DvrSetupGlobalBufferPtr)(DvrGlobalBufferKey key, size_t size, uint64_t usage, DvrBuffer** buffer_out); typedef int (*DvrDeleteGlobalBufferPtr)(DvrGlobalBufferKey key); typedef int (*DvrGetGlobalBufferPtr)(DvrGlobalBufferKey key, DvrBuffer** out_buffer); typedef int (*DvrSurfaceCreatePtr)(const DvrSurfaceAttribute* attributes, size_t attribute_count, DvrSurface** surface_out); typedef void (*DvrSurfaceDestroyPtr)(DvrSurface* surface); typedef int (*DvrSurfaceGetIdPtr)(DvrSurface* surface); typedef int (*DvrSurfaceSetAttributesPtr)(DvrSurface* surface, const DvrSurfaceAttribute* attributes, size_t attribute_count); typedef int (*DvrSurfaceCreateWriteBufferQueuePtr)( DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** queue_out); typedef int (*DvrGetNativeDisplayMetricsPtr)(size_t sizeof_metrics, DvrNativeDisplayMetrics* metrics); // dvr_vsync.h typedef int (*DvrVSyncClientCreatePtr)(DvrVSyncClient** client_out); typedef void (*DvrVSyncClientDestroyPtr)(DvrVSyncClient* client); typedef int (*DvrVSyncClientGetSchedInfoPtr)(DvrVSyncClient* client, int64_t* vsync_period_ns, int64_t* next_timestamp_ns, uint32_t* next_vsync_count); // libs/vr/libvrsensor/include/dvr/pose_client.h typedef DvrPoseClient* (*DvrPoseClientCreatePtr)(); typedef void (*DvrPoseClientDestroyPtr)(DvrPoseClient* client); typedef int (*DvrPoseClientGetPtr)(DvrPoseClient* client, uint32_t vsync_count, DvrPoseAsync* out_pose); typedef uint32_t (*DvrPoseClientGetVsyncCountPtr)(DvrPoseClient* client); typedef int (*DvrPoseClientGetControllerPtr)(DvrPoseClient* client, int32_t controller_id, uint32_t vsync_count, DvrPoseAsync* out_pose); typedef int (*DvrPoseClientSensorsEnablePtr)(DvrPoseClient* client, bool enabled); typedef int (*DvrPoseClientDataCapturePtr)(DvrPoseClient* client, const DvrPoseDataCaptureRequest* request); typedef int (*DvrPoseClientDataReaderDestroyPtr)(DvrPoseClient* client, uint64_t data_type); // dvr_pose.h typedef int (*DvrPoseClientGetDataReaderPtr)(DvrPoseClient* client, uint64_t data_type, DvrReadBufferQueue** read_queue); // services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h // Touchpad IDs for *Touch*() and *ButtonState*() calls. enum { DVR_VIRTUAL_TOUCHPAD_PRIMARY = 0, DVR_VIRTUAL_TOUCHPAD_VIRTUAL = 1, }; typedef DvrVirtualTouchpad* (*DvrVirtualTouchpadCreatePtr)(); typedef void (*DvrVirtualTouchpadDestroyPtr)(DvrVirtualTouchpad* client); typedef int (*DvrVirtualTouchpadAttachPtr)(DvrVirtualTouchpad* client); typedef int (*DvrVirtualTouchpadDetachPtr)(DvrVirtualTouchpad* client); typedef int (*DvrVirtualTouchpadTouchPtr)(DvrVirtualTouchpad* client, int touchpad, float x, float y, float pressure); typedef int (*DvrVirtualTouchpadButtonStatePtr)(DvrVirtualTouchpad* client, int touchpad, int buttons); typedef int (*DvrVirtualTouchpadScrollPtr)(DvrVirtualTouchpad* client, int touchpad, float x, float y); // dvr_hardware_composer_client.h typedef struct DvrHwcClient DvrHwcClient; typedef struct DvrHwcFrame DvrHwcFrame; typedef int (*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame); typedef DvrHwcClient* (*DvrHwcClientCreatePtr)(DvrHwcOnFrameCallback callback, void* client_state); typedef void (*DvrHwcClientDestroyPtr)(DvrHwcClient* client); typedef void (*DvrHwcFrameDestroyPtr)(DvrHwcFrame* frame); typedef DvrHwcDisplay (*DvrHwcFrameGetDisplayIdPtr)(DvrHwcFrame* frame); typedef int32_t (*DvrHwcFrameGetDisplayWidthPtr)(DvrHwcFrame* frame); typedef int32_t (*DvrHwcFrameGetDisplayHeightPtr)(DvrHwcFrame* frame); typedef bool (*DvrHwcFrameGetDisplayRemovedPtr)(DvrHwcFrame* frame); typedef size_t (*DvrHwcFrameGetLayerCountPtr)(DvrHwcFrame* frame); typedef DvrHwcLayer (*DvrHwcFrameGetLayerIdPtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetActiveConfigPtr)(DvrHwcFrame* frame); typedef uint32_t (*DvrHwcFrameGetColorModePtr)(DvrHwcFrame* frame); typedef void (*DvrHwcFrameGetColorTransformPtr)(DvrHwcFrame* frame, float* out_matrix, int32_t* out_hint); typedef uint32_t (*DvrHwcFrameGetPowerModePtr)(DvrHwcFrame* frame); typedef uint32_t (*DvrHwcFrameGetVsyncEnabledPtr)(DvrHwcFrame* frame); typedef AHardwareBuffer* (*DvrHwcFrameGetLayerBufferPtr)(DvrHwcFrame* frame, size_t layer_index); typedef int (*DvrHwcFrameGetLayerFencePtr)(DvrHwcFrame* frame, size_t layer_index); typedef DvrHwcRecti (*DvrHwcFrameGetLayerDisplayFramePtr)(DvrHwcFrame* frame, size_t layer_index); typedef DvrHwcRectf (*DvrHwcFrameGetLayerCropPtr)(DvrHwcFrame* frame, size_t layer_index); typedef DvrHwcBlendMode (*DvrHwcFrameGetLayerBlendModePtr)(DvrHwcFrame* frame, size_t layer_index); typedef float (*DvrHwcFrameGetLayerAlphaPtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetLayerTypePtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetLayerApplicationIdPtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetLayerZOrderPtr)(DvrHwcFrame* frame, size_t layer_index); typedef void (*DvrHwcFrameGetLayerCursorPtr)(DvrHwcFrame* frame, size_t layer_index, int32_t* out_x, int32_t* out_y); typedef uint32_t (*DvrHwcFrameGetLayerTransformPtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetLayerDataspacePtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetLayerColorPtr)(DvrHwcFrame* frame, size_t layer_index); typedef uint32_t (*DvrHwcFrameGetLayerNumVisibleRegionsPtr)(DvrHwcFrame* frame, size_t layer_index); typedef DvrHwcRecti (*DvrHwcFrameGetLayerVisibleRegionPtr)(DvrHwcFrame* frame, size_t layer_index, size_t index); typedef uint32_t (*DvrHwcFrameGetLayerNumDamagedRegionsPtr)(DvrHwcFrame* frame, size_t layer_index); typedef DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame, size_t layer_index, size_t index); // dvr_performance.h typedef int (*DvrPerformanceSetSchedulerPolicyPtr)( pid_t task_id, const char* scheduler_policy); // dvr_tracking.h typedef int (*DvrTrackingCameraCreatePtr)(DvrTrackingCamera** out_camera); typedef void (*DvrTrackingCameraDestroyPtr)(DvrTrackingCamera* camera); typedef int (*DvrTrackingCameraStartPtr)(DvrTrackingCamera* camera, DvrWriteBufferQueue* write_queue); typedef int (*DvrTrackingCameraStopPtr)(DvrTrackingCamera* camera); typedef int (*DvrTrackingFeatureExtractorCreatePtr)( DvrTrackingFeatureExtractor** out_extractor); typedef void (*DvrTrackingFeatureExtractorDestroyPtr)( DvrTrackingFeatureExtractor* extractor); typedef void (*DvrTrackingFeatureCallback)(void* context, const DvrTrackingFeatures* event); typedef int (*DvrTrackingFeatureExtractorStartPtr)( DvrTrackingFeatureExtractor* extractor, DvrTrackingFeatureCallback callback, void* context); typedef int (*DvrTrackingFeatureExtractorStopPtr)( DvrTrackingFeatureExtractor* extractor); typedef int (*DvrTrackingFeatureExtractorProcessBufferPtr)( DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer, const DvrTrackingBufferMetadata* metadata, bool* out_skipped); typedef void (*DvrTrackingSensorEventCallback)(void* context, DvrTrackingSensorEvent* event); typedef int (*DvrTrackingSensorsCreatePtr)(DvrTrackingSensors** out_sensors, const char* mode); typedef void (*DvrTrackingSensorsDestroyPtr)(DvrTrackingSensors* sensors); typedef int (*DvrTrackingSensorsStartPtr)( DvrTrackingSensors* sensors, DvrTrackingSensorEventCallback callback, void* context); typedef int (*DvrTrackingSensorsStopPtr)(DvrTrackingSensors* sensors); // The buffer metadata that an Android Surface (a.k.a. ANativeWindow) // will populate. A DvrWriteBufferQueue must be created with this metadata iff // ANativeWindow access is needed. Please do not remove, modify, or reorder // existing data members. If new fields need to be added, please take extra care // to make sure that new data field is padded properly the size of the struct // stays same. // TODO(b/118893702): move the definition to libnativewindow or libui struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata { #ifdef __cplusplus DvrNativeBufferMetadata() : timestamp(0), is_auto_timestamp(0), dataspace(0), crop_left(0), crop_top(0), crop_right(0), crop_bottom(0), scaling_mode(0), transform(0), index(0), user_metadata_size(0), user_metadata_ptr(0), release_fence_mask(0), reserved{0} {} #endif // Timestamp of the frame. int64_t timestamp; // Whether the buffer is using auto timestamp. int32_t is_auto_timestamp; // Must be one of the HAL_DATASPACE_XXX value defined in system/graphics.h int32_t dataspace; // Crop extracted from an ACrop or android::Crop object. int32_t crop_left; int32_t crop_top; int32_t crop_right; int32_t crop_bottom; // Must be one of the NATIVE_WINDOW_SCALING_MODE_XXX value defined in // system/window.h. int32_t scaling_mode; // Must be one of the ANATIVEWINDOW_TRANSFORM_XXX value defined in // android/native_window.h int32_t transform; // The index of the frame. int64_t index; // Size of additional metadata requested by user. uint64_t user_metadata_size; // Raw memory address of the additional user defined metadata. Only valid when // user_metadata_size is non-zero. uint64_t user_metadata_ptr; // Only applicable for metadata retrieved from GainAsync. This indicates which // consumer has pending fence that producer should epoll on. uint32_t release_fence_mask; // Reserved bytes for so that the struct is forward compatible and padding to // 104 bytes so the size is a multiple of 8. int32_t reserved[9]; }; #ifdef __cplusplus // Warning: DvrNativeBufferMetadata is part of the DVR API and changing its size // will cause compatiblity issues between different DVR API releases. static_assert(sizeof(DvrNativeBufferMetadata) == 104, "Unexpected size for DvrNativeBufferMetadata"); #endif struct DvrApi_v1 { // Defines an API entry for V1 (no version suffix). #define DVR_V1_API_ENTRY(name) Dvr##name##Ptr name #define DVR_V1_API_ENTRY_DEPRECATED(name) Dvr##name##Ptr name #include "dvr_api_entries.h" // Undefine macro definitions to play nice with Google3 style rules. #undef DVR_V1_API_ENTRY #undef DVR_V1_API_ENTRY_DEPRECATED }; int dvrGetApi(void* api, size_t struct_size, int version); #ifdef __cplusplus } // extern "C" #endif #endif // ANDROID_DVR_API_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_api_entries.h��������������������������������������������������������0100644 0000000 0000000 00000015765 13756501735 020245� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// dvr_api_entries.h // // Defines the DVR platform library API entries. // // Do not include this header directly. #ifndef DVR_V1_API_ENTRY #error Do not include this header directly. #endif #ifndef DVR_V1_API_ENTRY_DEPRECATED #error Do not include this header directly. #endif // Do not delete this line: BEGIN CODEGEN OUTPUT // Display manager client DVR_V1_API_ENTRY(DisplayManagerCreate); DVR_V1_API_ENTRY(DisplayManagerDestroy); DVR_V1_API_ENTRY(DisplayManagerGetEventFd); DVR_V1_API_ENTRY(DisplayManagerTranslateEpollEventMask); DVR_V1_API_ENTRY(DisplayManagerGetSurfaceState); DVR_V1_API_ENTRY(DisplayManagerGetReadBufferQueue); DVR_V1_API_ENTRY(ConfigurationDataGet); DVR_V1_API_ENTRY(ConfigurationDataDestroy); DVR_V1_API_ENTRY(SurfaceStateCreate); DVR_V1_API_ENTRY(SurfaceStateDestroy); DVR_V1_API_ENTRY(SurfaceStateGetSurfaceCount); DVR_V1_API_ENTRY(SurfaceStateGetUpdateFlags); DVR_V1_API_ENTRY(SurfaceStateGetSurfaceId); DVR_V1_API_ENTRY(SurfaceStateGetProcessId); DVR_V1_API_ENTRY(SurfaceStateGetQueueCount); DVR_V1_API_ENTRY(SurfaceStateGetQueueIds); DVR_V1_API_ENTRY(SurfaceStateGetZOrder); DVR_V1_API_ENTRY(SurfaceStateGetVisible); DVR_V1_API_ENTRY(SurfaceStateGetAttributeCount); DVR_V1_API_ENTRY(SurfaceStateGetAttributes); // Write buffer DVR_V1_API_ENTRY_DEPRECATED(WriteBufferCreateEmpty); DVR_V1_API_ENTRY(WriteBufferDestroy); DVR_V1_API_ENTRY(WriteBufferIsValid); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferClear); DVR_V1_API_ENTRY(WriteBufferGetId); DVR_V1_API_ENTRY(WriteBufferGetAHardwareBuffer); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferPost); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferGain); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferGainAsync); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferGetNativeHandle); // Read buffer DVR_V1_API_ENTRY_DEPRECATED(ReadBufferCreateEmpty); DVR_V1_API_ENTRY(ReadBufferDestroy); DVR_V1_API_ENTRY(ReadBufferIsValid); DVR_V1_API_ENTRY_DEPRECATED(ReadBufferClear); DVR_V1_API_ENTRY(ReadBufferGetId); DVR_V1_API_ENTRY(ReadBufferGetAHardwareBuffer); DVR_V1_API_ENTRY_DEPRECATED(ReadBufferAcquire); DVR_V1_API_ENTRY_DEPRECATED(ReadBufferRelease); DVR_V1_API_ENTRY_DEPRECATED(ReadBufferReleaseAsync); DVR_V1_API_ENTRY_DEPRECATED(ReadBufferGetNativeHandle); // Buffer DVR_V1_API_ENTRY(BufferDestroy); DVR_V1_API_ENTRY(BufferGetAHardwareBuffer); DVR_V1_API_ENTRY_DEPRECATED(BufferGetNativeHandle); DVR_V1_API_ENTRY(BufferGlobalLayoutVersionGet); // Write buffer queue DVR_V1_API_ENTRY(WriteBufferQueueDestroy); DVR_V1_API_ENTRY(WriteBufferQueueGetCapacity); DVR_V1_API_ENTRY(WriteBufferQueueGetId); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferQueueGetExternalSurface); DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue); DVR_V1_API_ENTRY_DEPRECATED(WriteBufferQueueDequeue); DVR_V1_API_ENTRY(WriteBufferQueueResizeBuffer); // Read buffer queue DVR_V1_API_ENTRY(ReadBufferQueueDestroy); DVR_V1_API_ENTRY(ReadBufferQueueGetCapacity); DVR_V1_API_ENTRY(ReadBufferQueueGetId); DVR_V1_API_ENTRY(ReadBufferQueueCreateReadQueue); DVR_V1_API_ENTRY_DEPRECATED(ReadBufferQueueDequeue); DVR_V1_API_ENTRY(ReadBufferQueueSetBufferAvailableCallback); DVR_V1_API_ENTRY(ReadBufferQueueSetBufferRemovedCallback); DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents); // V-Sync client DVR_V1_API_ENTRY_DEPRECATED(VSyncClientCreate); DVR_V1_API_ENTRY_DEPRECATED(VSyncClientDestroy); DVR_V1_API_ENTRY_DEPRECATED(VSyncClientGetSchedInfo); // Display surface DVR_V1_API_ENTRY(SurfaceCreate); DVR_V1_API_ENTRY(SurfaceDestroy); DVR_V1_API_ENTRY(SurfaceGetId); DVR_V1_API_ENTRY(SurfaceSetAttributes); DVR_V1_API_ENTRY(SurfaceCreateWriteBufferQueue); DVR_V1_API_ENTRY(SetupGlobalBuffer); DVR_V1_API_ENTRY(DeleteGlobalBuffer); DVR_V1_API_ENTRY(GetGlobalBuffer); // Pose client DVR_V1_API_ENTRY(PoseClientCreate); DVR_V1_API_ENTRY(PoseClientDestroy); DVR_V1_API_ENTRY(PoseClientGet); DVR_V1_API_ENTRY(PoseClientGetVsyncCount); DVR_V1_API_ENTRY(PoseClientGetController); // Virtual touchpad client DVR_V1_API_ENTRY(VirtualTouchpadCreate); DVR_V1_API_ENTRY(VirtualTouchpadDestroy); DVR_V1_API_ENTRY(VirtualTouchpadAttach); DVR_V1_API_ENTRY(VirtualTouchpadDetach); DVR_V1_API_ENTRY(VirtualTouchpadTouch); DVR_V1_API_ENTRY(VirtualTouchpadButtonState); // VR HWComposer client DVR_V1_API_ENTRY(HwcClientCreate); DVR_V1_API_ENTRY(HwcClientDestroy); DVR_V1_API_ENTRY(HwcFrameDestroy); DVR_V1_API_ENTRY(HwcFrameGetDisplayId); DVR_V1_API_ENTRY(HwcFrameGetDisplayWidth); DVR_V1_API_ENTRY(HwcFrameGetDisplayHeight); DVR_V1_API_ENTRY(HwcFrameGetDisplayRemoved); DVR_V1_API_ENTRY(HwcFrameGetActiveConfig); DVR_V1_API_ENTRY(HwcFrameGetColorMode); DVR_V1_API_ENTRY(HwcFrameGetColorTransform); DVR_V1_API_ENTRY(HwcFrameGetPowerMode); DVR_V1_API_ENTRY(HwcFrameGetVsyncEnabled); DVR_V1_API_ENTRY(HwcFrameGetLayerCount); DVR_V1_API_ENTRY(HwcFrameGetLayerId); DVR_V1_API_ENTRY(HwcFrameGetLayerBuffer); DVR_V1_API_ENTRY(HwcFrameGetLayerFence); DVR_V1_API_ENTRY(HwcFrameGetLayerDisplayFrame); DVR_V1_API_ENTRY(HwcFrameGetLayerCrop); DVR_V1_API_ENTRY(HwcFrameGetLayerBlendMode); DVR_V1_API_ENTRY(HwcFrameGetLayerAlpha); DVR_V1_API_ENTRY(HwcFrameGetLayerType); DVR_V1_API_ENTRY(HwcFrameGetLayerApplicationId); DVR_V1_API_ENTRY(HwcFrameGetLayerZOrder); DVR_V1_API_ENTRY(HwcFrameGetLayerCursor); DVR_V1_API_ENTRY(HwcFrameGetLayerTransform); DVR_V1_API_ENTRY(HwcFrameGetLayerDataspace); DVR_V1_API_ENTRY(HwcFrameGetLayerColor); DVR_V1_API_ENTRY(HwcFrameGetLayerNumVisibleRegions); DVR_V1_API_ENTRY(HwcFrameGetLayerVisibleRegion); DVR_V1_API_ENTRY(HwcFrameGetLayerNumDamagedRegions); DVR_V1_API_ENTRY(HwcFrameGetLayerDamagedRegion); // New entries added at the end to allow the DVR platform library API // to be updated before updating VrCore. // Virtual touchpad client DVR_V1_API_ENTRY(VirtualTouchpadScroll); // Read the native display metrics from the hardware composer DVR_V1_API_ENTRY(GetNativeDisplayMetrics); // Performance DVR_V1_API_ENTRY(PerformanceSetSchedulerPolicy); // Pose client DVR_V1_API_ENTRY(PoseClientSensorsEnable); // Read buffer queue DVR_V1_API_ENTRY(ReadBufferQueueGetEventFd); // Create write buffer queue locally DVR_V1_API_ENTRY(WriteBufferQueueCreate); // Gets an ANativeWindow from DvrWriteBufferQueue. DVR_V1_API_ENTRY(WriteBufferQueueGetANativeWindow); // Dvr{Read,Write}BufferQueue API for asynchronous IPC. DVR_V1_API_ENTRY(WriteBufferQueueGainBuffer); DVR_V1_API_ENTRY(WriteBufferQueuePostBuffer); DVR_V1_API_ENTRY(ReadBufferQueueAcquireBuffer); DVR_V1_API_ENTRY(ReadBufferQueueReleaseBuffer); // Pose client DVR_V1_API_ENTRY(PoseClientGetDataReader); DVR_V1_API_ENTRY(PoseClientDataCapture); DVR_V1_API_ENTRY(PoseClientDataReaderDestroy); // Tracking DVR_V1_API_ENTRY(TrackingCameraCreate); DVR_V1_API_ENTRY(TrackingCameraDestroy); DVR_V1_API_ENTRY(TrackingCameraStart); DVR_V1_API_ENTRY(TrackingCameraStop); DVR_V1_API_ENTRY(TrackingFeatureExtractorCreate); DVR_V1_API_ENTRY(TrackingFeatureExtractorDestroy); DVR_V1_API_ENTRY(TrackingFeatureExtractorStart); DVR_V1_API_ENTRY(TrackingFeatureExtractorStop); DVR_V1_API_ENTRY(TrackingFeatureExtractorProcessBuffer); DVR_V1_API_ENTRY(TrackingSensorsCreate); DVR_V1_API_ENTRY(TrackingSensorsDestroy); DVR_V1_API_ENTRY(TrackingSensorsStart); DVR_V1_API_ENTRY(TrackingSensorsStop); �����������libs/vr/libdvr/include/dvr/dvr_buffer.h�������������������������������������������������������������0100644 0000000 0000000 00000003705 13756501735 017203� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_H_ #define ANDROID_DVR_BUFFER_H_ #include #include #include #include __BEGIN_DECLS typedef struct DvrWriteBuffer DvrWriteBuffer; typedef struct DvrReadBuffer DvrReadBuffer; typedef struct DvrBuffer DvrBuffer; typedef struct AHardwareBuffer AHardwareBuffer; struct native_handle; // Destroys the write buffer. void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer); // Returns 1 if the given write buffer object contains a buffer, 0 otherwise. int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer); // Returns the global BufferHub id of this buffer. int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer); // Returns an AHardwareBuffer for the underlying buffer. // Caller must call AHardwareBuffer_release on hardware_buffer. int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer, AHardwareBuffer** hardware_buffer); // Destroys the read buffer. void dvrReadBufferDestroy(DvrReadBuffer* read_buffer); // Returns 1 if the given write buffer object contains a buffer, 0 otherwise. int dvrReadBufferIsValid(DvrReadBuffer* read_buffer); // Returns the global BufferHub id of this buffer. int dvrReadBufferGetId(DvrReadBuffer* read_buffer); // Returns an AHardwareBuffer for the underlying buffer. // Caller must call AHardwareBuffer_release on hardware_buffer. int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer, AHardwareBuffer** hardware_buffer); // Destroys the buffer. void dvrBufferDestroy(DvrBuffer* buffer); // Gets an AHardwareBuffer from the buffer. // Caller must call AHardwareBuffer_release on hardware_buffer. int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer, AHardwareBuffer** hardware_buffer); // Retrieve the shared buffer layout version defined in dvr_shared_buffers.h. int dvrBufferGlobalLayoutVersionGet(); __END_DECLS #endif // ANDROID_DVR_BUFFER_H_ �����������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_buffer_queue.h�������������������������������������������������������0100644 0000000 0000000 00000031157 13756501735 020411� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_QUEUE_H_ #define ANDROID_DVR_BUFFER_QUEUE_H_ #include #include __BEGIN_DECLS typedef struct ANativeWindow ANativeWindow; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; typedef struct DvrReadBufferQueue DvrReadBufferQueue; // Creates a write buffer queue to be used locally. // // Note that this API is mostly for testing purpose. For now there is no // mechanism to send a DvrWriteBufferQueue cross process. Use // dvrSurfaceCreateWriteBufferQueue if cross-process buffer transport is // intended. // // @param width The width of the buffers that this queue will produce. // @param height The height of buffers that this queue will produce. // @param format The format of the buffers that this queue will produce. This // must be one of the AHARDWAREBUFFER_FORMAT_XXX enums. // @param layer_count The number of layers of the buffers that this queue will // produce. // @param usage The usage of the buffers that this queue will produce. This // must a combination of the AHARDWAREBUFFER_USAGE_XXX flags. // @param capacity The number of buffer that this queue will allocate. Note that // all buffers will be allocated on create. Currently, the number of buffers // is the queue cannot be changed after creation though DVR API. However, // ANativeWindow can choose to reallocate, attach, or detach buffers from // a DvrWriteBufferQueue through Android platform logic. // @param metadata_size The size of metadata in bytes. // @param out_write_queue The pointer of a DvrWriteBufferQueue will be filled // here if the method call succeeds. The metadata size must match // the metadata size in dvrWriteBufferPost/dvrReadBufferAcquire. // @return Zero on success, or negative error code. int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** out_write_queue); // Destroy a write buffer queue. // // @param write_queue The DvrWriteBufferQueue of interest. void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue); // Get the total number of buffers in a write buffer queue. // // @param write_queue The DvrWriteBufferQueue of interest. // @return The capacity on success; or negative error code. ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue); // Get the system unique queue id of a write buffer queue. // // @param write_queue The DvrWriteBufferQueue of interest. // @return Queue id on success; or negative error code. int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue); // Gets an ANativeWindow backed by the DvrWriteBufferQueue // // Can be casted to a Java Surface using ANativeWindow_toSurface NDK API. Note // that the native window is lazily created at the first time |GetNativeWindow| // is called, and the created ANativeWindow will be cached so that multiple // calls to this method will return the same object. Also note that this method // does not acquire an additional reference to the ANativeWindow returned, don't // call ANativeWindow_release on it. // // @param write_queue The DvrWriteBufferQueue of interest. // @param out_window The pointer of an ANativeWindow will be filled here if // the method call succeeds. // @return Zero on success; or -EINVAL if this DvrWriteBufferQueue does not // support being used as an android Surface. int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue, ANativeWindow** out_window); // Create a read buffer queue from an existing write buffer queue. // // @param write_queue The DvrWriteBufferQueue of interest. // @param out_read_queue The pointer of a DvrReadBufferQueue will be filled here // if the method call succeeds. // @return Zero on success, or negative error code. int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue, DvrReadBufferQueue** out_read_queue); // Gains a buffer to write into. // // @param write_queue The DvrWriteBufferQueue to gain buffer from. // @param timeout Specifies the number of milliseconds that the method will // block. Specifying a timeout of -1 causes it to block indefinitely, // while specifying a timeout equal to zero cause it to return immediately, // even if no buffers are available. // @param out_buffer A targeting DvrWriteBuffer object to hold the output of the // dequeue operation. // @param out_meta A DvrNativeBufferMetadata object populated by the // corresponding dvrReadBufferQueueReleaseBuffer API. // @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which // signals the release of underlying buffer. The producer should wait until // this fence clears before writing data into it. // @return Zero on success, or negative error code. int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer** out_write_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd); // Posts a buffer and signals its readiness to be read from. // // @param write_queue The DvrWriteBufferQueue to post buffer into. // @param write_buffer The buffer to be posted. // @param meta The buffer metadata describing the buffer. // @param ready_fence_fd A sync fence fd defined in NDK's sync.h API, which // signals the readdiness of underlying buffer. When a valid fence gets // passed in, the consumer will wait the fence to be ready before it starts // to ready from the buffer. // @return Zero on success, or negative error code. int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue, DvrWriteBuffer* write_buffer, const DvrNativeBufferMetadata* meta, int ready_fence_fd); // Overrides buffer dimension with new width and height. // // After the call successfully returns, each |dvrWriteBufferQueueDequeue| call // will return buffer with newly assigned |width| and |height|. When necessary, // old buffer will be removed from the buffer queue and replaced with new buffer // matching the new buffer size. // // @param write_queue The DvrWriteBufferQueue of interest. // @param width Desired width, cannot be Zero. // @param height Desired height, cannot be Zero. // @return Zero on success, or negative error code. int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue, uint32_t width, uint32_t height); // Destroy a read buffer queue. // // @param read_queue The DvrReadBufferQueue of interest. void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue); // Get the total number of buffers in a read buffer queue. // // @param read_queue The DvrReadBufferQueue of interest. // @return The capacity on success; or negative error code. ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue); // Get the system unique queue id of a read buffer queue. // // @param read_queue The DvrReadBufferQueue of interest. // @return Queue id on success; or negative error code. int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue); // Get the event fd that signals when queue updates occur. // // Use ReadBufferQueueHandleEvents to trigger registered event callbacks. // // @param read_queue The DvrReadBufferQueue of interest. // @return Fd on success; or negative error code. int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue); // Create a read buffer queue from an existing read buffer queue. // // @param read_queue The DvrReadBufferQueue of interest. // @param out_read_queue The pointer of a DvrReadBufferQueue will be filled here // if the method call succeeds. // @return Zero on success, or negative error code. int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue, DvrReadBufferQueue** out_read_queue); // Dequeues a buffer to read from. // // @param read_queue The DvrReadBufferQueue to acquire buffer from. // @param timeout Specifies the number of milliseconds that the method will // block. Specifying a timeout of -1 causes it to block indefinitely, // while specifying a timeout equal to zero cause it to return immediately, // even if no buffers are available. // @param out_buffer A targeting DvrReadBuffer object to hold the output of the // dequeue operation. Must be created by |dvrReadBufferCreateEmpty|. // @param out_meta A DvrNativeBufferMetadata object populated by the // corresponding dvrWriteBufferQueuePostBuffer API. // @param out_fence_fd A sync fence fd defined in NDK's sync.h API, which // signals the release of underlying buffer. The consumer should wait until // this fence clears before reading data from it. // @return Zero on success, or negative error code. int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer** out_read_buffer, DvrNativeBufferMetadata* out_meta, int* out_fence_fd); // Releases a buffer and signals its readiness to be written into. // // @param read_queue The DvrReadBufferQueue to release buffer into. // @param read_buffer The buffer to be released. // @param meta The buffer metadata describing the buffer. // @param release_fence_fd A sync fence fd defined in NDK's sync.h API, which // signals the readdiness of underlying buffer. When a valid fence gets // passed in, the producer will wait the fence to be ready before it starts // to write into the buffer again. // @return Zero on success, or negative error code. int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue, DvrReadBuffer* read_buffer, const DvrNativeBufferMetadata* meta, int release_fence_fd); // Callback function which will be called when a buffer is avaiable. // // Note that there is no guarantee of thread safety and on which thread the // callback will be fired. // // @param context User provided opaque pointer. typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context); // Set buffer avaiable callback. // // @param read_queue The DvrReadBufferQueue of interest. // @param callback The callback function. Set this to NULL if caller no longer // needs to listen to new buffer available events. // @param context User provided opaque pointer, will be passed back during // callback. The caller is responsible for ensuring the validity of the // context through the life cycle of the DvrReadBufferQueue. // @return Zero on success, or negative error code. int dvrReadBufferQueueSetBufferAvailableCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferAvailableCallback callback, void* context); // Callback function which will be called when a buffer is about to be removed. // // Note that there is no guarantee of thread safety and on which thread the // callback will be fired. // // @param buffer The buffer being removed. Once the callbacks returns, this // buffer will be dereferenced from the buffer queue. If user has ever // cached other DvrReadBuffer/AHardwareBuffer/EglImageKHR objects derived // from this buffer, it's the user's responsibility to clean them up. // Note that the ownership of the read buffer is not passed to this // callback, so it should call dvrReadBufferDestroy on the buffer. // @param context User provided opaque pointer. typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer, void* context); // Set buffer removed callback. // // @param read_queue The DvrReadBufferQueue of interest. // @param callback The callback function. Set this to NULL if caller no longer // needs to listen to buffer removed events. // @param context User provided opaque pointer, will be passed back during // callback. The caller is responsible for ensuring the validity of the // context through the life cycle of the DvrReadBufferQueue. // @return Zero on success, or negative error code. int dvrReadBufferQueueSetBufferRemovedCallback( DvrReadBufferQueue* read_queue, DvrReadBufferQueueBufferRemovedCallback callback, void* context); // Handle all pending events on the read queue. // // @param read_queue The DvrReadBufferQueue of interest. // @return Zero on success, or negative error code. int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue); __END_DECLS #endif // ANDROID_DVR_BUFFER_QUEUE_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_config.h�������������������������������������������������������������0100644 0000000 0000000 00000002226 13756501735 017174� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_CONFIG_H #define ANDROID_DVR_CONFIG_H // This header is shared by VrCore and Android and must be kept in sync. #include #include __BEGIN_DECLS // This is a shared memory buffer for passing config data from VrCore to // libvrflinger in SurfaceFlinger. struct __attribute__((packed, aligned(16))) DvrConfig { // Offset before vsync to submit frames to hardware composer. int32_t frame_post_offset_ns{4000000}; // If the number of pending fences goes over this count at the point when we // are about to submit a new frame to HWC, we will drop the frame. This // should be a signal that the display driver has begun queuing frames. Note // that with smart displays (with RAM), the fence is signaled earlier than // the next vsync, at the point when the DMA to the display completes. // Currently we use a smart display and the EDS timing coincides with zero // pending fences, so this is 0. int32_t allowed_pending_fence_count{0}; // New fields should always be added to the end for backwards compat. // Reserved padding to 16 bytes. uint8_t pad[8]; }; __END_DECLS #endif // ANDROID_DVR_CONFIG_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_configuration_data.h�������������������������������������������������0100644 0000000 0000000 00000001210 13756501735 021557� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef DVR_CONFIGURATION_DATA_H_ #define DVR_CONFIGURATION_DATA_H_ #include #include #include #include #include #include __BEGIN_DECLS // Loads device configuration data of DVR_CONFIGURATION_DATA_*. // @return 0 on success. Otherwise returns a negative error value. int dvrConfigurationDataGet(int config_type, uint8_t** data, size_t* data_size); // Destroy the configuration data returned from dvrGetConfigurationData. void dvrConfigurationDataDestroy(uint8_t* data); __END_DECLS #endif // DVR_CONFIGURATION_DATA_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_deleter.h������������������������������������������������������������0100644 0000000 0000000 00000006276 13756501735 017364� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DELETER_H_ #define ANDROID_DVR_DELETER_H_ #include #include // Header-only C++ helper to delete opaque DVR objects. __BEGIN_DECLS // Use forward declarations to avoid dependency on other headers. typedef struct DvrBuffer DvrBuffer; typedef struct DvrReadBuffer DvrReadBuffer; typedef struct DvrWriteBuffer DvrWriteBuffer; typedef struct DvrReadBufferQueue DvrReadBufferQueue; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; typedef struct DvrDisplayManager DvrDisplayManager; typedef struct DvrSurfaceState DvrSurfaceState; typedef struct DvrSurface DvrSurface; typedef struct DvrHwcClient DvrHwcClient; typedef struct DvrHwcFrame DvrHwcFrame; void dvrBufferDestroy(DvrBuffer* buffer); void dvrReadBufferDestroy(DvrReadBuffer* read_buffer); void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer); void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue); void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue); void dvrDisplayManagerDestroy(DvrDisplayManager* client); void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); void dvrSurfaceDestroy(DvrSurface* surface); void dvrHwcClientDestroy(DvrHwcClient* client); void dvrHwcFrameDestroy(DvrHwcFrame* frame); __END_DECLS // Avoid errors if this header is included in C code. #if defined(__cplusplus) namespace android { namespace dvr { // Universal DVR object deleter. May be passed to smart pointer types to handle // deletion of DVR API objects. struct DvrObjectDeleter { void operator()(DvrBuffer* p) { dvrBufferDestroy(p); } void operator()(DvrReadBuffer* p) { dvrReadBufferDestroy(p); } void operator()(DvrWriteBuffer* p) { dvrWriteBufferDestroy(p); } void operator()(DvrReadBufferQueue* p) { dvrReadBufferQueueDestroy(p); } void operator()(DvrWriteBufferQueue* p) { dvrWriteBufferQueueDestroy(p); } void operator()(DvrDisplayManager* p) { dvrDisplayManagerDestroy(p); } void operator()(DvrSurfaceState* p) { dvrSurfaceStateDestroy(p); } void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); } void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); } void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); } }; // Helper to define unique pointers for DVR object types. template using MakeUniqueDvrPointer = std::unique_ptr; // Unique pointer types for DVR objects. using UniqueDvrBuffer = MakeUniqueDvrPointer; using UniqueDvrReadBuffer = MakeUniqueDvrPointer; using UniqueDvrWriteBuffer = MakeUniqueDvrPointer; using UniqueDvrReadBufferQueue = MakeUniqueDvrPointer; using UniqueDvrWriteBufferQueue = MakeUniqueDvrPointer; using UniqueDvrDisplayManager = MakeUniqueDvrPointer; using UniqueDvrSurfaceState = MakeUniqueDvrPointer; using UniqueDvrSurface = MakeUniqueDvrPointer; using UniqueDvrHwcClient = MakeUniqueDvrPointer; using UniqueDvrHwcFrame = MakeUniqueDvrPointer; // TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into // the relevant constructors. } // namespace dvr } // namespace android #endif // defined(__cplusplus) #endif // ANDROID_DVR_DELETER_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_display_manager.h����������������������������������������������������0100644 0000000 0000000 00000014355 13756501735 021074� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DISPLAY_MANAGER_H_ #define ANDROID_DVR_DISPLAY_MANAGER_H_ #include #include #include #include #include #include __BEGIN_DECLS typedef struct DvrBuffer DvrBuffer; typedef struct DvrDisplayManager DvrDisplayManager; typedef struct DvrSurfaceState DvrSurfaceState; typedef struct DvrReadBufferQueue DvrReadBufferQueue; typedef uint64_t DvrSurfaceUpdateFlags; // Attempts to connect to the display manager service. // @return 0 on success. Otherwise returns a negative error value. int dvrDisplayManagerCreate(DvrDisplayManager** client_out); // Destroys the display manager client object. void dvrDisplayManagerDestroy(DvrDisplayManager* client); // Returns an fd used to signal when surface updates occur. Note that depending // on the underlying transport, only a subset of the real event bits may be // supported. Use dvrDisplayManagerClientTranslateEpollEventMask to get the real // event flags. // @return the fd on success. Otherwise returns a negative error value. int dvrDisplayManagerGetEventFd(DvrDisplayManager* client); // @param in_events pass in the epoll revents that were initially returned by // poll/epoll. // @param on success, this value will be overwritten with the true poll/epoll // values. // @return 0 on success. Otherwise returns a negative error value. int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client, int in_events, int* out_events); // Queries the display manager service for the current state of the display // surfaces and stores the results in the given surface state object. // @return 0 on success. Otherwise returns a negative error value. int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client, DvrSurfaceState* surface_state); // Gets a read buffer queue from the surface |surface_id| named |queue_id|. Each // call returns a different read buffer queue connected to the same write buffer // queue. Callers should cache these instead of requesting new ones when // possible. int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client, int surface_id, int queue_id, DvrReadBufferQueue** queue_out); // Creates a new surface state object. This object may be used to receive the // results of a surface state query. More than one state object may be created // to keep multiple snapshots, if desired. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateCreate(DvrSurfaceState** surface_state); // Destorys the surface state object. void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); // Writes the number of surfaces described in the state object into |count_out|. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state, size_t* count_out); // Returns the update flags for the surface at |surface_index| in the state // object. The flags may be used to determine what changes, if any, occurred to // the surface since the last state update. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state, size_t surface_index, DvrSurfaceUpdateFlags* flags_out); // Returns the unique identifier of surface at |surface_index| in the state // object. The identifier may be used to distinguish between surfaces. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state, size_t surface_index, int* surface_id_out); // Returns the process id of surface at |surface_index| in the state object. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state, size_t surface_index, int* process_id_out); // Writes the number of queues in the surface at |surface_index| in the state // object into |count_out|. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out); // Returns up to |max_count| queue ids for the queues belonging to the surface // at |surface_index| in the state object. // @return The number of queue ids written on success. Otherwise returns a // negative error value. ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state, size_t surface_index, int* queue_ids, size_t max_count); // Writes the z-order of the surface at |surface_index| in surface state object // into |z_order_out|. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state, size_t surface_index, int* z_order_out); // Writes the visible state of the surface at |surface_index| in the surface // state object into |visible_out|. // @return 0 on success. Otherwise it returns a negative error value. int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state, size_t surface_index, bool* visible_out); // Writes the number of attributes on the surface at |surface_index| in the // state object into |count_out|. // @return 0 on success. Otherwise it returns a negative error value. int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state, size_t surface_index, size_t* count_out); // Writes the list of attribute key/value pairs for the surface at // |surface_index| in the surface state object into |attributes|. // @return The number of attributes written on success. Otherwise returns a // negative error value. ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state, size_t surface_index, DvrSurfaceAttribute* attributes, size_t max_count); __END_DECLS #endif // ANDROID_DVR_DISPLAY_MANAGER_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_display_types.h������������������������������������������������������0100644 0000000 0000000 00000004421 13756501735 020617� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DISPLAY_TYPES_H_ #define ANDROID_DVR_DISPLAY_TYPES_H_ #include __BEGIN_DECLS // Define types used in pose buffer fields. These types have atomicity // guarantees that are useful in lock-free shared memory ring buffers. #ifdef __ARM_NEON #include #else #ifndef __FLOAT32X4T_86 #define __FLOAT32X4T_86 typedef float float32x4_t __attribute__((__vector_size__(16))); typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t; #endif #endif // VrFlinger display manager surface state snapshots per surface flags // indicating what changed since the last snapshot. enum { // No changes. DVR_SURFACE_UPDATE_FLAGS_NONE = 0, // This surface is new. DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE = (1 << 0), // Buffer queues added/removed. DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED = (1 << 1), // Visibility/z-order changed. DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED = (1 << 2), // Generic attributes changed. DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED = (1 << 3), }; // Surface attribute keys. VrFlinger defines keys in the negative integer space. // The compositor is free to use keys in the positive integer space for // implementation-defined purposes. enum { // DIRECT: bool // Determines whether a direct surface is created (compositor output) or an // application surface. Defaults to false (application surface). May only be // set to true by a process with either UID=root or UID validated with // IsTrustedUid() (VrCore). DVR_SURFACE_ATTRIBUTE_DIRECT = -3, // Z_ORDER: int32_t // Interpreted by VrFlinger only on direct surfaces to order the corresponding // hardware layers. More positive values render on top of more negative // values. DVR_SURFACE_ATTRIBUTE_Z_ORDER = -2, // VISIBLE: bool // Interpreted by VrFlinger only on direct surfaces to determine whether a // surface is assigned to a hardware layer or ignored. DVR_SURFACE_ATTRIBUTE_VISIBLE = -1, // INVALID // Invalid key. No attributes should have this key. DVR_SURFACE_ATTRIBUTE_INVALID = 0, // FIRST_USER_KEY // VrFlinger ingores any keys with this value or greater, passing them to the // compositor through surface state query results. DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY = 1, }; __END_DECLS #endif // ANDROID_DVR_DISPLAY_TYPES_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h�������������������������������������������0100644 0000000 0000000 00000022373 13756501735 022776� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H #define ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H #include #include #ifdef __cplusplus extern "C" { #endif typedef struct AHardwareBuffer AHardwareBuffer; typedef struct DvrHwcClient DvrHwcClient; typedef struct DvrHwcFrame DvrHwcFrame; // Called when a new frame has arrived. // // @param client_state Pointer to client state passed in |dvrHwcCreateClient()|. // @param frame New frame. Owned by the client. // @return fence FD for the release of the last frame. typedef int(*DvrHwcOnFrameCallback)(void* client_state, DvrHwcFrame* frame); // @param callback Called when a new frame is available. // @param client_state Pointer to client state passed back in the callback. DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* client_state); // Called to free the DvrHwcClient pointer. void dvrHwcClientDestroy(DvrHwcClient* client); // Called to free the frame information. // @param frame Pointer for the valid frame used for the query. void dvrHwcFrameDestroy(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return Identifier for the display associated by the frame. DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return width of the physical display associated with |frame|. This does not // take into account any orientation changes. int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return height of the physical display associated with |frame|. This does not // take into account any orientation changes. int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return True if the display has been removed. In this case the current frame // does not contain any valid layers to display. It is a signal to clean up any // display related state. bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return Number of layers in the frame. size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return The ID of the currently active display configuration. uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return The ID of the current color mode. See HAL_COLOR_MODE_* for valid // values. uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @param out_matrix Output parameter for a float[16] array which will be filled // with the color transform matrix. // @param out_hint Output parameter which will contain the color transform hint. // See HAL_COLOR_TRANSFORM_* for valid values. void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix, int32_t* out_hint); // @param frame Pointer for the valid frame used for the query. // @return The current power mode for the display. See HWC2_POWER_MODE_* for // valid values. uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @return The current state of vsync. See HWC2_VSYNC_* for valid values. uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return A unique ID for the layer. DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index); // Return the graphic buffer associated with the layer at |layer_index| in // |frame|. // // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return Graphic buffer. Caller owns the buffer and is responsible for freeing // it. (see AHardwareBuffer_release()) AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame, size_t layer_index); // Returns the fence FD for the layer at index |layer_index| in |frame|. // // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return Fence FD. Caller owns the FD and is responsible for closing it. int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return describing the portion of the display covered by the layer. Will // not exceed the display dimensions. DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return describing the portion of the layer that will fill the display // frame. Will not exceed the layer dimensions. DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The blend mode of the layer. DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The alpha value to be applied to the whole layer. Will be in the // [0.0, 1.0] range. float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The type of the layer assigned by the window manager. uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The application id the layer belongs to. uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The z-order for the layer. uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @param out_x Output parameter for the x coordinate of the cursor location. // @param out_y Output parameter for the y coordinate of the cursor location. void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index, int32_t* out_x, int32_t* out_y); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The transformation that needs to be applied to the layer before // presenting it. See DVR_HWC_TRANSFORM_* for valid values. uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The dataspace which represents how the pixel values should be // interpreted. See HAL_DATASPACE_* for valid values. uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The color of the layer if layer composition is SOLID_COLOR. uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The number of visible regions. uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @param index The index of the visible region for the layer. // @return The rectangle describing the visible region. DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame, size_t layer_index, size_t index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @return The number of damanged regions. uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame, size_t layer_index); // @param frame Pointer for the valid frame used for the query. // @param layer_index The index of the layer in the frame. // @param index The index of the damanged region for the layer. // @return The rectangle describing the damaged region. DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame, size_t layer_index, size_t index); #ifdef __cplusplus } // extern "C" #endif #endif // ANDROID_DVR_HARDWARE_COMPOSER_CLIENT_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_hardware_composer_types.h��������������������������������������������0100644 0000000 0000000 00000002420 13756501735 022653� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_VR_HARDWARE_COMPOSER_DEFS_H #define ANDROID_VR_HARDWARE_COMPOSER_DEFS_H #include #ifdef __cplusplus extern "C" { #endif // NOTE: These definitions must match the ones in // //hardware/libhardware/include/hardware/hwcomposer2.h. They are used by the // client side which does not have access to hwc2 headers. enum DvrHwcBlendMode { DVR_HWC_BLEND_MODE_INVALID = 0, DVR_HWC_BLEND_MODE_NONE = 1, DVR_HWC_BLEND_MODE_PREMULTIPLIED = 2, DVR_HWC_BLEND_MODE_COVERAGE = 3, }; enum DvrHwcComposition { DVR_HWC_COMPOSITION_INVALID = 0, DVR_HWC_COMPOSITION_CLIENT = 1, DVR_HWC_COMPOSITION_DEVICE = 2, DVR_HWC_COMPOSITION_SOLID_COLOR = 3, DVR_HWC_COMPOSITION_CURSOR = 4, DVR_HWC_COMPOSITION_SIDEBAND = 5, }; enum DvrHwcTransform { DVR_HWC_TRANSFORM_NONE = 0, DVR_HWC_TRANSFORM_FLIP_H = 1, DVR_HWC_TRANSFORM_FLIP_V = 2, DVR_HWC_TRANSFORM_ROT_90 = 4, DVR_HWC_TRANSFORM_ROT_180 = 3, DVR_HWC_TRANSFORM_ROT_270 = 7, }; typedef uint64_t DvrHwcDisplay; typedef uint64_t DvrHwcLayer; struct DvrHwcRecti { int32_t left; int32_t top; int32_t right; int32_t bottom; }; struct DvrHwcRectf { float left; float top; float right; float bottom; }; #ifdef __cplusplus } // extern "C" #endif #endif // ANDROID_DVR_HARDWARE_COMPOSER_DEFS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_performance.h��������������������������������������������������������0100644 0000000 0000000 00000001354 13756501735 020231� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_PERFORMANCE_H_ #define ANDROID_DVR_PERFORMANCE_H_ #include #include __BEGIN_DECLS /// Sets the scheduler policy for a task. /// /// Sets the scheduler policy for a task to the class described by a semantic /// string. /// /// Supported policies are device-specific. /// /// @param task_id The task id of task to set the policy for. When task_id is 0 /// the current task id is substituted. /// @param scheduler_policy NULL-terminated ASCII string containing the desired /// scheduler policy. /// @returns Returns 0 on success or a negative errno error code on error. int dvrPerformanceSetSchedulerPolicy(pid_t task_id, const char* scheduler_policy); __END_DECLS #endif // ANDROID_DVR_PERFORMANCE_H_ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_pose.h���������������������������������������������������������������0100644 0000000 0000000 00000013245 13756501735 016700� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_PUBLIC_POSE_H_ #define ANDROID_DVR_PUBLIC_POSE_H_ #include #include __BEGIN_DECLS #ifdef __ARM_NEON #include #else #ifndef __FLOAT32X4T_86 #define __FLOAT32X4T_86 typedef float float32x4_t __attribute__((__vector_size__(16))); #endif #endif typedef struct DvrPoseClient DvrPoseClient; typedef struct DvrReadBufferQueue DvrReadBufferQueue; // Represents an estimated pose, accessed asynchronously through a shared ring // buffer. No assumptions should be made about the data in padding space. // The size of this struct is 128 bytes. typedef struct __attribute__((packed, aligned(16))) DvrPoseAsync { // Left eye head-from-start orientation quaternion x,y,z,w. float32x4_t orientation; // Left eye head-from-start position x,y,z,pad in meters. float32x4_t position; // Right eye head-from-start orientation quaternion x,y,z,w. float32x4_t right_orientation; // Right eye head-from-start position x,y,z,pad in meters. float32x4_t right_position; // Start-space angular velocity x,y,z,pad in radians per second. float32x4_t angular_velocity; // Start-space positional velocity x,y,z,pad in meters per second. float32x4_t velocity; // Timestamp of when this pose is predicted for, typically halfway through // scanout. int64_t timestamp_ns; // Bitmask of DVR_POSE_FLAG_* constants that apply to this pose. // // If DVR_POSE_FLAG_INVALID is set, the pose is indeterminate. uint64_t flags; // Reserved padding to 128 bytes. uint8_t pad[16]; } DvrPoseAsync; enum { DVR_POSE_FLAG_INVALID = (1ULL << 0), // This pose is invalid. DVR_POSE_FLAG_INITIALIZING = (1ULL << 1), // The pose delivered during // initialization and it may not be // correct. DVR_POSE_FLAG_3DOF = (1ULL << 2), // This pose is derived from 3Dof sensors. If // this is not set, pose is derived using // 3Dof and 6Dof sensors. DVR_POSE_FLAG_FLOOR_HEIGHT_INVALID = (1ULL << 3), // If set the floor height is invalid. // Bits that indicate the tracking system state. DVR_POSE_FLAG_SERVICE_EXCEPTION = (1ULL << 32), DVR_POSE_FLAG_FISHEYE_OVER_EXPOSED = (1ULL << 33), DVR_POSE_FLAG_FISHEYE_UNDER_EXPOSED = (1ULL << 34), DVR_POSE_FLAG_COLOR_OVER_EXPOSED = (1ULL << 35), DVR_POSE_FLAG_COLOR_UNDER_EXPOSED = (1ULL << 36), DVR_POSE_FLAG_TOO_FEW_FEATURES_TRACKED = (1ULL << 37) }; // Represents a sensor pose sample. typedef struct __attribute__((packed, aligned(16))) DvrPose { // Head-from-start orientation quaternion x,y,z,w. float32x4_t orientation; // The angular velocity where the x,y,z is the rotation axis and the // magnitude is the radians / second in the same coordinate frame as // orientation. float32x4_t angular_velocity; // Head-from-start position x,y,z,pad in meters. float32x4_t position; // In meters / second in the same coordinate frame as position. float32x4_t velocity; // In meters / second ^ 2 in the same coordinate frame as position. float32x4_t acceleration; // Timestamp for the measurement in nanoseconds. int64_t timestamp_ns; // The combination of flags above. uint64_t flags; // The current floor height. May be updated at a lower cadence than pose. float floor_height; // Padding to 112 bytes so the size is a multiple of 16. uint8_t padding[12]; } DvrPose; // Represents a data type that can be streamed from pose service. enum { DVR_POSE_RAW_DATA_STEREO_IMAGE = (1ULL << 0), DVR_POSE_RAW_DATA_POINT_CLOUD = (1ULL << 1), DVR_POSE_RAW_DATA_FEATURES = (1ULL << 2), // Always last. DVR_POSE_RAW_DATA_COUNT = (1ULL << 3), }; // A request to retrieve data from the pose service. Expects that a buffer // queue has been initialized through dvrPoseClientGetDataReader(). typedef struct DvrPoseDataCaptureRequest { // The type of data to capture. Refer to enum DVR_POSE_RAW_DATA_* for types. uint64_t data_type; // The sample interval. This can be used to skip samples. For example, a // value of 5 will capture every fifth frame and discard the 4 frames in // between. Set to 1 to capture all frames. uint32_t sample_interval; // The length of time to capture samples in milliseconds. Set to 0 to capture // indefinitely. uint32_t capture_time_ms; // Reserved fields. uint32_t reserved0; uint32_t reserved1; uint32_t reserved2; uint32_t reserved3; uint32_t reserved4; } DvrPoseDataCaptureRequest; // Gets a read buffer queue for the data type |data_type|. Each call returns a // different read buffer queue connected to the same write buffer queue. A // separate write buffer queue exists for each |data_type|. // // PoseService supports a single consumer per write buffer queue. The consumer // is expected to hold a single DvrReadBufferQueue at a time. Callers should // cache these instead of requesting new ones when possible. If the consumer // disconnects from the queue, it can regain a read buffer queue for the same // producer by calling this function. // // For data_type DVR_POSE_RAW_DATA_STEREO_IMAGE, each buffer consists of two // images formatted as a AHARDWAREBUFFER_FORMAT_BLOB, where height is 1 and // width is the total size of both images. The size of an individual image can // be found in the metadata struct DvrNativeBufferMetadata, where width is // |crop_right| and height is |crop_bottom|/2. Each image is contiguous in // memory with stride equal to width. int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type, DvrReadBufferQueue** queue_out); // TODO(b/65067592): Move pose api's from pose_client.h to here. __END_DECLS #endif // ANDROID_DVR_PUBLIC_POSE_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_shared_buffers.h�����������������������������������������������������0100644 0000000 0000000 00000006750 13756501735 020717� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_SHARED_BUFFERS_H_ #define ANDROID_DVR_SHARED_BUFFERS_H_ #include #include #include #include // This header is shared by VrCore and Android and must be kept in sync. namespace android { namespace dvr { // Increment when the layout for the buffers change. enum : uint32_t { kSharedBufferLayoutVersion = 2 }; // Note: These buffers will be mapped from various system processes as well // as VrCore and the application processes in a r/w manner. // // Therefore it is possible for the application to mess with the contents of // these buffers. // // While using them, assume garbage memory: Your logic must not crash or lead // to execution of unsafe code as a function of the contents of these buffers. // Sanity check for basic type sizes. static_assert(sizeof(DvrPoseAsync) == 128, "Unexpected size for DvrPoseAsync"); static_assert(sizeof(DvrPose) == 112, "Unexpected size for DvrPose"); static_assert(sizeof(DvrVsync) == 32, "Unexpected size for DvrVsync"); static_assert(sizeof(DvrConfig) == 16, "Unexpected size for DvrConfig"); // A helper class that provides compile time sized traits for the BroadcastRing. template class DvrRingBufferTraits { public: using Record = DvrType; static constexpr bool kUseStaticRecordSize = false; static constexpr uint32_t kStaticRecordCount = StaticCount; static constexpr int kMaxReservedRecords = 1; static constexpr int kMinAvailableRecords = 1; }; // Traits classes. using DvrPoseTraits = DvrRingBufferTraits; using DvrVsyncTraits = DvrRingBufferTraits; using DvrConfigTraits = DvrRingBufferTraits; // The broadcast ring classes that will expose the data. using DvrPoseRing = BroadcastRing; using DvrVsyncRing = BroadcastRing; using DvrConfigRing = BroadcastRing; // This is a shared memory buffer for passing pose data estimated at vsyncs. // // This will be primarily used for late latching and EDS where we bind this // buffer in a shader and extract the right vsync-predicted pose. struct __attribute__((packed, aligned(16))) DvrVsyncPoseBuffer { enum : int { // The number vsync predicted poses to keep in the ring buffer. // Must be a power of 2. kSize = 8, kIndexMask = kSize - 1, // The number of vsyncs (from the current vsync) we predict in vsync buffer. // The other poses are left alone. kMinFutureCount = 4 }; // The vsync predicted poses. // The pose for the vsync n is: // vsync_poses[n % kSize] // // This buffer is unsynchronized: It is possible to get torn reads as the // sensor service updates the predictions as new sensor measurements come // in. In particular, it is possible to get the position and an updated // orientation while reading. DvrPoseAsync vsync_poses[kSize]; // The latest sensor pose for GPU usage. DvrPose current_pose; // Current vsync_count (where sensord is writing poses from). uint32_t vsync_count; // For 16 byte alignment. uint8_t padding[12]; }; static_assert(sizeof(DvrVsyncPoseBuffer) == 1152, "Unexpected size for DvrVsyncPoseBuffer"); // The keys for the dvr global buffers. enum DvrGlobalBuffers : int32_t { kVsyncPoseBuffer = 1, kVsyncBuffer = 2, kSensorPoseBuffer = 3, kVrFlingerConfigBufferKey = 4 }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_SHARED_BUFFERS_H_ ������������������������libs/vr/libdvr/include/dvr/dvr_surface.h������������������������������������������������������������0100644 0000000 0000000 00000010553 13756501735 017361� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_SURFACE_H_ #define ANDROID_DVR_SURFACE_H_ #include #include #include #include #include #include #include #include __BEGIN_DECLS // Attribute types. The values are one-hot encoded to support singluar types or // masks of supported types. enum { DVR_SURFACE_ATTRIBUTE_TYPE_NONE = 0, DVR_SURFACE_ATTRIBUTE_TYPE_INT32 = (1 << 0), DVR_SURFACE_ATTRIBUTE_TYPE_INT64 = (1 << 1), DVR_SURFACE_ATTRIBUTE_TYPE_BOOL = (1 << 2), DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT = (1 << 3), DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2 = (1 << 4), DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3 = (1 << 5), DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4 = (1 << 6), DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8 = (1 << 7), DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16 = (1 << 8), }; typedef uint64_t DvrSurfaceAttributeType; typedef int32_t DvrSurfaceAttributeKey; typedef struct DvrSurfaceAttributeValue { DvrSurfaceAttributeType type; union { int32_t int32_value; int64_t int64_value; bool bool_value; float float_value; float float2_value[2]; float float3_value[3]; float float4_value[4]; float float8_value[8]; float float16_value[16]; }; } DvrSurfaceAttributeValue; typedef struct DvrSurfaceAttribute { DvrSurfaceAttributeKey key; DvrSurfaceAttributeValue value; } DvrSurfaceAttribute; // Creates a new display surface with the given attributes. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes, size_t attribute_count, DvrSurface** surface_out); // Destroys the display surface. void dvrSurfaceDestroy(DvrSurface* surface); // Gets the DisplayService global id for this surface. int dvrSurfaceGetId(DvrSurface* surface); // Sets attributes on the given display surface. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceSetAttributes(DvrSurface* surface, const DvrSurfaceAttribute* attributes, size_t attribute_count); // Creates a new write-side buffer queue on the given surface. Direct surfaces // may only have one queue, the latest call replacing any prior queue. Replaced // queues are still referenced and should be destryoed using the queue destroy // API. // @return 0 on success. Otherwise returns a negative error value. int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size, DvrWriteBufferQueue** queue_out); // Sets up a named buffer for shared memory data transfer between display // clients and the system. Protected API that may only be called with sufficient // privilege. // @return 0 on success. Otherwise returns a negative error value. int dvrSetupGlobalBuffer(DvrGlobalBufferKey key, size_t size, uint64_t usage, DvrBuffer** buffer_out); // Deletes a named buffer. WARNING: This is dangerous because any existing // clients of this buffer will not be notified and will remain attached to // the old buffer. This is useful for tests, but probably not for production // code. // @return 0 on success. Otherwise returns a negative error value. int dvrDeleteGlobalBuffer(DvrGlobalBufferKey key); // Get a global buffer from the display service. // @return 0 on success. Otherwise returns a negative error value. int dvrGetGlobalBuffer(DvrGlobalBufferKey key, DvrBuffer** out_buffer); // Read the native device display metrics as reported by the hardware composer. // This is useful as otherwise the device metrics are only reported as // relative to the current device orientation. // @param sizeof_metrics the size of the passed in metrics struct. This is used // to ensure we don't break each other during active development. // @param metrics on success holds the retrieved device metrics. // @return 0 on success. Otherwise returns a negative error value (typically // this means the display service is not available). int dvrGetNativeDisplayMetrics(size_t metrics_struct_size, DvrNativeDisplayMetrics* metrics); __END_DECLS #endif // ANDROID_DVR_SURFACE_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_tracking.h�����������������������������������������������������������0100644 0000000 0000000 00000020323 13756501735 017527� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_TRACKING_H_ #define ANDROID_DVR_TRACKING_H_ #include #include #include __BEGIN_DECLS typedef struct DvrReadBuffer DvrReadBuffer; typedef struct DvrTrackingCamera DvrTrackingCamera; typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor; typedef struct DvrTrackingSensors DvrTrackingSensors; typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; // The callback for DvrTrackingFeatureExtractor that will deliver the feature // events. This callback is passed to dvrTrackingFeatureExtractorStart. typedef void (*DvrTrackingFeatureCallback)(void* context, const DvrTrackingFeatures* event); // The callback for DvrTrackingSensors session that will deliver the events. // This callback is passed to dvrTrackingSensorsStart. typedef void (*DvrTrackingSensorEventCallback)(void* context, DvrTrackingSensorEvent* event); // Creates a DvrTrackingCamera session. // // On creation, the session is not in operating mode. Client code must call // dvrTrackingCameraStart to bootstrap the underlying camera stack. // // There is no plan to expose camera configuration through this API. All camera // parameters are determined by the system optimized for better tracking // results. See b/78662281 for detailed deprecation plan of this API and the // Stage 2 of VR tracking data source refactoring. // // @param out_camera The pointer of a DvrTrackingCamera will be filled here if // the method call succeeds. // @return Zero on success, or negative error code. int dvrTrackingCameraCreate(DvrTrackingCamera** out_camera); // Destroys a DvrTrackingCamera handle. // // @param camera The DvrTrackingCamera of interest. void dvrTrackingCameraDestroy(DvrTrackingCamera* camera); // Starts the DvrTrackingCamera. // // On successful return, all DvrReadBufferQueue's associated with the given // write_queue will start to receive buffers from the camera stack. Note that // clients of this API should not assume the buffer dimension, format, and/or // usage of the outcoming buffers, as they are governed by the underlying camera // logic. Also note that it's the client's responsibility to consume buffers // from DvrReadBufferQueue on time and return them back to the producer; // otherwise the camera stack might be blocked. // // @param camera The DvrTrackingCamera of interest. // @param write_queue A DvrWriteBufferQueue that the camera stack can use to // populate the buffer into. The queue must be empty and the camera stack // will request buffer allocation with proper buffer dimension, format, and // usage. Note that the write queue must be created with user_metadata_size // set to sizeof(DvrTrackingBufferMetadata). On success, the write_queue // handle will become invalid and the ownership of the queue handle will be // transferred into the camera; otherwise, the write_queue handle will keep // untouched and the caller still has the ownership. // @return Zero on success, or negative error code. int dvrTrackingCameraStart(DvrTrackingCamera* camera, DvrWriteBufferQueue* write_queue); // Stops the DvrTrackingCamera. // // On successful return, the DvrWriteBufferQueue set during // dvrTrackingCameraStart will stop getting new buffers from the camera stack. // // @param camera The DvrTrackingCamera of interest. // @return Zero on success, or negative error code. int dvrTrackingCameraStop(DvrTrackingCamera* camera); // Creates a DvrTrackingSensors session. // // This will initialize but not start device sensors (gyro / accel). Upon // successfull creation, the clients can call dvrTrackingSensorsStart to start // receiving sensor events. // // @param out_sensors The pointer of a DvrTrackingSensors will be filled here if // the method call succeeds. // @param mode The sensor mode. // mode="ndk": Use the Android NDK. // mode="direct": Use direct mode sensors (lower latency). // @return Zero on success, or negative error code. int dvrTrackingSensorsCreate(DvrTrackingSensors** out_sensors, const char* mode); // Destroys a DvrTrackingSensors session. // // @param sensors The DvrTrackingSensors struct to destroy. void dvrTrackingSensorsDestroy(DvrTrackingSensors* sensors); // Starts the tracking sensor session. // // This will start the device sensors and start pumping the feature and sensor // events as they arrive. // // @param client A tracking client created by dvrTrackingSensorsCreate. // @param context A client supplied pointer that will be passed to the callback. // @param callback A callback that will receive the sensor events on an // arbitrary thread. // @return Zero on success, or negative error code. int dvrTrackingSensorsStart(DvrTrackingSensors* sensors, DvrTrackingSensorEventCallback callback, void* context); // Stops a DvrTrackingSensors session. // // This will stop the device sensors. dvrTrackingSensorsStart can be called to // restart them again. // // @param client A tracking client created by dvrTrackingClientCreate. // @return Zero on success, or negative error code. int dvrTrackingSensorsStop(DvrTrackingSensors* sensors); // Creates a tracking feature extractor. // // This will initialize but not start the feature extraction session. Upon // successful creation, the client can call dvrTrackingFeatureExtractorStart to // start receiving features. // // @param out_extractor The pointer of a DvrTrackingFeatureExtractor will be // filled here if the method call succeeds. int dvrTrackingFeatureExtractorCreate( DvrTrackingFeatureExtractor** out_extractor); // Destroys a tracking feature extractor. // // @param extractor The DvrTrackingFeatureExtractor to destroy. void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor* extractor); // Starts the tracking feature extractor. // // This will start the extractor and start pumping the output feature events to // the registered callback. Note that this method will create one or more // threads to handle feature processing. // // @param extractor The DvrTrackingFeatureExtractor to destroy. int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor* extractor, DvrTrackingFeatureCallback callback, void* context); // Stops the tracking feature extractor. // // This will stop the extractor session and clean up all internal resourcse // related to this extractor. On succssful return, all internal therad started // by dvrTrackingFeatureExtractorStart should be stopped. // // @param extractor The DvrTrackingFeatureExtractor to destroy. int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor* extractor); // Processes one buffer to extract features from. // // The buffer will be sent over to DSP for feature extraction. Once the process // is done, the processing thread will invoke DvrTrackingFeatureCallback with // newly extracted features. Note that not all buffers will be processed, as the // underlying DSP can only process buffers at a certain framerate. If a buffer // needs to be skipped, out_skipped filed will be set to true. Also note that // for successfully processed stereo buffer, two callbacks (one for each eye) // will be fired. // // @param extractor The DvrTrackingFeatureExtractor to destroy. // @param buffer The buffer to extract features from. Note that the buffer must // be in acquired state for the buffer to be processed. Also note that the // buffer will be released back to its producer on successful return of the // method. // @param metadata The metadata associated with the buffer. Should be populated // by DvrTrackingCamera session as user defined metadata. // @param out_skipped On successful return, the field will be set to true iff // the buffer was skipped; and false iff the buffer was processed. This // field is optional and nullptr can be passed here to ignore the field. // @return Zero on success, or negative error code. int dvrTrackingFeatureExtractorProcessBuffer( DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer, const DvrTrackingBufferMetadata* metadata, bool* out_skipped); __END_DECLS #endif // ANDROID_DVR_TRACKING_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_tracking_types.h�����������������������������������������������������0100644 0000000 0000000 00000005712 13756501735 020760� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_TRACKING_TYPES_H_ #define ANDROID_DVR_TRACKING_TYPES_H_ #include #include __BEGIN_DECLS typedef struct DvrTrackingBufferMetadata { // Specifies the source of this image. uint32_t camera_mask; // Specifies the memory format of this image. uint32_t format; /// The width of the image data. uint32_t width; /// The height of the image data. uint32_t height; /// The number of bytes per scanline of image data. uint32_t stride; /// The frame number of this image. int32_t frame_number; /// The timestamp of this image in nanoseconds. Taken in the middle of the /// exposure interval. int64_t timestamp_ns; // This is the timestamp for recording when the system using the HAL // received the callback. It will not be populated by the HAL. int64_t callback_timestamp_ns; /// The exposure duration of this image in nanoseconds. int64_t exposure_duration_ns; } DvrTrackingBufferMetadata; // Represents a set of features extracted from a camera frame. Note that this // should be in sync with TangoHalCallbacks defined in tango-hal.h. typedef struct DvrTrackingFeatures { // Specifies the source of the features. uint32_t camera_mask; // This is unused. uint32_t unused; // The timestamp in nanoseconds from the image that generated the features. // Taken in the middle of the exposure interval. int64_t timestamp_ns; // This is the timestamp for recording when the system using the HAL // received the callback. It will not be populated by the HAL. int64_t callback_timestamp_ns; // The frame number from the image that generated the features. int64_t frame_number; // The number of features. int count; // An array of 2D image points for each feature in the current image. // This is sub-pixel refined extremum location at the fine resolution. float (*positions)[2]; // The id of these measurements. int32_t* ids; // The feature descriptors. uint64_t (*descriptors)[8]; // Laplacian scores for each feature. float* scores; // Is this feature a minimum or maximum in the Laplacian image. // 0 if the feature is a maximum, 1 if it is a minimum. int32_t* is_minimum; // This corresponds to the sub-pixel index of the laplacian image // that the extremum was found. float* scales; // Computed orientation of keypoint as part of FREAK extraction, except // it's represented in radians and measured anti-clockwise. float* angles; // Edge scores for each feature. float* edge_scores; } DvrTrackingFeatures; // Represents a sensor event. typedef struct DvrTrackingSensorEvent { // The sensor type. int32_t sensor; // Event type. int32_t type; // This is the timestamp recorded from the device. Taken in the middle // of the integration interval and adjusted for any low pass filtering. int64_t timestamp_ns; // The event data. float x; float y; float z; } DvrTrackingSensorEvent; __END_DECLS #endif // ANDROID_DVR_TRACKING_TYPES_H_ ������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_vsync.h��������������������������������������������������������������0100644 0000000 0000000 00000001507 13756501735 017072� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_VSYNC_H_ #define ANDROID_DVR_VSYNC_H_ #include #include __BEGIN_DECLS // Represents a vsync sample. The size of this struct is 32 bytes. typedef struct __attribute__((packed, aligned(16))) DvrVsync { // The timestamp for the last vsync in nanoseconds. uint64_t vsync_timestamp_ns; // The index of the last vsync. uint32_t vsync_count; // Scan out for the left eye = vsync_timestamp_ns + vsync_left_eye_offset_ns. int32_t vsync_left_eye_offset_ns; // Scan out for the right eye = vsync_timestamp_ns + vsync_right_eye_offset_ns int32_t vsync_right_eye_offset_ns; // The period of a vsync in nanoseconds. uint32_t vsync_period_ns; // Padding to 32 bytes so the size is a multiple of 16. uint8_t padding[8]; } DvrVsync; __END_DECLS #endif // ANDROID_DVR_VSYNC_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013630� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/Android.bp���������������������������������������������������������������������0100644 0000000 0000000 00000004624 13756501735 015536� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (C) 2017 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. cc_test { srcs: [ "dvr_display_manager-test.cpp", "dvr_named_buffer-test.cpp", "dvr_tracking-test.cpp", ], header_libs: ["libdvr_headers"], static_libs: [ "libdvr_static.google", "libchrome", "libdvrcommon", "libdisplay", "libbroadcastring", ], shared_libs: [ "libbase", "libbinder", "libbufferhubqueue", "libcutils", "libgui", "liblog", "libhardware", "libui", "libutils", "libnativewindow", "libpdx_default_transport", ], cflags: [ "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"dvr_api-test\"", "-DTRACE=0", "-Wno-missing-field-initializers", "-O0", "-g", ], name: "dvr_api-test", } cc_test { name: "dvr_buffer_queue-test", // Includes the dvr_api.h header. Tests should only include "dvr_api.h", // and shall only get access to |dvrGetApi|, as other symbols are hidden // from the library. include_dirs: ["frameworks/native/libs/vr/libdvr/include"], srcs: ["dvr_buffer_queue-test.cpp"], shared_libs: [ "libandroid", "liblog", ], cflags: [ "-DTRACE=0", "-O2", "-g", ], // DTS Should only link to NDK libraries. sdk_version: "26", stl: "c++_static", } cc_test { name: "dvr_display-test", include_dirs: [ "frameworks/native/libs/vr/libdvr/include", "frameworks/native/libs/nativewindow/include", ], srcs: ["dvr_display-test.cpp"], shared_libs: [ "libandroid", "liblog", ], cflags: [ "-DTRACE=0", "-O2", "-g", ], // DTS Should only link to NDK libraries. sdk_version: "26", stl: "c++_static", } ������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_api_test.h�����������������������������������������������������������������0100644 0000000 0000000 00000002243 13756501735 016462� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include /** DvrTestBase loads the libdvr.so at runtime and get the Dvr API version 1. */ class DvrApiTest : public ::testing::Test { protected: void SetUp() override { int flags = RTLD_NOW | RTLD_LOCAL; // Here we need to ensure that libdvr is loaded with RTLD_NODELETE flag set // (so that calls to `dlclose` don't actually unload the library). This is a // workaround for an Android NDK bug. See more detail: // https://github.com/android-ndk/ndk/issues/360 flags |= RTLD_NODELETE; platform_handle_ = dlopen("libdvr.google.so", flags); ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing."; auto dvr_get_api = reinterpret_cast( dlsym(platform_handle_, "dvrGetApi")); ASSERT_NE(nullptr, dvr_get_api) << "Platform library missing dvrGetApi."; ASSERT_EQ(dvr_get_api(&api_, sizeof(api_), /*version=*/1), 0) << "Unable to find compatible Dvr API."; } void TearDown() override { if (platform_handle_ != nullptr) { dlclose(platform_handle_); } } void* platform_handle_ = nullptr; DvrApi_v1 api_; }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp������������������������������������������������������0100644 0000000 0000000 00000047142 13756501735 020646� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include "dvr_api_test.h" #define LOG_TAG "dvr_buffer_queue-test" #ifndef ALOGD #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #endif #ifndef ALOGD_IF #define ALOGD_IF(cond, ...) \ ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0) #endif namespace { static constexpr uint32_t kBufferWidth = 100; static constexpr uint32_t kBufferHeight = 1; static constexpr uint32_t kLayerCount = 1; static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB; static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; static constexpr size_t kQueueCapacity = 3; class DvrBufferQueueTest : public DvrApiTest { public: static void BufferAvailableCallback(void* context) { DvrBufferQueueTest* thiz = static_cast(context); thiz->HandleBufferAvailable(); } static void BufferRemovedCallback(DvrReadBuffer* buffer, void* context) { DvrBufferQueueTest* thiz = static_cast(context); thiz->HandleBufferRemoved(buffer); } protected: void TearDown() override { if (write_queue_ != nullptr) { api_.WriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } DvrApiTest::TearDown(); } void HandleBufferAvailable() { buffer_available_count_ += 1; ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_); } void HandleBufferRemoved(DvrReadBuffer* buffer) { buffer_removed_count_ += 1; ALOGD_IF(TRACE, "Buffer removed, buffer=%p, count=%d", buffer, buffer_removed_count_); } DvrWriteBufferQueue* write_queue_ = nullptr; int buffer_available_count_{0}; int buffer_removed_count_{0}; }; TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); api_.WriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); size_t capacity = api_.WriteBufferQueueGetCapacity(write_queue_); ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); } TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); api_.ReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue1 = nullptr; DvrReadBufferQueue* read_queue2 = nullptr; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue1); ret = api_.ReadBufferQueueCreateReadQueue(read_queue1, &read_queue2); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue2); ASSERT_NE(read_queue1, read_queue2); api_.ReadBufferQueueDestroy(read_queue1); api_.ReadBufferQueueDestroy(read_queue2); } TEST_F(DvrBufferQueueTest, GainBuffer) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrWriteBuffer* wb = nullptr; EXPECT_FALSE(api_.WriteBufferIsValid(wb)); DvrNativeBufferMetadata meta; int fence_fd = -1; ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta, &fence_fd); ASSERT_EQ(ret, 0); EXPECT_EQ(fence_fd, -1); EXPECT_NE(wb, nullptr); EXPECT_TRUE(api_.WriteBufferIsValid(wb)); } TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrReadBufferQueue* read_queue = nullptr; DvrReadBuffer* rb = nullptr; DvrWriteBuffer* wb = nullptr; DvrNativeBufferMetadata meta1; DvrNativeBufferMetadata meta2; int fence_fd = -1; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(ret, 0); ASSERT_NE(read_queue, nullptr); api_.ReadBufferQueueSetBufferAvailableCallback( read_queue, &BufferAvailableCallback, this); // Gain buffer for writing. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1, &fence_fd); ASSERT_EQ(ret, 0); ASSERT_NE(wb, nullptr); ASSERT_TRUE(api_.WriteBufferIsValid(wb)); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", wb, fence_fd); close(fence_fd); // Post buffer to the read_queue. meta1.timestamp = 42; ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); ASSERT_EQ(ret, 0); ASSERT_FALSE(api_.WriteBufferIsValid(wb)); wb = nullptr; // Acquire buffer for reading. ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, &meta2, &fence_fd); ASSERT_EQ(ret, 0); ASSERT_NE(rb, nullptr); // Dequeue is successfully, BufferAvailableCallback should be fired once. ASSERT_EQ(buffer_available_count_, 1); ASSERT_TRUE(api_.ReadBufferIsValid(rb)); // Metadata should be passed along from producer to consumer properly. ASSERT_EQ(meta1.timestamp, meta2.timestamp); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb, fence_fd); close(fence_fd); // Release buffer to the write_queue. ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); ASSERT_FALSE(api_.ReadBufferIsValid(rb)); rb = nullptr; // TODO(b/34387835) Currently buffer allocation has to happen after all queues // are initialized. size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); api_.ReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, GetANativeWindow) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, /*user_metadata_size=*/0, &write_queue_); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, write_queue_); ANativeWindow* window = nullptr; ret = api_.WriteBufferQueueGetANativeWindow(write_queue_, &window); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, window); uint32_t width = ANativeWindow_getWidth(window); uint32_t height = ANativeWindow_getHeight(window); uint32_t format = ANativeWindow_getFormat(window); ASSERT_EQ(kBufferWidth, width); ASSERT_EQ(kBufferHeight, height); ASSERT_EQ(kBufferFormat, format); } // Create buffer queue of three buffers and dequeue three buffers out of it. // Before each dequeue operation, we resize the buffer queue and expect the // queue always return buffer with desired dimension. TEST_F(DvrBufferQueueTest, ResizeBuffer) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); int fence_fd = -1; DvrNativeBufferMetadata meta; DvrReadBufferQueue* read_queue = nullptr; DvrWriteBuffer* wb1 = nullptr; DvrWriteBuffer* wb2 = nullptr; DvrWriteBuffer* wb3 = nullptr; AHardwareBuffer* ahb1 = nullptr; AHardwareBuffer* ahb2 = nullptr; AHardwareBuffer* ahb3 = nullptr; AHardwareBuffer_Desc buffer_desc; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); api_.ReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback, this); // Handle all pending events on the read queue. ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue); ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); // Resize before dequeuing. constexpr uint32_t w1 = 10; ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight); ASSERT_EQ(0, ret); // Gain first buffer for writing. All buffers will be resized. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(api_.WriteBufferIsValid(wb1)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1); close(fence_fd); // Check the buffer dimension. ret = api_.WriteBufferGetAHardwareBuffer(wb1, &ahb1); ASSERT_EQ(0, ret); AHardwareBuffer_describe(ahb1, &buffer_desc); ASSERT_EQ(w1, buffer_desc.width); ASSERT_EQ(kBufferHeight, buffer_desc.height); AHardwareBuffer_release(ahb1); // For the first resize, all buffers are reallocated. int expected_buffer_removed_count = kQueueCapacity; ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); // Resize the queue. We are testing with blob format, keep height to be 1. constexpr uint32_t w2 = 20; ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight); ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(api_.WriteBufferIsValid(wb2)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2, fence_fd); close(fence_fd); // Check the buffer dimension, should be new width ret = api_.WriteBufferGetAHardwareBuffer(wb2, &ahb2); ASSERT_EQ(0, ret); AHardwareBuffer_describe(ahb2, &buffer_desc); ASSERT_EQ(w2, buffer_desc.width); AHardwareBuffer_release(ahb2); // For the second resize, all but one buffers are reallocated. expected_buffer_removed_count += (kQueueCapacity - 1); ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); // Resize the queue for the third time. constexpr uint32_t w3 = 30; ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight); ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(api_.WriteBufferIsValid(wb3)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3, fence_fd); close(fence_fd); // Check the buffer dimension, should be new width ret = api_.WriteBufferGetAHardwareBuffer(wb3, &ahb3); ASSERT_EQ(0, ret); AHardwareBuffer_describe(ahb3, &buffer_desc); ASSERT_EQ(w3, buffer_desc.width); AHardwareBuffer_release(ahb3); // For the third resize, all but two buffers are reallocated. expected_buffer_removed_count += (kQueueCapacity - 2); ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); api_.ReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, ReadQueueEventFd) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); int event_fd = api_.ReadBufferQueueGetEventFd(read_queue); ASSERT_GT(event_fd, 0); } // Verifies a Dvr{Read,Write}BufferQueue contains the same set of // Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id, // the corresponding AHardwareBuffer handle stays the same. TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); int fence_fd = -1; DvrReadBufferQueue* read_queue = nullptr; EXPECT_EQ(0, api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue)); // Read buffers. std::array rbs; // Write buffers. std::array wbs; // Buffer metadata. std::array metas; // Hardware buffers for Read buffers. std::unordered_map rhbs; // Hardware buffers for Write buffers. std::unordered_map whbs; constexpr int kNumTests = 100; // This test runs the following operations many many times. Thus we prefer to // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output. std::function Gain = [&](size_t i) { int ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10, &wbs[i], &metas[i], &fence_fd); ASSERT_EQ(ret, 0); ASSERT_LT(fence_fd, 0); // expect invalid fence. ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i])); int buffer_id = api_.WriteBufferGetId(wbs[i]); ASSERT_GT(buffer_id, 0); AHardwareBuffer* hb = nullptr; ASSERT_EQ(0, api_.WriteBufferGetAHardwareBuffer(wbs[i], &hb)); auto whb_it = whbs.find(buffer_id); if (whb_it == whbs.end()) { // If this is a new buffer id, check that total number of unique // hardware buffers won't exceed queue capacity. ASSERT_LT(whbs.size(), kQueueCapacity); whbs.emplace(buffer_id, hb); } else { // If this is a buffer id we have seen before, check that the // buffer_id maps to the same AHardwareBuffer handle. ASSERT_EQ(hb, whb_it->second); } }; std::function Post = [&](size_t i) { ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i])); metas[i].timestamp++; int ret = api_.WriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i], /*fence=*/-1); ASSERT_EQ(ret, 0); }; std::function Acquire = [&](size_t i) { int ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rbs[i], &metas[i], &fence_fd); ASSERT_EQ(ret, 0); ASSERT_LT(fence_fd, 0); // expect invalid fence. ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i])); int buffer_id = api_.ReadBufferGetId(rbs[i]); ASSERT_GT(buffer_id, 0); AHardwareBuffer* hb = nullptr; ASSERT_EQ(0, api_.ReadBufferGetAHardwareBuffer(rbs[i], &hb)); auto rhb_it = rhbs.find(buffer_id); if (rhb_it == rhbs.end()) { // If this is a new buffer id, check that total number of unique hardware // buffers won't exceed queue capacity. ASSERT_LT(rhbs.size(), kQueueCapacity); rhbs.emplace(buffer_id, hb); } else { // If this is a buffer id we have seen before, check that the buffer_id // maps to the same AHardwareBuffer handle. ASSERT_EQ(hb, rhb_it->second); } }; std::function Release = [&](size_t i) { ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i])); int ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i], /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); }; // Scenario one: for (int i = 0; i < kNumTests; i++) { // Gain all write buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Gain(i)); } // Post all write buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Post(i)); } // Acquire all read buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Acquire(i)); } // Release all read buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Release(i)); } } // Scenario two: for (int i = 0; i < kNumTests; i++) { // Gain and post all write buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Gain(i)); ASSERT_NO_FATAL_FAILURE(Post(i)); } // Acquire and release all read buffers. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Acquire(i)); ASSERT_NO_FATAL_FAILURE(Release(i)); } } // Scenario three: for (int i = 0; i < kNumTests; i++) { // Gain all write buffers then post them in reversed order. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Gain(i)); } for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i)); } // Acquire all write buffers then release them in reversed order. for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Acquire(i)); } for (size_t i = 0; i < kQueueCapacity; i++) { ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i)); } } } TEST_F(DvrBufferQueueTest, ConsumerReleaseAfterProducerDestroy) { int ret = api_.WriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrReadBufferQueue* read_queue = nullptr; DvrReadBuffer* rb = nullptr; DvrWriteBuffer* wb = nullptr; DvrNativeBufferMetadata meta1; DvrNativeBufferMetadata meta2; int fence_fd = -1; ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(ret, 0); api_.ReadBufferQueueSetBufferAvailableCallback( read_queue, &BufferAvailableCallback, this); // Gain buffer for writing. ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1, &fence_fd); ASSERT_EQ(ret, 0); close(fence_fd); // Post buffer to the read_queue. ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); ASSERT_EQ(ret, 0); wb = nullptr; // Acquire buffer for reading. ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb, &meta2, &fence_fd); ASSERT_EQ(ret, 0); close(fence_fd); // Destroy the write buffer queue and make sure the reader queue is picking // these events up. api_.WriteBufferQueueDestroy(write_queue_); ret = api_.ReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); // Release buffer to the write_queue. ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); rb = nullptr; api_.ReadBufferQueueDestroy(read_queue); } } // namespace ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_display-test.cpp�����������������������������������������������������������0100644 0000000 0000000 00000034626 13756501735 017641� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include "dvr_api_test.h" #define LOG_TAG "dvr_display-test" #ifndef ALOGD #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #endif class DvrDisplayTest : public DvrApiTest { protected: void SetUp() override { DvrApiTest::SetUp(); int ret = api_.GetNativeDisplayMetrics(sizeof(display_metrics_), &display_metrics_); ASSERT_EQ(ret, 0) << "Failed to get display metrics."; ALOGD( "display_width: %d, display_height: %d, display_x_dpi: %d, " "display_y_dpi: %d, vsync_period_ns: %d.", display_metrics_.display_width, display_metrics_.display_height, display_metrics_.display_x_dpi, display_metrics_.display_y_dpi, display_metrics_.vsync_period_ns); } void TearDown() override { if (write_queue_ != nullptr) { api_.WriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } if (direct_surface_ != nullptr) { api_.SurfaceDestroy(direct_surface_); direct_surface_ = nullptr; } DvrApiTest::TearDown(); } /* Convert a write buffer to an android hardware buffer and fill in * color_textures evenly to the buffer. * AssertionError if the width of the buffer is not equal to the input width, * AssertionError if the height of the buffer is not equal to the input * height. */ void FillWriteBuffer(DvrWriteBuffer* write_buffer, const std::vector& color_textures, uint32_t width, uint32_t height); // Write buffer queue properties. static constexpr uint64_t kUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; uint32_t kFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; static constexpr size_t kMetadataSize = 0; static constexpr int kTimeoutMs = 1000; // Time for getting buffer. uint32_t kLayerCount = 1; DvrWriteBufferQueue* write_queue_ = nullptr; DvrSurface* direct_surface_ = nullptr; // Device display properties. DvrNativeDisplayMetrics display_metrics_; }; TEST_F(DvrDisplayTest, DisplayWithOneBuffer) { // Create a direct surface. std::vector direct_surface_attributes = { {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, .value.int32_value = 10}, {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, }; int ret = api_.SurfaceCreate(direct_surface_attributes.data(), direct_surface_attributes.size(), &direct_surface_); ASSERT_EQ(ret, 0) << "Failed to create direct surface."; // Create a buffer queue with the direct surface. constexpr size_t kCapacity = 1; uint32_t width = display_metrics_.display_width; uint32_t height = display_metrics_.display_height; ret = api_.SurfaceCreateWriteBufferQueue( direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity, kMetadataSize, &write_queue_); EXPECT_EQ(0, ret) << "Failed to create buffer queue."; ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null."; // Get buffer from WriteBufferQueue. DvrWriteBuffer* write_buffer = nullptr; DvrNativeBufferMetadata out_meta; int out_fence_fd = -1; ret = api_.WriteBufferQueueGainBuffer(write_queue_, kTimeoutMs, &write_buffer, &out_meta, &out_fence_fd); EXPECT_EQ(0, ret) << "Failed to get the buffer."; ASSERT_NE(nullptr, write_buffer) << "Gained buffer should not be null."; // Color the write buffer. FillWriteBuffer(write_buffer, {0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}, width, height); // Post buffer. int ready_fence_fd = -1; ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta, ready_fence_fd); EXPECT_EQ(0, ret) << "Failed to post the buffer."; sleep(5); // For visual check on the device under test. // Should observe three primary colors on the screen center. } TEST_F(DvrDisplayTest, DisplayWithDoubleBuffering) { // Create a direct surface. std::vector direct_surface_attributes = { {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, .value.int32_value = 10}, {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, }; int ret = api_.SurfaceCreate(direct_surface_attributes.data(), direct_surface_attributes.size(), &direct_surface_); ASSERT_EQ(ret, 0) << "Failed to create direct surface."; // Create a buffer queue with the direct surface. constexpr size_t kCapacity = 2; uint32_t width = display_metrics_.display_width; uint32_t height = display_metrics_.display_height; ret = api_.SurfaceCreateWriteBufferQueue( direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity, kMetadataSize, &write_queue_); EXPECT_EQ(0, ret) << "Failed to create buffer queue."; ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null."; int num_display_cycles_in_5s = 5 / (display_metrics_.vsync_period_ns / 1e9); ALOGD("The number of display cycles: %d", num_display_cycles_in_5s); int bufferhub_id_prev_write_buffer = -1; for (int i = 0; i < num_display_cycles_in_5s; ++i) { // Get a buffer from the WriteBufferQueue. DvrWriteBuffer* write_buffer = nullptr; DvrNativeBufferMetadata out_meta; int out_fence_fd = -1; ret = api_.WriteBufferQueueGainBuffer( write_queue_, kTimeoutMs, &write_buffer, &out_meta, &out_fence_fd); EXPECT_EQ(0, ret) << "Failed to get the a write buffer."; ASSERT_NE(nullptr, write_buffer) << "The gained buffer should not be null."; int bufferhub_id = api_.WriteBufferGetId(write_buffer); ALOGD("Display cycle: %d, bufferhub id of the write buffer: %d", i, bufferhub_id); EXPECT_NE(bufferhub_id_prev_write_buffer, bufferhub_id) << "Double buffering should be using the two buffers in turns, not " "reusing the same write buffer."; bufferhub_id_prev_write_buffer = bufferhub_id; // Color the write buffer. if (i % 2) { FillWriteBuffer(write_buffer, {0xffff0000, 0xff00ff00, 0xff0000ff}, width, height); } else { FillWriteBuffer(write_buffer, {0xff00ff00, 0xff0000ff, 0xffff0000}, width, height); } // Post the write buffer. int ready_fence_fd = -1; ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta, ready_fence_fd); EXPECT_EQ(0, ret) << "Failed to post the buffer."; } // Should observe blinking screen in secondary colors // although it is actually displaying primary colors. } TEST_F(DvrDisplayTest, DisplayWithTwoHardwareLayers) { // Create the direct_surface_0 of z order 10 and direct_surface_1 of z // order 11. DvrSurface* direct_surface_0 = nullptr; std::vector direct_surface_0_attributes = { {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, .value.int32_value = 10}, {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, }; int ret = api_.SurfaceCreate(direct_surface_0_attributes.data(), direct_surface_0_attributes.size(), &direct_surface_0); EXPECT_EQ(ret, 0) << "Failed to create direct surface."; DvrSurface* direct_surface_1 = nullptr; std::vector direct_surface_1_attributes = { {.key = DVR_SURFACE_ATTRIBUTE_DIRECT, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32, .value.int32_value = 11}, {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE, .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, .value.bool_value = true}, }; ret = api_.SurfaceCreate(direct_surface_1_attributes.data(), direct_surface_1_attributes.size(), &direct_surface_1); EXPECT_EQ(ret, 0) << "Failed to create direct surface."; // Create a buffer queue for each of the direct surfaces. constexpr size_t kCapacity = 1; uint32_t width = display_metrics_.display_width; uint32_t height = display_metrics_.display_height; DvrWriteBufferQueue* write_queue_0 = nullptr; ret = api_.SurfaceCreateWriteBufferQueue( direct_surface_0, width, height, kFormat, kLayerCount, kUsage, kCapacity, kMetadataSize, &write_queue_0); EXPECT_EQ(0, ret) << "Failed to create buffer queue."; EXPECT_NE(nullptr, write_queue_0) << "Write buffer queue should not be null."; DvrWriteBufferQueue* write_queue_1 = nullptr; ret = api_.SurfaceCreateWriteBufferQueue( direct_surface_1, width, height, kFormat, kLayerCount, kUsage, kCapacity, kMetadataSize, &write_queue_1); EXPECT_EQ(0, ret) << "Failed to create buffer queue."; EXPECT_NE(nullptr, write_queue_1) << "Write buffer queue should not be null."; // Get a buffer from each of the write buffer queues. DvrWriteBuffer* write_buffer_0 = nullptr; DvrNativeBufferMetadata out_meta_0; int out_fence_fd = -1; ret = api_.WriteBufferQueueGainBuffer( write_queue_0, kTimeoutMs, &write_buffer_0, &out_meta_0, &out_fence_fd); EXPECT_EQ(0, ret) << "Failed to get the buffer."; EXPECT_NE(nullptr, write_buffer_0) << "Gained buffer should not be null."; DvrWriteBuffer* write_buffer_1 = nullptr; DvrNativeBufferMetadata out_meta_1; out_fence_fd = -1; ret = api_.WriteBufferQueueGainBuffer( write_queue_1, kTimeoutMs, &write_buffer_1, &out_meta_1, &out_fence_fd); EXPECT_EQ(0, ret) << "Failed to get the buffer."; EXPECT_NE(nullptr, write_buffer_1) << "Gained buffer should not be null."; // Color the write buffers. FillWriteBuffer(write_buffer_0, {0xffff0000, 0xff00ff00, 0xff0000ff}, width, height); FillWriteBuffer(write_buffer_1, {0x7f00ff00, 0x7f0000ff, 0x7fff0000}, width, height); // Post buffers. int ready_fence_fd = -1; ret = api_.WriteBufferQueuePostBuffer(write_queue_0, write_buffer_0, &out_meta_0, ready_fence_fd); EXPECT_EQ(0, ret) << "Failed to post the buffer."; ready_fence_fd = -1; ret = api_.WriteBufferQueuePostBuffer(write_queue_1, write_buffer_1, &out_meta_1, ready_fence_fd); EXPECT_EQ(0, ret) << "Failed to post the buffer."; sleep(5); // For visual check on the device under test. // Should observe three secondary colors. // Test finished. Clean up buffers and surfaces. if (write_queue_0 != nullptr) { api_.WriteBufferQueueDestroy(write_queue_0); write_queue_0 = nullptr; } if (write_queue_1 != nullptr) { api_.WriteBufferQueueDestroy(write_queue_1); write_queue_1 = nullptr; } if (direct_surface_0 != nullptr) { api_.SurfaceDestroy(direct_surface_0); } if (direct_surface_1 != nullptr) { api_.SurfaceDestroy(direct_surface_1); } } void DvrDisplayTest::FillWriteBuffer( DvrWriteBuffer* write_buffer, const std::vector& color_textures, uint32_t width, uint32_t height) { uint32_t num_colors = color_textures.size(); // Convert the first write buffer to an android hardware buffer. AHardwareBuffer* ah_buffer = nullptr; int ret = api_.WriteBufferGetAHardwareBuffer(write_buffer, &ah_buffer); ASSERT_EQ(0, ret) << "Failed to get a hardware buffer from the write buffer."; ASSERT_NE(nullptr, ah_buffer) << "AHardware buffer should not be null."; AHardwareBuffer_Desc ah_buffer_describe; AHardwareBuffer_describe(ah_buffer, &ah_buffer_describe); ASSERT_EQ(ah_buffer_describe.format, kFormat) << "The format of the android hardware buffer is wrong."; ASSERT_EQ(ah_buffer_describe.layers, kLayerCount) << "The obtained android hardware buffer should have 2 layers."; ASSERT_EQ(ah_buffer_describe.width, width) << "The obtained android hardware buffer width is wrong."; ASSERT_EQ(ah_buffer_describe.height, height) << "The obtained android hardware buffer height is wrong."; // Change the content of the android hardware buffer. void* buffer_data = nullptr; int32_t fence = -1; ret = AHardwareBuffer_lock(ah_buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, fence, nullptr, &buffer_data); ASSERT_EQ(0, ret) << "Failed to lock the hardware buffer."; ASSERT_NE(nullptr, buffer_data) << "Buffer data should not be null."; uint32_t num_pixels = width * height / num_colors; for (uint32_t color_index = 0; color_index < num_colors - 1; ++color_index) { uint32_t color_texture = color_textures[color_index]; for (uint32_t i = 0; i < num_pixels; ++i) { memcpy(reinterpret_cast(reinterpret_cast(buffer_data) + (i + num_pixels * color_index) * sizeof(color_texture)), &color_texture, sizeof(color_texture)); } } uint32_t color_texture = color_textures[num_colors - 1]; uint32_t num_colored_pixels = num_pixels * (num_colors - 1); num_pixels = width * height - num_colored_pixels; for (uint32_t i = 0; i < num_pixels; ++i) { memcpy(reinterpret_cast(reinterpret_cast(buffer_data) + (i + num_colored_pixels) * sizeof(color_texture)), &color_texture, sizeof(color_texture)); } fence = -1; ret = AHardwareBuffer_unlock(ah_buffer, &fence); EXPECT_EQ(0, ret) << "Failed to unlock the hardware buffer."; // Release the android hardware buffer. AHardwareBuffer_release(ah_buffer); } ����������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_display_manager-test.cpp���������������������������������������������������0100644 0000000 0000000 00000075012 13756501735 021325� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using android::pdx::ErrorStatus; using android::pdx::Status; namespace android { namespace dvr { namespace { using ::testing::Test; DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE; return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int32_t value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32; attribute.value.int32_value = value; return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int64_t value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64; attribute.value.int64_value = value; return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, bool value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL; attribute.value.bool_value = value; return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, float value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT; attribute.value.float_value = value; return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, const std::array& value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2; std::copy(value.begin(), value.end(), attribute.value.float2_value); return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, const std::array& value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3; std::copy(value.begin(), value.end(), attribute.value.float3_value); return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, const std::array& value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4; std::copy(value.begin(), value.end(), attribute.value.float4_value); return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, const std::array& value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8; std::copy(value.begin(), value.end(), attribute.value.float8_value); return attribute; } DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, const std::array& value) { DvrSurfaceAttribute attribute; attribute.key = key; attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16; std::copy(value.begin(), value.end(), attribute.value.float16_value); return attribute; } Status CreateApplicationSurface(bool visible = false, int32_t z_order = 0) { DvrSurface* surface = nullptr; DvrSurfaceAttribute attributes[] = { MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order), MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)}; const int ret = dvrSurfaceCreate( attributes, std::extent::value, &surface); if (ret < 0) return ErrorStatus(-ret); else return {UniqueDvrSurface(surface)}; } Status CreateSurfaceQueue( const UniqueDvrSurface& surface, uint32_t width, uint32_t height, uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size) { DvrWriteBufferQueue* queue; const int ret = dvrSurfaceCreateWriteBufferQueue( surface.get(), width, height, format, layer_count, usage, capacity, metadata_size, &queue); if (ret < 0) return ErrorStatus(-ret); else return {UniqueDvrWriteBufferQueue(queue)}; } Status> GetConfigData(int config_type) { uint8_t* data = nullptr; size_t data_size = 0; int error = dvrConfigurationDataGet(config_type, &data, &data_size); if (error < 0) { return ErrorStatus(-error); } if (!data || data_size == 0) { return ErrorStatus(EINVAL); } std::vector data_result(data, data + data_size); dvrConfigurationDataDestroy(data); std::string s(data, data + data_size); return {std::move(data_result)}; } class TestDisplayManager { public: TestDisplayManager(UniqueDvrDisplayManager display_manager, UniqueDvrSurfaceState surface_state) : display_manager_(std::move(display_manager)), surface_state_(std::move(surface_state)) { const int fd = dvrDisplayManagerGetEventFd(display_manager_.get()); LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd); display_manager_event_fd_ = fd; } Status GetReadBufferQueue(int surface_id, int queue_id) { DvrReadBufferQueue* queue; const int ret = dvrDisplayManagerGetReadBufferQueue( display_manager_.get(), surface_id, queue_id, &queue); if (ret < 0) return ErrorStatus(-ret); else return {UniqueDvrReadBufferQueue(queue)}; } Status UpdateSurfaceState() { const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(), surface_state_.get()); if (ret < 0) return ErrorStatus(-ret); else return {}; } enum : int { kTimeoutMs = 10000 }; // 10s Status WaitForUpdate(int timeout_ms = kTimeoutMs) { if (display_manager_event_fd_ < 0) return ErrorStatus(-display_manager_event_fd_); pollfd pfd = {display_manager_event_fd_, POLLIN, 0}; const int count = poll(&pfd, 1, timeout_ms); if (count < 0) return ErrorStatus(errno); else if (count == 0) return ErrorStatus(ETIMEDOUT); int events; const int ret = dvrDisplayManagerTranslateEpollEventMask( display_manager_.get(), pfd.revents, &events); if (ret < 0) return ErrorStatus(-ret); else if (events & POLLIN) return UpdateSurfaceState(); else return ErrorStatus(EPROTO); } Status GetSurfaceCount() { size_t count = 0; const int ret = dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count); if (ret < 0) return ErrorStatus(-ret); else return {count}; } Status GetUpdateFlags(size_t surface_index) { DvrSurfaceUpdateFlags update_flags; const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(), surface_index, &update_flags); if (ret < 0) return ErrorStatus(-ret); else return {update_flags}; } Status GetSurfaceId(size_t surface_index) { int surface_id; const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(), surface_index, &surface_id); if (ret < 0) return ErrorStatus(-ret); else return {surface_id}; } Status GetProcessId(size_t surface_index) { int process_id; const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(), surface_index, &process_id); if (ret < 0) return ErrorStatus(-ret); else return {process_id}; } Status> GetAttributes(size_t surface_index) { std::vector attributes; size_t count = 0; const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(), surface_index, &count); if (ret < 0) return ErrorStatus(-ret); attributes.resize(count); const ssize_t return_count = dvrSurfaceStateGetAttributes( surface_state_.get(), surface_index, attributes.data(), count); if (return_count < 0) return ErrorStatus(-return_count); attributes.resize(return_count); return {std::move(attributes)}; } Status> GetQueueIds(size_t surface_index) { std::vector queue_ids; size_t count = 0; const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(), surface_index, &count); if (ret < 0) return ErrorStatus(-ret); if (count > 0) { queue_ids.resize(count); const ssize_t return_count = dvrSurfaceStateGetQueueIds( surface_state_.get(), surface_index, queue_ids.data(), count); if (return_count < 0) return ErrorStatus(-return_count); queue_ids.resize(return_count); } return {std::move(queue_ids)}; } private: UniqueDvrDisplayManager display_manager_; UniqueDvrSurfaceState surface_state_; // Owned by object in display_manager_, do not explicitly close. int display_manager_event_fd_; TestDisplayManager(const TestDisplayManager&) = delete; void operator=(const TestDisplayManager&) = delete; }; class DvrDisplayManagerTest : public Test { protected: void SetUp() override { // dvr display manager test doesn't apply to standalone vr devices because // tests cannot create display manager client on these devices. if (property_get_bool("ro.boot.vr", false)) { GTEST_SKIP() << "All tests in DvrDisplayManagerTest test case are skipped " "because the device boot to VR."; } int ret; DvrDisplayManager* display_manager; DvrSurfaceState* surface_state; ret = dvrDisplayManagerCreate(&display_manager); ASSERT_EQ(0, ret) << "Failed to create display manager client"; ASSERT_NE(nullptr, display_manager); ret = dvrSurfaceStateCreate(&surface_state); ASSERT_EQ(0, ret) << "Failed to create surface state object"; ASSERT_NE(nullptr, surface_state); manager_.reset( new TestDisplayManager(UniqueDvrDisplayManager(display_manager), UniqueDvrSurfaceState(surface_state))); } void TearDown() override {} std::unique_ptr manager_; }; // TODO(eieio): Consider moving these somewhere more central because they are // broadly useful. template testing::AssertionResult StatusOk(const char* status_expression, const Status& status) { if (!status.ok()) { return testing::AssertionFailure() << "(" << status_expression << ") expected to indicate success but actually contains error (" << status.error() << ")"; } else { return testing::AssertionSuccess(); } } template testing::AssertionResult StatusError(const char* status_expression, const Status& status) { if (status.ok()) { return testing::AssertionFailure() << "(" << status_expression << ") expected to indicate error but instead indicates success."; } else { return testing::AssertionSuccess(); } } template testing::AssertionResult StatusHasError(const char* status_expression, const char* /*error_code_expression*/, const Status& status, int error_code) { if (status.ok()) { return StatusError(status_expression, status); } else if (status.error() != error_code) { return testing::AssertionFailure() << "(" << status_expression << ") expected to indicate error (" << error_code << ") but actually indicates error (" << status.error() << ")"; } else { return testing::AssertionSuccess(); } } template testing::AssertionResult StatusHasValue(const char* status_expression, const char* /*value_expression*/, const Status& status, const U& value) { if (!status.ok()) { return StatusOk(status_expression, status); } else if (status.get() != value) { return testing::AssertionFailure() << "(" << status_expression << ") expected to contain value (" << testing::PrintToString(value) << ") but actually contains value (" << testing::PrintToString(status.get()) << ")"; } else { return testing::AssertionSuccess(); } } template testing::AssertionResult StatusPred(const char* status_expression, const char* pred_expression, const Status& status, Op pred) { if (!status.ok()) { return StatusOk(status_expression, status); } else if (!pred(status.get())) { return testing::AssertionFailure() << status_expression << " value (" << testing::PrintToString(status.get()) << ") failed to pass predicate " << pred_expression; } else { return testing::AssertionSuccess(); } } #define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status) #define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status) #define ASSERT_STATUS_ERROR_VALUE(value, status) \ ASSERT_PRED_FORMAT2(StatusHasError, status, value) #define ASSERT_STATUS_EQ(value, status) \ ASSERT_PRED_FORMAT2(StatusHasValue, status, value) #define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status) #define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status) #define EXPECT_STATUS_ERROR_VALUE(value, status) \ EXPECT_PRED_FORMAT2(StatusHasError, status, value) #define EXPECT_STATUS_EQ(value, status) \ EXPECT_PRED_FORMAT2(StatusHasValue, status, value) #define EXPECT_STATUS_PRED(pred, status) \ EXPECT_PRED_FORMAT2(StatusPred, status, pred) #if 0 // Verify utility predicate/macro functionality. This section is commented out // because it is designed to fail in some cases to validate the helpers. TEST_F(Test, ExpectVoid) { Status status_error{ErrorStatus{EINVAL}}; Status status_ok{}; EXPECT_STATUS_ERROR(status_error); EXPECT_STATUS_ERROR(status_ok); EXPECT_STATUS_OK(status_error); EXPECT_STATUS_OK(status_ok); EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error); EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error); EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok); EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok); } TEST_F(Test, ExpectInt) { Status status_error{ErrorStatus{EINVAL}}; Status status_ok{10}; EXPECT_STATUS_ERROR(status_error); EXPECT_STATUS_ERROR(status_ok); EXPECT_STATUS_OK(status_error); EXPECT_STATUS_OK(status_ok); EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error); EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error); EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok); EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok); EXPECT_STATUS_EQ(10, status_error); EXPECT_STATUS_EQ(20, status_error); EXPECT_STATUS_EQ(10, status_ok); EXPECT_STATUS_EQ(20, status_ok); auto pred1 = [](const auto& value) { return value < 15; }; auto pred2 = [](const auto& value) { return value > 5; }; auto pred3 = [](const auto& value) { return value > 15; }; auto pred4 = [](const auto& value) { return value < 5; }; EXPECT_STATUS_PRED(pred1, status_error); EXPECT_STATUS_PRED(pred2, status_error); EXPECT_STATUS_PRED(pred3, status_error); EXPECT_STATUS_PRED(pred4, status_error); EXPECT_STATUS_PRED(pred1, status_ok); EXPECT_STATUS_PRED(pred2, status_ok); EXPECT_STATUS_PRED(pred3, status_ok); EXPECT_STATUS_PRED(pred4, status_ok); } #endif TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { // Get surface state and verify there are no surfaces. ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); // Get flags for invalid surface index. EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0)); // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); UniqueDvrSurface surface = surface_status.take(); ASSERT_NE(nullptr, surface.get()); const int surface_id = dvrSurfaceGetId(surface.get()); ASSERT_GE(surface_id, 0); // Now there should be one new surface. ASSERT_STATUS_OK(manager_->WaitForUpdate()); EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount()); // Verify the new surface flag is set. auto check_flags = [](const auto& value) { return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE; }; EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); // Verify the surface id matches. EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0)); // Verify the owning process of the surface. EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0)); surface.reset(); ASSERT_STATUS_OK(manager_->WaitForUpdate()); EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount()); } TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) { // Get surface state and verify there are no surfaces. ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); // Get attributes for an invalid surface index. EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0)); const bool kInitialVisibility = true; const int32_t kInitialZOrder = 10; auto surface_status = CreateApplicationSurface(kInitialVisibility, kInitialZOrder); ASSERT_STATUS_OK(surface_status); auto surface = surface_status.take(); ASSERT_NE(nullptr, surface.get()); ASSERT_STATUS_OK(manager_->WaitForUpdate()); ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); // Check the initial attribute values. auto attribute_status = manager_->GetAttributes(0); ASSERT_STATUS_OK(attribute_status); auto attributes = attribute_status.take(); EXPECT_GE(attributes.size(), 2u); std::set actual_keys; std::set expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE}; // Collect all the keys in attributes that match the expected keys. auto compare_keys = [](const auto& attributes, const auto& expected_keys) { std::set keys; for (const auto& attribute : attributes) { if (expected_keys.find(attribute.key) != expected_keys.end()) keys.emplace(attribute.key); } return keys; }; // If the sets match then attributes contained at least the expected keys, // even if other keys were also present. actual_keys = compare_keys(attributes, expected_keys); EXPECT_EQ(expected_keys, actual_keys); std::vector attributes_to_set = { MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, 0)}; // Test invalid args. EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(nullptr, attributes_to_set.data(), attributes_to_set.size())); EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(surface.get(), nullptr, attributes_to_set.size())); // Test attribute change events. ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), attributes_to_set.size())); ASSERT_STATUS_OK(manager_->WaitForUpdate()); // Verify the attributes changed flag is set. auto check_flags = [](const auto& value) { return value & DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED; }; EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); attribute_status = manager_->GetAttributes(0); ASSERT_STATUS_OK(attribute_status); attributes = attribute_status.take(); EXPECT_GE(attributes.size(), 2u); expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE}; actual_keys.clear(); actual_keys = compare_keys(attributes, expected_keys); EXPECT_EQ(expected_keys, actual_keys); // Test setting and then deleting an attribute. const DvrSurfaceAttributeKey kUserKey = 1; attributes_to_set = {MakeAttribute(kUserKey, 1024)}; ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), attributes_to_set.size())); ASSERT_STATUS_OK(manager_->WaitForUpdate()); EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); attribute_status = manager_->GetAttributes(0); ASSERT_STATUS_OK(attribute_status); attributes = attribute_status.take(); EXPECT_GE(attributes.size(), 2u); expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE, kUserKey}; actual_keys.clear(); actual_keys = compare_keys(attributes, expected_keys); EXPECT_EQ(expected_keys, actual_keys); // Delete the attribute. attributes_to_set = {MakeAttribute(kUserKey, nullptr)}; ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), attributes_to_set.size())); ASSERT_STATUS_OK(manager_->WaitForUpdate()); EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); attribute_status = manager_->GetAttributes(0); ASSERT_STATUS_OK(attribute_status); attributes = attribute_status.take(); EXPECT_GE(attributes.size(), 2u); expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE, kUserKey}; actual_keys.clear(); actual_keys = compare_keys(attributes, expected_keys); EXPECT_NE(expected_keys, actual_keys); // Test deleting a reserved attribute. attributes_to_set = {MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, nullptr)}; EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), attributes_to_set.size())); // Failed attribute operations should not trigger update events. const int kTimeoutMs = 100; // 0.1s EXPECT_STATUS_ERROR_VALUE(ETIMEDOUT, manager_->WaitForUpdate(kTimeoutMs)); attribute_status = manager_->GetAttributes(0); ASSERT_STATUS_OK(attribute_status); attributes = attribute_status.take(); EXPECT_GE(attributes.size(), 2u); expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE}; actual_keys.clear(); actual_keys = compare_keys(attributes, expected_keys); EXPECT_EQ(expected_keys, actual_keys); } TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) { // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); UniqueDvrSurface surface = surface_status.take(); ASSERT_NE(nullptr, surface.get()); enum : std::int32_t { kInt32Key = 1, kInt64Key, kBoolKey, kFloatKey, kFloat2Key, kFloat3Key, kFloat4Key, kFloat8Key, kFloat16Key, }; const std::vector attributes_to_set = { MakeAttribute(kInt32Key, int32_t{0}), MakeAttribute(kInt64Key, int64_t{0}), MakeAttribute(kBoolKey, false), MakeAttribute(kFloatKey, 0.0f), MakeAttribute(kFloat2Key, std::array{{1.0f, 2.0f}}), MakeAttribute(kFloat3Key, std::array{{3.0f, 4.0f, 5.0f}}), MakeAttribute(kFloat4Key, std::array{{6.0f, 7.0f, 8.0f, 9.0f}}), MakeAttribute(kFloat8Key, std::array{{10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f}}), MakeAttribute(kFloat16Key, std::array{ {18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f, 30.0f, 31.0f, 32.0f, 33.0f}})}; EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(), attributes_to_set.size())); ASSERT_STATUS_OK(manager_->WaitForUpdate()); auto attribute_status = manager_->GetAttributes(0); ASSERT_STATUS_OK(attribute_status); auto attributes = attribute_status.take(); EXPECT_GE(attributes.size(), attributes_to_set.size()); auto HasAttribute = [](const auto& attributes, DvrSurfaceAttributeKey key) -> bool { for (const auto& attribute : attributes) { if (attribute.key == key) return true; } return false; }; auto AttributeType = [](const auto& attributes, DvrSurfaceAttributeKey key) -> DvrSurfaceAttributeType { for (const auto& attribute : attributes) { if (attribute.key == key) return attribute.value.type; } return DVR_SURFACE_ATTRIBUTE_TYPE_NONE; }; ASSERT_TRUE(HasAttribute(attributes, kInt32Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT32, AttributeType(attributes, kInt32Key)); ASSERT_TRUE(HasAttribute(attributes, kInt64Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT64, AttributeType(attributes, kInt64Key)); ASSERT_TRUE(HasAttribute(attributes, kBoolKey)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_BOOL, AttributeType(attributes, kBoolKey)); ASSERT_TRUE(HasAttribute(attributes, kFloatKey)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT, AttributeType(attributes, kFloatKey)); ASSERT_TRUE(HasAttribute(attributes, kFloat2Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2, AttributeType(attributes, kFloat2Key)); ASSERT_TRUE(HasAttribute(attributes, kFloat3Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3, AttributeType(attributes, kFloat3Key)); ASSERT_TRUE(HasAttribute(attributes, kFloat4Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4, AttributeType(attributes, kFloat4Key)); ASSERT_TRUE(HasAttribute(attributes, kFloat8Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8, AttributeType(attributes, kFloat8Key)); ASSERT_TRUE(HasAttribute(attributes, kFloat16Key)); EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16, AttributeType(attributes, kFloat16Key)); } TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); UniqueDvrSurface surface = surface_status.take(); ASSERT_NE(nullptr, surface.get()); const int surface_id = dvrSurfaceGetId(surface.get()); ASSERT_GE(surface_id, 0); // Get surface state and verify there is one surface. ASSERT_STATUS_OK(manager_->WaitForUpdate()); ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); // Verify there are no queues for the surface recorded in the state // snapshot. EXPECT_STATUS_EQ(std::vector{}, manager_->GetQueueIds(0)); // Create a new queue in the surface. auto write_queue_status = CreateSurfaceQueue( surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0); ASSERT_STATUS_OK(write_queue_status); UniqueDvrWriteBufferQueue write_queue = write_queue_status.take(); ASSERT_NE(nullptr, write_queue.get()); const int queue_id = dvrWriteBufferQueueGetId(write_queue.get()); ASSERT_GE(queue_id, 0); // Update surface state. ASSERT_STATUS_OK(manager_->WaitForUpdate()); ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); // Verify the buffers changed flag is set. auto check_flags = [](const auto& value) { return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED; }; EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); auto queue_ids_status = manager_->GetQueueIds(0); ASSERT_STATUS_OK(queue_ids_status); auto queue_ids = queue_ids_status.take(); ASSERT_EQ(1u, queue_ids.size()); EXPECT_EQ(queue_id, queue_ids[0]); auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id); ASSERT_STATUS_OK(read_queue_status); UniqueDvrReadBufferQueue read_queue = read_queue_status.take(); ASSERT_NE(nullptr, read_queue.get()); EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get())); write_queue.reset(); // Verify that destroying the queue generates a surface update event. ASSERT_STATUS_OK(manager_->WaitForUpdate()); ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); // Verify that the buffers changed flag is set. EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0)); // Verify that the queue ids reflect the change. queue_ids_status = manager_->GetQueueIds(0); ASSERT_STATUS_OK(queue_ids_status); queue_ids = queue_ids_status.take(); ASSERT_EQ(0u, queue_ids.size()); } TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) { // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); UniqueDvrSurface surface = surface_status.take(); ASSERT_NE(nullptr, surface.get()); // Get surface state and verify there is one surface. ASSERT_STATUS_OK(manager_->WaitForUpdate()); ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount()); // Create a new queue in the surface. const uint32_t kLayerCount = 3; auto write_queue_status = CreateSurfaceQueue( surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0); ASSERT_STATUS_OK(write_queue_status); UniqueDvrWriteBufferQueue write_queue = write_queue_status.take(); ASSERT_NE(nullptr, write_queue.get()); DvrWriteBuffer* buffer = nullptr; DvrNativeBufferMetadata metadata; int fence_fd = -1; int error = dvrWriteBufferQueueGainBuffer(write_queue.get(), /*timeout=*/1000, &buffer, &metadata, &fence_fd); ASSERT_EQ(0, error); AHardwareBuffer* hardware_buffer = nullptr; error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer); ASSERT_EQ(0, error); AHardwareBuffer_Desc desc = {}; AHardwareBuffer_describe(hardware_buffer, &desc); ASSERT_EQ(kLayerCount, desc.layers); AHardwareBuffer_release(hardware_buffer); dvrWriteBufferDestroy(buffer); } TEST_F(Test, ConfigurationData) { // TODO(hendrikw): Move this test and GetConfigData helper function out of the // display manager tests. auto data1 = GetConfigData(-1); ASSERT_STATUS_ERROR(data1); const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics"; // This should be run on devices with and without built in metrics. bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty(); auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS); if (has_metric) { ASSERT_STATUS_OK(data2); ASSERT_NE(0u, data2.get().size()); } else { ASSERT_STATUS_ERROR(data2); } } } // namespace } // namespace dvr } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_named_buffer-test.cpp������������������������������������������������������0100644 0000000 0000000 00000021767 13756501735 020613� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include namespace android { namespace dvr { namespace { TEST(DvrGlobalBufferTest, TestGlobalBuffersSameName) { const DvrGlobalBufferKey buffer_key = 101; DvrBuffer* buffer1 = nullptr; int ret1 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer1); ASSERT_EQ(0, ret1); ASSERT_NE(nullptr, buffer1); DvrBuffer* buffer2 = nullptr; int ret2 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer2); ASSERT_EQ(0, ret2); ASSERT_NE(nullptr, buffer2); AHardwareBuffer* hardware_buffer1 = nullptr; int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1); ASSERT_EQ(0, e1); ASSERT_NE(nullptr, hardware_buffer1); AHardwareBuffer* hardware_buffer2 = nullptr; int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2); ASSERT_EQ(0, e2); ASSERT_NE(nullptr, hardware_buffer2); AHardwareBuffer_Desc desc1 = {}; AHardwareBuffer_describe(hardware_buffer1, &desc1); AHardwareBuffer_Desc desc2 = {}; AHardwareBuffer_describe(hardware_buffer2, &desc2); ASSERT_EQ(desc1.width, 10u); ASSERT_EQ(desc1.height, 1u); ASSERT_EQ(desc1.layers, 1u); ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB); ASSERT_EQ(desc1.usage, 0u); ASSERT_EQ(desc2.width, 10u); ASSERT_EQ(desc2.height, 1u); ASSERT_EQ(desc2.layers, 1u); ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB); ASSERT_EQ(desc2.usage, 0u); dvrBufferDestroy(buffer1); dvrBufferDestroy(buffer2); DvrBuffer* buffer3 = nullptr; int e3 = dvrGetGlobalBuffer(buffer_key, &buffer3); ASSERT_NE(nullptr, buffer3); ASSERT_EQ(0, e3); AHardwareBuffer* hardware_buffer3 = nullptr; int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3); ASSERT_EQ(0, e4); ASSERT_NE(nullptr, hardware_buffer3); AHardwareBuffer_Desc desc3 = {}; AHardwareBuffer_describe(hardware_buffer3, &desc3); ASSERT_EQ(desc3.width, 10u); ASSERT_EQ(desc3.height, 1u); ASSERT_EQ(desc3.layers, 1u); ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB); ASSERT_EQ(desc3.usage, 0u); dvrBufferDestroy(buffer3); AHardwareBuffer_release(hardware_buffer1); AHardwareBuffer_release(hardware_buffer2); AHardwareBuffer_release(hardware_buffer3); } TEST(DvrGlobalBufferTest, TestMultipleGlobalBuffers) { const DvrGlobalBufferKey buffer_key1 = 102; const DvrGlobalBufferKey buffer_key2 = 103; DvrBuffer* setup_buffer1 = nullptr; int ret1 = dvrSetupGlobalBuffer(buffer_key1, 10, 0, &setup_buffer1); ASSERT_EQ(0, ret1); ASSERT_NE(nullptr, setup_buffer1); dvrBufferDestroy(setup_buffer1); DvrBuffer* setup_buffer2 = nullptr; int ret2 = dvrSetupGlobalBuffer(buffer_key2, 10, 0, &setup_buffer2); ASSERT_EQ(0, ret2); ASSERT_NE(nullptr, setup_buffer2); dvrBufferDestroy(setup_buffer2); DvrBuffer* buffer1 = nullptr; int e1 = dvrGetGlobalBuffer(buffer_key1, &buffer1); ASSERT_NE(nullptr, buffer1); ASSERT_EQ(0, e1); dvrBufferDestroy(buffer1); DvrBuffer* buffer2 = nullptr; int e2 = dvrGetGlobalBuffer(buffer_key2, &buffer2); ASSERT_NE(nullptr, buffer2); ASSERT_EQ(0, e2); dvrBufferDestroy(buffer2); } TEST(DvrGlobalBufferTest, TestGlobalBufferUsage) { const DvrGlobalBufferKey buffer_key = 100; // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different. // If all is good, when we get the AHardwareBuffer, it should be converted // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE; DvrBuffer* setup_buffer = nullptr; int e1 = dvrSetupGlobalBuffer(buffer_key, 10, usage, &setup_buffer); ASSERT_NE(nullptr, setup_buffer); ASSERT_EQ(0, e1); AHardwareBuffer* hardware_buffer = nullptr; int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); ASSERT_EQ(0, e2); ASSERT_NE(nullptr, hardware_buffer); AHardwareBuffer_Desc desc = {}; AHardwareBuffer_describe(hardware_buffer, &desc); ASSERT_EQ(usage, desc.usage); dvrBufferDestroy(setup_buffer); AHardwareBuffer_release(hardware_buffer); } TEST(DvrGlobalBufferTest, TestGlobalBufferCarriesData) { const DvrGlobalBufferKey buffer_name = 110; uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; constexpr size_t size = 1024 * sizeof(uint64_t); constexpr uint64_t value = 0x123456787654321; { // Allocate some data and set it to something. DvrBuffer* setup_buffer = nullptr; int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer); ASSERT_NE(nullptr, setup_buffer); ASSERT_EQ(0, e1); AHardwareBuffer* hardware_buffer = nullptr; int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); ASSERT_EQ(0, e2); ASSERT_NE(nullptr, hardware_buffer); void* buffer; int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer); ASSERT_EQ(0, e3); ASSERT_NE(nullptr, buffer); // Verify that the buffer pointer is at least 16 byte aligned. ASSERT_EQ(0U, reinterpret_cast(buffer) & (16 - 1)); uint64_t* data = static_cast(buffer); constexpr size_t num_values = size / sizeof(uint64_t); for (size_t i = 0; i < num_values; ++i) { data[i] = value; } int32_t fence = -1; int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence); ASSERT_EQ(0, e4); dvrBufferDestroy(setup_buffer); AHardwareBuffer_release(hardware_buffer); } { // Get the buffer and check that all the data is still present. DvrBuffer* setup_buffer = nullptr; int e1 = dvrGetGlobalBuffer(buffer_name, &setup_buffer); ASSERT_NE(nullptr, setup_buffer); ASSERT_EQ(0, e1); AHardwareBuffer* hardware_buffer = nullptr; int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); ASSERT_EQ(0, e2); ASSERT_NE(nullptr, hardware_buffer); void* buffer; int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer); ASSERT_EQ(0, e3); ASSERT_NE(nullptr, buffer); // Verify that the buffer pointer is at least 16 byte aligned. ASSERT_EQ(0U, reinterpret_cast(buffer) & (16 - 1)); uint64_t* data = static_cast(buffer); constexpr size_t num_values = size / sizeof(uint64_t); bool is_equal = true; for (size_t i = 0; i < num_values; ++i) { is_equal &= (data[i] == value); } ASSERT_TRUE(is_equal); int32_t fence = -1; int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence); ASSERT_EQ(0, e4); dvrBufferDestroy(setup_buffer); AHardwareBuffer_release(hardware_buffer); } } TEST(DvrGlobalBufferTest, TestGlobalBufferZeroed) { const DvrGlobalBufferKey buffer_name = 120; // Allocate 1MB and check that it is all zeros. uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; constexpr size_t size = 1024 * 1024; DvrBuffer* setup_buffer = nullptr; int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer); ASSERT_NE(nullptr, setup_buffer); ASSERT_EQ(0, e1); AHardwareBuffer* hardware_buffer = nullptr; int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer); ASSERT_EQ(0, e2); ASSERT_NE(nullptr, hardware_buffer); void* buffer; int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer); ASSERT_EQ(0, e3); ASSERT_NE(nullptr, buffer); // Verify that the buffer pointer is at least 16 byte aligned. ASSERT_EQ(0U, reinterpret_cast(buffer) & (16 - 1)); uint64_t* data = static_cast(buffer); constexpr size_t num_values = size / sizeof(uint64_t); uint64_t zero = 0; for (size_t i = 0; i < num_values; ++i) { zero |= data[i]; } ASSERT_EQ(0U, zero); int32_t fence = -1; int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence); ASSERT_EQ(0, e4); dvrBufferDestroy(setup_buffer); AHardwareBuffer_release(hardware_buffer); } TEST(DvrGlobalBufferTest, TestVrflingerConfigBuffer) { const DvrGlobalBufferKey buffer_name = DvrGlobalBuffers::kVrFlingerConfigBufferKey; // First delete any existing buffer so we can test the failure case. dvrDeleteGlobalBuffer(buffer_name); const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY; size_t correct_size = DvrConfigRing::MemorySize(); size_t wrong_size = DvrConfigRing::MemorySize(0); // Setup an invalid config buffer (too small) and assert that it fails. DvrBuffer* setup_buffer = nullptr; int e1 = dvrSetupGlobalBuffer(buffer_name, wrong_size, usage, &setup_buffer); ASSERT_EQ(nullptr, setup_buffer); ASSERT_GT(0, e1); // Setup a correct config buffer. int e2 = dvrSetupGlobalBuffer(buffer_name, correct_size, usage, &setup_buffer); ASSERT_NE(nullptr, setup_buffer); ASSERT_EQ(0, e2); dvrBufferDestroy(setup_buffer); } } // namespace } // namespace dvr } // namespace android ���������libs/vr/libdvr/tests/dvr_tracking-test.cpp����������������������������������������������������������0100644 0000000 0000000 00000006642 13756501735 017773� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include "dvr_api_test.h" namespace { class DvrTrackingTest : public DvrApiTest {}; #if DVR_TRACKING_IMPLEMENTED TEST_F(DvrTrackingTest, Implemented) { ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); ASSERT_TRUE(api_.TrackingCameraStart != nullptr); ASSERT_TRUE(api_.TrackingCameraStop != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); } TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) { int ret; ret = api_.TrackingCameraCreate(nullptr); EXPECT_EQ(ret, -EINVAL); DvrTrackingCamera* camera = reinterpret_cast(42); ret = api_.TrackingCameraCreate(&camera); EXPECT_EQ(ret, -EINVAL); } TEST_F(DvrTrackingTest, CameraCreateDestroy) { DvrTrackingCamera* camera = nullptr; int ret = api_.TrackingCameraCreate(&camera); EXPECT_EQ(ret, 0); ASSERT_TRUE(camera != nullptr); api_.TrackingCameraDestroy(camera); } TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) { int ret; ret = api_.TrackingFeatureExtractorCreate(nullptr); EXPECT_EQ(ret, -EINVAL); DvrTrackingFeatureExtractor* camera = reinterpret_cast(42); ret = api_.TrackingFeatureExtractorCreate(&camera); EXPECT_EQ(ret, -EINVAL); } TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) { DvrTrackingFeatureExtractor* camera = nullptr; int ret = api_.TrackingFeatureExtractorCreate(&camera); EXPECT_EQ(ret, 0); ASSERT_TRUE(camera != nullptr); api_.TrackingFeatureExtractorDestroy(camera); } #else // !DVR_TRACKING_IMPLEMENTED TEST_F(DvrTrackingTest, NotImplemented) { ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr); ASSERT_TRUE(api_.TrackingCameraStart != nullptr); ASSERT_TRUE(api_.TrackingCameraStop != nullptr); EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS); ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr, nullptr, nullptr), -ENOSYS); ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr); ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr); ASSERT_TRUE(api_.TrackingSensorsStart != nullptr); ASSERT_TRUE(api_.TrackingSensorsStop != nullptr); EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS); EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS); } #endif // DVR_TRACKING_IMPLEMENTED } // namespace ����������������������������������������������������������������������������������������������libs/vr/libdvrcommon/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 013677� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/Android.bp���������������������������������������������������������������������0100644 0000000 0000000 00000003160 13756501735 015577� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. localIncludeFiles = [ "include", ] sharedLibraries = [ "libbase", "libcutils", "liblog", "libutils", "libEGL", "libGLESv2", "libui", "libgui", "libhardware", "libpdx_default_transport", ] staticLibraries = ["libbroadcastring"] headerLibraries = [ "libeigen", ] cc_library { local_include_dirs: localIncludeFiles, cflags: [ "-DLOG_TAG=\"libdvrcommon\"", "-DTRACE=0", "-Wall", "-Werror", ], export_include_dirs: localIncludeFiles, header_libs: headerLibraries, export_header_lib_headers: headerLibraries, name: "libdvrcommon", } testFiles = [ "tests/numeric_test.cpp", "tests/pose_test.cpp", ] cc_test { name: "libdvrcommon_test", srcs: testFiles, cflags: [ "-Wall", "-Werror", "-Wno-unused-parameter", ], shared_libs: sharedLibraries, static_libs: [ "libgmock_main", "libgmock", "libgtest", "libdvrcommon", ] + staticLibraries, } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/�����������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015322� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/���������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 016774� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/�����������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 017567� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/benchmark.h������������������������������������������������0100644 0000000 0000000 00000006205 13756501735 021672� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BENCHMARK_H_ #define ANDROID_DVR_BENCHMARK_H_ #include #include #include #include // Set benchmark traces, using Android systrace. // // The simplest one-parameter version of btrace automatically sets the // timestamp with the system clock. The other versions can optionally set the // timestamp manually, or pass additional data to be written to the log line. // // Example: // Btrace("Start execution"); // ... code to benchmark ... // Btrace("End execution"); // // Use compute_benchmarks.py // with the trace path "Start execution,End execution", // to report the elapsed time between the two calls. // // Btrace will either output to standard atrace, or to a file if specified. // The versions BtraceData also allow an int64_t to be included in the trace. // Btrace without data payload. static inline void Btrace(const char* name, int64_t nanoseconds_monotonic); static inline void Btrace(const char* name); static inline void Btrace(FILE* file, const char* name, int64_t nanoseconds_monotonic); static inline void Btrace(FILE* file, const char* name); // Btrace with data payload. static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic, int64_t data); static inline void BtraceData(const char* name, int64_t data); static inline void BtraceData(FILE* file, const char* name, int64_t nanoseconds_monotonic, int64_t data); static inline void BtraceData(FILE* file, const char* name, int64_t data); static inline void Btrace(const char* name, int64_t nanoseconds_monotonic) { const int kLogMessageLength = 256; char log_message[kLogMessageLength]; snprintf(log_message, kLogMessageLength, "#btrace#%s", name); atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic); } static inline void Btrace(const char* name) { Btrace(name, android::dvr::GetSystemClockNs()); } static inline void Btrace(FILE* file, const char* name, int64_t nanoseconds_monotonic) { fprintf(file, "#btrace#%s|%" PRId64 "\n", name, nanoseconds_monotonic); } static inline void Btrace(FILE* file, const char* name) { Btrace(file, name, android::dvr::GetSystemClockNs()); } static inline void BtraceData(const char* name, int64_t nanoseconds_monotonic, int64_t data) { const int kLogMessageLength = 256; char log_message[kLogMessageLength]; snprintf(log_message, kLogMessageLength, "#btrace#%s|%" PRId64, name, data); atrace_int64(ATRACE_TAG_WEBVIEW, log_message, nanoseconds_monotonic); } static inline void BtraceData(const char* name, int64_t data) { BtraceData(name, android::dvr::GetSystemClockNs(), data); } static inline void BtraceData(FILE* file, const char* name, int64_t nanoseconds_monotonic, int64_t data) { fprintf(file, "#btrace#%s|%" PRId64 "|%" PRId64 "\n", name, data, nanoseconds_monotonic); } static inline void BtraceData(FILE* file, const char* name, int64_t data) { BtraceData(file, name, android::dvr::GetSystemClockNs(), data); } #endif // ANDROID_DVR_BENCHMARK_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/clock_ns.h�������������������������������������������������0100644 0000000 0000000 00000004520 13756501735 021531� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_CLOCK_NS_H_ #define ANDROID_DVR_CLOCK_NS_H_ #include #include namespace android { namespace dvr { constexpr int64_t kNanosPerSecond = 1000000000ll; // Returns the standard Dream OS monotonic system time that corresponds with all // timestamps found in Dream OS APIs. static inline timespec GetSystemClock() { timespec t; clock_gettime(CLOCK_MONOTONIC, &t); return t; } static inline timespec GetSystemClockRaw() { timespec t; clock_gettime(CLOCK_MONOTONIC_RAW, &t); return t; } static inline int64_t GetSystemClockNs() { timespec t = GetSystemClock(); int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec; return ns; } static inline int64_t GetSystemClockRawNs() { timespec t = GetSystemClockRaw(); int64_t ns = kNanosPerSecond * (int64_t)t.tv_sec + (int64_t)t.tv_nsec; return ns; } static inline double NsToSec(int64_t nanoseconds) { return nanoseconds / static_cast(kNanosPerSecond); } static inline double GetSystemClockSec() { return NsToSec(GetSystemClockNs()); } static inline double GetSystemClockMs() { return GetSystemClockSec() * 1000.0; } // Converts a nanosecond timestamp to a timespec. Based on the kernel function // of the same name. static inline timespec NsToTimespec(int64_t ns) { timespec t; int32_t remainder; t.tv_sec = ns / kNanosPerSecond; remainder = ns % kNanosPerSecond; if (remainder < 0) { t.tv_nsec--; remainder += kNanosPerSecond; } t.tv_nsec = remainder; return t; } // Timestamp comparison functions that handle wrapping values correctly. static inline bool TimestampLT(int64_t a, int64_t b) { return static_cast(static_cast(a) - static_cast(b)) < 0; } static inline bool TimestampLE(int64_t a, int64_t b) { return static_cast(static_cast(a) - static_cast(b)) <= 0; } static inline bool TimestampGT(int64_t a, int64_t b) { return static_cast(static_cast(a) - static_cast(b)) > 0; } static inline bool TimestampGE(int64_t a, int64_t b) { return static_cast(static_cast(a) - static_cast(b)) >= 0; } } // namespace dvr } // namespace android #endif // ANDROID_DVR_CLOCK_NS_H_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/debug.h����������������������������������������������������0100644 0000000 0000000 00000002451 13756501735 021025� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DEBUG_H_ #define ANDROID_DVR_DEBUG_H_ #include #include #include #ifndef NDEBUG #define CHECK_GL() \ do { \ const GLenum err = glGetError(); \ if (err != GL_NO_ERROR) { \ ALOGE("OpenGL error %d", err); \ } \ } while (0) #define CHECK_GL_FBO() \ do { \ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \ switch (status) { \ case GL_FRAMEBUFFER_COMPLETE: \ break; \ case GL_FRAMEBUFFER_UNSUPPORTED: \ ALOGE("GL_FRAMEBUFFER_UNSUPPORTED"); \ break; \ default: \ ALOGE("FBO user error: %d", status); \ break; \ } \ } while (0) #else #define CHECK_GL() #define CHECK_GL_FBO() #endif #endif // ANDROID_DVR_DEBUG_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/eigen.h����������������������������������������������������0100644 0000000 0000000 00000003250 13756501735 021024� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_EIGEN_H_ #define ANDROID_DVR_EIGEN_H_ #include #include namespace Eigen { // Eigen doesn't take advantage of C++ template typedefs, but we can template using Vector = Matrix; template using Vector2 = Vector; template using Vector3 = Vector; template using Vector4 = Vector; template using RowVector = Matrix; template using RowVector2 = RowVector; template using RowVector3 = RowVector; template using RowVector4 = RowVector; // In Eigen, the type you should be using for transformation matrices is the // `Transform` class, instead of a raw `Matrix`. // The `Projective` option means this will not make any assumptions about the // last row of the object, making this suitable for use as general OpenGL // projection matrices (which is the most common use-case). The one caveat // is that in order to apply this transformation to non-homogeneous vectors // (e.g., vec3), you must use the `.linear()` method to get the affine part of // the matrix. // // Example: // mat4 transform; // vec3 position; // vec3 transformed = transform.linear() * position; // // Note, the use of N-1 is because the parameter passed to Eigen is the ambient // dimension of the transformation, not the size of the matrix iself. // However graphics programmers sometimes get upset when they see a 3 next // to a matrix when they expect a 4, so I'm hoping this will avoid that. template using AffineMatrix = Transform; } // namespace Eigen #endif // ANDROID_DVR_EIGEN_H_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/field_of_view.h��������������������������������������������0100644 0000000 0000000 00000006273 13756501735 022546� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_ #define ANDROID_DVR_FIELD_OF_VIEW_H_ #include #include namespace android { namespace dvr { // Encapsulates a generalized, asymmetric field of view with four half angles. // Each half angle denotes the angle between the corresponding frustum plane. // Together with a near and far plane, a FieldOfView forms the frustum of an // off-axis perspective projection. class FieldOfView { public: // The default constructor sets an angle of 0 (in any unit) for all four // half-angles. FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {} // Constructs a FieldOfView from four angles. FieldOfView(float left, float right, float bottom, float top) : left_(left), right_(right), bottom_(bottom), top_(top) {} explicit FieldOfView(const float* fov) : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {} // Accessors for all four half-angles. float GetLeft() const { return left_; } float GetRight() const { return right_; } float GetBottom() const { return bottom_; } float GetTop() const { return top_; } // Setters for all four half-angles. void SetLeft(float left) { left_ = left; } void SetRight(float right) { right_ = right; } void SetBottom(float bottom) { bottom_ = bottom; } void SetTop(float top) { top_ = top; } Eigen::AffineMatrix GetProjectionMatrix(float z_near, float z_far) const { float x_left = -std::tan(left_) * z_near; float x_right = std::tan(right_) * z_near; float y_bottom = -std::tan(bottom_) * z_near; float y_top = std::tan(top_) * z_near; float zero = 0.0f; if (x_left == x_right || y_bottom == y_top || z_near == z_far || z_near <= zero || z_far <= zero) { return Eigen::AffineMatrix::Identity(); } float x = (2 * z_near) / (x_right - x_left); float y = (2 * z_near) / (y_top - y_bottom); float a = (x_right + x_left) / (x_right - x_left); float b = (y_top + y_bottom) / (y_top - y_bottom); float c = (z_near + z_far) / (z_near - z_far); float d = (2 * z_near * z_far) / (z_near - z_far); // Note: Eigen matrix initialization syntax is always 'column-major' // even if the storage is row-major. Or in other words, just write the // matrix like you'd see in a math textbook. Eigen::AffineMatrix result; result.matrix() << x, 0, a, 0, 0, y, b, 0, 0, 0, c, d, 0, 0, -1, 0; return result; } static FieldOfView FromProjectionMatrix( const Eigen::AffineMatrix& m) { // Compute tangents. float tan_vert_fov = 1.0f / m(1, 1); float tan_horz_fov = 1.0f / m(0, 0); float t = (m(1, 2) + 1.0f) * tan_vert_fov; float b = (m(1, 2) - 1.0f) * tan_vert_fov; float l = (m(0, 2) - 1.0f) * tan_horz_fov; float r = (m(0, 2) + 1.0f) * tan_horz_fov; return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b), std::atan(t)); } private: float left_; float right_; float bottom_; float top_; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_FIELD_OF_VIEW_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/log_helpers.h����������������������������������������������0100644 0000000 0000000 00000004320 13756501735 022237� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_LOG_HELPERS_H_ #define ANDROID_DVR_LOG_HELPERS_H_ #include #include #include #include namespace android { namespace dvr { template inline std::ostream& operator<<(std::ostream& out, const Eigen::Vector& vec) { return out << "vec2(" << vec.x() << ',' << vec.y() << ')'; } template inline std::ostream& operator<<(std::ostream& out, const Eigen::Vector& vec) { return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')'; } template inline std::ostream& operator<<(std::ostream& out, const Eigen::Vector& vec) { return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ',' << vec.w() << ')'; } template inline std::ostream& operator<<(std::ostream& out, const Eigen::AffineMatrix& mat) { out << std::setfill(' ') << std::setprecision(4) << std::fixed << std::showpos; out << "\nmat4["; out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " " << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3); out << "]\n ["; out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " " << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3); out << "]\n ["; out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " " << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3); out << "]\n ["; out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " " << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3); out << "]\n"; return out; } inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) { return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ',' << (fov.GetRight() * 180.0f / M_PI) << ',' << (fov.GetBottom() * 180.0f / M_PI) << ',' << (fov.GetTop() * 180.0f / M_PI) << ')'; } } // namespace dvr } // namespace android #endif // ANDROID_DVR_LOG_HELPERS_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/matrix_helpers.h�������������������������������������������0100644 0000000 0000000 00000001325 13756501735 022764� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_MATRIX_HELPERS_H_ #define ANDROID_DVR_MATRIX_HELPERS_H_ #include #include namespace android { namespace dvr { // A helper function for creating a mat4 directly. inline mat4 MakeMat4(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33) { Eigen::Matrix4f matrix; matrix << m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; return mat4(matrix); } } // namespace dvr } // namespace android #endif // ANDROID_DVR_LOG_HELPERS_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/numeric.h��������������������������������������������������0100644 0000000 0000000 00000011310 13756501735 021373� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_NUMERIC_H_ #define ANDROID_DVR_NUMERIC_H_ #include #include #include #include #include #include namespace android { namespace dvr { template static inline FT ToDeg(FT f) { return f * static_cast(180.0 / M_PI); } template static inline FT ToRad(FT f) { return f * static_cast(M_PI / 180.0); } // Adjusts `x` to the periodic range `[lo, hi]` (to normalize angle values // for example). template T NormalizePeriodicRange(T x, T lo, T hi) { T range_size = hi - lo; while (x < lo) { x += range_size; } while (x > hi) { x -= range_size; } return x; } // Normalizes a measurement in radians. // @param x the angle to be normalized // @param centre the point around which to normalize the range // @return the value of x, normalized to the range [centre - 180, centre + 180] template T NormalizeDegrees(T x, T centre = static_cast(180.0)) { return NormalizePeriodicRange(x, centre - static_cast(180.0), centre + static_cast(180.0)); } // Normalizes a measurement in radians. // @param x the angle to be normalized // @param centre the point around which to normalize the range // @return the value of x, normalized to the range // [centre - M_PI, centre + M_PI] // @remark the centre parameter is to make it possible to specify a different // periodic range. This is useful if you are planning on comparing two // angles close to 0 or M_PI, so that one might not accidentally end // up on the other side of the range template T NormalizeRadians(T x, T centre = static_cast(M_PI)) { return NormalizePeriodicRange(x, centre - static_cast(M_PI), centre + static_cast(M_PI)); } static inline vec2i Round(const vec2& v) { return vec2i(roundf(v.x()), roundf(v.y())); } static inline vec2i Scale(const vec2i& v, float scale) { return vec2i(roundf(static_cast(v.x()) * scale), roundf(static_cast(v.y()) * scale)); } // Re-maps `x` from `[lba,uba]` to `[lbb,ubb]`. template T ConvertRange(T x, T lba, T uba, T lbb, T ubb) { return (((x - lba) * (ubb - lbb)) / (uba - lba)) + lbb; } template static inline vec2 MapPoint(const vec2& pt, const R1& from, const R2& to) { vec2 normalized((pt - vec2(from.p1)).array() / vec2(from.GetSize()).array()); return (normalized * vec2(to.GetSize())) + vec2(to.p1); } template inline bool IsZero(const T& v, const T& tol = std::numeric_limits::epsilon()) { return std::abs(v) <= tol; } template inline bool IsEqual(const T& a, const T& b, const T& tol = std::numeric_limits::epsilon()) { return std::abs(b - a) <= tol; } template T Square(const T& x) { return x * x; } template T RandomInRange(T lo, T hi, typename std::enable_if::value>::type* = 0) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution distro(lo, hi); return distro(gen); } template T RandomInRange(T lo, T hi, typename std::enable_if::value>::type* = 0) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution distro(lo, hi); return distro(gen); } template Derived1 RandomInRange( const Eigen::MatrixBase& lo, const Eigen::MatrixBase& hi) { EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Derived1, Derived2); Derived1 result = Eigen::MatrixBase::Zero(); for (int row = 0; row < result.rows(); ++row) { for (int col = 0; col < result.cols(); ++col) { result(row, col) = RandomInRange(lo(row, col), hi(row, col)); } } return result; } template T RandomRange(T x) { return RandomInRange(-x, x); } template T Clamp(T x, T lo, T hi) { return std::min(std::max(x, lo), hi); } inline mat3 ScaleMatrix(const vec2& scale_xy) { return mat3(Eigen::Scaling(scale_xy[0], scale_xy[1], 1.0f)); } inline mat3 TranslationMatrix(const vec2& translation) { return mat3(Eigen::Translation2f(translation)); } inline mat4 TranslationMatrix(const vec3& translation) { return mat4(Eigen::Translation3f(translation)); } inline vec2 TransformPoint(const mat3& m, const vec2& p) { return m.linear() * p + m.translation(); } inline vec2 TransformVector(const mat3& m, const vec2& p) { return m.linear() * p; } } // namespace dvr } // namespace android #endif // ANDROID_DVR_NUMERIC_H_ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/ortho.h����������������������������������������������������0100644 0000000 0000000 00000001522 13756501735 021070� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_ORTHO_H_ #define ANDROID_DVR_ORTHO_H_ #include namespace android { namespace dvr { template Eigen::AffineMatrix OrthoMatrix(T left, T right, T bottom, T top, T znear, T zfar) { Eigen::AffineMatrix result; const T t2 = static_cast(2); const T a = t2 / (right - left); const T b = t2 / (top - bottom); const T c = t2 / (zfar - znear); const T xoff = -(right + left) / (right - left); const T yoff = -(top + bottom) / (top - bottom); const T zoff = -(zfar + znear) / (zfar - znear); const T t1 = static_cast(1); result.matrix() << a, 0, 0, xoff, 0, b, 0, yoff, 0, 0, c, zoff, 0, 0, 0, t1; return result; } } // namespace android } // namespace dvr #endif // ANDROID_DVR_ORTHO_H_ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/pose.h�����������������������������������������������������0100644 0000000 0000000 00000006724 13756501735 020714� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_POSE_H_ #define ANDROID_DVR_POSE_H_ #include namespace android { namespace dvr { // Encapsulates a 3D pose (rotation and position). // // @tparam T Data type for storing the position coordinate and rotation // quaternion. template class Pose { public: // Creates identity pose. Pose() : rotation_(Eigen::Quaternion::Identity()), position_(Eigen::Vector3::Zero()) {} // Initializes a pose with given rotation and position. // // rotation Initial rotation. // position Initial position. Pose(Eigen::Quaternion rotation, Eigen::Vector3 position) : rotation_(rotation), position_(position) {} void Invert() { rotation_ = rotation_.inverse(); position_ = rotation_ * -position_; } Pose Inverse() const { Pose result(*this); result.Invert(); return result; } // Compute the composition of this pose with another, storing the result // in the current object void ComposeInPlace(const Pose& other) { position_ = position_ + rotation_ * other.position_; rotation_ = rotation_ * other.rotation_; } // Computes the composition of this pose with another, and returns the result Pose Compose(const Pose& other) const { Pose result(*this); result.ComposeInPlace(other); return result; } Eigen::Vector3 TransformPoint(const Eigen::Vector3& v) const { return rotation_ * v + position_; } Eigen::Vector3 Transform(const Eigen::Vector3& v) const { return rotation_ * v; } Pose& operator*=(const Pose& other) { ComposeInPlace(other); return *this; } Pose operator*(const Pose& other) const { return Compose(other); } // Gets the rotation of the 3D pose. Eigen::Quaternion GetRotation() const { return rotation_; } // Gets the position of the 3D pose. Eigen::Vector3 GetPosition() const { return position_; } // Sets the rotation of the 3D pose. void SetRotation(Eigen::Quaternion rotation) { rotation_ = rotation; } // Sets the position of the 3D pose. void SetPosition(Eigen::Vector3 position) { position_ = position; } // Gets a 4x4 matrix representing a transform from the reference space (that // the rotation and position of the pose are relative to) to the object space. Eigen::AffineMatrix GetObjectFromReferenceMatrix() const; // Gets a 4x4 matrix representing a transform from the object space to the // reference space (that the rotation and position of the pose are relative // to). Eigen::AffineMatrix GetReferenceFromObjectMatrix() const; private: Eigen::Quaternion rotation_; Eigen::Vector3 position_; }; template Eigen::AffineMatrix Pose::GetObjectFromReferenceMatrix() const { // The transfrom from the reference is the inverse of the pose. Eigen::AffineMatrix matrix(rotation_.inverse().toRotationMatrix()); return matrix.translate(-position_); } template Eigen::AffineMatrix Pose::GetReferenceFromObjectMatrix() const { // The transfrom to the reference. Eigen::AffineMatrix matrix(rotation_.toRotationMatrix()); return matrix.pretranslate(position_); } //------------------------------------------------------------------------------ // Type-specific typedefs. //------------------------------------------------------------------------------ using Posef = Pose; using Posed = Pose; } // namespace dvr } // namespace android #endif // ANDROID_DVR_POSE_H_ ��������������������������������������������libs/vr/libdvrcommon/include/private/dvr/range.h����������������������������������������������������0100644 0000000 0000000 00000002026 13756501735 021031� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_RANGE_H_ #define ANDROID_DVR_RANGE_H_ #include namespace android { namespace dvr { // TODO(skiazyk): Replace all instances of this with Eigen::AlignedBox // Container of two points that define a 2D range. template struct Range { // Construct an uninitialized Range. Range() {} Range(Eigen::Vector p1, Eigen::Vector p2) : p1(p1), p2(p2) {} static Range FromSize(Eigen::Vector p1, Eigen::Vector size) { return Range(p1, p1 + size); } bool operator==(const Range& rhs) const { return p1 == rhs.p1 && p2 == rhs.p2; } Eigen::Vector GetMinPoint() const { return p1; } Eigen::Vector GetMaxPoint() const { return p2; } Eigen::Vector GetSize() const { return p2 - p1; } Eigen::Vector p1; Eigen::Vector p2; }; typedef Range Range2i; typedef Range Range2f; } // namespace dvr } // namespace android #endif // ANDROID_DVR_RANGE_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/ring_buffer.h����������������������������������������������0100644 0000000 0000000 00000004440 13756501735 022227� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_RING_BUFFER_H_ #define ANDROID_DVR_RING_BUFFER_H_ #include #include namespace android { namespace dvr { // A simple ring buffer implementation. // // A vector works but you either have to keep track of start_ and size_ yourself // or erase() from the front which is inefficient. // // A deque works but the common usage pattern of Append() PopFront() Append() // PopFront() looks like it allocates each time size goes from 0 --> 1, which we // don't want. This class allocates only once. template class RingBuffer { public: RingBuffer() { Reset(0); } explicit RingBuffer(size_t capacity) { Reset(capacity); } RingBuffer(const RingBuffer& other) = default; RingBuffer(RingBuffer&& other) noexcept = default; RingBuffer& operator=(const RingBuffer& other) = default; RingBuffer& operator=(RingBuffer&& other) noexcept = default; void Append(const T& val) { if (IsFull()) PopFront(); Get(size_) = val; size_++; } void Append(T&& val) { if (IsFull()) PopFront(); Get(size_) = std::move(val); size_++; } bool IsEmpty() const { return size_ == 0; } bool IsFull() const { return size_ == buffer_.size(); } size_t GetSize() const { return size_; } size_t GetCapacity() const { return buffer_.size(); } T& Get(size_t i) { return buffer_[(start_ + i) % buffer_.size()]; } const T& Get(size_t i) const { return buffer_[(start_ + i) % buffer_.size()]; } const T& Back() const { return Get(size_ - 1); } T& Back() { return Get(size_ - 1); } const T& Front() const { return Get(0); } T& Front() { return Get(0); } void PopBack() { if (size_ != 0) { Get(size_ - 1) = T(); size_--; } } void PopFront() { if (size_ != 0) { Get(0) = T(); start_ = (start_ + 1) % buffer_.size(); size_--; } } void Clear() { Reset(GetCapacity()); } void Reset(size_t capacity) { start_ = size_ = 0; buffer_.clear(); buffer_.resize(capacity); } private: // Ideally we'd allocate our own memory and use placement new to instantiate // instances of T instead of using a vector, but the vector is simpler. std::vector buffer_; size_t start_, size_; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_RING_BUFFER_H_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/test/������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 020546� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h�����������������������������������������0100644 0000000 0000000 00000011056 13756501735 023242� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_ #define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_ #include #include #include namespace android { namespace dvr { template ::testing::AssertionResult CmpArrayLikeFloatEq( const char* expectedStr, const char* actualStr, const char* toleranceStr, const A& expected, const B& actual, const T& tolerance) { for (int i = 0; i < N; ++i) { if (!IsEqual(expected[i], actual[i], tolerance)) { return ::testing::AssertionFailure() << "\"" << expectedStr << "\" and \"" << actualStr << "\" differ at element " << i << " by at least " << tolerance << " : " << " Expected \"" << expected[i] << "\", was \"" << actual[i] << "\"."; } } return ::testing::AssertionSuccess(); } template ::testing::AssertionResult CmpMatrixLikeFloatEq( const char* expectedStr, const char* actualStr, const char* toleranceStr, const A& expected, const B& actual, const T& tolerance) { for (int r = 0; r < N; ++r) { for (int c = 0; c < N; ++c) { if (!IsEqual(expected(r, c), actual(r, c), tolerance)) { return ::testing::AssertionFailure() << "\"" << expectedStr << "\" and \"" << actualStr << "\" differ at (" << r << "," << c << ")" << " by at least " << tolerance << " : " << " Expected \"" << expected(r, c) << "\", was \"" << actual(r, c) << "\"."; } } } return ::testing::AssertionSuccess(); } template ::testing::AssertionResult CmpArrayLikeFloatNe( const char* expectedStr, const char* actualStr, const char* toleranceStr, const A& expected, const B& actual, const T& tolerance) { for (int i = 0; i < N; ++i) { if (!IsEqual(expected[i], actual[i], tolerance)) { return ::testing::AssertionSuccess(); } } ::testing::Message message; message << "Expected \"" << expectedStr << "\" to differ from provided value \"" << actualStr << "\" by at least " << tolerance << "."; return ::testing::AssertionFailure(message); } template ::testing::AssertionResult CmpMatrixLikeFloatNe( const char* expectedStr, const char* actualStr, const char* toleranceStr, const A& expected, const B& actual, const T& tolerance) { for (int r = 0; r < N; ++r) { for (int c = 0; c < N; ++c) { if (!IsEqual(expected(r, c), actual(r, c), tolerance)) { return ::testing::AssertionSuccess(); } } } ::testing::Message message; message << "Expected \"" << expectedStr << "\" to differ from provided value \"" << actualStr << "\" by at least " << tolerance << "."; return ::testing::AssertionFailure(message); } } // namespace dvr } // namespace android #define EXPECT_VEC3_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected, actual, \ tol) #define EXPECT_VEC3_NOT_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected, actual, \ tol) #define EXPECT_QUAT_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatEq<3>, expected.coeffs(), \ actual.coeffs(), tol) #define EXPECT_QUAT_NOT_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpArrayLikeFloatNe<3>, expected.coeffs(), \ actual.coeffs(), tol) #define EXPECT_MAT4_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatEq<4>, expected, actual, \ tol) #define EXPECT_MAT4_NOT_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<4>, expected, actual, \ tol) #define EXPECT_MAT3_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr \ : CmpMatrixLikeFloatEq<3>, expected, actual, tol) #define EXPECT_MAT3_NOT_NEAR(expected, actual, tol) \ EXPECT_PRED_FORMAT3(android::dvr::CmpMatrixLikeFloatNe<3>, expected, actual, \ tol) #endif // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_TEST_TEST_MACROS_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/types.h����������������������������������������������������0100644 0000000 0000000 00000002257 13756501735 021107� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_TYPES_H_ #define ANDROID_DVR_TYPES_H_ // All basic types used by VR code. #include #include #include #include namespace android { namespace dvr { enum RgbColorChannel { kRed, kGreen, kBlue }; // EyeType: 0 for left, 1 for right. enum EyeType { kLeftEye = 0, kRightEye = 1 }; // In the context of VR, vector types are used as much as base types. using vec2f = Eigen::Vector2f; using vec2d = Eigen::Vector2d; using vec2i = Eigen::Vector2i; using vec2 = vec2f; using vec3f = Eigen::Vector3f; using vec3d = Eigen::Vector3d; using vec3i = Eigen::Vector3i; using vec3 = vec3f; using vec4f = Eigen::Vector4f; using vec4d = Eigen::Vector4d; using vec4i = Eigen::Vector4i; using vec4 = vec4f; using mat3f = Eigen::AffineMatrix; using mat3d = Eigen::AffineMatrix; using mat3 = mat3f; using mat4f = Eigen::AffineMatrix; using mat4d = Eigen::AffineMatrix; using mat4 = mat4f; using quatf = Eigen::Quaternionf; using quatd = Eigen::Quaterniond; using quat = quatf; } // namespace dvr } // namespace android #endif // ANDROID_DVR_TYPES_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/tests/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015041� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/tests/numeric_test.cpp���������������������������������������������������������0100644 0000000 0000000 00000003012 13756501735 020237� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include using TestTypes = ::testing::Types; using android::dvr::RandomInRange; template class NumericTest : public ::testing::TestWithParam { public: using FT = T; }; TYPED_TEST_CASE(NumericTest, TestTypes); TYPED_TEST(NumericTest, RandomInRange) { using FT = typename TestFixture::FT; const int kNumTrials = 50; const FT kLowRange = static_cast(-100); const FT kHighRange = static_cast(100); for (int i = 0; i < kNumTrials; ++i) { FT value = RandomInRange(kLowRange, kHighRange); EXPECT_LE(kLowRange, value); EXPECT_GE(kHighRange, value); } } TEST(RandomInRange, TestIntVersion) { // This checks specifically that the function does not always give the lo // value (this was previously a bug) const int kNumTrials = 50; const int kLowRange = -100; const int kHighRange = 100; for (int i = 0; i < kNumTrials; ++i) { int value = RandomInRange(kLowRange, kHighRange); if (value != kLowRange) { SUCCEED(); return; } } FAIL() << "Did not produce a value other than the range minimum for " << "integers."; } TEST(RandomInRange, TestVectorVersion) { Eigen::Vector3d lo(-3.0, -4.0, -5.0); Eigen::Vector3d hi(5.0, 4.0, 3.0); const int kNumTrials = 50; for (int i = 0; i < kNumTrials; ++i) { Eigen::Vector3d result = RandomInRange(lo, hi); for (int j = 0; j < 3; ++j) { EXPECT_LE(lo[j], result[j]); EXPECT_GE(hi[j], result[j]); } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/tests/pose_test.cpp������������������������������������������������������������0100644 0000000 0000000 00000012506 13756501735 017553� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include using PoseTypes = ::testing::Types; template class PoseTest : public ::testing::TestWithParam { public: using FT = T; using Pose_t = android::dvr::Pose; using quat_t = Eigen::Quaternion; using vec3_t = Eigen::Vector3; using mat4_t = Eigen::AffineMatrix; }; TYPED_TEST_CASE(PoseTest, PoseTypes); // Check that the two matrix methods are inverses of each other TYPED_TEST(PoseTest, SelfInverse) { using quat_t = typename TestFixture::quat_t; using vec3_t = typename TestFixture::vec3_t; using Pose_t = typename TestFixture::Pose_t; using mat4_t = typename TestFixture::mat4_t; using FT = typename TestFixture::FT; const auto tolerance = FT(0.0001); const quat_t initial_rotation(Eigen::AngleAxis( FT(M_PI / 3.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized())); const vec3_t initial_position = vec3_t(FT(2.0), FT(10.0), FT(-4.0)); const Pose_t initial_pose(initial_rotation, initial_position); auto result_pose = initial_pose.GetReferenceFromObjectMatrix() * initial_pose.GetObjectFromReferenceMatrix(); EXPECT_MAT4_NEAR(result_pose, mat4_t::Identity(), tolerance); } TYPED_TEST(PoseTest, TransformPoint) { using quat_t = typename TestFixture::quat_t; using vec3_t = typename TestFixture::vec3_t; using Pose_t = typename TestFixture::Pose_t; using FT = typename TestFixture::FT; const auto tolerance = FT(0.0001); const quat_t pose_rotation( Eigen::AngleAxis(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0)))); const auto pose_position = vec3_t(FT(1.0), FT(1.0), FT(2.0)); const Pose_t test_pose(pose_rotation, pose_position); for (int axis = 0; axis < 3; ++axis) { vec3_t start_position = vec3_t::Zero(); start_position[axis] = FT(1.0); const vec3_t expected_transformed = (pose_rotation * start_position) + pose_position; const vec3_t actual_transformed = test_pose.TransformPoint(start_position); EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance); } } TYPED_TEST(PoseTest, TransformVector) { using quat_t = typename TestFixture::quat_t; using vec3_t = typename TestFixture::vec3_t; using Pose_t = typename TestFixture::Pose_t; using FT = typename TestFixture::FT; const auto tolerance = FT(0.0001); const quat_t pose_rotation(Eigen::AngleAxis( FT(M_PI / 6.0), vec3_t(FT(3.0), FT(4.0), FT(5.0)).normalized())); const auto pose_position = vec3_t(FT(500.0), FT(-500.0), FT(300.0)); const Pose_t test_pose(pose_rotation, pose_position); for (int axis = 0; axis < 3; ++axis) { vec3_t start_position = vec3_t::Zero(); start_position[axis] = FT(1.0); const vec3_t expected_rotated = pose_rotation * start_position; const vec3_t actual_rotated = test_pose.Transform(start_position); EXPECT_VEC3_NEAR(expected_rotated, actual_rotated, tolerance); } } TYPED_TEST(PoseTest, Composition) { using quat_t = typename TestFixture::quat_t; using Pose_t = typename TestFixture::Pose_t; using vec3_t = typename TestFixture::vec3_t; using FT = typename TestFixture::FT; const auto tolerance = FT(0.0001); const quat_t first_rotation( Eigen::AngleAxis(FT(M_PI / 2.0), vec3_t(FT(0.0), FT(0.0), FT(1.0)))); const auto first_offset = vec3_t(FT(-3.0), FT(2.0), FT(-1.0)); const quat_t second_rotation(Eigen::AngleAxis( FT(M_PI / 3.0), vec3_t(FT(1.0), FT(-1.0), FT(0.0)).normalized())); const auto second_offset = vec3_t(FT(6.0), FT(-7.0), FT(-8.0)); const Pose_t first_pose(first_rotation, first_offset); const Pose_t second_pose(second_rotation, second_offset); const auto combined_pose(second_pose.Compose(first_pose)); for (int axis = 0; axis < 3; ++axis) { vec3_t start_position = vec3_t::Zero(); start_position[axis] = FT(1.0); const vec3_t expected_transformed = second_pose.TransformPoint(first_pose.TransformPoint(start_position)); const vec3_t actual_transformed = combined_pose.TransformPoint(start_position); EXPECT_VEC3_NEAR(expected_transformed, actual_transformed, tolerance); } } TYPED_TEST(PoseTest, Inverse) { using quat_t = typename TestFixture::quat_t; using vec3_t = typename TestFixture::vec3_t; using Pose_t = typename TestFixture::Pose_t; using FT = typename TestFixture::FT; const auto tolerance = FT(0.0001); const quat_t pose_rotation(Eigen::AngleAxis( FT(M_PI / 2.0), vec3_t(FT(4.0), FT(-2.0), FT(-1.0)).normalized())); const auto pose_position = vec3_t(FT(-1.0), FT(2.0), FT(-4.0)); Pose_t pose(pose_rotation, pose_position); const Pose_t pose_inverse = pose.Inverse(); for (int axis = 0; axis < 3; ++axis) { vec3_t start_position = vec3_t::Zero(); start_position[axis] = FT(1.0); const vec3_t transformed = pose.Transform(start_position); const vec3_t inverted = pose_inverse.Transform(transformed); EXPECT_VEC3_NEAR(start_position, inverted, tolerance); } Pose_t nullified_pose[2] = { pose.Compose(pose_inverse), pose_inverse.Compose(pose), }; for (int i = 0; i < 2; ++i) { EXPECT_QUAT_NEAR(quat_t::Identity(), nullified_pose[i].GetRotation(), tolerance); EXPECT_VEC3_NEAR(vec3_t::Zero(), nullified_pose[i].GetPosition(), tolerance); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/�������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 012466� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/Android.bp���������������������������������������������������������������������������0100644 0000000 0000000 00000002620 13756501735 014366� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������cc_library_headers { name: "libpdx_headers", export_include_dirs: ["private"], vendor_available: true, } cc_library_static { name: "libpdx", clang: true, cflags: [ "-Wall", "-Wextra", "-Werror", "-DLOG_TAG=\"libpdx\"", "-DTRACE=0", ], header_libs: ["libpdx_headers"], export_header_lib_headers: ["libpdx_headers"], srcs: [ "client.cpp", "service.cpp", "service_dispatcher.cpp", "status.cpp", ], shared_libs: [ "libbinder", "libcutils", "libutils", "liblog", ], } cc_test { name: "pdx_tests", clang: true, cflags: [ "-Wall", "-Wextra", "-Werror", ], srcs: [ "client_tests.cpp", "mock_tests.cpp", "serialization_tests.cpp", "service_tests.cpp", "status_tests.cpp", "thread_local_buffer_tests.cpp", "variant_tests.cpp", ], static_libs: [ "libcutils", "libgmock", "libpdx", "liblog", "libutils", "libvndksupport", ], } // Code analysis target. cc_test { name: "pdx_encoder_performance_test", clang: true, cflags: [ "-Wall", "-Wextra", "-Werror", "-O2", ], srcs: [ "encoder_performance_test.cpp", ], static_libs: [ "libpdx", ], } ����������������������������������������������������������������������������������������������������������������libs/vr/libpdx/client.cpp���������������������������������������������������������������������������0100644 0000000 0000000 00000020020 13756501735 014437� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "pdx/client.h" #include #include namespace android { namespace pdx { void Client::EnableAutoReconnect(int64_t reconnect_timeout_ms) { if (channel_factory_) { reconnect_timeout_ms_ = reconnect_timeout_ms; auto_reconnect_enabled_ = true; } } void Client::DisableAutoReconnect() { auto_reconnect_enabled_ = false; } bool Client::IsConnected() const { return channel_.get() != nullptr; } Status Client::CheckReconnect() { Status ret; bool was_disconnected = !IsConnected(); if (auto_reconnect_enabled_ && was_disconnected && channel_factory_) { auto status = channel_factory_->Connect(reconnect_timeout_ms_); if (!status) { error_ = -status.error(); ret.SetError(status.error()); return ret; } channel_ = status.take(); } if (!IsConnected()) { ret.SetError(ESHUTDOWN); } else { // Call the subclass OnConnect handler. The subclass may choose to close the // connection in the handler, in which case error_ will be non-zero. if (was_disconnected) OnConnect(); if (!IsConnected()) ret.SetError(-error_); else ret.SetValue(); } return ret; } bool Client::NeedToDisconnectChannel(int error) const { return error == ESHUTDOWN && auto_reconnect_enabled_; } void Client::CheckDisconnect(int error) { if (NeedToDisconnectChannel(error)) Close(error); } Client::Client(std::unique_ptr channel) : channel_{std::move(channel)} {} Client::Client(std::unique_ptr channel_factory, int64_t timeout_ms) : channel_factory_{std::move(channel_factory)} { auto status = channel_factory_->Connect(timeout_ms); if (!status) { ALOGE("Client::Client: Failed to connect to service because: %s", status.GetErrorMessage().c_str()); error_ = -status.error(); } else { channel_ = status.take(); } } bool Client::IsInitialized() const { return IsConnected() || (channel_factory_ && auto_reconnect_enabled_); } void Client::OnConnect() {} int Client::error() const { return error_; } Status Client::SendImpulse(int opcode) { PDX_TRACE_NAME("Client::SendImpulse"); auto status = CheckReconnect(); if (!status) return status; status = channel_->SendImpulse(opcode, nullptr, 0); CheckDisconnect(status); return status; } Status Client::SendImpulse(int opcode, const void* buffer, size_t length) { PDX_TRACE_NAME("Client::SendImpulse"); auto status = CheckReconnect(); if (!status) return status; status = channel_->SendImpulse(opcode, buffer, length); CheckDisconnect(status); return status; } void Client::Close(int error) { channel_.reset(); // Normalize error codes to negative integer space. error_ = error <= 0 ? error : -error; } int Client::event_fd() const { return IsConnected() ? channel_->event_fd() : -1; } LocalChannelHandle& Client::GetChannelHandle() { return channel_->GetChannelHandle(); } const LocalChannelHandle& Client::GetChannelHandle() const { return channel_->GetChannelHandle(); } ///////////////////////////// Transaction implementation ////////////////////// Transaction::Transaction(Client& client) : client_{client} {} Transaction::~Transaction() { if (state_allocated_ && client_.GetChannel()) client_.GetChannel()->FreeTransactionState(state_); } bool Transaction::EnsureStateAllocated() { if (!state_allocated_ && client_.GetChannel()) { state_ = client_.GetChannel()->AllocateTransactionState(); state_allocated_ = true; } return state_allocated_; } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { *ret = client_.CheckReconnect(); if (!*ret) return; if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } auto status = client_.GetChannel()->SendWithInt( state_, opcode, send_vector, send_count, receive_vector, receive_count); if (status) { ret->SetValue(); } else { ret->SetError(status.error()); } CheckDisconnect(status); } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { auto status = client_.CheckReconnect(); if (!status) { ret->SetError(status.error()); return; } if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } *ret = client_.GetChannel()->SendWithInt( state_, opcode, send_vector, send_count, receive_vector, receive_count); CheckDisconnect(*ret); } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { auto status = client_.CheckReconnect(); if (!status) { ret->SetError(status.error()); return; } if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } *ret = client_.GetChannel()->SendWithFileHandle( state_, opcode, send_vector, send_count, receive_vector, receive_count); CheckDisconnect(*ret); } void Transaction::SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { auto status = client_.CheckReconnect(); if (!status) { ret->SetError(status.error()); return; } if (!EnsureStateAllocated()) { ret->SetError(ESHUTDOWN); return; } *ret = client_.GetChannel()->SendWithChannelHandle( state_, opcode, send_vector, send_count, receive_vector, receive_count); CheckDisconnect(*ret); } Status Transaction::PushFileHandle(const LocalHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushFileHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushFileHandle( const BorrowedHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushFileHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushFileHandle(const RemoteHandle& handle) { return handle.Get(); } Status Transaction::PushChannelHandle( const LocalChannelHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushChannelHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushChannelHandle( const BorrowedChannelHandle& handle) { if (client_.CheckReconnect() && EnsureStateAllocated()) return client_.GetChannel()->PushChannelHandle(state_, handle); return ErrorStatus{ESHUTDOWN}; } Status Transaction::PushChannelHandle( const RemoteChannelHandle& handle) { return handle.value(); } bool Transaction::GetFileHandle(FileReference ref, LocalHandle* handle) { return client_.CheckReconnect() && EnsureStateAllocated() && client_.GetChannel()->GetFileHandle(state_, ref, handle); } bool Transaction::GetChannelHandle(ChannelReference ref, LocalChannelHandle* handle) { return client_.CheckReconnect() && EnsureStateAllocated() && client_.GetChannel()->GetChannelHandle(state_, ref, handle); } void Transaction::CheckDisconnect(int error) { if (client_.NeedToDisconnectChannel(error)) { if (state_allocated_) { if (client_.GetChannel()) client_.GetChannel()->FreeTransactionState(state_); state_ = nullptr; state_allocated_ = false; } client_.Close(error); } } } // namespace pdx } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/client_tests.cpp���������������������������������������������������������������������0100644 0000000 0000000 00000050312 13756501735 015670� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include using android::pdx::BorrowedChannelHandle; using android::pdx::BorrowedHandle; using android::pdx::ClientBase; using android::pdx::ClientChannel; using android::pdx::ClientChannelFactory; using android::pdx::ErrorStatus; using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::MockClientChannel; using android::pdx::MockClientChannelFactory; using android::pdx::RemoteChannelHandle; using android::pdx::RemoteHandle; using android::pdx::Status; using android::pdx::Transaction; using android::pdx::rpc::Void; using testing::A; using testing::AnyNumber; using testing::ByMove; using testing::Invoke; using testing::Ne; using testing::Return; using testing::_; namespace { inline void* IntToPtr(intptr_t addr) { return reinterpret_cast(addr); } inline const void* IntToConstPtr(intptr_t addr) { return reinterpret_cast(addr); } struct TestInterface final { // Op codes. enum { kOpAdd = 0, kOpSendFile, kOpGetFile, kOpPushChannel, }; // Methods. PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int)); PDX_REMOTE_METHOD(SendFile, kOpSendFile, void(const LocalHandle& fd)); PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int)); PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void)); PDX_REMOTE_API(API, Add, SendFile, GetFile, PushChannel); }; class SimpleClient : public ClientBase { public: explicit SimpleClient(std::unique_ptr channel) : BASE{std::move(channel)} {} SimpleClient(std::unique_ptr channel_factory, int64_t timeout_ms) : BASE{std::move(channel_factory), timeout_ms} { EnableAutoReconnect(timeout_ms); } using BASE::SendImpulse; using BASE::InvokeRemoteMethod; using BASE::InvokeRemoteMethodInPlace; using BASE::Close; using BASE::IsConnected; using BASE::EnableAutoReconnect; using BASE::DisableAutoReconnect; using BASE::event_fd; using BASE::GetChannel; MOCK_METHOD0(OnConnect, void()); }; class FailingClient : public ClientBase { public: explicit FailingClient(std::unique_ptr channel, int error_code) : BASE{std::move(channel)} { Close(error_code); } }; class ClientChannelTest : public testing::Test { public: ClientChannelTest() : client_{SimpleClient::Create( std::make_unique>())} {} MockClientChannel* mock_channel() { return static_cast(client_->GetChannel()); } std::unique_ptr client_; }; class ClientChannelFactoryTest : public testing::Test { public: ClientChannelFactoryTest() { auto factory = std::make_unique>(); ON_CALL(*factory, Connect(kTimeout)) .WillByDefault(Invoke(this, &ClientChannelFactoryTest::OnConnect)); client_ = SimpleClient::Create(std::move(factory), kTimeout); } MockClientChannel* mock_channel() { return static_cast(client_->GetChannel()); } Status> OnConnect(int64_t /*timeout_ms*/) { if (on_connect_error_) return ErrorStatus(on_connect_error_); std::unique_ptr channel = std::make_unique>(); if (on_connect_callback_) on_connect_callback_(channel.get()); return Status>{std::move(channel)}; } void OnConnectCallback(std::function callback) { on_connect_callback_ = callback; } void SetOnConnectError(int error) { on_connect_error_ = error; } void ResetOnConnectError() { on_connect_error_ = 0; } constexpr static int64_t kTimeout = 123; std::unique_ptr client_; std::function on_connect_callback_; int on_connect_error_{0}; }; constexpr int64_t ClientChannelFactoryTest::kTimeout; class ClientTransactionTest : public ClientChannelTest { public: ClientTransactionTest() : transaction_{*client_} {} Transaction transaction_; }; } // anonymous namespace TEST_F(ClientChannelTest, IsInitialized) { ASSERT_NE(client_.get(), nullptr); EXPECT_TRUE(client_->IsInitialized()); EXPECT_TRUE(client_->IsConnected()); } TEST_F(ClientChannelTest, CloseOnConstruction) { FailingClient failed_client1{std::make_unique(), EACCES}; ASSERT_FALSE(failed_client1.IsInitialized()); EXPECT_EQ(-EACCES, failed_client1.error()); FailingClient failed_client2{std::make_unique(), -EACCES}; ASSERT_FALSE(failed_client2.IsInitialized()); EXPECT_EQ(-EACCES, failed_client2.error()); auto failed_client3 = FailingClient::Create(std::make_unique(), EIO); ASSERT_EQ(failed_client3.get(), nullptr); } TEST_F(ClientChannelTest, IsConnected) { EXPECT_TRUE(client_->IsConnected()); EXPECT_EQ(0, client_->error()); client_->Close(-EINVAL); EXPECT_FALSE(client_->IsConnected()); EXPECT_EQ(-EINVAL, client_->error()); } TEST_F(ClientChannelTest, event_fd) { EXPECT_CALL(*mock_channel(), event_fd()).WillOnce(Return(12)); EXPECT_EQ(12, client_->event_fd()); } TEST_F(ClientChannelTest, SendImpulse) { EXPECT_CALL(*mock_channel(), SendImpulse(123, nullptr, 0)) .WillOnce(Return(Status{})); EXPECT_TRUE(client_->SendImpulse(123)); EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0)) .WillOnce(Return(ErrorStatus{EIO})); auto status = client_->SendImpulse(17); ASSERT_FALSE(status); EXPECT_EQ(EIO, status.error()); const void* const kTestPtr = IntToConstPtr(1234); EXPECT_CALL(*mock_channel(), SendImpulse(1, kTestPtr, 17)) .WillOnce(Return(Status{})); EXPECT_TRUE(client_->SendImpulse(1, kTestPtr, 17)); } TEST_F(ClientChannelTest, InvokeRemoteMethodNullTransactionState) { EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(nullptr)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, TestInterface::kOpAdd, _, _, nullptr, 0)) .WillOnce(Return(9)); EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); EXPECT_TRUE(client_->InvokeRemoteMethod(4, 5)); } TEST_F(ClientChannelTest, InvokeRemoteMethodAddSuccess) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL( *mock_channel(), SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0)) .WillOnce(Return(3)); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); Status status = client_->InvokeRemoteMethod(1, 2); ASSERT_TRUE(status); EXPECT_EQ(3, status.get()); } TEST_F(ClientChannelTest, InvokeRemoteMethodAddFailure) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL( *mock_channel(), SendWithInt(kTransactionState, TestInterface::kOpAdd, _, _, nullptr, 0)) .WillOnce(Return(ErrorStatus{EIO})); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); Status status = client_->InvokeRemoteMethod(1, 2); ASSERT_FALSE(status); EXPECT_EQ(EIO, status.error()); } TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileSuccess) { void* const kTransactionState = IntToPtr(123); int fd = eventfd(0, 0); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile, _, _, nullptr, 0)) .WillOnce(Return(ByMove(LocalHandle{fd}))); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); Status status = client_->InvokeRemoteMethod(); ASSERT_TRUE(status); EXPECT_EQ(fd, status.get().Get()); } TEST_F(ClientChannelTest, InvokeRemoteMethodGetFileFailure) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), SendWithFileHandle(kTransactionState, TestInterface::kOpGetFile, _, _, nullptr, 0)) .WillOnce(Return(ByMove(ErrorStatus{EACCES}))); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); Status status = client_->InvokeRemoteMethod("file", 0); ASSERT_FALSE(status); EXPECT_EQ(EACCES, status.error()); } TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelSuccess) { void* const kTransactionState = IntToPtr(123); const int32_t kHandleValue = 17; EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL( *mock_channel(), SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _, _, nullptr, 0)) .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, kHandleValue}))); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); Status status = client_->InvokeRemoteMethod(); ASSERT_TRUE(status); EXPECT_EQ(kHandleValue, status.get().value()); } TEST_F(ClientChannelTest, InvokeRemoteMethodPushChannelFailure) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL( *mock_channel(), SendWithChannelHandle(kTransactionState, TestInterface::kOpPushChannel, _, _, nullptr, 0)) .WillOnce(Return(ByMove(ErrorStatus{EACCES}))); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); Status status = client_->InvokeRemoteMethod(); ASSERT_FALSE(status); EXPECT_EQ(EACCES, status.error()); } TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileSuccess) { void* const kTransactionState = IntToPtr(123); LocalHandle fd{eventfd(0, 0)}; EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), PushFileHandle(kTransactionState, A())) .WillOnce(Return(1)); EXPECT_CALL(*mock_channel(), SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _, nullptr, 0)) .WillOnce(Return(0)); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); EXPECT_TRUE(client_->InvokeRemoteMethod(fd)); } TEST_F(ClientChannelTest, InvokeRemoteMethodSendFileFailure) { void* const kTransactionState = IntToPtr(123); LocalHandle fd{eventfd(0, 0)}; EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), PushFileHandle(kTransactionState, A())) .WillOnce(Return(1)); EXPECT_CALL(*mock_channel(), SendWithInt(kTransactionState, TestInterface::kOpSendFile, _, _, nullptr, 0)) .WillOnce(Return(ErrorStatus{EACCES})); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); EXPECT_FALSE(client_->InvokeRemoteMethod(fd)); } TEST_F(ClientChannelFactoryTest, IsInitialized) { ASSERT_NE(client_.get(), nullptr); EXPECT_TRUE(client_->IsInitialized()); EXPECT_TRUE(client_->IsConnected()); } TEST_F(ClientChannelFactoryTest, NotConnectedButInitialized) { auto factory = std::make_unique>(); EXPECT_CALL(*factory, Connect(kTimeout)) .WillOnce(Return(ByMove(ErrorStatus(ESHUTDOWN)))) .WillOnce(Invoke(this, &ClientChannelFactoryTest::OnConnect)); client_ = SimpleClient::Create(std::move(factory), kTimeout); ASSERT_NE(client_.get(), nullptr); EXPECT_TRUE(client_->IsInitialized()); EXPECT_FALSE(client_->IsConnected()); client_->DisableAutoReconnect(); ASSERT_FALSE(client_->SendImpulse(17)); EXPECT_FALSE(client_->IsConnected()); client_->EnableAutoReconnect(kTimeout); EXPECT_CALL(*client_, OnConnect()); OnConnectCallback([](auto* mock) { EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0)) .WillOnce(Return(Status{})); }); ASSERT_TRUE(client_->SendImpulse(17)); EXPECT_TRUE(client_->IsConnected()); } TEST_F(ClientChannelFactoryTest, CheckDisconnect) { EXPECT_CALL(*mock_channel(), SendImpulse(17, nullptr, 0)) .WillOnce(Return(ErrorStatus{ESHUTDOWN})); ASSERT_FALSE(client_->SendImpulse(17)); EXPECT_FALSE(client_->IsConnected()); EXPECT_EQ(-ESHUTDOWN, client_->error()); } TEST_F(ClientChannelFactoryTest, CheckReconnect) { client_->Close(ESHUTDOWN); ASSERT_FALSE(client_->IsConnected()); EXPECT_CALL(*client_, OnConnect()); OnConnectCallback([](auto* mock) { EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0)) .WillOnce(Return(Status{})); }); ASSERT_TRUE(client_->SendImpulse(17)); EXPECT_TRUE(client_->IsConnected()); } TEST_F(ClientChannelFactoryTest, CloseOnConnect) { client_->Close(ESHUTDOWN); EXPECT_CALL(*client_, OnConnect()).WillOnce(Invoke([this] { client_->Close(EIO); })); auto status = client_->SendImpulse(17); ASSERT_FALSE(status); EXPECT_EQ(EIO, status.error()); EXPECT_FALSE(client_->IsConnected()); EXPECT_EQ(-EIO, client_->error()); } TEST_F(ClientChannelFactoryTest, DisableAutoReconnect) { client_->Close(EIO); ASSERT_FALSE(client_->IsConnected()); client_->DisableAutoReconnect(); auto status = client_->SendImpulse(17); ASSERT_FALSE(status); EXPECT_EQ(ESHUTDOWN, status.error()); EXPECT_FALSE(client_->IsConnected()); client_->EnableAutoReconnect(kTimeout); EXPECT_CALL(*client_, OnConnect()); OnConnectCallback([](auto* mock) { EXPECT_CALL(*mock, SendImpulse(17, nullptr, 0)) .WillOnce(Return(Status{})); }); ASSERT_TRUE(client_->SendImpulse(17)); EXPECT_TRUE(client_->IsConnected()); } TEST_F(ClientTransactionTest, SendNoData) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); EXPECT_CALL(*mock_channel(), SendWithInt(kTransactionState, 1, nullptr, 0, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.Send(1)); EXPECT_CALL(*mock_channel(), SendWithFileHandle(kTransactionState, 2, nullptr, 0, nullptr, 0)) .WillOnce(Return(ByMove(LocalHandle{-1}))); EXPECT_TRUE(transaction_.Send(2)); EXPECT_CALL(*mock_channel(), SendWithChannelHandle(kTransactionState, 3, nullptr, 0, nullptr, 0)) .WillOnce(Return(ByMove(LocalChannelHandle{nullptr, 1}))); EXPECT_TRUE(transaction_.Send(3)); } TEST_F(ClientTransactionTest, SendNoState) { EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(nullptr)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0)) .WillOnce(Return(0)); EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); EXPECT_TRUE(transaction_.Send(1)); } TEST_F(ClientTransactionTest, SendBuffers) { const void* const kSendBuffer = IntToConstPtr(123); const size_t kSendSize = 12; void* const kReceiveBuffer = IntToPtr(456); const size_t kReceiveSize = 34; EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(nullptr)); EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.Send(1, nullptr, 0, nullptr, 0)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, Ne(nullptr), 1, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.Send(2, kSendBuffer, kSendSize, nullptr, 0)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, nullptr, 0, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.Send(3, kSendBuffer, 0, nullptr, 0)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, Ne(nullptr), 1)) .WillOnce(Return(0)); EXPECT_TRUE( transaction_.Send(4, nullptr, 0, kReceiveBuffer, kReceiveSize)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, nullptr, 0, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.Send(5, nullptr, 0, kReceiveBuffer, 0)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, Ne(nullptr), 1, Ne(nullptr), 1)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.Send(5, kSendBuffer, kSendSize, kReceiveBuffer, kReceiveSize)); } TEST_F(ClientTransactionTest, SendVector) { iovec send[3] = {}; iovec recv[4] = {}; EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(nullptr)); EXPECT_CALL(*mock_channel(), FreeTransactionState(nullptr)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 1, nullptr, 0, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.SendVector(1, nullptr, 0, nullptr, 0)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 2, send, 3, recv, 4)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.SendVector(2, send, 3, recv, 4)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 3, send, 3, nullptr, 0)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.SendVector(3, send, nullptr)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 4, nullptr, 0, recv, 4)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.SendVector(4, nullptr, recv)); EXPECT_CALL(*mock_channel(), SendWithInt(nullptr, 5, send, 3, recv, 4)) .WillOnce(Return(0)); EXPECT_TRUE(transaction_.SendVector(5, send, recv)); } TEST_F(ClientTransactionTest, PushHandle) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); EXPECT_CALL(*mock_channel(), PushFileHandle(kTransactionState, A())) .WillOnce(Return(1)); EXPECT_EQ(1, transaction_.PushFileHandle(LocalHandle{-1}).get()); EXPECT_CALL(*mock_channel(), PushFileHandle(kTransactionState, A())) .WillOnce(Return(2)); EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}).get()); EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}).get()); EXPECT_CALL( *mock_channel(), PushChannelHandle(kTransactionState, A())) .WillOnce(Return(11)); EXPECT_EQ( 11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}).get()); EXPECT_CALL( *mock_channel(), PushChannelHandle(kTransactionState, A())) .WillOnce(Return(12)); EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}).get()); EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}).get()); } TEST_F(ClientTransactionTest, GetHandle) { void* const kTransactionState = IntToPtr(123); EXPECT_CALL(*mock_channel(), AllocateTransactionState()) .WillOnce(Return(kTransactionState)); EXPECT_CALL(*mock_channel(), FreeTransactionState(kTransactionState)); EXPECT_CALL(*mock_channel(), GetFileHandle(kTransactionState, 1, _)) .WillOnce(Return(false)) .WillOnce(Return(true)); LocalHandle file_handle; EXPECT_FALSE(transaction_.GetFileHandle(1, &file_handle)); EXPECT_TRUE(transaction_.GetFileHandle(1, &file_handle)); EXPECT_CALL(*mock_channel(), GetChannelHandle(kTransactionState, 2, _)) .WillOnce(Return(false)) .WillOnce(Return(true)); LocalChannelHandle channel_handle; EXPECT_FALSE(transaction_.GetChannelHandle(2, &channel_handle)); EXPECT_TRUE(transaction_.GetChannelHandle(2, &channel_handle)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/encoder_performance_test.cpp���������������������������������������������������������0100644 0000000 0000000 00000047705 13756501735 020243� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #include #include #include #include using namespace android::pdx::rpc; using namespace android::pdx; using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; using std::placeholders::_4; using std::placeholders::_5; using std::placeholders::_6; namespace { constexpr size_t kMaxStaticBufferSize = 20480; // Provide numpunct facet that formats numbers with ',' as thousands separators. class CommaNumPunct : public std::numpunct { protected: char do_thousands_sep() const override { return ','; } std::string do_grouping() const override { return "\03"; } }; class TestPayload : public MessagePayload, public MessageWriter, public MessageReader, public NoOpResourceMapper { public: // MessageWriter void* GetNextWriteBufferSection(size_t size) override { const size_t section_offset = Size(); Extend(size); return Data() + section_offset; } OutputResourceMapper* GetOutputResourceMapper() override { return this; } // MessageReader BufferSection GetNextReadBufferSection() override { return {&*ConstCursor(), &*ConstEnd()}; } void ConsumeReadBufferSectionData(const void* new_start) override { std::advance(ConstCursor(), PointerDistance(new_start, &*ConstCursor())); } InputResourceMapper* GetInputResourceMapper() override { return this; } }; class StaticBuffer : public MessageWriter, public MessageReader, public NoOpResourceMapper { public: void Clear() { read_ptr_ = buffer_; write_ptr_ = 0; } void Rewind() { read_ptr_ = buffer_; } // MessageWriter void* GetNextWriteBufferSection(size_t size) override { void* ptr = buffer_ + write_ptr_; write_ptr_ += size; return ptr; } OutputResourceMapper* GetOutputResourceMapper() override { return this; } // MessageReader BufferSection GetNextReadBufferSection() override { return {read_ptr_, std::end(buffer_)}; } void ConsumeReadBufferSectionData(const void* new_start) override { read_ptr_ = static_cast(new_start); } InputResourceMapper* GetInputResourceMapper() override { return this; } private: uint8_t buffer_[kMaxStaticBufferSize]; const uint8_t* read_ptr_{buffer_}; size_t write_ptr_{0}; }; // Simple callback function to clear/reset the input/output buffers for // serialization. Using raw function pointer here instead of std::function to // minimize the overhead of invocation in the tight test loop over millions of // iterations. using ResetFunc = void(void*); // Serialization test function signature, used by the TestRunner. using SerializeTestSignature = std::chrono::nanoseconds(MessageWriter* writer, size_t iterations, ResetFunc* write_reset, void* reset_data); // Deserialization test function signature, used by the TestRunner. using DeserializeTestSignature = std::chrono::nanoseconds( MessageReader* reader, MessageWriter* writer, size_t iterations, ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data); // Generic serialization test runner method. Takes the |value| of type T and // serializes it into the output buffer represented by |writer|. template std::chrono::nanoseconds SerializeTestRunner(MessageWriter* writer, size_t iterations, ResetFunc* write_reset, void* reset_data, const T& value) { auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < iterations; i++) { write_reset(reset_data); Serialize(value, writer); } auto stop = std::chrono::high_resolution_clock::now(); return stop - start; } // Generic deserialization test runner method. Takes the |value| of type T and // temporarily serializes it into the output buffer, then repeatedly // deserializes the data back from that buffer. template std::chrono::nanoseconds DeserializeTestRunner( MessageReader* reader, MessageWriter* writer, size_t iterations, ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, const T& value) { write_reset(reset_data); Serialize(value, writer); T output_data; auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < iterations; i++) { read_reset(reset_data); Deserialize(&output_data, reader); } auto stop = std::chrono::high_resolution_clock::now(); if (output_data != value) return start - stop; // Return negative value to indicate error. return stop - start; } // Special version of SerializeTestRunner that doesn't perform any serialization // but does all the same setup steps and moves data of size |data_size| into // the output buffer. Useful to determine the baseline to calculate time used // just for serialization layer. std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer, size_t iterations, ResetFunc* write_reset, void* reset_data, size_t data_size) { std::vector dummy_data(data_size); auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < iterations; i++) { write_reset(reset_data); memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), dummy_data.data(), dummy_data.size()); } auto stop = std::chrono::high_resolution_clock::now(); return stop - start; } // Special version of DeserializeTestRunner that doesn't perform any // deserialization but invokes Rewind on the input buffer repeatedly. // Useful to determine the baseline to calculate time used just for // deserialization layer. std::chrono::nanoseconds DeserializeBaseTest( MessageReader* reader, MessageWriter* writer, size_t iterations, ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, size_t data_size) { std::vector dummy_data(data_size); write_reset(reset_data); memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), dummy_data.data(), dummy_data.size()); auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < iterations; i++) { read_reset(reset_data); auto section = reader->GetNextReadBufferSection(); memcpy(dummy_data.data(), section.first, dummy_data.size()); reader->ConsumeReadBufferSectionData( AdvancePointer(section.first, dummy_data.size())); } auto stop = std::chrono::high_resolution_clock::now(); return stop - start; } // The main class that accumulates individual tests to be executed. class TestRunner { public: struct BufferInfo { BufferInfo(const std::string& buffer_name, MessageReader* reader, MessageWriter* writer, ResetFunc* read_reset_func, ResetFunc* write_reset_func, void* reset_data) : name{buffer_name}, reader{reader}, writer{writer}, read_reset_func{read_reset_func}, write_reset_func{write_reset_func}, reset_data{reset_data} {} std::string name; MessageReader* reader; MessageWriter* writer; ResetFunc* read_reset_func; ResetFunc* write_reset_func; void* reset_data; }; void AddTestFunc(const std::string& name, std::function serialize_test, std::function deserialize_test, size_t data_size) { tests_.emplace_back(name, std::move(serialize_test), std::move(deserialize_test), data_size); } template void AddSerializationTest(const std::string& name, T&& value) { const size_t data_size = GetSerializedSize(value); auto serialize_test = std::bind(static_cast( &SerializeTestRunner), _1, _2, _3, _4, std::forward(value)); tests_.emplace_back(name, std::move(serialize_test), std::function{}, data_size); } template void AddDeserializationTest(const std::string& name, T&& value) { const size_t data_size = GetSerializedSize(value); auto deserialize_test = std::bind(static_cast(&DeserializeTestRunner), _1, _2, _3, _4, _5, _6, std::forward(value)); tests_.emplace_back(name, std::function{}, std::move(deserialize_test), data_size); } template void AddTest(const std::string& name, T&& value) { const size_t data_size = GetSerializedSize(value); if (data_size > kMaxStaticBufferSize) { std::cerr << "Test '" << name << "' requires " << data_size << " bytes in the serialization buffer but only " << kMaxStaticBufferSize << " are available." << std::endl; exit(1); } auto serialize_test = std::bind(static_cast( &SerializeTestRunner), _1, _2, _3, _4, value); auto deserialize_test = std::bind(static_cast(&DeserializeTestRunner), _1, _2, _3, _4, _5, _6, std::forward(value)); tests_.emplace_back(name, std::move(serialize_test), std::move(deserialize_test), data_size); } std::string CenterString(std::string text, size_t column_width) { if (text.size() < column_width) { text = std::string((column_width - text.size()) / 2, ' ') + text; } return text; } void RunTests(size_t iteration_count, const std::vector& buffers) { using float_seconds = std::chrono::duration; const std::string name_column_separator = " : "; const std::string buffer_column_separator = " || "; const std::string buffer_timing_column_separator = " | "; const size_t data_size_column_width = 6; const size_t time_column_width = 9; const size_t qps_column_width = 18; const size_t buffer_column_width = time_column_width + buffer_timing_column_separator.size() + qps_column_width; auto compare_name_length = [](const TestEntry& t1, const TestEntry& t2) { return t1.name.size() < t2.name.size(); }; auto test_with_longest_name = std::max_element(tests_.begin(), tests_.end(), compare_name_length); size_t name_column_width = test_with_longest_name->name.size(); size_t total_width = name_column_width + name_column_separator.size() + data_size_column_width + buffer_column_separator.size() + buffers.size() * (buffer_column_width + buffer_column_separator.size()); const std::string dbl_separator(total_width, '='); const std::string separator(total_width, '-'); auto print_header = [&](const std::string& header) { std::cout << dbl_separator << std::endl; std::stringstream ss; ss.imbue(std::locale(ss.getloc(), new CommaNumPunct)); ss << header << " (" << iteration_count << " iterations)"; std::cout << CenterString(ss.str(), total_width) << std::endl; std::cout << dbl_separator << std::endl; std::cout << std::setw(name_column_width) << "Test Name" << std::left << name_column_separator << std::setw(data_size_column_width) << CenterString("Size", data_size_column_width) << buffer_column_separator; for (const auto& buffer_info : buffers) { std::cout << std::setw(buffer_column_width) << CenterString(buffer_info.name, buffer_column_width) << buffer_column_separator; } std::cout << std::endl; std::cout << std::setw(name_column_width) << "" << name_column_separator << std::setw(data_size_column_width) << CenterString("bytes", data_size_column_width) << buffer_column_separator << std::left; for (size_t i = 0; i < buffers.size(); i++) { std::cout << std::setw(time_column_width) << CenterString("Time, s", time_column_width) << buffer_timing_column_separator << std::setw(qps_column_width) << CenterString("QPS", qps_column_width) << buffer_column_separator; } std::cout << std::right << std::endl; std::cout << separator << std::endl; }; print_header("Serialization benchmarks"); for (const auto& test : tests_) { if (test.serialize_test) { std::cout << std::setw(name_column_width) << test.name << " : " << std::setw(data_size_column_width) << test.data_size << buffer_column_separator; for (const auto& buffer_info : buffers) { auto seconds = std::chrono::duration_cast(test.serialize_test( buffer_info.writer, iteration_count, buffer_info.write_reset_func, buffer_info.reset_data)); double qps = iteration_count / seconds.count(); std::cout << std::fixed << std::setprecision(3) << std::setw(time_column_width) << seconds.count() << buffer_timing_column_separator << std::setw(qps_column_width) << qps << buffer_column_separator; } std::cout << std::endl; } } print_header("Deserialization benchmarks"); for (const auto& test : tests_) { if (test.deserialize_test) { std::cout << std::setw(name_column_width) << test.name << " : " << std::setw(data_size_column_width) << test.data_size << buffer_column_separator; for (const auto& buffer_info : buffers) { auto seconds = std::chrono::duration_cast(test.deserialize_test( buffer_info.reader, buffer_info.writer, iteration_count, buffer_info.read_reset_func, buffer_info.write_reset_func, buffer_info.reset_data)); double qps = iteration_count / seconds.count(); std::cout << std::fixed << std::setprecision(3) << std::setw(time_column_width) << seconds.count() << buffer_timing_column_separator << std::setw(qps_column_width) << qps << buffer_column_separator; } std::cout << std::endl; } } std::cout << dbl_separator << std::endl; } private: struct TestEntry { TestEntry(const std::string& test_name, std::function serialize_test, std::function deserialize_test, size_t data_size) : name{test_name}, serialize_test{std::move(serialize_test)}, deserialize_test{std::move(deserialize_test)}, data_size{data_size} {} std::string name; std::function serialize_test; std::function deserialize_test; size_t data_size; }; std::vector tests_; }; std::string GenerateContainerName(const std::string& type, size_t count) { std::stringstream ss; ss << type << "(" << count << ")"; return ss.str(); } } // anonymous namespace int main(int /*argc*/, char** /*argv*/) { const size_t iteration_count = 10000000; // 10M iterations. TestRunner test_runner; std::cout.imbue(std::locale(std::cout.getloc(), new CommaNumPunct)); // Baseline tests to figure out the overhead of buffer resizing and data // transfers. for (size_t len : {0, 1, 9, 66, 259}) { auto serialize_base_test = std::bind(&SerializeBaseTest, _1, _2, _3, _4, len); auto deserialize_base_test = std::bind(&DeserializeBaseTest, _1, _2, _3, _4, _5, _6, len); test_runner.AddTestFunc("--Baseline--", std::move(serialize_base_test), std::move(deserialize_base_test), len); } // Individual serialization/deserialization tests. test_runner.AddTest("bool", true); test_runner.AddTest("int32_t", 12); for (size_t len : {0, 1, 8, 64, 256}) { test_runner.AddTest(GenerateContainerName("string", len), std::string(len, '*')); } // Serialization is too slow to handle such large strings, add this test for // deserialization only. test_runner.AddDeserializationTest(GenerateContainerName("string", 10240), std::string(10240, '*')); for (size_t len : {0, 1, 8, 64, 256}) { std::vector int_vector(len); std::iota(int_vector.begin(), int_vector.end(), 0); test_runner.AddTest(GenerateContainerName("vector", len), std::move(int_vector)); } std::vector vector_of_strings = { "012345678901234567890123456789", "012345678901234567890123456789", "012345678901234567890123456789", "012345678901234567890123456789", "012345678901234567890123456789", }; test_runner.AddTest( GenerateContainerName("vector", vector_of_strings.size()), std::move(vector_of_strings)); test_runner.AddTest("tuple", std::make_tuple(123, true, std::string{"foobar"}, 1.1)); for (size_t len : {0, 1, 8, 64}) { std::map test_map; for (size_t i = 0; i < len; i++) test_map.emplace(i, std::to_string(i)); test_runner.AddTest(GenerateContainerName("map", len), std::move(test_map)); } for (size_t len : {0, 1, 8, 64}) { std::unordered_map test_map; for (size_t i = 0; i < len; i++) test_map.emplace(i, std::to_string(i)); test_runner.AddTest( GenerateContainerName("unordered_map", len), std::move(test_map)); } // BufferWrapper can't be used with deserialization tests right now because // it requires external buffer to be filled in, which is not available. std::vector> data_buffers; for (size_t len : {0, 1, 8, 64, 256}) { data_buffers.emplace_back(len); test_runner.AddSerializationTest( GenerateContainerName("BufferWrapper", len), BufferWrapper(data_buffers.back().data(), data_buffers.back().size())); } // Various backing buffers to run the tests on. std::vector buffers; Payload buffer; buffers.emplace_back("Non-TLS Buffer", &buffer, &buffer, [](void* ptr) { static_cast(ptr)->Rewind(); }, [](void* ptr) { static_cast(ptr)->Clear(); }, &buffer); TestPayload tls_buffer; buffers.emplace_back( "TLS Buffer", &tls_buffer, &tls_buffer, [](void* ptr) { static_cast(ptr)->Rewind(); }, [](void* ptr) { static_cast(ptr)->Clear(); }, &tls_buffer); StaticBuffer static_buffer; buffers.emplace_back( "Static Buffer", &static_buffer, &static_buffer, [](void* ptr) { static_cast(ptr)->Rewind(); }, [](void* ptr) { static_cast(ptr)->Clear(); }, &static_buffer); // Finally, run all the tests. test_runner.RunTests(iteration_count, buffers); return 0; } �����������������������������������������������������������libs/vr/libpdx/mock_tests.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000001340 13756501735 015340� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include TEST(MockTypes, Instantiation) { // Make sure all our interfaces are mocked out properly and mock instances // can be created. android::pdx::MockClientChannel client_channel; android::pdx::MockClientChannelFactory client_channel_factory; android::pdx::MockInputResourceMapper input_resource_mapper; android::pdx::MockMessageReader message_reader; android::pdx::MockOutputResourceMapper output_resource_mapper; android::pdx::MockMessageWriter message_writer; android::pdx::MockEndpoint endpoint; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/�����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014140� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 014733� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/channel_handle.h���������������������������������������������������������0100644 0000000 0000000 00000006413 13756501735 020030� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_CHANNEL_HANDLE_H_ #define ANDROID_PDX_CHANNEL_HANDLE_H_ #include #include namespace android { namespace pdx { enum class ChannelHandleMode { Local, Borrowed, Remote, }; class ChannelManagerInterface { public: virtual void CloseHandle(std::int32_t handle) = 0; protected: // Nobody should be allowed to delete the instance of channel manager // through this interface. virtual ~ChannelManagerInterface() = default; }; class ChannelHandleBase { public: ChannelHandleBase() = default; explicit ChannelHandleBase(const int32_t& value) : value_{value} {} ChannelHandleBase(const ChannelHandleBase&) = delete; ChannelHandleBase& operator=(const ChannelHandleBase&) = delete; std::int32_t value() const { return value_; } bool valid() const { return value_ >= 0; } explicit operator bool() const { return valid(); } void Close() { value_ = kEmptyHandle; } protected: // Must not be used by itself. Must be derived from. ~ChannelHandleBase() = default; enum : std::int32_t { kEmptyHandle = -1 }; std::int32_t value_{kEmptyHandle}; }; template class ChannelHandle : public ChannelHandleBase { public: ChannelHandle() = default; using ChannelHandleBase::ChannelHandleBase; ChannelHandle(ChannelHandle&& other) noexcept : ChannelHandleBase{other.value_} { other.value_ = kEmptyHandle; } ~ChannelHandle() = default; ChannelHandle Duplicate() const { return ChannelHandle{value_}; } ChannelHandle& operator=(ChannelHandle&& other) noexcept { value_ = other.value_; other.value_ = kEmptyHandle; return *this; } }; template <> class ChannelHandle : public ChannelHandleBase { public: ChannelHandle() = default; ChannelHandle(ChannelManagerInterface* manager, int32_t value) : ChannelHandleBase{value}, manager_{manager} {} ChannelHandle(const ChannelHandle&) = delete; ChannelHandle& operator=(const ChannelHandle&) = delete; ChannelHandle(ChannelHandle&& other) noexcept : ChannelHandleBase{other.value_}, manager_{other.manager_} { other.manager_ = nullptr; other.value_ = kEmptyHandle; } ChannelHandle& operator=(ChannelHandle&& other) noexcept { value_ = other.value_; manager_ = other.manager_; other.value_ = kEmptyHandle; other.manager_ = nullptr; return *this; } ~ChannelHandle() { if (manager_) manager_->CloseHandle(value_); } ChannelHandle Borrow() const { return ChannelHandle{value_}; } void Close() { if (manager_) manager_->CloseHandle(value_); manager_ = nullptr; value_ = kEmptyHandle; } private: ChannelManagerInterface* manager_{nullptr}; }; using LocalChannelHandle = ChannelHandle; using BorrowedChannelHandle = ChannelHandle; using RemoteChannelHandle = ChannelHandle; // ChannelReference is a 32 bit integer used in IPC serialization to be // transferred across processes. You can convert this value to a local channel // handle by calling Transaction.GetChannelHandle(). using ChannelReference = int32_t; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CHANNEL_HANDLE_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/channel_parcelable.h�����������������������������������������������������0100644 0000000 0000000 00000001723 13756501735 020666� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_CHANNEL_PARCELABLE_H_ #define ANDROID_PDX_CHANNEL_PARCELABLE_H_ #include #include namespace android { namespace pdx { /** * A parcelable object holds all necessary objects to recreate a ClientChannel. * In addition to the android::Parcelable interface, this interface exposees * more PDX-related interface. */ class ChannelParcelable : public Parcelable { public: virtual ~ChannelParcelable() = default; // Returns whether the parcelable object holds a valid client channel. virtual bool IsValid() const = 0; // Returns a channel handle constructed from this parcelable object and takes // the ownership of all resources from the parcelable object. In another word, // the parcelable object will become invalid after TakeChannelHandle returns. virtual LocalChannelHandle TakeChannelHandle() = 0; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CHANNEL_PARCELABLE_H_ ���������������������������������������������libs/vr/libpdx/private/pdx/client.h�����������������������������������������������������������������0100644 0000000 0000000 00000024146 13756501735 016366� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_CLIENT_H_ #define ANDROID_PDX_CLIENT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace pdx { class Transaction; /* * Base class of client-side service API classes. */ class Client { public: static const int64_t kInfiniteTimeout = -1; virtual ~Client() = default; /* * Returns true if the Client instance successfully initialized, false * otherwise. Subclasses that can fail to initialize must override this and * AND their initialization result with this base class method's result. * * This method is not intended to perform initialization, only to report * the status of the initialization. */ virtual bool IsInitialized() const; /* * Returns the error code describing the Client initialization failure, or 0 * if there was no failure. */ int error() const; // Returns a reference to IPC channel handle. LocalChannelHandle& GetChannelHandle(); const LocalChannelHandle& GetChannelHandle() const; protected: friend Transaction; explicit Client(std::unique_ptr channel); explicit Client(std::unique_ptr channel_factory, int64_t timeout_ms = kInfiniteTimeout); /* * Called by Client::Connect() after successfully connecting to the service * endpoint. Subclasses may override this method to perform additional setup, * including sending messages to complete the connection process. * * Subclasses may call Client::Close() within this method to terminate the * connection; Client::Connect() returns the negated error passed to * Client::Close() when this happens. */ virtual void OnConnect(); enum : size_t { MAX_IMPULSE_LENGTH = sizeof(uint64_t) * 4 }; Status SendImpulse(int opcode); Status SendImpulse(int opcode, const void* buffer, size_t length); /* * Remote method call API using pdx::rpc serialization. * Include pdx/rpc/remote_method.h to use these methods. */ template Status InvokeRemoteMethod(Args&&... args); template Status InvokeRemoteMethodInPlace(ReturnType* return_value, Args&&... args); /* * Close the endpoint file descriptor and optionally indicate an error, which * may be retrieved through error(). Subclasses may use this in their * constructor to signal failure during initialization or at other times * during operation. */ void Close(int error); /* * Returns true if the client is connected to the service, false otherwise. */ bool IsConnected() const; /* * Enables auto-reconnect with the given timeout. Use kInfiniteTimeout (-1) * for no timeout. Auto-reconnect can only be enabled if the Client class * was constructed with a ClientChannelFactory. */ void EnableAutoReconnect(int64_t reconnect_timeout_ms); /* * Disables auto-reconnect. */ void DisableAutoReconnect(); /* * Returns an fd that the client may use to check/wait for asynchronous * notifications to the channel. It is implementation dependent how the * transport backend handles this feature, however all implementations must * support at least POLLIN/EPOLLIN/readable. * * For uses that require more than one type of event, use * ClientChannel::GetEventMask() to distinguish between events. */ int event_fd() const; /* * Returns the underlying ClientChannel object. */ ClientChannel* GetChannel() const { return channel_.get(); } std::unique_ptr&& TakeChannel() { return std::move(channel_); } private: Client(const Client&) = delete; void operator=(const Client&) = delete; Status CheckReconnect(); bool NeedToDisconnectChannel(int error) const; void CheckDisconnect(int error); template inline void CheckDisconnect(const Status& status) { if (!status) CheckDisconnect(status.error()); } std::unique_ptr channel_; int error_{0}; // Reconnection state. std::unique_ptr channel_factory_; int64_t reconnect_timeout_ms_{0}; bool auto_reconnect_enabled_{false}; }; /* * Utility template base class for client-side service API classes. Handles * initialization checks during allocation and automatically cleans up on * failure. * * @tparam T Type of the class extending this one. * @tparam C Client class to wrap. Defaults to the Client class. */ template class ClientBase : public ParentClient { public: // Type of the client this class wraps. using ClientType = ParentClient; static_assert(std::is_base_of::value, "The provided parent client is not a Client subclass."); /* * Allocates a new instance of the superclass and checks for successful * initialization. * * The variadic arguments must expand to match one of type T's constructors * and are passed through unchanged. If a timeout is desired, subclasses are * responsible for passing this through to the appropriate ClientBase * constructor. * * Returns a unique_ptr to the new instance on success, or an empty unique_ptr * otherwise. */ template static inline std::unique_ptr Create(Args&&... args) { std::unique_ptr client(new T(std::forward(args)...)); if (client->IsInitialized()) return client; else return nullptr; } protected: /* * Type of the base class. Useful for referencing the base class type and * constructor in subclasses. Subclasses with non-public constructors * must declare BASE a friend. */ using BASE = ClientBase; /* * Type of the unique_ptr deleter. Useful for friend declarations. */ using deleter_type = typename std::unique_ptr::deleter_type; using ParentClient::ParentClient; }; class Transaction final : public OutputResourceMapper, public InputResourceMapper { public: explicit Transaction(Client& client); ~Transaction(); template Status Send(int opcode) { return SendVector(opcode, nullptr, 0, nullptr, 0); } template Status Send(int opcode, const void* send_buffer, size_t send_length, void* receive_buffer, size_t receive_length) { const bool send = (send_buffer && send_length); const bool receive = (receive_buffer && receive_length); const iovec send_vector = {const_cast(send_buffer), send_length}; const iovec receive_vector = {receive_buffer, receive_length}; return SendVector(opcode, send ? &send_vector : nullptr, send ? 1 : 0, receive ? &receive_vector : nullptr, receive ? 1 : 0); } template Status SendVector(int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) { Status ret; SendTransaction(opcode, &ret, send_vector, send_count, receive_vector, receive_count); return ret; } template Status SendVector(int opcode, const iovec (&send_vector)[send_count], const iovec (&receive_vector)[receive_count]) { return SendVector(opcode, send_vector, send_count, receive_vector, receive_count); } template Status SendVector(int opcode, const iovec (&send_vector)[send_count], std::nullptr_t) { return SendVector(opcode, send_vector, send_count, nullptr, 0); } template Status SendVector(int opcode, std::nullptr_t, const iovec (&receive_vector)[receive_count]) { return SendVector(opcode, nullptr, 0, receive_vector, receive_count); } // OutputResourceMapper Status PushFileHandle(const LocalHandle& handle) override; Status PushFileHandle(const BorrowedHandle& handle) override; Status PushFileHandle(const RemoteHandle& handle) override; Status PushChannelHandle( const LocalChannelHandle& handle) override; Status PushChannelHandle( const BorrowedChannelHandle& handle) override; Status PushChannelHandle( const RemoteChannelHandle& handle) override; // InputResourceMapper bool GetFileHandle(FileReference ref, LocalHandle* handle) override; bool GetChannelHandle(ChannelReference ref, LocalChannelHandle* handle) override; private: bool EnsureStateAllocated(); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void SendTransaction(int opcode, Status* ret, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count); void CheckDisconnect(int error); template inline void CheckDisconnect(const Status& status) { if (!status) CheckDisconnect(status.error()); } Client& client_; void* state_{nullptr}; bool state_allocated_{false}; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CLIENT_H_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/client_channel.h���������������������������������������������������������0100644 0000000 0000000 00000005751 13756501735 020057� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_ #define ANDROID_PDX_CLIENT_CHANNEL_H_ #include #include #include #include #include struct iovec; namespace android { namespace pdx { class ClientChannel { public: virtual ~ClientChannel() = default; // Returns a tag that uniquely identifies a specific underlying IPC transport. virtual uint32_t GetIpcTag() const = 0; virtual int event_fd() const = 0; virtual Status GetEventMask(int events) = 0; struct EventSource { int event_fd; int event_mask; }; // Returns a set of event-generating fds with and event mask for each. These // fds are owned by the ClientChannel and must never be closed by the caller. virtual std::vector GetEventSources() const = 0; virtual LocalChannelHandle& GetChannelHandle() = 0; virtual const LocalChannelHandle& GetChannelHandle() const = 0; virtual void* AllocateTransactionState() = 0; virtual void FreeTransactionState(void* state) = 0; virtual Status SendImpulse(int opcode, const void* buffer, size_t length) = 0; virtual Status SendWithInt(void* transaction_state, int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) = 0; virtual Status SendWithFileHandle( void* transaction_state, int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) = 0; virtual Status SendWithChannelHandle( void* transaction_state, int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count) = 0; virtual FileReference PushFileHandle(void* transaction_state, const LocalHandle& handle) = 0; virtual FileReference PushFileHandle(void* transaction_state, const BorrowedHandle& handle) = 0; virtual ChannelReference PushChannelHandle( void* transaction_state, const LocalChannelHandle& handle) = 0; virtual ChannelReference PushChannelHandle( void* transaction_state, const BorrowedChannelHandle& handle) = 0; virtual bool GetFileHandle(void* transaction_state, FileReference ref, LocalHandle* handle) const = 0; virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref, LocalChannelHandle* handle) const = 0; // Returns the internal state of the channel as a parcelable object. The // ClientChannel is invalidated however, the channel is kept alive by the // parcelable object and may be transferred to another process. virtual std::unique_ptr TakeChannelParcelable() = 0; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CLIENT_CHANNEL_H_ �����������������������libs/vr/libpdx/private/pdx/client_channel_factory.h�������������������������������������������������0100644 0000000 0000000 00000000720 13756501735 021575� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_ #define ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_ #include #include namespace android { namespace pdx { class ClientChannelFactory { public: virtual ~ClientChannelFactory() = default; virtual Status> Connect( int64_t timeout_ms) const = 0; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CLIENT_CHANNEL_FACTORY_H_ ������������������������������������������������libs/vr/libpdx/private/pdx/file_handle.h������������������������������������������������������������0100644 0000000 0000000 00000010531 13756501735 017333� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_FILE_HANDLE_H_ #define ANDROID_PDX_FILE_HANDLE_H_ #include #include #include namespace android { namespace pdx { enum class FileHandleMode { Local, Remote, Borrowed, }; // Manages ownership, sharing, and lifetime of file descriptors. template class FileHandle { public: static constexpr int kEmptyFileHandle = -1; // Constructs an empty FileHandle object. FileHandle() : fd_(kEmptyFileHandle) {} // Constructs a FileHandle from an integer file descriptor and takes // ownership. explicit FileHandle(int fd) : fd_(fd) {} // Constructs a FileHandle by opening |path|. The arguments follow the // semantics of open(). FileHandle(const std::string& path, int flags, mode_t mode = 0) { fd_ = open(path.c_str(), flags, mode); } // Constructs a FileHandle by opening |path| relative to |dir_fd|, following // the semantics of openat(). FileHandle(const int directory_fd, const std::string& path, int flags, mode_t mode = 0) { fd_ = openat(directory_fd, path.c_str(), flags, mode); } // Move constructor that assumes ownership of the file descriptor, leaving the // other FileHandle object empty. FileHandle(FileHandle&& other) noexcept { fd_ = other.fd_; other.fd_ = kEmptyFileHandle; } // Returns a FileHandle as a duplicate handle of |fd|. Borrowed handles and // handles in remote handle space are not duplicated. static FileHandle AsDuplicate(const int fd) { if (Mode == FileHandleMode::Local) return FileHandle(dup(fd)); else return FileHandle(fd); } // Destructor closes the file descriptor when non-empty. ~FileHandle() { Close(); } // Move assignment operator that assumes ownership of the underlying file // descriptor, leaving the other FileHandle object empty. FileHandle& operator=(FileHandle&& other) noexcept { if (this != &other) { Reset(other.fd_); other.fd_ = kEmptyFileHandle; } return *this; } // Resets the underlying file handle to |fd|. void Reset(int fd) { Close(); fd_ = fd; } // Closes the underlying file descriptor when non-empty. void Close() { if (IsValid() && Mode == FileHandleMode::Local) close(fd_); fd_ = kEmptyFileHandle; } // Return the internal fd, passing ownership to the caller. int Release() { int release_fd = fd_; fd_ = kEmptyFileHandle; return release_fd; } // Duplicates the underlying file descriptor and returns a FileHandle that // owns the new file descriptor. File descriptors are not duplicated when Mode // is Remote or Borrowed. FileHandle Duplicate() const { return FileHandle(Mode == FileHandleMode::Local ? dup(fd_) : fd_); } FileHandle Borrow() const { return FileHandle(Get()); } // Gets the underlying file descriptor value. int Get() const { return fd_; } bool IsValid() const { return fd_ >= 0; } explicit operator bool() const { return IsValid(); } private: int fd_; FileHandle(const FileHandle&) = delete; void operator=(const FileHandle&) = delete; }; // Alias for a file handle in the local process' handle space. using LocalHandle = FileHandle; // Alias for a file handle in another process' handle space. Handles returned // from pushing a file object or channel must be stored in this type of handle // class, which doesn't close the underlying file descriptor. The underlying // file descriptor in this wrapper should not be passed to close() because doing // so would close an unrelated file descriptor in the local handle space. using RemoteHandle = FileHandle; // Alias for borrowed handles in the local process' handle space. A borrowed // file handle is not close() because this wrapper does not own the underlying // file descriptor. Care must be take to ensure that a borrowed file handle // remains valid during use. using BorrowedHandle = FileHandle; // FileReference is a 16 bit integer used in IPC serialization to be // transferred across processes. You can convert this value to a local file // handle by calling Transaction.GetFileHandle() on client side and // Message.GetFileHandle() on service side. using FileReference = int16_t; } // namespace pdx } // namespace android #endif // ANDROID_PDX_FILE_HANDLE_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/message_reader.h���������������������������������������������������������0100644 0000000 0000000 00000001712 13756501735 020050� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MESSAGE_READER_H_ #define ANDROID_PDX_MESSAGE_READER_H_ #include #include #include namespace android { namespace pdx { class InputResourceMapper { public: virtual bool GetFileHandle(FileReference ref, LocalHandle* handle) = 0; virtual bool GetChannelHandle(ChannelReference ref, LocalChannelHandle* handle) = 0; protected: virtual ~InputResourceMapper() = default; }; class MessageReader { public: // Pointers to start/end of the region in the read buffer. using BufferSection = std::pair; virtual BufferSection GetNextReadBufferSection() = 0; virtual void ConsumeReadBufferSectionData(const void* new_start) = 0; virtual InputResourceMapper* GetInputResourceMapper() = 0; protected: virtual ~MessageReader() = default; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MESSAGE_READER_H_ ������������������������������������������������������libs/vr/libpdx/private/pdx/message_writer.h���������������������������������������������������������0100644 0000000 0000000 00000002170 13756501735 020121� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MESSAGE_WRITER_H_ #define ANDROID_PDX_MESSAGE_WRITER_H_ #include #include #include namespace android { namespace pdx { class OutputResourceMapper { public: virtual Status PushFileHandle(const LocalHandle& handle) = 0; virtual Status PushFileHandle( const BorrowedHandle& handle) = 0; virtual Status PushFileHandle(const RemoteHandle& handle) = 0; virtual Status PushChannelHandle( const LocalChannelHandle& handle) = 0; virtual Status PushChannelHandle( const BorrowedChannelHandle& handle) = 0; virtual Status PushChannelHandle( const RemoteChannelHandle& handle) = 0; protected: virtual ~OutputResourceMapper() = default; }; class MessageWriter { public: virtual void* GetNextWriteBufferSection(size_t size) = 0; virtual OutputResourceMapper* GetOutputResourceMapper() = 0; protected: virtual ~MessageWriter() = default; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MESSAGE_WRITER_H_ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/mock_client_channel.h����������������������������������������������������0100644 0000000 0000000 00000005406 13756501735 021065� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_ #define ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_ #include #include namespace android { namespace pdx { class MockClientChannel : public ClientChannel { public: MOCK_CONST_METHOD0(GetIpcTag, uint32_t()); MOCK_CONST_METHOD0(event_fd, int()); MOCK_CONST_METHOD0(GetEventSources, std::vector()); MOCK_METHOD1(GetEventMask, Status(int)); MOCK_METHOD0(GetChannelHandle, LocalChannelHandle&()); MOCK_CONST_METHOD0(GetChannelHandle, const LocalChannelHandle&()); MOCK_METHOD0(AllocateTransactionState, void*()); MOCK_METHOD1(FreeTransactionState, void(void* state)); MOCK_METHOD3(SendImpulse, Status(int opcode, const void* buffer, size_t length)); MOCK_METHOD6(SendWithInt, Status(void* transaction_state, int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count)); MOCK_METHOD6(SendWithFileHandle, Status(void* transaction_state, int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count)); MOCK_METHOD6(SendWithChannelHandle, Status(void* transaction_state, int opcode, const iovec* send_vector, size_t send_count, const iovec* receive_vector, size_t receive_count)); MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state, const LocalHandle& handle)); MOCK_METHOD2(PushFileHandle, FileReference(void* transaction_state, const BorrowedHandle& handle)); MOCK_METHOD2(PushChannelHandle, ChannelReference(void* transaction_state, const LocalChannelHandle& handle)); MOCK_METHOD2(PushChannelHandle, ChannelReference(void* transaction_state, const BorrowedChannelHandle& handle)); MOCK_CONST_METHOD3(GetFileHandle, bool(void* transaction_state, FileReference ref, LocalHandle* handle)); MOCK_CONST_METHOD3(GetChannelHandle, bool(void* transaction_state, ChannelReference ref, LocalChannelHandle* handle)); MOCK_METHOD0(TakeChannelParcelable, std::unique_ptr()); }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/mock_client_channel_factory.h��������������������������������������������0100644 0000000 0000000 00000000737 13756501735 022616� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_ #define ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_ #include #include namespace android { namespace pdx { class MockClientChannelFactory : public ClientChannelFactory { public: MOCK_CONST_METHOD1( Connect, Status>(int64_t timeout_ms)); }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MOCK_CLIENT_CHANNEL_FACTORY_H_ ���������������������������������libs/vr/libpdx/private/pdx/mock_message_reader.h����������������������������������������������������0100644 0000000 0000000 00000001425 13756501735 021062� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MOCK_MESSAGE_READER_H_ #define ANDROID_PDX_MOCK_MESSAGE_READER_H_ #include #include namespace android { namespace pdx { class MockInputResourceMapper : public InputResourceMapper { public: MOCK_METHOD2(GetFileHandle, bool(FileReference ref, LocalHandle* handle)); MOCK_METHOD2(GetChannelHandle, bool(ChannelReference ref, LocalChannelHandle* handle)); }; class MockMessageReader : public MessageReader { public: MOCK_METHOD0(GetNextReadBufferSection, BufferSection()); MOCK_METHOD1(ConsumeReadBufferSectionData, void(const void* new_start)); MOCK_METHOD0(GetInputResourceMapper, InputResourceMapper*()); }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MOCK_MESSAGE_READER_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/mock_message_writer.h����������������������������������������������������0100644 0000000 0000000 00000002221 13756501735 021127� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MOCK_MESSAGE_WRITER_H_ #define ANDROID_PDX_MOCK_MESSAGE_WRITER_H_ #include #include namespace android { namespace pdx { class MockOutputResourceMapper : public OutputResourceMapper { public: MOCK_METHOD1(PushFileHandle, Status(const LocalHandle& handle)); MOCK_METHOD1(PushFileHandle, Status(const BorrowedHandle& handle)); MOCK_METHOD1(PushFileHandle, Status(const RemoteHandle& handle)); MOCK_METHOD1(PushChannelHandle, Status(const LocalChannelHandle& handle)); MOCK_METHOD1(PushChannelHandle, Status(const BorrowedChannelHandle& handle)); MOCK_METHOD1(PushChannelHandle, Status(const RemoteChannelHandle& handle)); }; class MockMessageWriter : public MessageWriter { public: MOCK_METHOD1(GetNextWriteBufferSection, void*(size_t size)); MOCK_METHOD0(GetOutputResourceMapper, OutputResourceMapper*()); }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MOCK_MESSAGE_WRITER_H_ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/mock_service_endpoint.h��������������������������������������������������0100644 0000000 0000000 00000006537 13756501735 021465� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_MOCK_ENDPOINT_H_ #define ANDROID_PDX_MOCK_ENDPOINT_H_ #include #include namespace android { namespace pdx { class MockEndpoint : public Endpoint { public: MOCK_CONST_METHOD0(GetIpcTag, uint32_t()); MOCK_METHOD1(SetService, Status(Service* service)); MOCK_METHOD2(SetChannel, Status(int channel_id, Channel* channel)); MOCK_METHOD1(CloseChannel, Status(int channel_id)); MOCK_METHOD3(ModifyChannelEvents, Status(int channel_id, int clear_mask, int set_mask)); MOCK_METHOD4(PushChannel, Status(Message* message, int flags, Channel* channel, int* channel_id)); MOCK_METHOD3(CheckChannel, Status(const Message* message, ChannelReference ref, Channel** channel)); MOCK_METHOD1(MessageReceive, Status(Message* message)); MOCK_METHOD2(MessageReply, Status(Message* message, int return_code)); MOCK_METHOD2(MessageReplyFd, Status(Message* message, unsigned int push_fd)); MOCK_METHOD2(MessageReplyChannelHandle, Status(Message* message, const LocalChannelHandle& handle)); MOCK_METHOD2(MessageReplyChannelHandle, Status(Message* message, const BorrowedChannelHandle& handle)); MOCK_METHOD2(MessageReplyChannelHandle, Status(Message* message, const RemoteChannelHandle& handle)); MOCK_METHOD3(ReadMessageData, Status(Message* message, const iovec* vector, size_t vector_length)); MOCK_METHOD3(WriteMessageData, Status(Message* message, const iovec* vector, size_t vector_length)); MOCK_METHOD2(PushFileHandle, Status(Message* message, const LocalHandle& handle)); MOCK_METHOD2(PushFileHandle, Status(Message* message, const BorrowedHandle& handle)); MOCK_METHOD2(PushFileHandle, Status(Message* message, const RemoteHandle& handle)); MOCK_METHOD2(PushChannelHandle, Status(Message* message, const LocalChannelHandle& handle)); MOCK_METHOD2(PushChannelHandle, Status(Message* message, const BorrowedChannelHandle& handle)); MOCK_METHOD2(PushChannelHandle, Status(Message* message, const RemoteChannelHandle& handle)); MOCK_CONST_METHOD2(GetFileHandle, LocalHandle(Message* message, FileReference ref)); MOCK_CONST_METHOD2(GetChannelHandle, LocalChannelHandle(Message* message, ChannelReference ref)); MOCK_METHOD0(AllocateMessageState, void*()); MOCK_METHOD1(FreeMessageState, void(void* state)); MOCK_METHOD0(Cancel, Status()); MOCK_CONST_METHOD0(epoll_fd, int()); }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_MOCK_ENDPOINT_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/rpc/���������������������������������������������������������������������0040755 0000000 0000000 00000000000 13756501735 015517� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/rpc/argument_encoder.h���������������������������������������������������0100644 0000000 0000000 00000013305 13756501735 021210� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_ #define ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_ #include #include #include #include #include namespace android { namespace pdx { namespace rpc { // Provides automatic serialization of argument lists and return // values by analyzing the supplied function signature types. // Examples: // ArgumentEncoder encoder(writer); // encoder.EncodeArguments(1, 1.0); template class ArgumentEncoder; // Specialization of ArgumentEncoder for void return types. template class ArgumentEncoder { public: explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {} // Serializes the arguments as a tuple. void EncodeArguments(Args... args) { Serialize(std::forward_as_tuple(args...), writer_); } private: MessageWriter* writer_; }; // Specialization of ArgumentEncoder for non-void return types. template class ArgumentEncoder { public: // Simplified types with reference and cv removed. using ReturnType = typename std::decay::type; explicit ArgumentEncoder(MessageWriter* writer) : writer_{writer} {} // Serializes the arguments as a tuple. void EncodeArguments(Args... args) { Serialize(std::forward_as_tuple(args...), writer_); } // Serializes the return value for rvalue references. void EncodeReturn(const ReturnType& return_value) { Serialize(return_value, writer_); } private: MessageWriter* writer_; }; // Utility to build an ArgumentEncoder from a function pointer and a message // writer. template inline ArgumentEncoder MakeArgumentEncoder( Return (*)(Args...), MessageWriter* writer) { return ArgumentEncoder(writer); } // Utility to build an ArgumentEncoder from a method pointer and a message // writer. template inline ArgumentEncoder MakeArgumentEncoder( Return (Class::*)(Args...), MessageWriter* writer) { return ArgumentEncoder(writer); } // Utility to build an ArgumentEncoder from a const method pointer and a // message writer. template inline ArgumentEncoder MakeArgumentEncoder( Return (Class::*)(Args...) const, MessageWriter* writer) { return ArgumentEncoder(writer); } // Utility to build an ArgumentEncoder from a function type and a message // writer. template inline ArgumentEncoder MakeArgumentEncoder(MessageWriter* writer) { return ArgumentEncoder(writer); } ////////////////////////////////////////////////////////////////////////////// // Provides automatic deserialization of argument lists and return // values by analyzing the supplied function signature types. // Examples: // auto decoder = MakeArgumentDecoder(reader); // ErrorType error = decoder.DecodeReturn(&return_value); template class ArgumentDecoder; // Specialization of ArgumentDecoder for void return types. template class ArgumentDecoder { public: // Simplified types with reference and cv removed. using ArgsTupleType = std::tuple::type...>; explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {} // Deserializes arguments into a tuple. ArgsTupleType DecodeArguments(ErrorType* error) { ArgsTupleType value; *error = Deserialize(&value, reader_); return value; } private: MessageReader* reader_; }; // Specialization of ArgumentDecoder for non-void return types. template class ArgumentDecoder { public: // Simplified types with reference and cv removed. using ArgsTupleType = std::tuple::type...>; using ReturnType = typename std::decay::type; explicit ArgumentDecoder(MessageReader* reader) : reader_{reader} {} // Deserializes arguments into a tuple. ArgsTupleType DecodeArguments(ErrorType* error) { ArgsTupleType value; *error = Deserialize(&value, reader_); return value; } // Deserializes the return value. ErrorType DecodeReturn(ReturnType* value) { return Deserialize(value, reader_); } private: MessageReader* reader_; }; // Utility to build an ArgumentDecoder from a function pointer and a message // reader. template inline ArgumentDecoder MakeArgumentDecoder( Return (*)(Args...), MessageReader* reader) { return ArgumentDecoder(reader); } // Utility to build an ArgumentDecoder from a method pointer and a message // reader. template inline ArgumentDecoder MakeArgumentDecoder( Return (Class::*)(Args...), MessageReader* reader) { return ArgumentDecoder(reader); } // Utility to build an ArgumentDecoder from a const method pointer and a // message reader. template inline ArgumentDecoder MakeArgumentDecoder( Return (Class::*)(Args...) const, MessageReader* reader) { return ArgumentDecoder(reader); } // Utility to build an ArgumentDecoder from a function type and a message // reader. template inline ArgumentDecoder MakeArgumentDecoder(MessageReader* reader) { return ArgumentDecoder(reader); } } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_ARGUMENT_ENCODER_H_ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/rpc/array_wrapper.h������������������������������������������������������0100644 0000000 0000000 00000006143 13756501735 020547� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_RPC_ARRAY_WRAPPER_H_ #define ANDROID_PDX_RPC_ARRAY_WRAPPER_H_ #include #include #include #include namespace android { namespace pdx { namespace rpc { // Wrapper class for C array buffers, providing an interface suitable for // SerializeObject and DeserializeObject. This class serializes to the same // format as std::vector, and may be substituted for std::vector during // serialization and deserialization. This substitution makes handling of C // arrays more efficient by avoiding unnecessary copies when remote method // signatures specify std::vector arguments or return values. template class ArrayWrapper { public: // Define types in the style of STL containers to support STL operators. typedef T value_type; typedef std::size_t size_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; typedef const T* const_pointer; ArrayWrapper() : buffer_(nullptr), capacity_(0), end_(0) {} ArrayWrapper(pointer buffer, size_type capacity, size_type size) : buffer_(&buffer[0]), capacity_(capacity), end_(capacity < size ? capacity : size) {} ArrayWrapper(pointer buffer, size_type size) : ArrayWrapper(buffer, size, size) {} ArrayWrapper(const ArrayWrapper& other) { *this = other; } ArrayWrapper(ArrayWrapper&& other) noexcept { *this = std::move(other); } ArrayWrapper& operator=(const ArrayWrapper& other) { if (&other == this) { return *this; } else { buffer_ = other.buffer_; capacity_ = other.capacity_; end_ = other.end_; } return *this; } ArrayWrapper& operator=(ArrayWrapper&& other) noexcept { if (&other == this) { return *this; } else { buffer_ = other.buffer_; capacity_ = other.capacity_; end_ = other.end_; other.buffer_ = nullptr; other.capacity_ = 0; other.end_ = 0; } return *this; } pointer data() { return buffer_; } const_pointer data() const { return buffer_; } pointer begin() { return &buffer_[0]; } pointer end() { return &buffer_[end_]; } const_pointer begin() const { return &buffer_[0]; } const_pointer end() const { return &buffer_[end_]; } size_type size() const { return end_; } size_type max_size() const { return capacity_; } size_type capacity() const { return capacity_; } // Moves the end marker to |size|, clamping the end marker to the max capacity // of the underlying array. This method does not change the size of the // underlying array. void resize(size_type size) { if (size <= capacity_) end_ = size; else end_ = capacity_; } reference operator[](size_type pos) { return buffer_[pos]; } const_reference operator[](size_type pos) const { return buffer_[pos]; } private: pointer buffer_; size_type capacity_; size_type end_; }; template ArrayWrapper WrapArray(T* buffer, SizeType size) { return ArrayWrapper(buffer, size); } } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_ARRAY_WRAPPER_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/rpc/buffer_wrapper.h�����������������������������������������������������0100644 0000000 0000000 00000012771 13756501735 020706� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_RPC_BUFFER_WRAPPER_H_ #define ANDROID_PDX_RPC_BUFFER_WRAPPER_H_ #include #include #include #include namespace android { namespace pdx { namespace rpc { // Wrapper class for buffers, providing an interface suitable for // SerializeObject and DeserializeObject. This class supports serialization of // buffers as raw bytes. template class BufferWrapper; template class BufferWrapper { public: // Define types in the style of STL containers to support STL operators. typedef T value_type; typedef std::size_t size_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; typedef const T* const_pointer; BufferWrapper() : buffer_(nullptr), capacity_(0), end_(0) {} BufferWrapper(pointer buffer, size_type capacity, size_type size) : buffer_(&buffer[0]), capacity_(capacity), end_(capacity < size ? capacity : size) {} BufferWrapper(pointer buffer, size_type size) : BufferWrapper(buffer, size, size) {} BufferWrapper(const BufferWrapper& other) { *this = other; } BufferWrapper(BufferWrapper&& other) noexcept { *this = std::move(other); } BufferWrapper& operator=(const BufferWrapper& other) { if (&other == this) { return *this; } else { buffer_ = other.buffer_; capacity_ = other.capacity_; end_ = other.end_; } return *this; } BufferWrapper& operator=(BufferWrapper&& other) noexcept { if (&other == this) { return *this; } else { buffer_ = other.buffer_; capacity_ = other.capacity_; end_ = other.end_; other.buffer_ = nullptr; other.capacity_ = 0; other.end_ = 0; } return *this; } pointer data() { return buffer_; } const_pointer data() const { return buffer_; } pointer begin() { return &buffer_[0]; } pointer end() { return &buffer_[end_]; } const_pointer begin() const { return &buffer_[0]; } const_pointer end() const { return &buffer_[end_]; } size_type size() const { return end_; } size_type max_size() const { return capacity_; } size_type capacity() const { return capacity_; } void resize(size_type size) { if (size <= capacity_) end_ = size; else end_ = capacity_; } reference operator[](size_type pos) { return buffer_[pos]; } const_reference operator[](size_type pos) const { return buffer_[pos]; } private: pointer buffer_; size_type capacity_; size_type end_; }; template class BufferWrapper> { public: using BufferType = typename std::vector; using value_type = typename BufferType::value_type; using size_type = typename BufferType::size_type; using reference = typename BufferType::reference; using const_reference = typename BufferType::const_reference; using pointer = typename BufferType::pointer; using const_pointer = typename BufferType::const_pointer; using iterator = typename BufferType::iterator; using const_iterator = typename BufferType::const_iterator; BufferWrapper() {} explicit BufferWrapper(const BufferType& buffer) : buffer_(buffer) {} BufferWrapper(const BufferType& buffer, const Allocator& allocator) : buffer_(buffer, allocator) {} explicit BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {} BufferWrapper(BufferType&& buffer, const Allocator& allocator) : buffer_(std::move(buffer), allocator) {} BufferWrapper(const BufferWrapper&) = default; BufferWrapper(BufferWrapper&&) noexcept = default; BufferWrapper& operator=(const BufferWrapper&) = default; BufferWrapper& operator=(BufferWrapper&&) noexcept = default; pointer data() { return buffer_.data(); } const_pointer data() const { return buffer_.data(); } iterator begin() { return buffer_.begin(); } iterator end() { return buffer_.end(); } const_iterator begin() const { return buffer_.begin(); } const_iterator end() const { return buffer_.end(); } size_type size() const { return buffer_.size(); } size_type max_size() const { return buffer_.capacity(); } size_type capacity() const { return buffer_.capacity(); } void resize(size_type size) { buffer_.resize(size); } void reserve(size_type size) { buffer_.reserve(size); } reference operator[](size_type pos) { return buffer_[pos]; } const_reference operator[](size_type pos) const { return buffer_[pos]; } BufferType& buffer() { return buffer_; } const BufferType& buffer() const { return buffer_; } private: BufferType buffer_; }; template BufferWrapper WrapBuffer(T* buffer, SizeType size) { return BufferWrapper(buffer, size); } template BufferWrapper WrapBuffer(void* buffer, SizeType size) { return BufferWrapper(static_cast(buffer), size); } template BufferWrapper WrapBuffer(const void* buffer, SizeType size) { return BufferWrapper( static_cast(buffer), size); } template > BufferWrapper> WrapBuffer( std::vector&& buffer) { return BufferWrapper>( std::forward>(buffer)); } } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_BUFFER_WRAPPER_H_ �������libs/vr/libpdx/private/pdx/rpc/copy_cv_reference.h��������������������������������������������������0100644 0000000 0000000 00000002623 13756501735 021350� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_ #define ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_ #include namespace android { namespace pdx { namespace rpc { // Copies const, void, and reference qualifiers from type T to type U, such that // the new type U' carries the same cv-reference qualifiers as T, with the same // underlying type as U. template class CopyCVReference { private: using R = typename std::remove_reference::type; using U1 = typename std::conditional::value, typename std::add_const::type, U>::type; using U2 = typename std::conditional::value, typename std::add_volatile::type, U1>::type; using U3 = typename std::conditional::value, typename std::add_lvalue_reference::type, U2>::type; using U4 = typename std::conditional::value, typename std::add_rvalue_reference::type, U3>::type; public: using Type = U4; }; template using CopyCVReferenceType = typename CopyCVReference::Type; } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_COPY_CV_REFERENCE_H_ libs/vr/libpdx/private/pdx/rpc/default_initialization_allocator.h0100644 0000000 0000000 00000003171 13756501735 024462 0ustar000000000 0000000 #ifndef ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_ #define ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_ #include namespace android { namespace pdx { namespace rpc { // Allocator adaptor that interposes construct() calls to convert value // initialization into default initialization. All standard containers // value-initialize their elements when constructed with a single size_type // argument or when grown by a call to resize. This allocator avoids potentially // costly value-initialization in these situations for value types that are // default constructible. As a consequence, elements of non-class types are left // uninitialized; this is desirable when using std::vector as a resizable // buffer, for example. template > class DefaultInitializationAllocator : public Allocator { typedef std::allocator_traits AllocatorTraits; public: template struct rebind { using other = DefaultInitializationAllocator< U, typename AllocatorTraits::template rebind_alloc>; }; using Allocator::Allocator; template void construct(U* pointer) noexcept( std::is_nothrow_default_constructible::value) { ::new (static_cast(pointer)) U; } template void construct(U* pointer, Args&&... args) { AllocatorTraits::construct(static_cast(*this), pointer, std::forward(args)...); } }; } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_DEFAULT_INITIALIZATION_ALLOCATOR_H_ libs/vr/libpdx/private/pdx/rpc/encoding.h0100644 0000000 0000000 00000043631 13756501735 017462 0ustar000000000 0000000 #ifndef ANDROID_PDX_RPC_ENCODING_H_ #define ANDROID_PDX_RPC_ENCODING_H_ #include #include #include #include #include #include #include #include #include #include #include #include "array_wrapper.h" #include "buffer_wrapper.h" #include "string_wrapper.h" #include "variant.h" namespace android { namespace pdx { namespace rpc { // This library uses a subset, or profile, of MessagePack (http://msgpack.org) // to encode supported data types during serialization and to verify the // expected data types during deserialization. One notable deviation from the // MessagePack specification is that little-endian byte order is used for // multi-byte numeric types to avoid unnecessary conversion on nearly all // relevant architectures. // // Some data types, integers for example, support multiple encoding strategies. // This library attempts to optimize for space based on the value of such types. // However, during decode all valid encodings for a given type are accepted. // Prefix byte for type encodings. This is the complete list of prefix bytes // from the MessagePack specification, even though not all types are used by // this library. enum EncodingPrefix { ENCODING_TYPE_POSITIVE_FIXINT = 0x00, ENCODING_TYPE_POSITIVE_FIXINT_MIN = 0x00, ENCODING_TYPE_POSITIVE_FIXINT_MAX = 0x7f, ENCODING_TYPE_POSITIVE_FIXINT_MASK = 0x7f, ENCODING_TYPE_FIXMAP = 0x80, ENCODING_TYPE_FIXMAP_MIN = 0x80, ENCODING_TYPE_FIXMAP_MAX = 0x8f, ENCODING_TYPE_FIXMAP_MASK = 0x0f, ENCODING_TYPE_FIXARRAY = 0x90, ENCODING_TYPE_FIXARRAY_MIN = 0x90, ENCODING_TYPE_FIXARRAY_MAX = 0x9f, ENCODING_TYPE_FIXARRAY_MASK = 0x0f, ENCODING_TYPE_FIXSTR = 0xa0, ENCODING_TYPE_FIXSTR_MIN = 0xa0, ENCODING_TYPE_FIXSTR_MAX = 0xbf, ENCODING_TYPE_FIXSTR_MASK = 0x1f, ENCODING_TYPE_NIL = 0xc0, ENCODING_TYPE_RESERVED = 0xc1, ENCODING_TYPE_FALSE = 0xc2, ENCODING_TYPE_TRUE = 0xc3, ENCODING_TYPE_BIN8 = 0xc4, ENCODING_TYPE_BIN16 = 0xc5, ENCODING_TYPE_BIN32 = 0xc6, ENCODING_TYPE_EXT8 = 0xc7, ENCODING_TYPE_EXT16 = 0xc8, ENCODING_TYPE_EXT32 = 0xc9, ENCODING_TYPE_FLOAT32 = 0xca, ENCODING_TYPE_FLOAT64 = 0xcb, ENCODING_TYPE_UINT8 = 0xcc, ENCODING_TYPE_UINT16 = 0xcd, ENCODING_TYPE_UINT32 = 0xce, ENCODING_TYPE_UINT64 = 0xcf, ENCODING_TYPE_INT8 = 0xd0, ENCODING_TYPE_INT16 = 0xd1, ENCODING_TYPE_INT32 = 0xd2, ENCODING_TYPE_INT64 = 0xd3, ENCODING_TYPE_FIXEXT1 = 0xd4, ENCODING_TYPE_FIXEXT2 = 0xd5, ENCODING_TYPE_FIXEXT4 = 0xd6, ENCODING_TYPE_FIXEXT8 = 0xd7, ENCODING_TYPE_FIXEXT16 = 0xd8, ENCODING_TYPE_STR8 = 0xd9, ENCODING_TYPE_STR16 = 0xda, ENCODING_TYPE_STR32 = 0xdb, ENCODING_TYPE_ARRAY16 = 0xdc, ENCODING_TYPE_ARRAY32 = 0xdd, ENCODING_TYPE_MAP16 = 0xde, ENCODING_TYPE_MAP32 = 0xdf, ENCODING_TYPE_NEGATIVE_FIXINT = 0xe0, ENCODING_TYPE_NEGATIVE_FIXINT_MIN = 0xe0, ENCODING_TYPE_NEGATIVE_FIXINT_MAX = 0xff, }; // Base encoding classes grouping multi-strategy encodings. enum EncodingClass { ENCODING_CLASS_BOOL, ENCODING_CLASS_NIL, ENCODING_CLASS_INT, ENCODING_CLASS_UINT, ENCODING_CLASS_FLOAT, ENCODING_CLASS_ARRAY, ENCODING_CLASS_MAP, ENCODING_CLASS_STRING, ENCODING_CLASS_BINARY, ENCODING_CLASS_EXTENSION, }; // Encoding prefixes are unsigned bytes. typedef std::uint8_t EncodingType; // Extension encoding types defined by this library. enum EncodingExtType : int8_t { ENCODING_EXT_TYPE_FILE_DESCRIPTOR, ENCODING_EXT_TYPE_CHANNEL_HANDLE, }; // Encoding predicates. Determines whether the given encoding is of a specific // type. inline constexpr bool IsFixintEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: return true; default: return false; } } inline constexpr bool IsUnsignedFixintEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: return true; default: return false; } } inline constexpr bool IsInt8Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: case ENCODING_TYPE_INT8: return true; default: return false; } } inline constexpr bool IsUInt8Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_UINT8: return true; default: return false; } } inline constexpr bool IsInt16Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: case ENCODING_TYPE_INT8: case ENCODING_TYPE_INT16: return true; default: return false; } } inline constexpr bool IsUInt16Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_UINT8: case ENCODING_TYPE_UINT16: return true; default: return false; } } inline constexpr bool IsInt32Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: case ENCODING_TYPE_INT8: case ENCODING_TYPE_INT16: case ENCODING_TYPE_INT32: return true; default: return false; } } inline constexpr bool IsUInt32Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_UINT8: case ENCODING_TYPE_UINT16: case ENCODING_TYPE_UINT32: return true; default: return false; } } inline constexpr bool IsInt64Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: case ENCODING_TYPE_INT8: case ENCODING_TYPE_INT16: case ENCODING_TYPE_INT32: case ENCODING_TYPE_INT64: return true; default: return false; } } inline constexpr bool IsUInt64Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_UINT8: case ENCODING_TYPE_UINT16: case ENCODING_TYPE_UINT32: case ENCODING_TYPE_UINT64: return true; default: return false; } } inline constexpr bool IsFixmapEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX: return true; default: return false; } } inline constexpr bool IsFixarrayEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX: return true; default: return false; } } inline constexpr bool IsFixstrEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX: return true; default: return false; } } inline constexpr bool IsFixextEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FIXEXT1: case ENCODING_TYPE_FIXEXT2: case ENCODING_TYPE_FIXEXT4: case ENCODING_TYPE_FIXEXT8: case ENCODING_TYPE_FIXEXT16: return true; default: return false; } } inline constexpr bool IsFloat32Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FLOAT32: return true; default: return false; } } inline constexpr bool IsFloat64Encoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FLOAT32: case ENCODING_TYPE_FLOAT64: return true; default: return false; } } inline constexpr bool IsBoolEncoding(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FALSE: case ENCODING_TYPE_TRUE: return true; default: return false; } } inline constexpr std::size_t GetFixstrSize(EncodingType encoding) { return encoding & ENCODING_TYPE_FIXSTR_MASK; } inline constexpr std::size_t GetFixarraySize(EncodingType encoding) { return encoding & ENCODING_TYPE_FIXARRAY_MASK; } inline constexpr std::size_t GetFixmapSize(EncodingType encoding) { return encoding & ENCODING_TYPE_FIXMAP_MASK; } inline constexpr std::size_t GetFixextSize(EncodingType encoding) { switch (encoding) { case ENCODING_TYPE_FIXEXT1: return 1; case ENCODING_TYPE_FIXEXT2: return 2; case ENCODING_TYPE_FIXEXT4: return 4; case ENCODING_TYPE_FIXEXT8: return 8; case ENCODING_TYPE_FIXEXT16: return 16; default: return 0; // Invalid fixext size. } } // Gets the size of the encoding in bytes, not including external payload data. inline constexpr std::size_t GetEncodingSize(EncodingType encoding) { switch (encoding) { // Encoding is fully contained within the type value. case ENCODING_TYPE_POSITIVE_FIXINT_MIN ... ENCODING_TYPE_POSITIVE_FIXINT_MAX: case ENCODING_TYPE_NEGATIVE_FIXINT_MIN ... ENCODING_TYPE_NEGATIVE_FIXINT_MAX: case ENCODING_TYPE_FIXMAP_MIN ... ENCODING_TYPE_FIXMAP_MAX: case ENCODING_TYPE_FIXARRAY_MIN ... ENCODING_TYPE_FIXARRAY_MAX: case ENCODING_TYPE_FIXSTR_MIN ... ENCODING_TYPE_FIXSTR_MAX: case ENCODING_TYPE_NIL: case ENCODING_TYPE_RESERVED: case ENCODING_TYPE_FALSE: case ENCODING_TYPE_TRUE: return 1; // Encoding type followed by one-byte size or immediate value. case ENCODING_TYPE_BIN8: case ENCODING_TYPE_EXT8: case ENCODING_TYPE_UINT8: case ENCODING_TYPE_INT8: case ENCODING_TYPE_STR8: // Encoding type followed by one-byte extension type. case ENCODING_TYPE_FIXEXT1: case ENCODING_TYPE_FIXEXT2: case ENCODING_TYPE_FIXEXT4: case ENCODING_TYPE_FIXEXT8: case ENCODING_TYPE_FIXEXT16: return 2; // Encoding type followed by two-byte size or immediate value. case ENCODING_TYPE_BIN16: case ENCODING_TYPE_EXT16: case ENCODING_TYPE_UINT16: case ENCODING_TYPE_INT16: case ENCODING_TYPE_STR16: case ENCODING_TYPE_ARRAY16: case ENCODING_TYPE_MAP16: return 3; // Encoding type followed by four-byte size or immediate value. case ENCODING_TYPE_BIN32: case ENCODING_TYPE_EXT32: case ENCODING_TYPE_FLOAT32: case ENCODING_TYPE_UINT32: case ENCODING_TYPE_INT32: case ENCODING_TYPE_STR32: case ENCODING_TYPE_ARRAY32: case ENCODING_TYPE_MAP32: return 5; // Encoding type followed by eight-byte immediate value. case ENCODING_TYPE_FLOAT64: case ENCODING_TYPE_UINT64: case ENCODING_TYPE_INT64: return 9; default: return 0; } } // Encoding for standard types. Each supported data type has an associated // encoding or set of encodings. These functions determine the MessagePack // encoding based on the data type, value, and size of their arguments. inline constexpr EncodingType EncodeArrayType(std::size_t size) { if (size < (1U << 4)) return ENCODING_TYPE_FIXARRAY | (size & ENCODING_TYPE_FIXARRAY_MASK); else if (size < (1U << 16)) return ENCODING_TYPE_ARRAY16; else return ENCODING_TYPE_ARRAY32; } inline constexpr EncodingType EncodeMapType(std::size_t size) { if (size < (1U << 4)) return ENCODING_TYPE_FIXMAP | (size & ENCODING_TYPE_FIXMAP_MASK); else if (size < (1U << 16)) return ENCODING_TYPE_MAP16; else return ENCODING_TYPE_MAP32; } inline constexpr EncodingType EncodeStringType(std::size_t size) { if (size < (1U << 5)) return ENCODING_TYPE_FIXSTR | (size & ENCODING_TYPE_FIXSTR_MASK); else if (size < (1U << 8)) return ENCODING_TYPE_STR8; else if (size < (1U << 16)) return ENCODING_TYPE_STR16; else return ENCODING_TYPE_STR32; } inline constexpr EncodingType EncodeBinType(std::size_t size) { if (size < (1U << 8)) return ENCODING_TYPE_BIN8; else if (size < (1U << 16)) return ENCODING_TYPE_BIN16; else return ENCODING_TYPE_BIN32; } inline EncodingType EncodeType(const EmptyVariant& /*empty*/) { return ENCODING_TYPE_NIL; } // Variant is encoded as a single-element map, with the type index as the key. template inline EncodingType EncodeType(const Variant& /*variant*/) { return EncodeMapType(1); } template inline constexpr EncodingType EncodeType(const StringWrapper& value) { return EncodeStringType(value.length()); } inline constexpr EncodingType EncodeType(const std::string& value) { return EncodeStringType(value.length()); } template inline constexpr EncodingType EncodeType(const std::array& /*value*/) { return EncodeArrayType(Size); } template inline constexpr EncodingType EncodeType(const ArrayWrapper& value) { return EncodeArrayType(value.size()); } template inline constexpr EncodingType EncodeType( const std::vector& value) { return EncodeArrayType(value.size()); } template inline constexpr EncodingType EncodeType( const std::map& value) { return EncodeMapType(value.size()); } template inline constexpr EncodingType EncodeType( const std::unordered_map& value) { return EncodeMapType(value.size()); } template inline constexpr EncodingType EncodeType(const BufferWrapper& value) { // BIN size is in bytes. return EncodeBinType(value.size() * sizeof(typename BufferWrapper::value_type)); } template inline constexpr EncodingType EncodeType(const std::pair& /*value*/) { return EncodeArrayType(2); } template inline constexpr EncodingType EncodeType(const std::tuple& /*value*/) { return EncodeArrayType(sizeof...(T)); } // FileHandle is encoded as a FIXEXT2 with a type code for "FileDescriptor" // and a signed 16-bit index into the pushed fd array. Empty file descriptor // have an array index of -1. template inline constexpr EncodingType EncodeType(const FileHandle& /*fd*/) { return ENCODING_TYPE_FIXEXT2; } // ChannelHandle is encoded as a FIXEXT4 with a type of // ENCODING_EXT_TYPE_CHANNEL_HANDLE and a signed 32-bit value representing // a client channel in a remote process. Empty handle has a value of -1. template inline constexpr EncodingType EncodeType( const ChannelHandle& /*handle*/) { return ENCODING_TYPE_FIXEXT4; } inline constexpr EncodingType EncodeType(const bool& value) { return value ? ENCODING_TYPE_TRUE : ENCODING_TYPE_FALSE; } // Type 'char' is a little bit special in that it is distinct from 'signed char' // and 'unsigned char'. Treating it as an unsigned 8-bit value is safe for // encoding purposes and nicely handles 7-bit ASCII encodings as FIXINT. inline constexpr EncodingType EncodeType(const char& value) { if (value < static_cast(1 << 7)) return value; else return ENCODING_TYPE_UINT8; } inline constexpr EncodingType EncodeType(const uint8_t& value) { if (value < (1U << 7)) return value; else return ENCODING_TYPE_UINT8; } inline constexpr EncodingType EncodeType(const int8_t& value) { if (value >= -32) return value; else return ENCODING_TYPE_INT8; } inline constexpr EncodingType EncodeType(const uint16_t& value) { if (value < (1U << 7)) return static_cast(value); else if (value < (1U << 8)) return ENCODING_TYPE_UINT8; else return ENCODING_TYPE_UINT16; } inline constexpr EncodingType EncodeType(const int16_t& value) { if (value >= -32 && value <= 127) return static_cast(value); else if (value >= -128 && value <= 127) return ENCODING_TYPE_INT8; else return ENCODING_TYPE_INT16; } inline constexpr EncodingType EncodeType(const uint32_t& value) { if (value < (1U << 7)) return static_cast(value); else if (value < (1U << 8)) return ENCODING_TYPE_UINT8; else if (value < (1U << 16)) return ENCODING_TYPE_UINT16; else return ENCODING_TYPE_UINT32; } inline constexpr EncodingType EncodeType(const int32_t& value) { if (value >= -32 && value <= 127) return static_cast(value); else if (value >= -128 && value <= 127) return ENCODING_TYPE_INT8; else if (value >= -32768 && value <= 32767) return ENCODING_TYPE_INT16; else return ENCODING_TYPE_INT32; } inline constexpr EncodingType EncodeType(const uint64_t& value) { if (value < (1ULL << 7)) return static_cast(value); else if (value < (1ULL << 8)) return ENCODING_TYPE_UINT8; else if (value < (1ULL << 16)) return ENCODING_TYPE_UINT16; else if (value < (1ULL << 32)) return ENCODING_TYPE_UINT32; else return ENCODING_TYPE_UINT64; } inline constexpr EncodingType EncodeType(const int64_t& value) { if (value >= -32 && value <= 127) return static_cast(value); else if (value >= -128 && value <= 127) // Effectively [-128, -32). return ENCODING_TYPE_INT8; else if (value >= -32768 && value <= 32767) return ENCODING_TYPE_INT16; else if (value >= -2147483648 && value <= 2147483647) return ENCODING_TYPE_INT32; else return ENCODING_TYPE_INT64; } inline constexpr EncodingType EncodeType(const float& /*value*/) { return ENCODING_TYPE_FLOAT32; } inline constexpr EncodingType EncodeType(const double& /*value*/) { return ENCODING_TYPE_FLOAT64; } } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_ENCODING_H_ libs/vr/libpdx/private/pdx/rpc/enumeration.h0100644 0000000 0000000 00000004523 13756501735 020217 0ustar000000000 0000000 #ifndef ANDROID_PDX_RPC_ENUMERATION_H_ #define ANDROID_PDX_RPC_ENUMERATION_H_ #include namespace android { namespace pdx { namespace rpc { // Utility for manipulating lists of types. Provides operations to lookup an // element by type or index. namespace detail { // Helper type that captures type and index for each element of a type // enumeration. template struct IndexedElement { using Type = T; static constexpr std::size_t Index = I; }; // Helper type that captures an IndexSequence and corresponding list of types. template struct ElementIndexer; // Partial specialization that generates an instantiation of IndexElement // for each element of a type enumeration using inheritance. Once a type // enumeration is instantiated this way the compiler is able to deduce either I // or T from the other using the method below. template struct ElementIndexer, Ts...> : IndexedElement... { }; // Helper function that causes the compiler to deduce an IndexedElement // given T. template static IndexedElement SelectElementByType(IndexedElement); // Helper function that causes the compiler to deduce an IndexedElement // given I. template static IndexedElement SelectElementByIndex(IndexedElement); } // namespace detail // Deduces the IndexedElement given T and a type sequence Ts. This may be // used to determine the index of T within Ts at compile time. template using ElementForType = decltype(detail::SelectElementByType( detail::ElementIndexer::type, Ts...>{})); // Deduces the IndexedElement given I and a type sequence Ts. This may be // used to determine the type of the element at index I within Ts at compile // time. Tuple operations may also be used to accomplish the same task, however // this implementation is provided here for symmetry. template using ElementForIndex = decltype(detail::SelectElementByIndex( detail::ElementIndexer::type, Ts...>{})); } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_ENUMERATION_H_ libs/vr/libpdx/private/pdx/rpc/find_replace.h0100644 0000000 0000000 00000002770 13756501735 020306 0ustar000000000 0000000 #ifndef ANDROID_PDX_RPC_FIND_REPLACE_H_ #define ANDROID_PDX_RPC_FIND_REPLACE_H_ #include #include namespace android { namespace pdx { namespace rpc { // Utility class to capture types to find and replace. template struct FindReplace; template using IsSameBaseType = typename std::is_same::type, typename std::decay::type>; // Replaces the type Subject with type Replace if type Subject is the same type // as type Find, excluding cv-reference qualifiers in the match. template using ReplaceType = typename std::conditional::value, CopyCVReferenceType, Subject>::type; // Determines whether the type Find (excluding cv-reference qualifiers) is in // the given parameter pack. template struct ContainsType : std::true_type {}; template struct ContainsType : std::conditional::value, std::true_type, ContainsType>::type {}; template struct ContainsType : std::false_type {}; } // namespace rpc } // namespace pdx } // namespace android #endif // ANDROID_PDX_RPC_FIND_REPLACE_H_ libs/vr/libpdx/private/pdx/rpc/function_traits.h0100644 0000000 0000000 00000003573 13756501735 021110 0ustar000000000 0000000 #ifndef ANDROID_PDX_RPC_FUNCTION_TRAITS_H_ #define ANDROID_PDX_RPC_FUNCTION_TRAITS_H_ #include #include namespace android { namespace pdx { namespace rpc { // Utility type to capture return and argument types of a function signature. // Examples: // typedef SignatureType SignatureType; // using SignatureType = SignatureType; template using SignatureType = T; // Utility class to extract return and argument types from function types. // Provides nested types for return value, arguments, and full signature. Also // provides accessor types for individual arguments, argument-arity, and type // substitution. template struct FunctionTraits; template struct FunctionTraits { using Return = Return_; using Args = std::tuple; using Signature = SignatureType; enum : std::size_t { Arity = sizeof...(Args_) }; template using Arg = typename std::tuple_element::type; template using RewriteArgs = SignatureType...)>; template using RewriteSignature = SignatureType( ConditionalRewrite...)>; template