.clang-format0100644 0000000 0000000 00000000535 13300556574 012173 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 13300556574 011517 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"], } MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 012740 0ustar000000000 0000000 NOTICE0100644 0000000 0000000 00000042420 13300556574 010523 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. aidl/0040755 0000000 0000000 00000000000 13300556574 010531 5ustar000000000 0000000 aidl/binder/0040755 0000000 0000000 00000000000 13300556574 011774 5ustar000000000 0000000 aidl/binder/android/0040755 0000000 0000000 00000000000 13300556574 013414 5ustar000000000 0000000 aidl/binder/android/os/0040755 0000000 0000000 00000000000 13300556574 014035 5ustar000000000 0000000 aidl/binder/android/os/PersistableBundle.aidl0100644 0000000 0000000 00000001306 13300556574 020274 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 13300556574 011315 5ustar000000000 0000000 aidl/gui/android/0040755 0000000 0000000 00000000000 13300556574 012735 5ustar000000000 0000000 aidl/gui/android/view/0040755 0000000 0000000 00000000000 13300556574 013707 5ustar000000000 0000000 aidl/gui/android/view/Surface.aidl0100644 0000000 0000000 00000001346 13300556574 016133 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 13300556574 010717 5ustar000000000 0000000 build/phone-hdpi-512-dalvik-heap.mk0100644 0000000 0000000 00000001675 13300556574 016001 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 13300556574 015470 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 13300556574 016243 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 13300556574 016254 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 13300556574 017161 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 13300556574 016706 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 13300556574 017110 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 13300556574 014705 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 13300556574 010546 5ustar000000000 0000000 cmds/atrace/0040755 0000000 0000000 00000000000 13300556574 012005 5ustar000000000 0000000 cmds/atrace/Android.bp0100644 0000000 0000000 00000001022 13300556574 013700 0ustar000000000 0000000 // Copyright 2012 The Android Open Source Project cc_binary { name: "atrace", srcs: ["atrace.cpp"], shared_libs: [ "libbinder", "libhwbinder", "libhidlbase", "libhidltransport", "liblog", "libutils", "libcutils", "libz", "libbase", ], static_libs: [ "libpdx_default_transport", ], init_rc: ["atrace.rc"], product_variables: { debuggable: { init_rc: ["atrace_userdebug.rc"], }, }, } cmds/atrace/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 015125 0ustar000000000 0000000 cmds/atrace/NOTICE0100644 0000000 0000000 00000024702 13300556574 012713 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/atrace.cpp0100644 0000000 0000000 00000122556 13300556574 013760 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 using namespace android; using pdx::default_transport::ServiceUtility; using std::string; #define MAX_SYS_FILES 10 #define MAX_PACKAGES 16 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags"; 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, { { OPT, "events/mdss/enable" }, } }, { "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, { } }, { "app", "Application", ATRACE_TAG_APP, { } }, { "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, { } }, { 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/cgroup/enable" }, } }, { "irq", "IRQ Events", 0, { { REQ, "events/irq/enable" }, { OPT, "events/ipi/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/cpu_frequency_limits/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, { { REQ, "events/sync/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" }, { REQ, "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" }, { 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" }, } }, }; /* 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 = NULL; static const char* g_kernelTraceFuncs = NULL; 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; /* Sys file paths */ static const char* k_traceClockPath = "trace_clock"; static const char* k_traceBufferSizePath = "buffer_size_kb"; static const char* k_traceCmdlineSizePath = "saved_cmdlines_size"; 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_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_funcgraphDurationPath = "options/funcgraph-duration"; 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 != NULL) { if (req) { if (!fileIsWritable(path)) { return false; } else { ok = true; } } else { ok = true; } } } 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 != NULL) { 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); } // 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"; int len; if (size < 1) { size = 1; } snprintf(str, 32, "%d", size); return writeStr(k_traceBufferSizePath, str); } // Set the default size of cmdline hashtable static bool setCmdlineSize() { if (fileExists(k_traceCmdlineSizePath)) { return writeStr(k_traceCmdlineSizePath, "8192"); } return true; } // 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) { if (fileExists(k_printTgidPath)) { return setKernelOptionEnable(k_printTgidPath, 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 != NULL) { Parcel data; if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data, NULL, 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() { for (int i = 0; i < MAX_PACKAGES; i++) { std::string key = android::base::StringPrintf(k_traceAppsPropertyTemplate, i); if (!android::base::SetProperty(key, "")) { fprintf(stderr, "failed to clear system property: %s\n", key.c_str()); } } 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 != NULL) { if (i == MAX_PACKAGES) { fprintf(stderr, "error: only 16 packages could be traced at once\n"); clearAppProperties(); return false; } char* end = strchr(start, ','); if (end != NULL) { *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 != NULL && 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(NULL, ","); } 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 == NULL || 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(NULL, ","); } free(myFuncs); // Verify that the set functions are being traced. if (ok) { ok &= verifyKernelTraceFuncs(funcs); } } return ok; } static bool setCategoryEnable(const char* name, bool enable) { 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] = enable; 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; } } } 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 = NULL; 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(), true); } delete tokenizer; return ok; } // Set all the kernel tracing settings to the desired state for this trace // capture. static bool setUpTrace() { bool ok = 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); // 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(); } // 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 != NULL) { 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 cleanUpTrace() { // Disable all tracing that we're able to. disableKernelTraceEvents(); // Reset the system properties. setTagsProperty(0); clearAppProperties(); pokeBinderServices(); if (g_tracePdx) { ServiceUtility::PokeServices(); } // Set the options back to their defaults. setTraceOverwriteEnable(true); setTraceBufferSizeKB(1); setPrintTgidEnableIfPresent(false); setKernelTraceFuncs(NULL); } // 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, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); } 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); } } } // 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\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; } int main(int argc, char **argv) { bool async = false; bool traceStart = true; bool traceStop = true; bool traceDump = true; bool traceStream = 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); } for (;;) { int ret; int option_index = 0; static struct option long_options[] = { {"async_start", no_argument, 0, 0 }, {"async_stop", no_argument, 0, 0 }, {"async_dump", no_argument, 0, 0 }, {"list_categories", no_argument, 0, 0 }, {"stream", no_argument, 0, 0 }, { 0, 0, 0, 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], true)) { 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, "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; } } registerSigHandler(); if (g_initialSleepSecs > 0) { sleep(g_initialSleepSecs); } bool ok = true; ok &= setUpTrace(); ok &= startTrace(); if (ok && traceStart) { if (!traceStream) { 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. 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) stopTrace(); if (ok && traceDump) { 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) cleanUpTrace(); return g_traceAborted ? 1 : 0; } cmds/atrace/atrace.rc0100644 0000000 0000000 00000023003 13300556574 013565 0ustar000000000 0000000 ## Permissions to allow system-wide tracing to the kernel trace buffer. ## on post-fs # Allow writing to the kernel trace log. chmod 0222 /sys/kernel/debug/tracing/trace_marker chmod 0222 /sys/kernel/tracing/trace_marker # Allow the shell group to enable (some) kernel tracing. chown root shell /sys/kernel/debug/tracing/trace_clock chown root shell /sys/kernel/tracing/trace_clock chown root shell /sys/kernel/debug/tracing/buffer_size_kb chown root shell /sys/kernel/tracing/buffer_size_kb chown root shell /sys/kernel/debug/tracing/options/overwrite chown root shell /sys/kernel/tracing/options/overwrite chown root shell /sys/kernel/debug/tracing/options/print-tgid chown root shell /sys/kernel/tracing/options/print-tgid chown root shell /sys/kernel/debug/tracing/saved_cmdlines_size chown root shell /sys/kernel/tracing/saved_cmdlines_size chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable chown root shell /sys/kernel/tracing/events/sched/sched_switch/enable chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chown root shell /sys/kernel/tracing/events/sched/sched_wakeup/enable chown root shell /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable chown root shell /sys/kernel/tracing/events/sched/sched_blocked_reason/enable chown root shell /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable chown root shell /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable chown root shell /sys/kernel/debug/tracing/events/cgroup/enable chown root shell /sys/kernel/tracing/events/cgroup/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chown root shell /sys/kernel/tracing/events/power/cpu_frequency/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable chown root shell /sys/kernel/tracing/events/power/cpu_idle/enable chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable chown root shell /sys/kernel/tracing/events/power/clock_set_rate/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable chown root shell /sys/kernel/tracing/events/power/cpu_frequency_limits/enable chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chown root shell /sys/kernel/tracing/events/cpufreq_interactive/enable chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable chown root shell /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable chown root shell /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction/enable chown root shell /sys/kernel/tracing/events/binder/binder_transaction/enable chown root shell /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable chown root shell /sys/kernel/tracing/events/binder/binder_transaction_received/enable chown root shell /sys/kernel/debug/tracing/events/binder/binder_lock/enable chown root shell /sys/kernel/tracing/events/binder/binder_lock/enable chown root shell /sys/kernel/debug/tracing/events/binder/binder_locked/enable chown root shell /sys/kernel/tracing/events/binder/binder_locked/enable chown root shell /sys/kernel/debug/tracing/events/binder/binder_unlock/enable chown root shell /sys/kernel/tracing/events/binder/binder_unlock/enable chown root shell /sys/kernel/debug/tracing/events/lowmemorykiller/enable chown root shell /sys/kernel/tracing/events/lowmemorykiller/enable chown root shell /sys/kernel/debug/tracing/tracing_on chown root shell /sys/kernel/tracing/tracing_on chmod 0664 /sys/kernel/debug/tracing/trace_clock chmod 0664 /sys/kernel/tracing/trace_clock chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb chmod 0664 /sys/kernel/tracing/buffer_size_kb chmod 0664 /sys/kernel/debug/tracing/options/overwrite chmod 0664 /sys/kernel/tracing/options/overwrite chmod 0664 /sys/kernel/debug/tracing/options/print-tgid chmod 0664 /sys/kernel/tracing/options/print-tgid chmod 0664 /sys/kernel/debug/tracing/saved_cmdlines_size chmod 0664 /sys/kernel/tracing/saved_cmdlines_size chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable chmod 0664 /sys/kernel/tracing/events/sched/sched_switch/enable chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable chmod 0664 /sys/kernel/tracing/events/sched/sched_wakeup/enable chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable chmod 0664 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable chmod 0664 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable chmod 0664 /sys/kernel/debug/tracing/events/cgroup/enable chmod 0664 /sys/kernel/tracing/events/cgroup/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable chmod 0664 /sys/kernel/tracing/events/power/cpu_idle/enable chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable chmod 0664 /sys/kernel/tracing/events/power/clock_set_rate/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable chmod 0664 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chmod 0664 /sys/kernel/tracing/events/cpufreq_interactive/enable chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_direct_reclaim_end/enable chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_wake/enable chmod 0664 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable chmod 0664 /sys/kernel/tracing/events/vmscan/mm_vmscan_kswapd_sleep/enable chmod 0664 /sys/kernel/debug/tracing/tracing_on chmod 0664 /sys/kernel/tracing/tracing_on chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction/enable chmod 0664 /sys/kernel/tracing/events/binder/binder_transaction/enable chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable chmod 0664 /sys/kernel/tracing/events/binder/binder_transaction_received/enable chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_lock/enable chmod 0664 /sys/kernel/tracing/events/binder/binder_lock/enable chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_locked/enable chmod 0664 /sys/kernel/tracing/events/binder/binder_locked/enable chmod 0664 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable chmod 0664 /sys/kernel/tracing/events/binder/binder_unlock/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/enable chmod 0664 /sys/kernel/tracing/events/i2c/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable chmod 0664 /sys/kernel/tracing/events/i2c/i2c_read/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_write/enable chmod 0664 /sys/kernel/tracing/events/i2c/i2c_write/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_result/enable chmod 0664 /sys/kernel/tracing/events/i2c/i2c_result/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/i2c_reply/enable chmod 0664 /sys/kernel/tracing/events/i2c/i2c_reply/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_read/enable chmod 0664 /sys/kernel/tracing/events/i2c/smbus_read/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_write/enable chmod 0664 /sys/kernel/tracing/events/i2c/smbus_write/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_result/enable chmod 0664 /sys/kernel/tracing/events/i2c/smbus_result/enable chmod 0664 /sys/kernel/debug/tracing/events/i2c/smbus_reply/enable chmod 0664 /sys/kernel/tracing/events/i2c/smbus_reply/enable chmod 0664 /sys/kernel/debug/tracing/events/lowmemorykiller/enable chmod 0664 /sys/kernel/tracing/events/lowmemorykiller/enable # Tracing disabled by default write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 # Allow only the shell group to read and truncate the kernel trace. chown root shell /sys/kernel/debug/tracing/trace chown root shell /sys/kernel/tracing/trace chmod 0660 /sys/kernel/debug/tracing/trace chmod 0660 /sys/kernel/tracing/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 00000005212 13300556574 015634 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 # Allow the shell group to enable kernel tracepoints: on post-fs chown root shell /sys/kernel/debug/tracing/events/sync/enable chown root shell /sys/kernel/debug/tracing/events/workqueue/enable chown root shell /sys/kernel/debug/tracing/events/regulator/enable chown root shell /sys/kernel/debug/tracing/events/pagecache/enable # irq chown root shell /sys/kernel/debug/tracing/events/irq/enable chown root shell /sys/kernel/debug/tracing/events/ipi/enable # disk chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable chown root shell /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable chown root shell /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable chown root shell /sys/kernel/debug/tracing/events/block/block_rq_issue/enable chown root shell /sys/kernel/debug/tracing/events/block/block_rq_complete/enable chmod 0664 /sys/kernel/debug/tracing/events/sync/enable chmod 0664 /sys/kernel/debug/tracing/events/workqueue/enable chmod 0664 /sys/kernel/debug/tracing/events/regulator/enable chmod 0664 /sys/kernel/debug/tracing/events/pagecache/enable # irq chmod 0664 /sys/kernel/debug/tracing/events/irq/enable chmod 0664 /sys/kernel/debug/tracing/events/ipi/enable # disk chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_exit/enable chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_begin/enable chmod 0664 /sys/kernel/debug/tracing/events/f2fs/f2fs_write_end/enable chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable chmod 0664 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_issue/enable chmod 0664 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable cmds/bugreport/0040755 0000000 0000000 00000000000 13300556574 012557 5ustar000000000 0000000 cmds/bugreport/Android.bp0100644 0000000 0000000 00000000171 13300556574 014456 0ustar000000000 0000000 cc_binary { name: "bugreport", srcs: ["bugreport.cpp"], cflags: ["-Wall"], shared_libs: ["libcutils"], } cmds/bugreport/bugreport.cpp0100644 0000000 0000000 00000006367 13300556574 015305 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 13300556574 012751 5ustar000000000 0000000 cmds/bugreportz/.clang-format0100644 0000000 0000000 00000000440 13300556574 015317 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.mk0100644 0000000 0000000 00000001241 13300556574 014655 0ustar000000000 0000000 LOCAL_PATH:= $(call my-dir) # bugreportz # ========== include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ bugreportz.cpp \ main.cpp \ LOCAL_MODULE:= bugreportz LOCAL_CFLAGS := -Werror -Wall LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ include $(BUILD_EXECUTABLE) # bugreportz_test # =============== include $(CLEAR_VARS) LOCAL_MODULE := bugreportz_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_MODULE_TAGS := tests LOCAL_CFLAGS := -Werror -Wall LOCAL_SRC_FILES := \ bugreportz.cpp \ bugreportz_test.cpp \ LOCAL_STATIC_LIBRARIES := \ libgmock \ LOCAL_SHARED_LIBRARIES := \ libbase \ libutils \ include $(BUILD_NATIVE_TEST) cmds/bugreportz/AndroidTest.xml0100644 0000000 0000000 00000002323 13300556574 015710 0ustar000000000 0000000 cmds/bugreportz/bugreportz.cpp0100644 0000000 0000000 00000004701 13300556574 015657 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)); break; } // 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); if (close(s) == -1) { fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); } return EXIT_SUCCESS; } cmds/bugreportz/bugreportz.h0100644 0000000 0000000 00000001434 13300556574 015324 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. int bugreportz(int s, bool show_progress); #endif // BUGREPORTZ_H cmds/bugreportz/bugreportz_test.cpp0100644 0000000 0000000 00000010013 13300556574 016707 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 00000005710 13300556574 014401 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_SUCCESS; } // 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: %s\n", strerror(errno)); } bugreportz(s, show_progress); } cmds/bugreportz/readme.md0100644 0000000 0000000 00000001355 13300556574 014531 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 13300556574 011311 5ustar000000000 0000000 cmds/cmd/Android.mk0100644 0000000 0000000 00000000532 13300556574 013217 0ustar000000000 0000000 LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ cmd.cpp LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libselinux \ libbinder LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) ifeq ($(TARGET_OS),linux) LOCAL_CFLAGS += -DXP_UNIX #LOCAL_SHARED_LIBRARIES += librt endif LOCAL_MODULE:= cmd include $(BUILD_EXECUTABLE) cmds/cmd/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 014431 0ustar000000000 0000000 cmds/cmd/NOTICE0100644 0000000 0000000 00000024707 13300556574 012224 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 00000014275 13300556574 012566 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" #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: bool mActive = true; virtual int openOutputFile(const String16& path, const String16& seLinuxContext) { String8 path8(path); char cwd[256]; getcwd(cwd, 256); String8 fullPath(cwd); fullPath.appendPath(path8); if (!mActive) { aerr << "Open attempt after active for: " << fullPath << endl; return -EPERM; } int fd = open(fullPath.string(), O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG); if (fd < 0) { return fd; } if (is_selinux_enabled() && seLinuxContext.size() > 0) { String8 seLinuxContext8(seLinuxContext); security_context_t tmp = NULL; int ret = getfilecon(fullPath.string(), &tmp); Unique_SecurityContext context(tmp); int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), "file", "write", NULL); if (accessGranted != 0) { close(fd); aerr << "System server has no access to 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 main(int argc, char* const argv[]) { signal(SIGPIPE, SIG_IGN); sp proc = ProcessState::self(); // setThreadPoolMaxThreadCount(0) actually tells the kernel it's // not allowed to spawn any additional threads, but we still spawn // a binder thread from userspace when we call startThreadPool(). // This is safe because we only have 2 callbacks, neither of which // block. // See b/36066697 for rationale proc->setThreadPoolMaxThreadCount(0); proc->startThreadPool(); sp sm = defaultServiceManager(); fflush(stdout); if (sm == NULL) { ALOGW("Unable to get default service manager!"); aerr << "cmd: Unable to get default service manager!" << endl; return 20; } if (argc == 1) { aerr << "cmd: No service specified; use -l to list all services" << endl; return 20; } if ((argc == 2) && (strcmp(argv[1], "-l") == 0)) { Vector services = sm->listServices(); services.sort(sort_func); aout << "Currently running services:" << endl; for (size_t i=0; i service = sm->checkService(services[i]); if (service != NULL) { aout << " " << services[i] << endl; } } return 0; } Vector args; for (int i=2; i service = sm->checkService(cmd); if (service == NULL) { ALOGW("Can't find service %s", argv[1]); aerr << "cmd: Can't find service: " << argv[1] << endl; return 20; } sp cb = new MyShellCallback(); sp result = new MyResultReceiver(); #if DEBUG ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", argv[1], STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO); #endif // TODO: block until a result is returned to MyResultReceiver. status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args, cb, result); if (err < 0) { const char* errstr; switch (err) { 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(-err); break; } ALOGW("Failure calling service %s: %s (%d)", argv[1], errstr, -err); aout << "cmd: Failure calling service " << argv[1] << ": " << errstr << " (" << (-err) << ")" << endl; return err; } cb->mActive = false; status_t res = result->waitForResult(); #if DEBUG ALOGD("result=%d", (int)res); #endif return res; } cmds/dumpstate/0040755 0000000 0000000 00000000000 13300556574 012554 5ustar000000000 0000000 cmds/dumpstate/.clang-format0100644 0000000 0000000 00000000440 13300556574 015122 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 00000006107 13300556574 014460 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_defaults", cflags: [ "-Wall", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-variable", "-Wunused-parameter", ], } cc_library_headers { name: "dumpstate_headers", vendor_available: true, export_include_dirs: ["."], header_libs: [ "libbase_headers", "libutils_headers", ], export_header_lib_headers: [ "libbase_headers", "libutils_headers", ], } cc_library_shared { name: "libdumpstateutil", defaults: ["dumpstate_defaults"], vendor_available: true, vndk: { enabled: true, }, header_libs: ["dumpstate_headers"], export_header_lib_headers: ["dumpstate_headers"], srcs: [ "DumpstateInternal.cpp", "DumpstateUtil.cpp", ], shared_libs: [ "libbase", "liblog", ], } cc_library_shared { name: "libdumpstateaidl", defaults: ["dumpstate_defaults"], shared_libs: [ "libbinder", "libutils", ], aidl: { local_include_dirs: ["binder"], export_aidl_headers: true, }, srcs: [ "binder/android/os/IDumpstate.aidl", "binder/android/os/IDumpstateListener.aidl", "binder/android/os/IDumpstateToken.aidl", ], } cc_binary { name: "dumpstate", defaults: ["dumpstate_defaults"], header_libs: ["dumpstate_headers"], shared_libs: [ "android.hardware.dumpstate@1.0", "libziparchive", "libbase", "libbinder", "libcrypto", "libcutils", "libdebuggerd_client", "libdumpstateaidl", "libdumpstateutil", "libhidlbase", "libhidltransport", "liblog", "libutils", ], srcs: [ "DumpstateInternal.cpp", "DumpstateService.cpp", "utils.cpp", "dumpstate.cpp", ], init_rc: ["dumpstate.rc"], } cc_test { name: "dumpstate_test", defaults: ["dumpstate_defaults"], header_libs: ["dumpstate_headers"], shared_libs: [ "libziparchive", "libbase", "libbinder", "libcutils", "libdebuggerd_client", "libdumpstateaidl", "libdumpstateutil", "libhidlbase", "libhidltransport", "liblog", "libutils", ], srcs: [ "DumpstateInternal.cpp", "DumpstateService.cpp", "utils.cpp", "tests/dumpstate_test.cpp", ], static_libs: ["libgmock"], } cmds/dumpstate/Android.mk0100644 0000000 0000000 00000001121 13300556574 014455 0ustar000000000 0000000 LOCAL_PATH:= $(call my-dir) # =======================# # dumpstate_test_fixture # # =======================# include $(CLEAR_VARS) LOCAL_MODULE := dumpstate_test_fixture LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_MODULE_TAGS := tests LOCAL_CFLAGS := \ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_SRC_FILES := \ tests/dumpstate_test_fixture.cpp LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata) include $(BUILD_NATIVE_TEST) cmds/dumpstate/AndroidTest.xml0100644 0000000 0000000 00000002357 13300556574 015522 0ustar000000000 0000000 cmds/dumpstate/DumpstateInternal.cpp0100644 0000000 0000000 00000014022 13300556574 016717 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 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; capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG); capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG); capdata[0].inheritable = 0; capdata[1].inheritable = 0; if (capset(&capheader, &capdata[0]) < 0) { MYLOGE("capset failed: %s\n", 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; fd_set read_set; timeval tm; while (true) { FD_ZERO(&read_set); FD_SET(fd, &read_set); /* Timeout if no data is read for 30 seconds. */ tm.tv_sec = 30; tm.tv_usec = 0; uint64_t elapsed = Nanotime(); int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm)); if (ret == -1) { dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno)); newline = true; break; } else if (ret == 0) { elapsed = Nanotime() - elapsed; 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; } } } close(fd); if (!newline) dprintf(out_fd, "\n"); if (!title.empty()) dprintf(out_fd, "\n"); return 0; } cmds/dumpstate/DumpstateInternal.h0100644 0000000 0000000 00000003476 13300556574 016377 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/DumpstateService.cpp0100644 0000000 0000000 00000007321 13300556574 016547 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 "android/os/BnDumpstate.h" #include "DumpstateInternal.h" namespace android { namespace os { namespace { class DumpstateToken : public BnDumpstateToken {}; } DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) { } 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; } binder::Status DumpstateService::setListener(const std::string& name, const sp& listener, 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_.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; *returned_token = new DumpstateToken(); return binder::Status::ok(); } status_t DumpstateService::dump(int fd, const Vector&) { dprintf(fd, "id: %d\n", ds_.id_); dprintf(fd, "pid: %d\n", ds_.pid_); dprintf(fd, "update_progress: %s\n", ds_.update_progress_ ? "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_.args_.c_str()); dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str()); dprintf(fd, "version: %s\n", ds_.version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.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_.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_.notification_title.c_str()); dprintf(fd, "notification description: %s\n", ds_.notification_description.c_str()); return NO_ERROR; } } // namespace os } // namespace android cmds/dumpstate/DumpstateService.h0100644 0000000 0000000 00000002670 13300556574 016216 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 "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, sp* returned_token) override; private: Dumpstate& ds_; std::mutex lock_; }; } // namespace os } // namespace android #endif // ANDROID_OS_DUMPSTATE_H_ cmds/dumpstate/DumpstateUtil.cpp0100644 0000000 0000000 00000027564 13300556574 016077 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 "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_seconds, 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 = timeout_seconds; ts.tv_nsec = 0; int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts)); int saved_errno = errno; // Set the signals back the way they were. if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -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) : values(timeout) { } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() { values.always_ = true; return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() { values.account_mode_ = SU_ROOT; 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) : timeout_(timeout), 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 values.timeout_; } 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) { return CommandOptions::CommandOptionsBuilder(timeout); } std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -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; } int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)); if (fd < 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, 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, NULL); 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.Timeout(), &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, 5, nullptr)) { kill(pid, SIGKILL); if (!waitpid_with_timeout(pid, 5, 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 00000013243 13300556574 015531 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 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: CommandOptionsValues(int64_t timeout); int64_t timeout_; bool always_; PrivilegeMode account_mode_; OutputMode output_mode_; std::string logging_message_; friend class CommandOptions; friend class CommandOptionsBuilder; }; 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` */ CommandOptionsBuilder& AsRoot(); /* 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: CommandOptionsBuilder(int64_t timeout); CommandOptionsValues values; friend class CommandOptions; }; /** Gets the command timeout, in seconds. */ int64_t Timeout() 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. */ static CommandOptionsBuilder WithTimeout(int64_t timeout); // 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(); private: static std::string build_type_; static int dry_run_; }; /* * 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/README.md0100644 0000000 0000000 00000004164 13300556574 014035 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 shell am bug-report ``` ## To build, deploy, and run unit tests First create `/data/nativetest`: ``` adb shell mkdir /data/nativetest ``` Then run: ``` mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test ``` And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`): ``` mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs ``` ## To take quick bugreports ``` adb shell setprop dumpstate.dry_run 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 13300556574 014017 5ustar000000000 0000000 cmds/dumpstate/binder/android/0040755 0000000 0000000 00000000000 13300556574 015437 5ustar000000000 0000000 cmds/dumpstate/binder/android/os/0040755 0000000 0000000 00000000000 13300556574 016060 5ustar000000000 0000000 cmds/dumpstate/binder/android/os/IDumpstate.aidl0100644 0000000 0000000 00000002215 13300556574 020767 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 { /* * 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). */ IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener); } cmds/dumpstate/binder/android/os/IDumpstateListener.aidl0100644 0000000 0000000 00000001472 13300556574 022501 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. * * {@hide} */ interface IDumpstateListener { void onProgressUpdated(int progress); void onMaxProgressUpdated(int maxProgress); } cmds/dumpstate/binder/android/os/IDumpstateToken.aidl0100644 0000000 0000000 00000001375 13300556574 021776 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 00000010124 13300556574 016370 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 (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made: - The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`). ## 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 00000223311 13300556574 015265 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 "DumpstateInternal.h" #include "DumpstateService.h" #include "dumpstate.h" using ::android::hardware::dumpstate::V1_0::IDumpstateDevice; // TODO: remove once moved to namespace using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::PropertiesHelper; using android::os::dumpstate::GetPidByName; /* read before root is shed */ static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = NULL; // 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 RAFT_DIR "/data/misc/raft" #define RECOVERY_DIR "/cache/recovery" #define RECOVERY_DATA_DIR "/data/misc/recovery" #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 WLUTIL "/vendor/xbin/wlutil" // 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_"; struct DumpData { std::string name; int fd; time_t mtime; }; static bool operator<(const DumpData& d1, const DumpData& d2) { return d1.mtime > d2.mtime; } static std::unique_ptr> tombstone_data; static std::unique_ptr> anr_data; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); 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 dumpsysTimeout = 0) { return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout); } 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"; // Must be hardcoded because dumpstate HAL implementation need SELinux access to it static const std::string kDumpstateBoardPath = "/bugreports/"; static const std::string kDumpstateBoardFiles[] = { "dumpstate_board.txt", "dumpstate_board.bin" }; static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles); static const std::string kLsHalDebugPath = "/bugreports/dumpstate_lshal.txt"; 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_data(new std::vector()); std::unique_ptr dump_dir(opendir(dir_path.c_str()), closedir); 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; } DumpData data = {.name = abs_path, .fd = fd.release(), .mtime = st.st_mtime}; dump_data->push_back(data); } std::sort(dump_data->begin(), dump_data->end()); if (limit_by_count && dump_data->size() > 10) { dump_data->erase(dump_data->begin() + 10, dump_data->end()); } return dump_data.release(); } 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; if (ds.IsZipping() && add_to_zip) { if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) { MYLOGE("Unable to add %s %s to zip file\n", name.c_str(), type_name); } } else { dump_file_from_fd(type_name, name.c_str(), fd); } close(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 == NULL) { 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/xbin/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 { if (remove(path)) { MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno)); } return true; } } else { MYLOGE("Can't stats any trace file under %s\n", trace_path); } } return false; } static void dump_systrace() { if (!ds.IsZipping()) { MYLOGD("Not dumping systrace because it's not a zipped bugreport\n"); return; } std::string systrace_path = ds.GetPath("-systrace.txt"); if (systrace_path.empty()) { MYLOGE("Not dumping systrace because path is empty\n"); return; } const char* path = "/sys/kernel/debug/tracing/tracing_on"; long int is_tracing; if (read_file_as_long(path, &is_tracing)) { return; // error already logged } if (is_tracing <= 0) { MYLOGD("Skipping systrace because '%s' content is '%ld'\n", path, is_tracing); return; } MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes", systrace_path.c_str()); if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path}, CommandOptions::WithTimeout(120).Build())) { MYLOGE("systrace timed out, its zip entry will be incomplete\n"); // TODO: RunCommand tries to kill the process, but atrace doesn't die // peacefully; ideally, we should call strace to stop itself, but there is no such option // yet (just a --async_stop, which stops and dump // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) { // MYLOGE("could not stop systrace "); // } } if (!ds.AddZipEntry("systrace.txt", systrace_path)) { MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str()); } else { if (remove(systrace_path.c_str())) { MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno)); } } } static void dump_raft() { if (PropertiesHelper::IsUserBuild()) { return; } std::string raft_path = ds.GetPath("-raft_log.txt"); if (raft_path.empty()) { MYLOGD("raft_path is empty\n"); return; } struct stat s; if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) { MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR); return; } CommandOptions options = CommandOptions::WithTimeout(600).Build(); if (!ds.IsZipping()) { // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport. RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options); return; } RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options); if (!ds.AddZipEntry("raft_log.txt", raft_path)) { MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str()); } else { if (remove(raft_path.c_str())) { MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno)); } } } 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 = NULL; size_t i = 0; FILE *fp = fdopen(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; } /* timeout in ms */ static unsigned long logcat_timeout(const char *name) { log_id_t id = android_name_to_log_id(name); unsigned long property_size = __android_logger_get_buffer_size(id); /* Engineering margin is ten-fold our guess */ return 10 * (property_size + worst_write_perf) / worst_write_perf; } 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()); printf("Kernel: "); DumpFileToFd(STDOUT_FILENO, "", "/proc/version"); printf("Command line: %s\n", strtok(cmdline_buf, "\n")); 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(), args_.c_str(), 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" }; bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) { if (!IsZipping()) { MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n", entry_name.c_str()); return false; } 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 false; } std::vector buffer(65536); while (1) { 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 false; } err = zip_writer_->WriteBytes(buffer.data(), bytes_read); if (err) { MYLOGE("zip_writer_->WriteBytes(): %s\n", 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; } 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()); } /* 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) ? 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 DoLogcat() { unsigned long timeout; // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); // calculate timeout timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash"); if (timeout < 20000) { timeout = 20000; } RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeout(timeout / 1000).Build()); timeout = logcat_timeout("events"); if (timeout < 20000) { timeout = 20000; } RunCommand("EVENT LOG", {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeout(timeout / 1000).Build()); timeout = logcat_timeout("radio"); if (timeout < 20000) { timeout = 20000; } RunCommand("RADIO LOG", {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, CommandOptions::WithTimeout(timeout / 1000).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 AddGlobalAnrTraceFile(const bool add_to_zip, const std::string& anr_traces_file, const std::string& anr_traces_dir) { std::string dump_traces_dir; if (dump_traces_path != nullptr) { if (add_to_zip) { dump_traces_dir = dirname(dump_traces_path); MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str()); ds.AddDir(dump_traces_dir, true); } 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); } } // Make sure directory is not added twice. // TODO: this is an overzealous check because it's relying on dump_traces_path - which is // generated by dump_traces() - and anr_traces_path - which is retrieved from a system // property - but in reality they're the same path (although the former could be nullptr). // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should // be revisited. bool already_dumped = anr_traces_dir == dump_traces_dir; MYLOGD("AddGlobalAnrTraceFile(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n", dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped); int fd = TEMP_FAILURE_RETRY( open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)); if (fd < 0) { printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_file.c_str(), strerror(errno)); } else { if (add_to_zip) { if (!already_dumped) { MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n", anr_traces_dir.c_str()); ds.AddDir(anr_traces_dir, true); } } else { MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n", anr_traces_file.c_str()); dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd); } } } 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 (anr_data->size() > 0) { AddDumps(anr_data->begin(), anr_data->begin() + 1, "VM TRACES AT LAST ANR", add_to_zip); if (anr_data->size() > 1) { // NOTE: Historical ANRs are always added as separate entries in the // bugreport zip file. AddDumps(anr_data->begin() + 1, 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_file; std::string anr_traces_dir; bool is_global_trace_file = true; // First check whether the stack-trace-dir property is set. When it's set, // each ANR trace will be written to a separate file and not to a global // stack trace file. anr_traces_dir = android::base::GetProperty("dalvik.vm.stack-trace-dir", ""); if (anr_traces_dir.empty()) { anr_traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", ""); if (!anr_traces_file.empty()) { anr_traces_dir = dirname(anr_traces_file.c_str()); } } else { is_global_trace_file = false; } // We have neither configured a global trace file nor a trace directory, // there will be nothing to dump. if (anr_traces_file.empty() && anr_traces_dir.empty()) { printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n"); return; } if (is_global_trace_file) { AddGlobalAnrTraceFile(add_to_zip, anr_traces_file, anr_traces_dir); } else { AddAnrTraceDir(add_to_zip, anr_traces_dir); } /* slow traces for slow operations */ struct stat st; if (!anr_traces_dir.empty()) { 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 void dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); 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"}); 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"}); RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT); if (ds.IsZipping()) { RunCommand( "HARDWARE HALS", {"lshal", std::string("--debug=") + kLsHalDebugPath}, CommandOptions::AS_ROOT); ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath); unlink(kLsHalDebugPath.c_str()); } else { RunCommand( "HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT); } 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"}); } do_dmesg(); RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); 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(tombstone_data->begin(), 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(); 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"}); RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); RunCommand("SYSTEM PROPERTIES", {"getprop"}); RunCommand("VOLD DUMP", {"vdc", "dump"}); RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"}); RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build()); RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"}); printf("------ BACKLIGHTS ------\n"); printf("LCD brightness="); DumpFile("", "/sys/class/leds/lcd-backlight/brightness"); printf("Button brightness="); DumpFile("", "/sys/class/leds/button-backlight/brightness"); printf("Keyboard brightness="); DumpFile("", "/sys/class/leds/keyboard-backlight/brightness"); printf("ALS mode="); DumpFile("", "/sys/class/leds/lcd-backlight/als"); printf("LCD driver registers:\n"); DumpFile("", "/sys/class/leds/lcd-backlight/registers"); printf("\n"); /* 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"); 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"); RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(), 10); printf("========================================================\n"); printf("== Checkins\n"); printf("========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); 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"); RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}); printf("========================================================\n"); printf("== Running Application Services\n"); printf("========================================================\n"); RunDumpsys("APP SERVICES", {"activity", "service", "all"}); printf("========================================================\n"); printf("== Running Application Providers\n"); printf("========================================================\n"); RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"}); 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"); } // This method collects dumpsys for telephony debugging only static void DumpstateTelephonyOnly() { DurationReporter duration_reporter("DUMPSTATE"); DumpIpTablesAsRoot(); if (!DropRootUser()) { return; } do_dmesg(); DoLogcat(); DumpPacketStats(); DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); RunCommand("SYSTEM PROPERTIES", {"getprop"}); printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), 10); RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), 10); printf("========================================================\n"); printf("== Running Application Services\n"); printf("========================================================\n"); RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"}); printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } void Dumpstate::DumpstateBoard() { DurationReporter duration_reporter("dumpstate_board()"); printf("========================================================\n"); printf("== Board\n"); printf("========================================================\n"); ::android::sp dumpstate_device(IDumpstateDevice::getService()); if (dumpstate_device == nullptr) { MYLOGE("No IDumpstateDevice implementation\n"); return; } if (!IsZipping()) { MYLOGD("Not dumping board info because it's not a zipped bugreport\n"); return; } std::string path[NUM_OF_DUMPS]; android::base::unique_fd fd[NUM_OF_DUMPS]; int numFds = 0; for (int i = 0; i < NUM_OF_DUMPS; i++) { path[i] = kDumpstateBoardPath + kDumpstateBoardFiles[i]; MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path[i].c_str()); fd[i] = android::base::unique_fd( TEMP_FAILURE_RETRY(open(path[i].c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); if (fd[i] < 0) { MYLOGE("Could not open file %s: %s\n", path[i].c_str(), strerror(errno)); return; } else { numFds++; } } native_handle_t *handle = native_handle_create(numFds, 0); if (handle == nullptr) { MYLOGE("Could not create native_handle\n"); return; } for (int i = 0; i < numFds; i++) { handle->data[i] = fd[i].release(); } // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call. android::hardware::Return status = dumpstate_device->dumpstateBoard(handle); if (!status.isOk()) { MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); native_handle_close(handle); native_handle_delete(handle); return; } for (int i = 0; i < numFds; i++) { struct stat s; if (fstat(handle->data[i], &s) == -1) { MYLOGE("Failed to fstat %s: %d\n", kDumpstateBoardFiles[i].c_str(), errno); } else if (s.st_size > 0) { AddZipEntry(kDumpstateBoardFiles[i], path[i]); } else { MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str()); } } printf("*** See dumpstate-board.txt entry ***\n"); native_handle_close(handle); native_handle_delete(handle); for (int i = 0; i < numFds; i++) { if (remove(path[i].c_str()) != 0) { MYLOGE("Could not remove(%s): %s\n", path[i].c_str(), strerror(errno)); } } } static void ShowUsageAndExit(int exitCode = 1) { 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)" " -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" " -v: prints the dumpstate header and exit\n"); exit(exitCode); } static void ExitOnInvalidArgs() { fprintf(stderr, "invalid combination of args\n"); ShowUsageAndExit(); } static void sig_handler(int) { _exit(EXIT_FAILURE); } static void register_sig_handler() { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = sig_handler; sigaction(SIGPIPE, &sa, NULL); // broken pipe sigaction(SIGSEGV, &sa, NULL); // segment fault sigaction(SIGINT, &sa, NULL); // ctrl-c sigaction(SIGTERM, &sa, NULL); // killed sigaction(SIGQUIT, &sa, NULL); // quit } 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; } // ... and re-opens it for further logging. redirect_to_existing_file(stderr, const_cast(ds.log_path_.c_str())); 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()) if (remove(tmp_path_.c_str()) != 0) { MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno)); } return true; } static std::string SHA256_file_hash(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 NULL; } 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 NULL; } 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 } int main(int argc, char *argv[]) { int do_add_date = 0; int do_zip_file = 0; int do_vibrate = 1; char* use_outfile = 0; int use_socket = 0; int use_control_socket = 0; int do_fb = 0; int do_broadcast = 0; int is_remote_mode = 0; bool show_header_only = false; bool do_start_service = false; bool telephony_only = false; /* 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); } } /* parse arguments */ int c; while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) { switch (c) { // clang-format off case 'd': do_add_date = 1; break; case 'z': do_zip_file = 1; break; case 'o': use_outfile = optarg; break; case 's': use_socket = 1; break; case 'S': use_control_socket = 1; break; case 'v': show_header_only = true; break; case 'q': do_vibrate = 0; break; case 'p': do_fb = 1; break; case 'P': ds.update_progress_ = true; break; case 'R': is_remote_mode = 1; break; case 'B': do_broadcast = 1; break; case 'V': break; // compatibility no-op case 'h': ShowUsageAndExit(0); break; default: fprintf(stderr, "Invalid option: %c\n", c); ShowUsageAndExit(); // clang-format on } } // TODO: use helper function to convert argv into a string for (int i = 0; i < argc; i++) { ds.args_ += argv[i]; if (i < argc - 1) { ds.args_ += " "; } } ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, ""); if (!ds.extra_options_.empty()) { // Framework uses a system property to override some command-line args. // Currently, it contains the type of the requested bugreport. if (ds.extra_options_ == "bugreportplus") { // Currently, the dumpstate binder is only used by Shell to update progress. do_start_service = true; ds.update_progress_ = true; do_fb = 0; } else if (ds.extra_options_ == "bugreportremote") { do_vibrate = 0; is_remote_mode = 1; do_fb = 0; } else if (ds.extra_options_ == "bugreportwear") { ds.update_progress_ = true; } else if (ds.extra_options_ == "bugreporttelephony") { telephony_only = true; } else { MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str()); } // Reset the property android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, ""); } ds.notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, ""); if (!ds.notification_title.empty()) { // Reset the property android::base::SetProperty(PROPERTY_EXTRA_TITLE, ""); ds.notification_description = android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); if (!ds.notification_description.empty()) { // Reset the property android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); } MYLOGD("notification (title: %s, description: %s)\n", ds.notification_title.c_str(), ds.notification_description.c_str()); } if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) { ExitOnInvalidArgs(); } if (use_control_socket && !do_zip_file) { ExitOnInvalidArgs(); } if (ds.update_progress_ && !do_broadcast) { ExitOnInvalidArgs(); } if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) { ExitOnInvalidArgs(); } if (ds.version_ == VERSION_DEFAULT) { ds.version_ = VERSION_CURRENT; } if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) { MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n", ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(), VERSION_SPLIT_ANR.c_str()); exit(1); } if (show_header_only) { ds.PrintHeader(); exit(0); } /* redirect output if needed */ bool is_redirecting = !use_socket && use_outfile; // 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", dirname(use_outfile)) : ""; ds.progress_.reset(new Progress(stats_path)); /* gets the sequential id */ uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0); ds.id_ = ++last_id; android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id)); MYLOGI("begin\n"); register_sig_handler(); if (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", ds.id_, ds.args_.c_str(), ds.extra_options_.c_str()); MYLOGI("bugreport format version: %s\n", ds.version_.c_str()); ds.do_early_screenshot_ = ds.update_progress_; // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. if (use_socket) { redirect_to_socket(stdout, "dumpstate"); } if (use_control_socket) { MYLOGD("Opening control socket\n"); ds.control_socket_fd_ = open_socket("dumpstate"); ds.update_progress_ = 1; } if (is_redirecting) { ds.bugreport_dir_ = dirname(use_outfile); 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_ = android::base::StringPrintf("%s-%s-%s", basename(use_outfile), device_name.c_str(), build_id.c_str()); if (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 (telephony_only) { ds.base_name_ += "-telephony"; } if (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"); MYLOGD( "Bugreport dir: %s\n" "Base name: %s\n" "Suffix: %s\n" "Log path: %s\n" "Temporary path: %s\n" "Screenshot path: %s\n", ds.bugreport_dir_.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 (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)); do_zip_file = 0; } else { ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); } ds.AddTextZipEntry("version.txt", ds.version_); } if (ds.update_progress_) { if (do_broadcast) { // clang-format off std::vector am_args = { "--receiver-permission", "android.permission.DUMP", "--es", "android.intent.extra.NAME", ds.name_, "--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()), }; // clang-format on SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); } if (use_control_socket) { dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.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 (do_vibrate) { Vibrate(150); } if (do_fb && ds.do_early_screenshot_) { if (ds.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"); ds.TakeScreenshot(); } } if (do_zip_file) { if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(), strerror(errno)); } } if (is_redirecting) { redirect_to_file(stderr, const_cast(ds.log_path_.c_str())); if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", ds.log_path_.c_str(), strerror(errno)); } /* 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. */ redirect_to_file(stdout, const_cast(ds.tmp_path_.c_str())); if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", ds.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. ds.PrintHeader(); if (telephony_only) { DumpstateTelephonyOnly(); ds.DumpstateBoard(); } else { // Dumps systrace right away, otherwise it will be filled with unnecessary events. // First try to dump anrd trace if the daemon is running. Otherwise, dump // the raw trace. if (!dump_anrd_trace()) { dump_systrace(); } // Invoking the following dumpsys calls before dump_traces() to try and // keep the system stats as close to its initial state as possible. RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"}, CommandOptions::WithTimeout(90).DropRoot().Build()); RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"}, CommandOptions::WithTimeout(10).DropRoot().Build()); // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed. dump_raft(); /* collect stack traces from Dalvik and native processes (needs root) */ dump_traces_path = dump_traces(); /* Run some operations that require root. */ tombstone_data.reset(GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping())); anr_data.reset(GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping())); ds.AddDir(RECOVERY_DIR, true); ds.AddDir(RECOVERY_DATA_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()); // Run ss as root so we can see socket marks. RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build()); if (!DropRootUser()) { return -1; } dumpstate(); } /* close output if needed */ if (is_redirecting) { fclose(stdout); } /* rename or zip the (now complete) .tmp file to its final location */ if (use_outfile) { /* 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 (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; // Since zip file is already created, it needs to be renamed. 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 (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()); } } } /* vibrate a few but shortly times to let user know it's finished */ for (int i = 0; i < 3; i++) { Vibrate(75); usleep((75 + 50) * 1000); } /* tell activity manager we're done */ if (do_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 (do_fb) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.SCREENSHOT"); am_args.push_back(ds.screenshot_path_); } if (!ds.notification_title.empty()) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.TITLE"); am_args.push_back(ds.notification_title); if (!ds.notification_description.empty()) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.DESCRIPTION"); am_args.push_back(ds.notification_description); } } if (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"); } } MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(), ds.progress_->GetInitialMax()); ds.progress_->Save(); MYLOGI("done (id %d)\n", ds.id_); if (is_redirecting) { fclose(stderr); } if (use_control_socket && ds.control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); close(ds.control_socket_fd_); } return 0; } cmds/dumpstate/dumpstate.h0100644 0000000 0000000 00000032567 13300556574 014745 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 "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 { 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: DurationReporter(const std::string& title, bool log_only = false); ~DurationReporter(); private: std::string title_; bool log_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; 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_; const std::string& path_; }; /* * List of supported zip format versions. * * See bugreport-format.md for more info. */ static std::string VERSION_CURRENT = "1.0"; /* * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version * will be bumped to 2.0-dev-1. */ static std::string VERSION_SPLIT_ANR = "2.0-dev-1"; /* * "Alias" for the current version. */ static std::string VERSION_DEFAULT = "default"; /* * 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: 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 = 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. */ bool AddZipEntryFromFd(const std::string& entry_name, int fd); /* * Adds a text entry 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); 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(); /* Gets the path of a bugreport file with the given suffix. */ std::string GetPath(const std::string& suffix) const; // TODO: initialize fields on constructor // dumpstate id - unique after each device reboot. uint32_t id_; // dumpstate pid pid_t pid_; // Whether progress updates should be published. bool update_progress_ = false; // 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; // Command-line arguments as string std::string args_; // Extra options passed as system property. std::string extra_options_; // Full path of the directory where the bugreport files will be written. std::string bugreport_dir_; // Full path of the temporary file containing the screenshot (when requested). std::string screenshot_path_; 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_; // Full path of the temporary file containing the bugreport. std::string tmp_path_; // Full path of the file containing the dumpstate logs. std::string log_path_; // Pointer to the actual path, be it zip or text. std::string 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 listing to progress. android::sp listener_; std::string listener_name_; // Notification title and description std::string notification_title; std::string notification_description; private: // Used by GetInstance() only. Dumpstate(const std::string& version = VERSION_CURRENT); 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); /* redirect output to a service control socket */ void redirect_to_socket(FILE *redirect, const char *service); /* redirect output to a new file */ void redirect_to_file(FILE *redirect, char *path); /* redirect output to an existing file */ void redirect_to_existing_file(FILE *redirect, char *path); /* create leading directories, if necessary */ void create_parent_dirs(const char *path); /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */ const char *dump_traces(); /* 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); #ifdef __cplusplus } #endif #endif /* FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ */ cmds/dumpstate/dumpstate.rc0100644 0000000 0000000 00000001160 13300556574 015103 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 cmds/dumpstate/tests/0040755 0000000 0000000 00000000000 13300556574 013716 5ustar000000000 0000000 cmds/dumpstate/tests/dumpstate_test.cpp0100644 0000000 0000000 00000123725 13300556574 017476 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 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; class DumpstateListenerMock : public IDumpstateListener { public: MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); 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; } 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 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_ = false; ds.update_progress_threshold_ = 0; } // 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.update_progress_ = 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\n", listener_name.c_str(), 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------")); EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\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(out, EndsWith("s was the duration of 'I AM GROOT' ------\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_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_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_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_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_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_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, 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")); EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\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------")); EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\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_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, &token).isOk()); ASSERT_THAT(token, IsNull()); } TEST_F(DumpstateServiceTest, SetListenerNoPointer) { sp token; EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk()); ASSERT_THAT(token, IsNull()); } TEST_F(DumpstateServiceTest, SetListenerTwice) { sp listener(new DumpstateListenerMock()); sp token; EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk()); ASSERT_THAT(token, NotNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); token.clear(); EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk()); ASSERT_THAT(token, IsNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); } 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, RunCommandTimesout) { 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, 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, 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 13300556574 021235 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 13300556574 015527 5ustar000000000 0000000 cmds/dumpstate/tests/testdata/empty-file.txt0100644 0000000 0000000 00000000000 13300556574 020326 0ustar000000000 0000000 cmds/dumpstate/tests/testdata/multiple-lines-with-newline.txt0100644 0000000 0000000 00000000041 13300556574 023633 0ustar000000000 0000000 I AM LINE1 I AM LINE2 I AM LINE3 cmds/dumpstate/tests/testdata/multiple-lines.txt0100644 0000000 0000000 00000000040 13300556574 021222 0ustar000000000 0000000 I AM LINE1 I AM LINE2 I AM LINE3cmds/dumpstate/tests/testdata/single-line-with-newline.txt0100644 0000000 0000000 00000000013 13300556574 023075 0ustar000000000 0000000 I AM LINE1 cmds/dumpstate/tests/testdata/single-line.txt0100644 0000000 0000000 00000000012 13300556574 020464 0ustar000000000 0000000 I AM LINE1cmds/dumpstate/tests/testdata/stats-invalid-1st-NAN.txt0100644 0000000 0000000 00000000017 13300556574 022164 0ustar000000000 0000000 SIX_SIX_SIX 42 cmds/dumpstate/tests/testdata/stats-invalid-1st-negative.txt0100644 0000000 0000000 00000000010 13300556574 023343 0ustar000000000 0000000 -666 42 cmds/dumpstate/tests/testdata/stats-invalid-1st-too-big.txt0100644 0000000 0000000 00000000016 13300556574 023107 0ustar000000000 0000000 4815162342 42 cmds/dumpstate/tests/testdata/stats-invalid-2nd-NAN.txt0100644 0000000 0000000 00000000016 13300556574 022137 0ustar000000000 0000000 666 FORTY_TWO cmds/dumpstate/tests/testdata/stats-invalid-2nd-negative.txt0100644 0000000 0000000 00000000010 13300556574 023317 0ustar000000000 0000000 666 -42 cmds/dumpstate/tests/testdata/stats-invalid-2nd-too-big.txt0100644 0000000 0000000 00000000017 13300556574 023064 0ustar000000000 0000000 666 4815162342 cmds/dumpstate/tests/testdata/stats-invalid-both-NAN.txt0100644 0000000 0000000 00000000016 13300556574 022410 0ustar000000000 0000000 N_RUNS AVERAGEcmds/dumpstate/tests/testdata/stats-one-run-no-newline.txt0100644 0000000 0000000 00000000004 13300556574 023047 0ustar000000000 0000000 1 10cmds/dumpstate/tests/testdata/stats-two-runs.txt0100644 0000000 0000000 00000000005 13300556574 021212 0ustar000000000 0000000 2 15 cmds/dumpstate/utils.cpp0100644 0000000 0000000 00000126222 13300556574 014422 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 #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); } /* 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/mediaserver", "/system/bin/sdcard", "/system/bin/surfaceflinger", "/system/bin/vehicle_network_service", "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec 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.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.media.omx@1.0::IOmx", "android.hardware.sensors@1.0::ISensors", "android.hardware.vr@1.0::IVr", NULL, }; // 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(); Dumpstate::Dumpstate(const std::string& version) : pid_(getpid()), 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 log_only) : title_(title), log_only_(log_only) { if (!title_.empty()) { started_ = Nanotime(); } } DurationReporter::~DurationReporter() { if (!title_.empty()) { uint64_t elapsed = Nanotime() - started_; if (log_only_) { MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC); } else { // Use "Yoda grammar" to make it easier to grep|sort sections. printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC, 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) { bool changed = false; if (delta >= 0) { progress_ += delta; 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 android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.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))) { 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))) { 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, NULL, 0); if (size <= 0) { printf("Unexpected klogctl return value: %d\n\n", size); return; } char *buf = (char *) malloc(size + 1); if (buf == NULL) { 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 = NULL; const char *slash = "/"; int fd, 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 == NULL) { 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 = NULL) { 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; } fd = TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); if (fd < 0) { retval = fd; printf("*** %s: %s\n", newpath, strerror(errno)); continue; } (*dump_from_fd)(NULL, newpath, fd); } 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)); close(fd); return -1; } else if (!(flags & O_NONBLOCK)) { printf("*** %s: fd must have O_NONBLOCK set.\n", path); close(fd); 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 dumpsysTimeout) { long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout(); std::vector dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)}; 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)); exit(1); } fcntl(s, F_SETFD, FD_CLOEXEC); if (listen(s, 4) < 0) { MYLOGE("listen(control socket): %s\n", strerror(errno)); exit(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)); exit(1); } return fd; } /* redirect output to a service control socket */ void redirect_to_socket(FILE *redirect, const char *service) { int fd = open_socket(service); fflush(redirect); dup2(fd, fileno(redirect)); close(fd); } // 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++ = '/'; } } } void _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)); exit(1); } TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); } void redirect_to_file(FILE *redirect, char *path) { _redirect_to_file(redirect, path, O_TRUNC); } void redirect_to_existing_file(FILE *redirect, char *path) { _redirect_to_file(redirect, path, O_APPEND); } static bool should_dump_hal_interface(const char* interface) { for (const char** i = hal_interfaces_to_dump; *i; i++) { if (!strcmp(*i, interface)) { return true; } } return false; } static 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; 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.c_str())) { continue; } pids.insert(info.pid); } }); if (!ret.isOk()) { MYLOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str()); } return pids; // whether it was okay or not } const char* DumpTraces(const std::string& traces_path); const char* DumpTracesTombstoned(const std::string& traces_dir); /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */ const char *dump_traces() { DurationReporter duration_reporter("DUMP TRACES"); const std::string traces_dir = android::base::GetProperty("dalvik.vm.stack-trace-dir", ""); if (!traces_dir.empty()) { return DumpTracesTombstoned(traces_dir); } const std::string traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", ""); if (!traces_file.empty()) { return DumpTraces(traces_file); } return nullptr; } static bool IsZygote(int pid) { static const std::string kZygotePrefix = "zygote"; std::string cmdline; if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &cmdline)) { return true; } return (cmdline.find(kZygotePrefix) == 0); } const char* DumpTracesTombstoned(const std::string& traces_dir) { const std::string temp_file_pattern = traces_dir + "/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 nullptr; } // 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 nullptr; } std::unique_ptr proc(opendir("/proc"), closedir); if (proc.get() == nullptr) { MYLOGE("opendir /proc failed: %s\n", strerror(errno)); return nullptr; } // 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()))) { 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) { dprintf(fd, "dumping failed, likely due to a timeout\n"); 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"); } return file_name_buf.release(); } const char* DumpTraces(const std::string& traces_path) { const char* result = NULL; /* move the old traces.txt (if any) out of the way temporarily */ std::string anrtraces_path = traces_path + ".anr"; if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) { MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno)); return nullptr; // Can't rename old traces.txt -- no permission? -- leave it alone instead } /* create a new, empty traces.txt file to receive stack dumps */ int fd = TEMP_FAILURE_RETRY( open(traces_path.c_str(), O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, 0666)); /* -rw-rw-rw- */ if (fd < 0) { MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno)); return nullptr; } int chmod_ret = fchmod(fd, 0666); if (chmod_ret < 0) { MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno)); close(fd); return nullptr; } /* Variables below must be initialized before 'goto' statements */ int dalvik_found = 0; int ifd, wfd = -1; std::set hal_pids = get_interesting_hal_pids(); /* walk /proc and kill -QUIT all Dalvik processes */ DIR *proc = opendir("/proc"); if (proc == NULL) { MYLOGE("/proc: %s\n", strerror(errno)); goto error_close_fd; } /* use inotify to find when processes are done dumping */ ifd = inotify_init(); if (ifd < 0) { MYLOGE("inotify_init: %s\n", strerror(errno)); goto error_close_fd; } wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE); if (wfd < 0) { MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno)); goto error_close_ifd; } struct dirent *d; while ((d = readdir(proc))) { int pid = atoi(d->d_name); if (pid <= 0) continue; char path[PATH_MAX]; char data[PATH_MAX]; snprintf(path, sizeof(path), "/proc/%d/exe", pid); ssize_t len = readlink(path, data, sizeof(data) - 1); if (len <= 0) { continue; } data[len] = '\0'; if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) { /* skip zygote -- it won't dump its stack anyway */ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); int cfd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)); len = read(cfd, data, sizeof(data) - 1); close(cfd); if (len <= 0) { continue; } data[len] = '\0'; if (!strncmp(data, "zygote", strlen("zygote"))) { continue; } ++dalvik_found; uint64_t start = Nanotime(); if (kill(pid, SIGQUIT)) { MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno)); continue; } /* wait for the writable-close notification from inotify */ struct pollfd pfd = { ifd, POLLIN, 0 }; int ret = poll(&pfd, 1, TRACE_DUMP_TIMEOUT_MS); if (ret < 0) { MYLOGE("poll: %s\n", strerror(errno)); } else if (ret == 0) { MYLOGE("warning: timed out dumping pid %d\n", pid); } else { struct inotify_event ie; read(ifd, &ie, sizeof(ie)); } if (lseek(fd, 0, SEEK_END) < 0) { MYLOGE("lseek: %s\n", strerror(errno)); } else { dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n", pid, (float)(Nanotime() - start) / NANOS_PER_SEC); } } else if (should_dump_native_traces(data) || hal_pids.find(pid) != hal_pids.end()) { /* dump native process if appropriate */ if (lseek(fd, 0, SEEK_END) < 0) { MYLOGE("lseek: %s\n", strerror(errno)); } else { static uint16_t timeout_failures = 0; uint64_t start = Nanotime(); /* If 3 backtrace dumps fail in a row, consider debuggerd dead. */ if (timeout_failures == 3) { dprintf(fd, "too many stack dump failures, skipping...\n"); } else if (dump_backtrace_to_file_timeout( pid, kDebuggerdNativeBacktrace, 20, fd) == -1) { dprintf(fd, "dumping failed, likely due to a timeout\n"); timeout_failures++; } else { timeout_failures = 0; } dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid, (float)(Nanotime() - start) / NANOS_PER_SEC); } } } if (dalvik_found == 0) { MYLOGE("Warning: no Dalvik processes found to dump stacks\n"); } static std::string dumptraces_path = android::base::StringPrintf( "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str())); if (rename(traces_path.c_str(), dumptraces_path.c_str())) { MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(), strerror(errno)); goto error_close_ifd; } result = dumptraces_path.c_str(); /* replace the saved [ANR] traces.txt file */ rename(anrtraces_path.c_str(), traces_path.c_str()); error_close_ifd: close(ifd); error_close_fd: close(fd); return result; } 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) { if (progress_ == nullptr) { MYLOGE("UpdateProgress: progress_ not set\n"); return; } // Always update progess so stats can be tuned... bool max_changed = progress_->Inc(delta); // ...but only notifiy listeners when necessary. if (!update_progress_) 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_); } if (listener_ != nullptr) { if (progress % 100 == 0) { // We don't want to spam logcat, so only log multiples of 100. MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max); } else { // stderr is ignored on normal invocations, but useful when calling // /system/bin/dumpstate directly for debuggging. fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max); } listener_->onProgressUpdated(progress); } } 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 13300556574 012252 5ustar000000000 0000000 cmds/dumpsys/.clang-format0100644 0000000 0000000 00000000440 13300556574 014620 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 00000001127 13300556574 014153 0ustar000000000 0000000 cc_defaults { name: "dumpsys_defaults", cflags: [ "-Wall", "-Werror", ], srcs: [ "dumpsys.cpp", ], shared_libs: [ "libbase", "libutils", "liblog", "libbinder", ], clang: true, } // // 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", ], } subdirs = ["tests"] cmds/dumpsys/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 015372 0ustar000000000 0000000 cmds/dumpsys/NOTICE0100644 0000000 0000000 00000024707 13300556574 013165 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/dumpsys.cpp0100644 0000000 0000000 00000023154 13300556574 014464 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 "dumpsys.h" using namespace android; using android::base::StringPrintf; using android::base::unique_fd; using android::base::WriteFully; 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] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n" " --help: shows this help\n" " -l: only list services, do not dump them\n" " -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\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; } int Dumpsys::main(int argc, char* const argv[]) { Vector services; Vector args; Vector skippedServices; bool showListOnly = false; bool skipServices = false; int timeoutArg = 10; static struct option longOptions[] = { {"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: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, "help")) { usage(); return 0; } break; case 't': { char *endptr; timeoutArg = strtol(optarg, &endptr, 10); if (*endptr != '\0' || timeoutArg <= 0) { fprintf(stderr, "Error: invalid timeout 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) { // gets all services services = sm_->listServices(); services.sort(sort_func); args.add(String16("-a")); } 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++) { String16 service_name = std::move(services[i]); if (IsSkipped(skippedServices, service_name)) continue; sp service = sm_->checkService(service_name); if (service != nullptr) { int sfd[2]; if (pipe(sfd) != 0) { aerr << "Failed to create pipe to dump service info for " << service_name << ": " << strerror(errno) << endl; continue; } unique_fd local_end(sfd[0]); unique_fd remote_end(sfd[1]); sfd[0] = sfd[1] = -1; if (N > 1) { aout << "------------------------------------------------------------" "-------------------" << endl; aout << "DUMP OF SERVICE " << service_name << ":" << endl; } // dump blocks until completion, so spawn a thread.. std::thread dump_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) << ") " << service_name << endl; } }); auto timeout = std::chrono::seconds(timeoutArg); auto start = std::chrono::steady_clock::now(); auto end = start + timeout; struct pollfd pfd = { .fd = local_end.get(), .events = POLLIN }; bool timed_out = false; bool error = false; 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 " << service_name << " : " << strerror(errno) << endl; error = true; break; } else if (rc == 0) { timed_out = true; break; } char buf[4096]; rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf))); if (rc < 0) { aerr << "Failed to read while dumping service " << service_name << ": " << strerror(errno) << endl; error = true; break; } else if (rc == 0) { // EOF. break; } if (!WriteFully(STDOUT_FILENO, buf, rc)) { aerr << "Failed to write while dumping service " << service_name << ": " << strerror(errno) << endl; error = true; break; } } if (timed_out) { aout << endl << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArg << "s) EXPIRED ***" << endl << endl; } if (timed_out || error) { dump_thread.detach(); } else { dump_thread.join(); } if (N > 1) { std::chrono::duration elapsed_seconds = std::chrono::steady_clock::now() - start; aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str() << "was the duration of dumpsys " << service_name; 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); aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S") << endl; } } else { aerr << "Can't find service: " << service_name << endl; } } return 0; } cmds/dumpsys/dumpsys.h0100644 0000000 0000000 00000001732 13300556574 014127 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 namespace android { class Dumpsys { public: Dumpsys(android::IServiceManager* sm) : sm_(sm) { } int main(int argc, char* const argv[]); private: android::IServiceManager* sm_; }; } #endif // FRAMEWORK_NATIVE_CMD_DUMPSYS_H_ cmds/dumpsys/main.cpp0100644 0000000 0000000 00000002332 13300556574 013677 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 13300556574 013414 5ustar000000000 0000000 cmds/dumpsys/tests/Android.bp0100644 0000000 0000000 00000000474 13300556574 015321 0ustar000000000 0000000 // Build the unit tests for dumpsys cc_test { name: "dumpsys_test", test_suites: ["device-tests"], srcs: ["dumpsys_test.cpp"], shared_libs: [ "libbase", "libbinder", "libutils", ], static_libs: [ "libdumpsys", "libgmock", ], clang: true, } cmds/dumpsys/tests/AndroidTest.xml0100644 0000000 0000000 00000002306 13300556574 016354 0ustar000000000 0000000 cmds/dumpsys/tests/dumpsys_test.cpp0100644 0000000 0000000 00000022770 13300556574 016670 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 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_METHOD3(addService, status_t(const String16&, const sp&, bool)); MOCK_METHOD0(listServices, Vector()); 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 = ::std::tr1::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 (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()).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 AssertRunningServices(const std::vector& services) { std::string expected("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)); } 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 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, DumpRunningServiceTimeout) { sp binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car"); CallMain({"-t", "1", "Valet"}); AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) 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' 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"); } cmds/flatland/0040755 0000000 0000000 00000000000 13300556574 012333 5ustar000000000 0000000 cmds/flatland/Android.mk0100644 0000000 0000000 00000001061 13300556574 014237 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_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 13300556574 015022 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 13300556574 014233 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 00000031571 13300556574 014505 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(NULL), 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 != NULL) { delete[] mShaderPrograms; mShaderPrograms = NULL; } if (mSurfaceComposerClient != NULL) { 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(), NULL); 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) { sp dpy = mSurfaceComposerClient->getBuiltInDisplay(0); if (dpy == NULL) { fprintf(stderr, "SurfaceComposer::getBuiltInDisplay 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 == NULL) { 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 == NULL || !sc->isValid()) { fprintf(stderr, "Failed to create SurfaceControl.\n"); return false; } float scale; result = computeWindowScale(w, h, &scale); if (!result) { return false; } SurfaceComposerClient::openGlobalTransaction(); err = sc->setLayer(0x7FFFFFFF); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err); return false; } err = sc->setMatrix(scale, 0.0f, 0.0f, scale); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposer::setMatrix error: %#x\n", err); return false; } err = sc->show(); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposer::show error: %#x\n", err); return false; } SurfaceComposerClient::closeGlobalTransaction(); sp anw = sc->getSurface(); EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); 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, NULL); 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, NULL, 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] != NULL; 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] != NULL; 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] != NULL; 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, NULL, 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 13300556574 014146 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 00000051130 13300556574 013720 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() : mFirstFrame(true), mGLHelper(NULL), 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 != NULL) { mComposer->tearDown(); delete mComposer; mComposer = NULL; } if (mRenderer != NULL) { mRenderer->tearDown(); delete mRenderer; mRenderer = NULL; } if (mSurface != EGL_NO_SURFACE) { mGLHelper->destroySurface(&mSurface); mGLConsumer->abandon(); } mGLHelper = NULL; 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: bool mFirstFrame; 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(NULL), mSurface(EGL_NO_SURFACE), mWindowSurface(EGL_NO_SURFACE) { } bool setUp() { ATRACE_CALL(); bool result; EGLint resulte; 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 != NULL) { if (mWindowSurface != EGL_NO_SURFACE) { mGLHelper->destroySurface(&mWindowSurface); } mGLHelper->destroySurface(&mSurface); mGLConsumer->abandon(); mGLConsumer.clear(); mSurfaceControl.clear(); mGLHelper->tearDown(); delete mGLHelper; mGLHelper = NULL; } } nsecs_t run(uint32_t warmUpFrames, uint32_t totalFrames) { ATRACE_CALL(); bool result; status_t err; 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 == NULL) { 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 13300556574 014034 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 13300556574 014772 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 13300556574 012360 5ustar000000000 0000000 cmds/installd/Android.bp0100644 0000000 0000000 00000002245 13300556574 014263 0ustar000000000 0000000 cc_defaults { name: "installd_defaults", cflags: [ "-Wall", "-Werror", ], srcs: [ "CacheItem.cpp", "CacheTracker.cpp", "InstalldNativeService.cpp", "dexopt.cpp", "globals.cpp", "utils.cpp", "binder/android/os/IInstalld.aidl", ], shared_libs: [ "libbase", "libbinder", "libcutils", "liblog", "liblogwrap", "libselinux", "libutils", ], clang: true, } // // Static library used in testing and executable // cc_library_static { name: "libinstalld", defaults: ["installd_defaults"], export_include_dirs: ["."], aidl: { export_aidl_headers: true, }, } // // Executable // cc_binary { name: "installd", defaults: ["installd_defaults"], srcs: ["installd.cpp"], static_libs: ["libdiskusage"], init_rc: ["installd.rc"], } // OTA chroot tool cc_binary { name: "otapreopt_chroot", cflags: [ "-Wall", "-Werror", ], clang: true, srcs: ["otapreopt_chroot.cpp"], shared_libs: [ "libbase", "liblog", ], } subdirs = ["tests"] cmds/installd/Android.mk0100644 0000000 0000000 00000003412 13300556574 014266 0ustar000000000 0000000 LOCAL_PATH := $(call my-dir) # # OTA Executable # include $(CLEAR_VARS) LOCAL_MODULE := otapreopt LOCAL_CFLAGS := -Wall -Werror # Base & ASLR boundaries for boot image creation. ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := -0x1000000 else LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) endif ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := 0x1000000 else LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) endif LOCAL_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) LOCAL_SRC_FILES := otapreopt.cpp globals.cpp utils.cpp dexopt.cpp LOCAL_HEADER_LIBRARIES := dex2oat_headers LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ liblog \ liblogwrap \ libselinux \ libutils \ LOCAL_STATIC_LIBRARIES := libdiskusage LOCAL_CLANG := true include $(BUILD_EXECUTABLE) # OTA slot script include $(CLEAR_VARS) LOCAL_MODULE:= otapreopt_slot LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_SRC_FILES := otapreopt_slot.sh LOCAL_INIT_RC := otapreopt.rc include $(BUILD_PREBUILT) # OTA postinstall script include $(CLEAR_VARS) LOCAL_MODULE:= otapreopt_script LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_SRC_FILES := 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. LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot include $(BUILD_PREBUILT) cmds/installd/CacheItem.cpp0100644 0000000 0000000 00000007666 13300556574 014722 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, NULL))) { 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 13300556574 014355 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 00000014743 13300556574 015411 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 #include "utils.h" using android::base::StringPrintf; namespace android { namespace installd { CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice) : cacheUsed(0), cacheQuota(0), mUserId(userId), mAppId(appId), mQuotaDevice(quotaDevice), mItemsLoaded(false) { } 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 (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 (!mQuotaDevice.empty() && cacheGid != -1 && extCacheGid != -1) { struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), cacheGid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid; } return false; } else { cacheUsed += dq.dqb_curspace; } if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), extCacheGid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid; } return false; } else { cacheUsed += dq.dqb_curspace; } 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, NULL))) { 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 (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 00000003571 13300556574 015053 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& quotaDevice); ~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; std::string mQuotaDevice; bool mItemsLoaded; 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 00000254250 13300556574 017333 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 // 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 "CacheTracker.h" #include "MatchExtensionGen.h" #ifndef LOG_TAG #define LOG_TAG "installd" #endif using android::base::StringPrintf; using std::endl; namespace android { namespace installd { 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"; // NOTE: keep in sync with Installer static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; static constexpr int FLAG_USE_QUOTA = 1 << 12; static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15; static constexpr int FLAG_FORCE = 1 << 16; 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) { 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 checkArgumentPackageName(const std::string& packageName) { if (is_valid_package_name(packageName.c_str())) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("Package name %s is malformed", packageName.c_str())); } } #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_PACKAGE_NAME(packageName) { \ binder::Status status = \ checkArgumentPackageName((packageName)); \ if (!status.isOk()) { \ return status; \ } \ } } // 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; } out << endl << "Quota reverse mounts:" << endl; for (const auto& n : mQuotaReverseMounts) { 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; } /** * Ensure that we have a hard-limit quota to protect against abusive apps; * they should never use more than 90% of blocks or 50% of inodes. */ static int prepare_app_quota(const std::unique_ptr& uuid, const std::string& device, uid_t uid) { if (device.empty()) return 0; struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast(&dq)) != 0) { PLOG(WARNING) << "Failed to find quota for " << uid; return -1; } #if APPLY_HARD_QUOTAS if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) { auto path = create_data_path(uuid ? uuid->c_str() : nullptr); struct statvfs stat; if (statvfs(path.c_str(), &stat) != 0) { PLOG(WARNING) << "Failed to statvfs " << path; return -1; } dq.dqb_valid = QIF_LIMITS; dq.dqb_bhardlimit = (((static_cast(stat.f_blocks) * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE; dq.dqb_ihardlimit = (stat.f_files / 2); if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast(&dq)) != 0) { PLOG(WARNING) << "Failed to set hard quota for " << uid; return -1; } else { LOG(DEBUG) << "Applied hard quotas for " << uid; return 0; } } else { // Hard quota already set; assume it's reasonable return 0; } #else // Hard quotas disabled return 0; #endif } 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_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) { return error("Failed to set hard quota " + path); } if (property_get_bool("dalvik.vm.usejitprofiles", false)) { const std::string profile_dir = create_primary_current_profile_package_dir_path(userId, pkgname); // read-write-execute only for the app user. if (fs_prepare_dir_strict(profile_dir.c_str(), 0700, uid, uid) != 0) { return error("Failed to prepare " + profile_dir); } const std::string profile_file = create_current_profile_path(userId, pkgname, /*is_secondary_dex*/false); // read-write only for the app user. if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) { return error("Failed to prepare " + profile_file); } const std::string ref_profile_path = create_primary_reference_profile_package_dir_path(pkgname); // dex2oat/profman runs under the shared app gid and it needs to read/write reference // profiles. int shared_app_gid = multiuser_get_shared_gid(0, appId); if ((shared_app_gid != -1) && fs_prepare_dir_strict( ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) { return error("Failed to prepare " + ref_profile_path); } } } 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) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); binder::Status res = ok(); if (!clear_primary_reference_profile(packageName)) { res = error("Failed to clear reference profile for " + packageName); } if (!clear_primary_current_profiles(packageName)) { 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); } } } 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 (!only_cache) { if (!clear_primary_current_profile(packageName, userId)) { res = error("Failed to clear current profile for " + packageName); } } } 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); } 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, NULL))) { 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; } } // Intentional fall through to 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; } } } fts_close(fts); ATRACE_END(); } 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); 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.c_str(), (char*) to_parent.c_str() }; LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); 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; } 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 */ nullptr, nullptr }; { auto from = create_data_user_de_package_path(from_uuid, user, package_name); auto to = create_data_user_de_path(to_uuid, user); argv[6] = (char*) from.c_str(); argv[7] = (char*) to.c_str(); LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); 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); argv[6] = (char*) from.c_str(); argv[7] = (char*) to.c_str(); LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); 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, NULL) != 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, NULL) != 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, NULL) != 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)); } } } // Data under /data/media doesn't have an app, but we still want // to limit it to prevent abuse. if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), multiuser_get_uid(userId, AID_MEDIA_RW))) { return error("Failed to set hard quota for media_rw"); } 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); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto data_path = create_data_path(uuid_); auto device = findQuotaDeviceForUuid(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, NULL))) { return error("Failed to fts_open"); } while ((p = fts_read(fts)) != NULL) { 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), device)); 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); 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& device, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { if (device.empty()) return; struct dqblk dq; if (stats != nullptr) { uid_t uid = multiuser_get_uid(userId, appId); 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; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; #endif stats->dataSize += dq.dqb_curspace; } int cacheGid = multiuser_get_cache_gid(userId, appId); if (cacheGid != -1) { if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace; #endif stats->cacheSize += dq.dqb_curspace; } } int sharedGid = multiuser_get_shared_gid(0, appId); if (sharedGid != -1) { if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << sharedGid << " " << dq.dqb_curspace; #endif stats->codeSize += dq.dqb_curspace; } } } if (extStats != nullptr) { int extGid = multiuser_get_ext_gid(userId, appId); if (extGid != -1) { if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extGid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extGid; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << extGid << " " << dq.dqb_curspace; #endif extStats->dataSize += dq.dqb_curspace; } } int extCacheGid = multiuser_get_ext_cache_gid(userId, appId); if (extCacheGid != -1) { if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extCacheGid, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extCacheGid; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << extCacheGid << " " << dq.dqb_curspace; #endif extStats->dataSize += dq.dqb_curspace; extStats->cacheSize += dq.dqb_curspace; } } } } 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, NULL))) { PLOG(ERROR) << "Failed to fts_open " << path; return; } while ((p = fts_read(fts)) != NULL) { 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; } // Fall through 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 (auto packageName : packageNames) { CHECK_ARGUMENT_PACKAGE_NAME(packageName); } // 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)); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto device = findQuotaDeviceForUuid(uuid); if (device.empty()) { flags &= ~FLAG_USE_QUOTA; } ATRACE_BEGIN("obb"); for (auto packageName : packageNames) { auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str()); calculate_tree_size(obbCodePath, &extStats.codeSize); } ATRACE_END(); if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) { ATRACE_BEGIN("code"); for (auto codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, multiuser_get_shared_gid(0, appId)); } ATRACE_END(); ATRACE_BEGIN("quota"); collectQuotaStats(device, userId, appId, &stats, &extStats); ATRACE_END(); } else { ATRACE_BEGIN("code"); for (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)); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto device = findQuotaDeviceForUuid(uuid); if (device.empty()) { flags &= ~FLAG_USE_QUOTA; } if (flags & FLAG_USE_QUOTA) { struct dqblk dq; ATRACE_BEGIN("obb"); if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB, reinterpret_cast(&dq)) != 0) { if (errno != ESRCH) { PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << AID_MEDIA_OBB; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace; #endif extStats.codeSize += dq.dqb_curspace; } 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 (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; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; #endif extStats.dataSize += dq.dqb_curspace; } 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(device, 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 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; auto device = findQuotaDeviceForUuid(uuid); if (device.empty()) { flags &= ~FLAG_USE_QUOTA; } if (flags & FLAG_USE_QUOTA) { struct dqblk dq; ATRACE_BEGIN("quota"); uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); 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; } } else { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; #endif totalSize = dq.dqb_curspace; } gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO); if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), audioGid, reinterpret_cast(&dq)) == 0) { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace; #endif audioSize = dq.dqb_curspace; } gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO); if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), videoGid, reinterpret_cast(&dq)) == 0) { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace; #endif videoSize = dq.dqb_curspace; } gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE); if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), imageGid, reinterpret_cast(&dq)) == 0) { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << imageGid << " " << dq.dqb_curspace; #endif imageSize = dq.dqb_curspace; } if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB, reinterpret_cast(&dq)) == 0) { #if MEASURE_DEBUG LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace; #endif obbSize = dq.dqb_curspace; } ATRACE_END(); ATRACE_BEGIN("apps"); struct stats extStats; memset(&extStats, 0, sizeof(extStats)); for (auto appId : appIds) { if (appId >= AID_APP_START) { collectQuotaStats(device, 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, NULL))) { return error("Failed to fts_open " + path); } while ((p = fts_read(fts)) != NULL) { 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; } } } // Fall through to 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; } 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 = create_data_media_obb_path(uuid_, ""); 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& codePaths, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); const char* pkgname = packageName.c_str(); const char* code_paths = codePaths.c_str(); *_aidl_return = dump_profiles(uid, pkgname, code_paths); 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, 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); return ok(); } // TODO: Consider returning error codes. binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard lock(mLock); *_aidl_return = analyze_primary_profiles(uid, packageName); return ok(); } 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) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); if (packageName && *packageName != "*") { CHECK_ARGUMENT_PACKAGE_NAME(*packageName); } std::lock_guard lock(mLock); const char* apk_path = apkPath.c_str(); const char* pkgname = packageName ? packageName->c_str() : "*"; const char* instruction_set = instructionSet.c_str(); const char* oat_dir = outputPath ? outputPath->c_str() : nullptr; const char* compiler_filter = compilerFilter.c_str(); const char* volume_uuid = uuid ? uuid->c_str() : nullptr; const char* class_loader_context = classLoaderContext ? classLoaderContext->c_str() : nullptr; const char* se_info = seInfo ? seInfo->c_str() : nullptr; 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); return res ? error(res, "Failed to dexopt") : ok(); } 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.path, 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(); } void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid, struct stat* statbuf) { while (path[basepos] != 0) { if (path[basepos] == '/') { path[basepos] = 0; if (lstat(path, statbuf) < 0) { ALOGV("Making directory: %s\n", path); if (mkdir(path, mode) == 0) { chown(path, uid, gid); } else { ALOGW("Unable to make directory %s: %s\n", path, strerror(errno)); } } path[basepos] = '/'; basepos++; } basepos++; } } 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); 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); } if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) { return error("Failed to chown " + _pkgdir); } 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, NULL) < 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; } out: 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*)NULL); 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*)NULL); 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 == NULL || idmap_path == NULL) { 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); 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) { 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); 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); std::lock_guard lock(mLock); if (validate_apk_path(packageDir.c_str())) { return error("Invalid path " + packageDir); } if (delete_dir_contents_and_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); 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); 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); 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(); } 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); 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::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); std::lock_guard lock(mMountsLock); mStorageMounts.clear(); mQuotaReverseMounts.clear(); 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 #if !BYPASS_QUOTA 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; // ext4 only enables DQUOT_USAGE_ENABLED by default, so we // need to kick it again to enable DQUOT_LIMITS_ENABLED. if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 && errno != EBUSY) { PLOG(ERROR) << "Failed to enable USRQUOTA on " << source; } if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 && errno != EBUSY) { PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source; } } } #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); } std::string InstalldNativeService::findQuotaDeviceForUuid( const std::unique_ptr& uuid) { std::lock_guard lock(mMountsLock); auto path = create_data_path(uuid ? uuid->c_str() : nullptr); return mQuotaReverseMounts[path]; } binder::Status InstalldNativeService::isQuotaSupported( const std::unique_ptr& volumeUuid, bool* _aidl_return) { *_aidl_return = !findQuotaDeviceForUuid(volumeUuid).empty(); return ok(); } } // namespace installd } // namespace android cmds/installd/InstalldNativeService.h0100644 0000000 0000000 00000015731 13300556574 016777 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 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); binder::Status rmdex(const std::string& codePath, const std::string& instructionSet); binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return); binder::Status dumpProfiles(int32_t uid, const std::string& packageName, const std::string& codePaths, bool* _aidl_return); binder::Status copySystemProfile(const std::string& systemProfile, int32_t uid, const std::string& packageName, bool* _aidl_return); binder::Status clearAppProfiles(const std::string& packageName); binder::Status destroyAppProfiles(const std::string& packageName); 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 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 invalidateMounts(); binder::Status isQuotaSupported(const std::unique_ptr& volumeUuid, bool* _aidl_return); 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 of all quota mounts from target to source */ std::unordered_map mQuotaReverseMounts; /* Map from UID to cache quota size */ std::unordered_map mCacheQuotas; std::string findDataMediaPath(const std::unique_ptr& uuid, userid_t userid); std::string findQuotaDeviceForUuid(const std::unique_ptr& uuid); }; } // namespace installd } // namespace android #endif // COMMANDS_H_ cmds/installd/MatchExtensionGen.h0100644 0000000 0000000 00000044571 13300556574 016124 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; } 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; } } } } } 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; } } 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; } case 'f': case 'F': switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } } } case 'm': case 'M': switch (ext[2]) { case 'r': case 'R': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } case 'r': case 'R': switch (ext[2]) { case 't': case 'T': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 's': case 'S': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } case 'v': case 'V': switch (ext[2]) { case 'i': case 'I': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } case 'w': case 'W': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } } 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; } } } 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; } } } 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; } } case 'l': case 'L': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'v': case 'V': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } } 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; } } case 'i': case 'I': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } } 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; } } case 's': case 'S': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } } 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; } } 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; } } case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } } 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; } case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } } 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; } } case '4': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } case 'k': case 'K': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } 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; } } } } case 'p': case 'P': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } case '3': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } case '4': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } 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; } } } 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; } } } case 'x': case 'X': switch (ext[2]) { case 'u': case 'U': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } } 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; } } case 'r': case 'R': switch (ext[2]) { case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } } 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; } case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } case 'r': case 'R': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } } 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; } } case 'c': case 'C': switch (ext[2]) { case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'e': case 'E': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'g': case 'G': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'l': case 'L': switch (ext[2]) { case 's': case 'S': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'p': case 'P': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 's': case 'S': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } } case 'q': case 'Q': switch (ext[1]) { case 't': case 'T': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } } 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; } case 's': case 'S': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'g': case 'G': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'm': case 'M': switch (ext[2]) { case '\0': return AID_MEDIA_AUDIO; } case 'w': case 'W': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } } 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; } } case 'n': case 'N': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } case 'r': case 'R': switch (ext[2]) { case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } 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; } } } } 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; } } } case 's': case 'S': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } } 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; } } } 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; } case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } } 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; } } } 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; } case 'p': case 'P': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } } } 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; } case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } case 'r': case 'R': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } case 'v': case 'V': switch (ext[2]) { case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } } } 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; } } case 'p': case 'P': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } case 'w': case 'W': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } } } } return 0; } cmds/installd/binder/0040755 0000000 0000000 00000000000 13300556574 013623 5ustar000000000 0000000 cmds/installd/binder/android/0040755 0000000 0000000 00000000000 13300556574 015243 5ustar000000000 0000000 cmds/installd/binder/android/os/0040755 0000000 0000000 00000000000 13300556574 015664 5ustar000000000 0000000 cmds/installd/binder/android/os/IInstalld.aidl0100644 0000000 0000000 00000011061 13300556574 020376 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); void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet); boolean mergeProfiles(int uid, @utf8InCpp String packageName); boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths); boolean copySystemProfile(@utf8InCpp String systemProfile, int uid, @utf8InCpp String packageName); void clearAppProfiles(@utf8InCpp String packageName); void destroyAppProfiles(@utf8InCpp String packageName); 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); boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, int storage_flag); void invalidateMounts(); boolean isQuotaSupported(@nullable @utf8InCpp String uuid); } cmds/installd/dexopt.cpp0100644 0000000 0000000 00000237164 13300556574 014401 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 "installed" #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 "dexopt.h" #include "installd_deps.h" #include "otapreopt_utils.h" #include "utils.h" using android::base::StringPrintf; using android::base::EndsWith; using android::base::unique_fd; namespace android { namespace installd { // 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 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 package name for primary apks or the dex path for secondary dex files. static bool clear_reference_profile(const std::string& location, bool is_secondary_dex) { return clear_profile(create_reference_profile_path(location, is_secondary_dex)); } // Clear the reference profile for the given location. // The location is the package name for primary apks or the dex path for secondary dex files. static bool clear_current_profile(const std::string& pkgname, userid_t user, bool is_secondary_dex) { return clear_profile(create_current_profile_path(user, pkgname, is_secondary_dex)); } // Clear the reference profile for the primary apk of the given package. bool clear_primary_reference_profile(const std::string& pkgname) { return clear_reference_profile(pkgname, /*is_secondary_dex*/false); } // Clear all current profile for the primary apk of the given package. bool clear_primary_current_profiles(const std::string& pkgname) { 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(pkgname, 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& pkgname, userid_t user) { return clear_current_profile(pkgname, user, /*is_secondary_dex*/false); } static int split_count(const char *str) { char *ctx; int count = 0; char buf[kPropertyValueMax]; strncpy(buf, str, sizeof(buf)); char *pBuf = buf; while(strtok_r(pBuf, " ", &ctx) != NULL) { count++; pBuf = NULL; } return count; } static int split(char *buf, const char **argv) { char *ctx; int count = 0; char *tok; char *pBuf = buf; while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) { argv[count++] = tok; pBuf = NULL; } return count; } static const char* get_location_from_path(const char* path) { static constexpr char kLocationSeparator = '/'; const char *location = strrchr(path, kLocationSeparator); if (location == NULL) { return path; } else { // Skip the separator character. return location + 1; } } static void run_dex2oat(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, int profile_fd, const char* class_loader_context) { static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { ALOGE("Instruction set %s longer than max length of %d", instruction_set, MAX_INSTRUCTION_SET_LEN); return; } // Get the relative path to the input file. const char* relative_input_file_name = get_location_from_path(input_file_name); char dex2oat_Xms_flag[kPropertyValueMax]; bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0; char dex2oat_Xmx_flag[kPropertyValueMax]; bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0; char dex2oat_threads_buf[kPropertyValueMax]; bool have_dex2oat_threads_flag = get_property(post_bootcomplete ? "dalvik.vm.dex2oat-threads" : "dalvik.vm.boot-dex2oat-threads", dex2oat_threads_buf, NULL) > 0; char dex2oat_threads_arg[kPropertyValueMax + 2]; if (have_dex2oat_threads_flag) { sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf); } char dex2oat_isa_features_key[kPropertyKeyMax]; sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set); char dex2oat_isa_features[kPropertyValueMax]; bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key, dex2oat_isa_features, NULL) > 0; char dex2oat_isa_variant_key[kPropertyKeyMax]; sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set); char dex2oat_isa_variant[kPropertyValueMax]; bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key, dex2oat_isa_variant, NULL) > 0; const char *dex2oat_norelocation = "-Xnorelocate"; bool have_dex2oat_relocation_skip_flag = false; char dex2oat_flags[kPropertyValueMax]; int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags", dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags); ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags); // If we are booting without the real /data, don't spend time compiling. char vold_decrypt[kPropertyValueMax]; bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0; bool skip_compilation = (have_vold_decrypt && (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || (strcmp(vold_decrypt, "1") == 0))); bool generate_debug_info = property_get_bool("debug.generate-debug-info", false); char app_image_format[kPropertyValueMax]; char image_format_arg[strlen("--image-format=") + kPropertyValueMax]; bool have_app_image_format = image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0; if (have_app_image_format) { sprintf(image_format_arg, "--image-format=%s", app_image_format); } char dex2oat_large_app_threshold[kPropertyValueMax]; bool have_dex2oat_large_app_threshold = get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0; char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax]; if (have_dex2oat_large_app_threshold) { sprintf(dex2oat_large_app_threshold_arg, "--very-large-app-threshold=%s", dex2oat_large_app_threshold); } static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; static const char* RUNTIME_ARG = "--runtime-arg"; static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig // clang FORTIFY doesn't let us use strlen in constant array bounds, so we // use arraysize instead. char zip_fd_arg[arraysize("--zip-fd=") + MAX_INT_LEN]; char zip_location_arg[arraysize("--zip-location=") + PKG_PATH_MAX]; char input_vdex_fd_arg[arraysize("--input-vdex-fd=") + MAX_INT_LEN]; char output_vdex_fd_arg[arraysize("--output-vdex-fd=") + MAX_INT_LEN]; char oat_fd_arg[arraysize("--oat-fd=") + MAX_INT_LEN]; char oat_location_arg[arraysize("--oat-location=") + PKG_PATH_MAX]; char instruction_set_arg[arraysize("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; char instruction_set_variant_arg[arraysize("--instruction-set-variant=") + kPropertyValueMax]; char instruction_set_features_arg[arraysize("--instruction-set-features=") + kPropertyValueMax]; char dex2oat_Xms_arg[arraysize("-Xms") + kPropertyValueMax]; char dex2oat_Xmx_arg[arraysize("-Xmx") + kPropertyValueMax]; char dex2oat_compiler_filter_arg[arraysize("--compiler-filter=") + kPropertyValueMax]; bool have_dex2oat_swap_fd = false; char dex2oat_swap_fd[arraysize("--swap-fd=") + MAX_INT_LEN]; bool have_dex2oat_image_fd = false; char dex2oat_image_fd[arraysize("--app-image-fd=") + MAX_INT_LEN]; size_t class_loader_context_size = arraysize("--class-loader-context=") + PKG_PATH_MAX; char class_loader_context_arg[class_loader_context_size]; if (class_loader_context != nullptr) { snprintf(class_loader_context_arg, class_loader_context_size, "--class-loader-context=%s", class_loader_context); } sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); sprintf(zip_location_arg, "--zip-location=%s", relative_input_file_name); sprintf(input_vdex_fd_arg, "--input-vdex-fd=%d", input_vdex_fd); sprintf(output_vdex_fd_arg, "--output-vdex-fd=%d", output_vdex_fd); sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); sprintf(oat_location_arg, "--oat-location=%s", output_file_name); sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant); sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features); if (swap_fd >= 0) { have_dex2oat_swap_fd = true; sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd); } if (image_fd >= 0) { have_dex2oat_image_fd = true; sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd); } if (have_dex2oat_Xms_flag) { sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag); } if (have_dex2oat_Xmx_flag) { sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag); } // Compute compiler filter. bool have_dex2oat_compiler_filter_flag = false; if (skip_compilation) { strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract"); have_dex2oat_compiler_filter_flag = true; have_dex2oat_relocation_skip_flag = true; } else if (compiler_filter != nullptr) { if (strlen(compiler_filter) + strlen("--compiler-filter=") < arraysize(dex2oat_compiler_filter_arg)) { sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter); have_dex2oat_compiler_filter_flag = true; } else { ALOGW("Compiler filter name '%s' is too large (max characters is %zu)", compiler_filter, kPropertyValueMax); } } if (!have_dex2oat_compiler_filter_flag) { char dex2oat_compiler_filter_flag[kPropertyValueMax]; have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter", dex2oat_compiler_filter_flag, NULL) > 0; if (have_dex2oat_compiler_filter_flag) { sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag); } } // Check whether all apps should be compiled debuggable. if (!debuggable) { char prop_buf[kPropertyValueMax]; debuggable = (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) && (prop_buf[0] == '1'); } char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN]; if (profile_fd != -1) { sprintf(profile_arg, "--profile-file-fd=%d", profile_fd); } // Get the directory of the apk to pass as a base classpath directory. char base_dir[arraysize("--classpath-dir=") + PKG_PATH_MAX]; 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); sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str()); } ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name); const char* argv[9 // program name, mandatory arguments and the final NULL + (have_dex2oat_isa_variant ? 1 : 0) + (have_dex2oat_isa_features ? 1 : 0) + (have_dex2oat_Xms_flag ? 2 : 0) + (have_dex2oat_Xmx_flag ? 2 : 0) + (have_dex2oat_compiler_filter_flag ? 1 : 0) + (have_dex2oat_threads_flag ? 1 : 0) + (have_dex2oat_swap_fd ? 1 : 0) + (have_dex2oat_image_fd ? 1 : 0) + (have_dex2oat_relocation_skip_flag ? 2 : 0) + (generate_debug_info ? 1 : 0) + (debuggable ? 1 : 0) + (have_app_image_format ? 1 : 0) + dex2oat_flags_count + (profile_fd == -1 ? 0 : 1) + (class_loader_context != nullptr ? 1 : 0) + (has_base_dir ? 1 : 0) + (have_dex2oat_large_app_threshold ? 1 : 0)]; int i = 0; argv[i++] = DEX2OAT_BIN; argv[i++] = zip_fd_arg; argv[i++] = zip_location_arg; argv[i++] = input_vdex_fd_arg; argv[i++] = output_vdex_fd_arg; argv[i++] = oat_fd_arg; argv[i++] = oat_location_arg; argv[i++] = instruction_set_arg; if (have_dex2oat_isa_variant) { argv[i++] = instruction_set_variant_arg; } if (have_dex2oat_isa_features) { argv[i++] = instruction_set_features_arg; } if (have_dex2oat_Xms_flag) { argv[i++] = RUNTIME_ARG; argv[i++] = dex2oat_Xms_arg; } if (have_dex2oat_Xmx_flag) { argv[i++] = RUNTIME_ARG; argv[i++] = dex2oat_Xmx_arg; } if (have_dex2oat_compiler_filter_flag) { argv[i++] = dex2oat_compiler_filter_arg; } if (have_dex2oat_threads_flag) { argv[i++] = dex2oat_threads_arg; } if (have_dex2oat_swap_fd) { argv[i++] = dex2oat_swap_fd; } if (have_dex2oat_image_fd) { argv[i++] = dex2oat_image_fd; } if (generate_debug_info) { argv[i++] = "--generate-debug-info"; } if (debuggable) { argv[i++] = "--debuggable"; } if (have_app_image_format) { argv[i++] = image_format_arg; } if (have_dex2oat_large_app_threshold) { argv[i++] = dex2oat_large_app_threshold_arg; } if (dex2oat_flags_count) { i += split(dex2oat_flags, argv + i); } if (have_dex2oat_relocation_skip_flag) { argv[i++] = RUNTIME_ARG; argv[i++] = dex2oat_norelocation; } if (profile_fd != -1) { argv[i++] = profile_arg; } if (has_base_dir) { argv[i++] = base_dir; } if (class_loader_context != nullptr) { argv[i++] = class_loader_context_arg; } // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; execv(DEX2OAT_BIN, (char * const *)argv); ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); } /* * 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". char dex2oat_prop_buf[kPropertyValueMax]; if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) { if (strcmp(dex2oat_prop_buf, "true") == 0) { return true; } else { return false; } } // 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; } bool is_low_mem = property_get_bool("ro.config.low_ram", false); if (is_low_mem) { 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) { ALOGE("set_sched_policy failed: %s\n", strerror(errno)); exit(70); } if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { ALOGE("setpriority failed: %s\n", strerror(errno)); exit(71); } } } static bool create_profile(int uid, const std::string& profile) { unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600))); if (fd.get() < 0) { if (errno == EEXIST) { return true; } else { PLOG(ERROR) << "Failed to create profile " << profile; return false; } } // 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 chwon profile " << profile; return false; } return true; } static unique_fd open_profile(int uid, const std::string& profile, bool read_write) { // Check if we need to open the profile for a read-write operation. If so, we // might need to create the profile since the file might not be there. Reference // profiles are created on the fly so they might not exist beforehand. if (read_write) { if (!create_profile(uid, profile)) { return invalid_unique_fd(); } } int flags = read_write ? O_RDWR : O_RDONLY; // 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; unique_fd fd(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& location, bool is_secondary_dex) { std::string profile = create_current_profile_path(user, location, is_secondary_dex); return open_profile(uid, profile, /*read_write*/false); } static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write, bool is_secondary_dex) { std::string profile = create_reference_profile_path(location, is_secondary_dex); return open_profile(uid, profile, read_write); } static void open_profile_files(uid_t uid, 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, 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, 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 void drop_capabilities(uid_t uid) { if (setgid(uid) != 0) { ALOGE("setgid(%d) failed in installd during dexopt\n", uid); exit(64); } if (setuid(uid) != 0) { ALOGE("setuid(%d) failed in installd during dexopt\n", uid); exit(65); } // 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) { ALOGE("capset failed: %s\n", strerror(errno)); exit(66); } } 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; static void run_profman_merge(const std::vector& profiles_fd, const unique_fd& reference_profile_fd) { static const size_t MAX_INT_LEN = 32; static const char* PROFMAN_BIN = "/system/bin/profman"; std::vector profile_args(profiles_fd.size()); char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN]; for (size_t k = 0; k < profiles_fd.size(); k++) { sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get()); profile_args[k].assign(profile_buf); } char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN]; sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get()); // program name, reference profile fd, the final NULL and the profile fds const char* argv[3 + profiles_fd.size()]; int i = 0; argv[i++] = PROFMAN_BIN; argv[i++] = reference_profile_arg; for (size_t k = 0; k < profile_args.size(); k++) { argv[i++] = profile_args[k].c_str(); } // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; execv(PROFMAN_BIN, (char * const *)argv); ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno)); exit(68); /* only get here on exec failure */ } // 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& location, bool is_secondary_dex) { std::vector profiles_fd; unique_fd reference_profile_fd; open_profile_files(uid, 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; } pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); run_profman_merge(profiles_fd, reference_profile_fd); exit(68); /* only get here on exec failure */ } /* 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(location, multiuser_get_user_id(uid), is_secondary_dex); } else { clear_primary_current_profiles(location); } } if (should_clear_reference_profile) { clear_reference_profile(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& pkgname) { return analyze_profiles(uid, pkgname, /*is_secondary_dex*/false); } static void run_profman_dump(const std::vector& profile_fds, const unique_fd& reference_profile_fd, const std::vector& dex_locations, const std::vector& apk_fds, const unique_fd& output_fd) { std::vector profman_args; static const char* PROFMAN_BIN = "/system/bin/profman"; profman_args.push_back(PROFMAN_BIN); profman_args.push_back("--dump-only"); profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd.get())); if (reference_profile_fd != -1) { profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d", reference_profile_fd.get())); } for (size_t i = 0; i < profile_fds.size(); i++) { profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fds[i].get())); } for (const std::string& dex_location : dex_locations) { profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str())); } for (size_t i = 0; i < apk_fds.size(); i++) { profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fds[i].get())); } const char **argv = new const char*[profman_args.size() + 1]; size_t i = 0; for (const std::string& profman_arg : profman_args) { argv[i++] = profman_arg.c_str(); } argv[i] = NULL; execv(PROFMAN_BIN, (char * const *)argv); ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno)); exit(68); /* only get here on exec failure */ } bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) { std::vector profile_fds; unique_fd reference_profile_fd; std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str()); open_profile_files(uid, pkgname, /*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) { ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str()); return false; } std::vector code_full_paths = base::Split(code_paths, ";"); std::vector dex_locations; std::vector apk_fds; for (const std::string& code_full_path : code_full_paths) { const char* full_path = code_full_path.c_str(); unique_fd apk_fd(open(full_path, O_RDONLY | O_NOFOLLOW)); if (apk_fd == -1) { ALOGE("installd cannot open '%s'\n", full_path); return false; } dex_locations.push_back(get_location_from_path(full_path)); apk_fds.push_back(std::move(apk_fd)); } pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); run_profman_dump(profile_fds, reference_profile_fd, dex_locations, apk_fds, output_fd); exit(68); /* only get here on exec failure */ } /* 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& data_profile_location) { unique_fd in_fd(open(system_profile.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); unique_fd out_fd(open_reference_profile(packageUid, data_profile_location, /*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 " << data_profile_location; 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 " << data_profile_location; } // 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 " << data_profile_location; return false; } bool truncated = ftruncate(out_fd.get(), 0) == 0; if (!truncated) { PLOG(WARNING) << "Could not truncate " << data_profile_location; } // 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 " << data_profile_location; } // 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.c_str())); 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 bool add_extension_to_file_name(char* file_name, const char* extension) { if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) { return false; } strcat(file_name, extension); return true; } 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 profile_guided, 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(); } // Use app images only if it is enabled (by a set image format) and we are compiling // profile-guided (so the app image doesn't conservatively contain all classes). if (!profile_guided) { // In case there is a stale image, remove it now. Ignore any error. unlink(image_path.c_str()); return Dex2oatFileWrapper(); } char app_image_format[kPropertyValueMax]; bool have_app_image_format = get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0; if (!have_app_image_format) { 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(); } // Make sure there really is enough space. char swap_file_name[PKG_PATH_MAX]; strcpy(swap_file_name, out_oat_path); if (!add_extension_to_file_name(swap_file_name, ".swap")) { return invalid_unique_fd(); } unique_fd swap_fd(open_output_file( swap_file_name, /*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); } else { // Immediately unlink. We don't really want to hit flash. if (unlink(swap_file_name) < 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, bool profile_guided, bool is_public, int uid, bool is_secondary_dex) { // Public apps should not be compiled with profile information ever. Same goes for the special // package '*' used for the system server. if (!profile_guided || is_public || (pkgname[0] == '*')) { return Dex2oatFileWrapper(); } // Open reference profile in read only mode as dex2oat does not get write permissions. const std::string location = is_secondary_dex ? dex_path : pkgname; unique_fd ufd = open_reference_profile(uid, location, /*read_write*/false, is_secondary_dex); const auto& cleanup = [location, is_secondary_dex]() { clear_reference_profile(location.c_str(), 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(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; } // 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. static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& instruction_set, const std::string& compiler_filter, bool profile_was_updated, bool downgrade) { static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer"; static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; if (instruction_set.size() >= MAX_INSTRUCTION_SET_LEN) { LOG(ERROR) << "Instruction set " << instruction_set << " longer than max length of " << MAX_INSTRUCTION_SET_LEN; return; } std::string dex_file_arg = "--dex-file=" + dex_file; 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"; // program name, dex file, isa, filter, the final NULL const int argc = 5 + (profile_was_updated ? 1 : 0) + (downgrade ? 1 : 0); const char* argv[argc]; int i = 0; argv[i++] = DEXOPTANALYZER_BIN; argv[i++] = dex_file_arg.c_str(); argv[i++] = isa_arg.c_str(); argv[i++] = compiler_filter_arg.c_str(); if (profile_was_updated) { argv[i++] = assume_profile_changed; } if (downgrade) { argv[i++] = downgrade_flag; } argv[i] = NULL; execv(DEXOPTANALYZER_BIN, (char * const *)argv); ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno)); } // 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, std::string* oat_dir_out) { 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()); oat_dir_out->assign(oat_dir); // Create oat/isa output directory. if (prepare_app_cache_dir(*oat_dir_out, 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; } static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200; // Verifies the result of dexoptanalyzer executed for the apk_path. // If the result is valid returns true and sets dexopt_needed_out to a valid value. // Returns false for errors or unexpected result values. static bool process_dexoptanalyzer_result(const std::string& dex_path, int result, int* dexopt_needed_out) { // The result values are defined in dexoptanalyzer. switch (result) { case 0: // no_dexopt_needed *dexopt_needed_out = NO_DEXOPT_NEEDED; return true; case 1: // dex2oat_from_scratch *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true; case 5: // dex2oat_for_bootimage_odex *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true; case 6: // dex2oat_for_filter_odex *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true; case 7: // dex2oat_for_relocation_odex *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true; case 2: // dex2oat_for_bootimage_oat case 3: // dex2oat_for_filter_oat case 4: // dex2oat_for_relocation_oat LOG(ERROR) << "Dexoptnalyzer return the status of an oat file." << " Expected odex file status for secondary dex " << dex_path << " : dexoptanalyzer result=" << result; return false; default: LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path << " exec_dexoptanalyzer result=" << result; return false; } } // 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 // - dex_path_out: the real path of the dex file static bool process_secondary_dex_dexopt(const char* original_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, std::string* dex_path_out, bool downgrade) { int storage_flag; if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) { storage_flag = FLAG_STORAGE_CE; if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) { LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set"; return false; } } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) { storage_flag = FLAG_STORAGE_DE; } else { LOG(ERROR) << "Secondary dex storage flag must be set"; return false; } { // As opposed to the primary apk, secondary dex files might contain symlinks. // Resolve the path before passing it to the validate method to // make sure the verification is done on the real location. UniqueCPtr dex_real_path_cstr(realpath(original_dex_path, nullptr)); if (dex_real_path_cstr == nullptr) { PLOG(ERROR) << "Could not get the real path of the secondary dex file " << original_dex_path; return false; } else { dex_path_out->assign(dex_real_path_cstr.get()); } } const std::string& dex_path = *dex_path_out; if (!validate_dex_path_size(dex_path)) { return false; } if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; return false; } // Check if the path exist. If not, there's nothing to do. struct stat dex_path_stat; if (stat(dex_path.c_str(), &dex_path_stat) != 0) { if (errno == ENOENT) { // Secondary dex files might be deleted any time by the app. // Nothing to do if that's the case ALOGV("Secondary dex does not exist %s", dex_path.c_str()); return NO_DEXOPT_NEEDED; } else { PLOG(ERROR) << "Could not access secondary dex " << dex_path; } } // 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. *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && ((dex_path_stat.st_mode & S_IROTH) != 0); // Prepare the oat directories. if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) { return false; } // Analyze profiles. bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true); pid_t pid = fork(); if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); // Run dexoptanalyzer to get dexopt_needed code. exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter, profile_was_updated, downgrade); exit(DEXOPTANALYZER_BIN_EXEC_ERROR); } /* parent */ int result = wait_child(pid); if (!WIFEXITED(result)) { LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result; return false; } result = WEXITSTATUS(result); bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out); // Run dexopt only if needed or forced. // Note that dexoptanalyzer is executed even if force compilation is enabled. // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result) // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not // for oat files from dalvik-cache. if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) { *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; } return success; } 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) { CHECK(pkgname != nullptr); CHECK(pkgname[0] != 0); if ((dexopt_flags & ~DEXOPT_MASK) != 0) { LOG_FATAL("dexopt flags contains unknown fields\n"); } if (!validate_dex_path_size(dex_path)) { return -1; } if (class_loader_context != nullptr && strlen(class_loader_context) > PKG_PATH_MAX) { LOG(ERROR) << "Class loader context exceeds the allowed size: " << class_loader_context; 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; // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; std::string dex_real_path; if (is_secondary_dex) { if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, &dex_real_path, downgrade)) { oat_dir = oat_dir_str.c_str(); dex_path = dex_real_path.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. } } else { return -1; // We had an error, logged in the process method. } } else { // Currently these flags are only use 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) { ALOGE("installd cannot open '%s' for input during dexopt\n", dex_path); 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) { return -1; } // Open vdex files. Dex2oatFileWrapper in_vdex_fd; Dex2oatFileWrapper out_vdex_fd; if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) { 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)) { LOG(ERROR) << "Failed to restorecon " << oat_dir; 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, profile_guided, is_public, uid, is_secondary_dex); // Open the reference profile if needed. Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile( pkgname, dex_path, profile_guided, is_public, uid, is_secondary_dex); ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path); 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) { ALOGE("flock(%s) failed: %s\n", out_oat_path, strerror(errno)); _exit(67); } run_dex2oat(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, reference_profile_fd.get(), class_loader_context); _exit(68); /* only get here on exec failure */ } else { int res = wait_child(pid); if (res == 0) { ALOGV("DexInv: --- END '%s' (success) ---\n", dex_path); } else { ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", dex_path, res); 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; } // 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, /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) { size_t dirIndex = dex_path.rfind('/'); if (dirIndex == std::string::npos) { LOG(ERROR) << "Unexpected dir structure for dex file " << 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 // use 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)) { LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path; return false; } return true; } // 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) { // Set out to false to start with, just in case we have validation errors. *out_secondary_dex_exists = false; if (!validate_dex_path_size(dex_path)) { return false; } if (isas.size() == 0) { LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector"; return false; } const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); // Note that we cannot validate the package path here because the file might not exist // and we cannot call realpath to resolve system symlinks. Since /data/user/0 symlinks to // /data/data/ a lot of validations will fail if we attempt to check the package path. // It is still ok to be more relaxed because any file removal is done after forking and // dropping capabilities. if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr, uid, storage_flag, /*validate_package_path*/ false)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; return false; } if (access(dex_path.c_str(), F_OK) == 0) { // The path exists, nothing to do. The odex files (if any) will be left untouched. *out_secondary_dex_exists = true; return true; } else if (errno != ENOENT) { PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path; 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) { // The secondary dex does not exist anymore. 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; /* child -- drop privileges before continuing */ drop_capabilities(uid); for (size_t i = 0; i < isas.size(); i++) { if (!create_secondary_dex_oat_layout(dex_path, isas[i], oat_dir, oat_isa_dir, oat_path)) { LOG(ERROR) << "Could not create secondary odex layout: " << dex_path; result = false; continue; } // 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), dex_path, /*is_secondary*/true); std::string reference_profile = create_reference_profile_path( 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; } result ? _exit(0) : _exit(1); } int return_code = wait_child(pid); return return_code == 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. std::string slot_suffix; { char buf[kPropertyValueMax]; if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) { return false; } slot_suffix = buf; 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; } } // namespace installd } // namespace android cmds/installd/dexopt.h0100644 0000000 0000000 00000006065 13300556574 014040 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 #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; static constexpr int DEX2OAT_FOR_RELOCATION = 4; // Clear the reference profile for the primary apk of the given package. bool clear_primary_reference_profile(const std::string& pkgname); // Clear the current profile for the primary apk of the given package and user. bool clear_primary_current_profile(const std::string& pkgname, userid_t user); // Clear all current profile for the primary apk of the given package. bool clear_primary_current_profiles(const std::string& pkgname); bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); // Decide if profile guided compilation is needed or not based on existing profiles. // The analysis is done for the primary apks (base + splits) 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& pkgname); bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths); bool copy_system_profile(const std::string& system_profile, uid_t packageUid, const std::string& data_profile_location); 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); 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); } // namespace installd } // namespace android #endif // DEXOPT_H_ cmds/installd/file_parsing.h0100644 0000000 0000000 00000002771 13300556574 015177 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 00000011740 13300556574 014507 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 // TODO: Move everything to base::logging. #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 /* Directory records that are used in execution of commands. */ dir_rec_t android_app_dir; dir_rec_t android_app_ephemeral_dir; dir_rec_t android_app_lib_dir; dir_rec_t android_app_private_dir; dir_rec_t android_asec_dir; dir_rec_t android_data_dir; dir_rec_t android_media_dir; dir_rec_t android_mnt_expand_dir; dir_rec_t android_profiles_dir; dir_rec_array_t android_system_dirs; /** * Initialize all the global variables that are used elsewhere. Returns 0 upon * success and -1 on error. */ void free_globals() { size_t i; for (i = 0; i < android_system_dirs.count; i++) { if (android_system_dirs.dirs[i].path != NULL) { free(android_system_dirs.dirs[i].path); } } free(android_system_dirs.dirs); } bool init_globals_from_data_and_root(const char* data, const char* root) { // Get the android data directory. if (get_path_from_string(&android_data_dir, data) < 0) { return false; } // Get the android app directory. if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) { return false; } // Get the android protected app directory. if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) { return false; } // Get the android ephemeral app directory. if (copy_and_append(&android_app_ephemeral_dir, &android_data_dir, EPHEMERAL_APP_SUBDIR) < 0) { return false; } // Get the android app native library directory. if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) { return false; } // Get the sd-card ASEC mount point. if (get_path_from_env(&android_asec_dir, ASEC_MOUNTPOINT_ENV_NAME) < 0) { return false; } // Get the android media directory. if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) { return false; } // Get the android external app directory. if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand/") < 0) { return false; } // Get the android profiles directory. if (copy_and_append(&android_profiles_dir, &android_data_dir, PROFILES_SUBDIR) < 0) { return false; } // Take note of the system and vendor directories. android_system_dirs.count = 4; android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t)); if (android_system_dirs.dirs == NULL) { ALOGE("Couldn't allocate array for dirs; aborting\n"); return false; } dir_rec_t android_root_dir; if (get_path_from_string(&android_root_dir, root) < 0) { return false; } android_system_dirs.dirs[0].path = build_string2(android_root_dir.path, APP_SUBDIR); android_system_dirs.dirs[0].len = strlen(android_system_dirs.dirs[0].path); android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR); android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path); android_system_dirs.dirs[2].path = strdup("/vendor/app/"); android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path); android_system_dirs.dirs[3].path = strdup("/oem/app/"); android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path); return true; } } // namespace installd } // namespace android cmds/installd/globals.h0100644 0000000 0000000 00000003067 13300556574 014157 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 namespace android { namespace installd { /* constants */ // Name of the environment variable that contains the asec mountpoint. static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT"; /* data structures */ struct dir_rec_t { char* path; size_t len; }; struct dir_rec_array_t { size_t count; dir_rec_t* dirs; }; extern dir_rec_t android_app_dir; extern dir_rec_t android_app_ephemeral_dir; extern dir_rec_t android_app_lib_dir; extern dir_rec_t android_app_private_dir; extern dir_rec_t android_asec_dir; extern dir_rec_t android_data_dir; extern dir_rec_t android_media_dir; extern dir_rec_t android_mnt_expand_dir; extern dir_rec_t android_profiles_dir; extern dir_rec_array_t android_system_dirs; void free_globals(); 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 00000024410 13300556574 014674 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 "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); } // 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 == NULL) { SLOGE("apk_path '%s' has no '/'s in it\n", apk_path); return false; } file_name_end = strrchr(apk_path, '.'); if (file_name_end < file_name_start) { SLOGE("apk_path '%s' has no extension\n", apk_path); return false; } // Calculate file_name int file_name_len = file_name_end - file_name_start - 1; char file_name[file_name_len + 1]; memcpy(file_name, file_name_start + 1, file_name_len); file_name[file_name_len] = '\0'; // /oat//.odex snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex", oat_dir, instruction_set, file_name); 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) { if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set) + strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) { SLOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path); return false; } strcpy(path, apk_path); char *end = strrchr(path, '/'); if (end == NULL) { SLOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); return false; } const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/'); strcpy(end + 1, "oat/"); // path = /system/framework/oat/\0 strcat(path, instruction_set); // path = /system/framework/oat/\0 strcat(path, apk_end); // path = /system/framework/oat//whatever.jar\0 end = strrchr(path, '.'); if (end == NULL) { SLOGE("apk_path '%s' has no extension.\n", apk_path); return false; } strcpy(end + 1, "odex"); return true; } bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { /* demand that we are an absolute path */ if ((src == nullptr) || (src[0] != '/') || strstr(src,"..")) { return false; } size_t srclen = strlen(src); if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX? return false; } size_t dstlen = android_data_dir.len + strlen(DALVIK_CACHE) + 1 + strlen(instruction_set) + srclen + strlen(DALVIK_CACHE_POSTFIX) + 2; if (dstlen > PKG_PATH_MAX) { return false; } sprintf(path,"%s%s/%s/%s", android_data_dir.path, DALVIK_CACHE, instruction_set, src + 1 /* skip the leading / */); char* tmp = path + android_data_dir.len + strlen(DALVIK_CACHE) + 1 + strlen(instruction_set) + 1; for(; *tmp; tmp++) { if (*tmp == '/') { *tmp = '@'; } } strcat(path, DALVIK_CACHE_POSTFIX); return true; } static bool initialize_globals() { const char* data_path = getenv("ANDROID_DATA"); if (data_path == nullptr) { SLOGE("Could not find ANDROID_DATA"); return false; } const char* root_path = getenv("ANDROID_ROOT"); if (root_path == nullptr) { SLOGE("Could not find ANDROID_ROOT"); return false; } return init_globals_from_data_and_root(data_path, root_path); } 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.path); 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.path); 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 != NULL) { 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 = atoi(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, 0); } if (access(keychain_removed_dir, F_OK) == 0) { delete_dir_contents(keychain_removed_dir, 1, 0); } } 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, ...) { 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 13300556574 014510 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 00000005162 13300556574 016440 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; /* 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; // 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 13300556574 015361 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 00000007152 13300556574 014522 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) dump(trie, 0) print """ return 0; } """ cmds/installd/otapreopt.cpp0100644 0000000 0000000 00000126527 13300556574 015113 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_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::Join; 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_MASK == 0x1fe, "DEXOPT_MASK unexpected."); 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. strncpy(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()); strncpy(value, prop_value->data(), size); value[size] = 0; return static_cast(size); } std::string GetOTADataDirectory() const { return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str()); } const std::string& GetTargetSlot() const { return target_slot_; } private: struct Parameters { 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; }; 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) { // 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 ReadArgumentsV2(argc, argv, false); } else { return ReadArgumentsV1(argc, argv); } } uint32_t version; if (!ParseUInt(argv[2], &version)) { LOG(ERROR) << "Could not parse version: " << argv[2]; return false; } switch (version) { case 2: return ReadArgumentsV2(argc, argv, true); case 3: return ReadArgumentsV3(argc, argv); default: LOG(ERROR) << "Unsupported version " << version; return false; } } bool ReadArgumentsV2(int argc ATTRIBUTE_UNUSED, char** argv, bool versioned) { 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\""; return false; } size_t param_index = 0; for (;; ++param_index) { const char* param = argv[dexopt_index + 1 + param_index]; if (param == nullptr) { break; } switch (param_index) { case 0: package_parameters_.apk_path = param; break; case 1: package_parameters_.uid = atoi(param); break; case 2: package_parameters_.pkgName = param; break; case 3: package_parameters_.instruction_set = param; break; case 4: package_parameters_.dexopt_needed = atoi(param); break; case 5: package_parameters_.oat_dir = param; break; case 6: package_parameters_.dexopt_flags = atoi(param); break; case 7: package_parameters_.compiler_filter = param; break; case 8: package_parameters_.volume_uuid = ParseNull(param); break; case 9: package_parameters_.shared_libraries = ParseNull(param); break; case 10: package_parameters_.se_info = ParseNull(param); break; default: LOG(ERROR) << "Too many arguments, got " << param; return false; } } // Set downgrade to false. It is only relevant when downgrading compiler // filter, which is not the case during ota. package_parameters_.downgrade = false; if (param_index != 11) { LOG(ERROR) << "Not enough parameters"; return false; } return true; } bool ReadArgumentsV3(int argc ATTRIBUTE_UNUSED, char** argv) { size_t dexopt_index = 3; // 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\""; return false; } size_t param_index = 0; for (;; ++param_index) { const char* param = argv[dexopt_index + 1 + param_index]; if (param == nullptr) { break; } switch (param_index) { case 0: package_parameters_.apk_path = param; break; case 1: package_parameters_.uid = atoi(param); break; case 2: package_parameters_.pkgName = param; break; case 3: package_parameters_.instruction_set = param; break; case 4: package_parameters_.dexopt_needed = atoi(param); break; case 5: package_parameters_.oat_dir = param; break; case 6: package_parameters_.dexopt_flags = atoi(param); break; case 7: package_parameters_.compiler_filter = param; break; case 8: package_parameters_.volume_uuid = ParseNull(param); break; case 9: package_parameters_.shared_libraries = ParseNull(param); break; case 10: package_parameters_.se_info = ParseNull(param); break; case 11: package_parameters_.downgrade = ParseBool(param); break; default: LOG(ERROR) << "Too many arguments, got " << param; return false; } } if (param_index != 12) { LOG(ERROR) << "Not enough parameters"; return false; } return true; } static int ReplaceMask(int input, int old_mask, int new_mask) { return (input & old_mask) != 0 ? new_mask : 0; } bool ReadArgumentsV1(int argc ATTRIBUTE_UNUSED, 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\""; return false; } size_t param_index = 0; for (;; ++param_index) { const char* param = argv[3 + param_index]; if (param == nullptr) { break; } switch (param_index) { case 0: package_parameters_.apk_path = param; break; case 1: package_parameters_.uid = atoi(param); break; case 2: package_parameters_.pkgName = param; break; case 3: package_parameters_.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. package_parameters_.dexopt_needed = DEX2OAT_FROM_SCRATCH; break; } case 5: package_parameters_.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; int input = atoi(param); package_parameters_.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: package_parameters_.compiler_filter = param; break; case 8: package_parameters_.volume_uuid = ParseNull(param); break; case 9: package_parameters_.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; } // Set se_info to null. It is only relevant for secondary dex files, which we won't // receive from a v1 A side. package_parameters_.se_info = nullptr; // Set downgrade to false. It is only relevant when downgrading compiler // filter, which is not the case during ota. package_parameters_.downgrade = false; return true; } 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 (package_parameters_.instruction_set == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; } const char* isa = package_parameters_.instruction_set; // Check whether the file exists where expected. std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE; std::string isa_path = dalvik_cache + "/" + isa; 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; } // 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; } } // Prepare to create. if (!cleared) { ClearDirectory(isa_path); } std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { return PatchoatBootImage(art_path, isa); } else { // No preopted boot image. Try to compile. 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 PatchoatBootImage(const std::string& art_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("/system/bin/patchoat"); cmd.push_back("--input-image-location=/system/framework/boot.art"); cmd.push_back(StringPrintf("--output-image-file=%s", art_path.c_str())); cmd.push_back(StringPrintf("--instruction-set=%s", isa)); int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA, ART_BASE_ADDRESS_MAX_DELTA); cmd.push_back(StringPrintf("--base-offset-delta=%d", base_offset)); std::string error_msg; bool result = Exec(cmd, &error_msg); if (!result) { LOG(ERROR) << "Could not generate boot image: " << error_msg; } return result; } 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("/system/bin/dex2oat"); 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_BASE_ADDRESS_MIN_DELTA, ART_BASE_ADDRESS_MAX_DELTA); cmd.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + 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 package_parameters_, but it beats postponing the decision or using the call- // backs to do weird things.) const char* apk_path = package_parameters_.apk_path; CHECK(apk_path != nullptr); if (StartsWith(apk_path, android_root_.c_str())) { 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) { 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 preopt of non-existing package " << apk_path; return true; } return false; } // Run dexopt with the parameters of package_parameters_. int Dexopt() { return dexopt(package_parameters_.apk_path, package_parameters_.uid, package_parameters_.pkgName, package_parameters_.instruction_set, package_parameters_.dexopt_needed, package_parameters_.oat_dir, package_parameters_.dexopt_flags, package_parameters_.compiler_filter, package_parameters_.volume_uuid, package_parameters_.shared_libraries, package_parameters_.se_info, package_parameters_.downgrade); } 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 ((package_parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) { return dexopt_result; } LOG(WARNING) << "Downgrading compiler filter in an attempt to progress compilation"; package_parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED; return Dexopt(); } //////////////////////////////////// // Helpers, mostly taken from ART // //////////////////////////////////// // Wrapper on fork/execv to run a command in a subprocess. static 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; } // 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 target_slot_; std::string android_root_; std::string android_data_; std::string boot_classpath_; std::string asec_mountpoint_; Parameters package_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 13300556574 014714 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 00000012504 13300556574 016456 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 "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); } } // 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) { // 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); } std::string vendor_partition = StringPrintf("/dev/block/bootdevice/by-name/vendor%s", arg[2]); int vendor_result = mount(vendor_partition.c_str(), "/postinstall/vendor", "ext4", MS_RDONLY, /* data */ nullptr); UNUSED(vendor_result); // 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); } // Now go on and run otapreopt. // Incoming: cmd + status-fd + target-slot + cmd... + null | Incoming | = argc + 1 // Outgoing: cmd + target-slot + cmd... + null | Outgoing | = argc const char** argv = new const char*[argc]; argv[0] = "/system/bin/otapreopt"; // The first parameter is the status file descriptor, skip. for (size_t i = 2; i <= static_cast(argc); ++i) { argv[i - 1] = arg[i]; } execv(argv[0], static_cast(const_cast(argv))); PLOG(ERROR) << "execv(OTAPREOPT) failed."; exit(99); } } // namespace installd } // namespace android int main(const int argc, char *argv[]) { return android::installd::otapreopt_chroot(argc, argv); } cmds/installd/otapreopt_script.sh0100644 0000000 0000000 00000004560 13300556574 016317 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 13300556574 015772 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.h0100644 0000000 0000000 00000002033 13300556574 015761 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 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); } } // namespace installd } // namespace android #endif // OTAPREOPT_UTILS_H_ cmds/installd/system_properties.h0100644 0000000 0000000 00000004274 13300556574 016335 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 13300556574 013522 5ustar000000000 0000000 cmds/installd/tests/Android.bp0100644 0000000 0000000 00000001757 13300556574 015434 0ustar000000000 0000000 // Build the unit tests for installd cc_test { name: "installd_utils_test", clang: true, srcs: ["installd_utils_test.cpp"], shared_libs: [ "libbase", "liblog", "libutils", "libcutils", ], static_libs: [ "libinstalld", "libdiskusage", ], } cc_test { name: "installd_cache_test", clang: true, srcs: ["installd_cache_test.cpp"], shared_libs: [ "libbase", "libbinder", "libcutils", "liblog", "liblogwrap", "libselinux", "libutils", ], static_libs: [ "libinstalld", "libdiskusage", ], } cc_test { name: "installd_service_test", clang: true, srcs: ["installd_service_test.cpp"], shared_libs: [ "libbase", "libbinder", "libcutils", "liblog", "liblogwrap", "libselinux", "libutils", ], static_libs: [ "libinstalld", "libdiskusage", ], } cmds/installd/tests/installd_cache_test.cpp0100644 0000000 0000000 00000031017 13300556574 020221 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; static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; 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_service_test.cpp0100644 0000000 0000000 00000012154 13300556574 020617 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"; static constexpr int FLAG_FORCE = 1 << 16; 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], const char *src, const char *instruction_set) { // Not really a valid path but it's good enough for testing. sprintf(path,"/data/dalvik-cache/%s/%s", instruction_set, src); return true; } static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); ::mkdir(fullPath, mode); ::chown(fullPath, owner, group); ::chmod(fullPath, mode); } static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { int fd = ::open(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), O_RDWR | O_CREAT, mode); ::fchown(fd, owner, group); ::fchmod(fd, mode); ::close(fd); } static int stat_gid(const char* path) { struct stat buf; ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf); return buf.st_gid; } static int stat_mode(const char* path) { struct stat buf; ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf); 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"); } 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, RmDexNoDalvikCache) { LOG(INFO) << "RmDexNoDalvikCache"; // Try to remove a non existing dalvik cache dex. The call should be // successful because there's nothing to remove. EXPECT_TRUE(service->rmdex("com.example", "arm").isOk()); } } // namespace installd } // namespace android cmds/installd/tests/installd_utils_test.cpp0100644 0000000 0000000 00000062453 13300556574 020326 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 "InstalldNativeService.h" #include "globals.h" #include "utils.h" #undef LOG_TAG #define LOG_TAG "utils_test" #define TEST_DATA_DIR "/data/" #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/" #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() { android_app_dir.path = (char*) TEST_APP_DIR; android_app_dir.len = strlen(TEST_APP_DIR); android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR; android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR); android_app_ephemeral_dir.path = (char*) TEST_APP_EPHEMERAL_DIR; android_app_ephemeral_dir.len = strlen(TEST_APP_EPHEMERAL_DIR); android_data_dir.path = (char*) TEST_DATA_DIR; android_data_dir.len = strlen(TEST_DATA_DIR); android_asec_dir.path = (char*) TEST_ASEC_DIR; android_asec_dir.len = strlen(TEST_ASEC_DIR); android_mnt_expand_dir.path = (char*) TEST_EXPAND_DIR; android_mnt_expand_dir.len = strlen(TEST_EXPAND_DIR); android_system_dirs.count = 2; android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t)); android_system_dirs.dirs[0].path = (char*) TEST_SYSTEM_DIR1; android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1); android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2; android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2); android_profiles_dir.path = (char*) TEST_PROFILE_DIR; android_profiles_dir.len = strlen(TEST_PROFILE_DIR); } virtual void TearDown() { free(android_system_dirs.dirs); } 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_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_DoubleSlashFail) { const char *badasec2 = TEST_ASEC_DIR "com.example.asec//pkg.apk"; EXPECT_EQ(-1, validate_apk_path(badasec2)) << badasec2 << " 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, GetPathFromString_NullPathFail) { dir_rec_t test1; EXPECT_EQ(-1, get_path_from_string(&test1, (const char *) NULL)) << "Should not allow NULL as a path."; } TEST_F(UtilsTest, GetPathFromString_EmptyPathFail) { dir_rec_t test1; EXPECT_EQ(-1, get_path_from_string(&test1, "")) << "Should not allow empty paths."; } TEST_F(UtilsTest, GetPathFromString_RelativePathFail) { dir_rec_t test1; EXPECT_EQ(-1, get_path_from_string(&test1, "mnt/asec")) << "Should not allow relative paths."; } TEST_F(UtilsTest, GetPathFromString_NonCanonical) { dir_rec_t test1; EXPECT_EQ(0, get_path_from_string(&test1, "/mnt/asec")) << "Should be able to canonicalize directory /mnt/asec"; EXPECT_STREQ("/mnt/asec/", test1.path) << "/mnt/asec should be canonicalized to /mnt/asec/"; EXPECT_EQ(10, (ssize_t) test1.len) << "path len should be equal to the length of /mnt/asec/ (10)"; free(test1.path); } TEST_F(UtilsTest, GetPathFromString_CanonicalPath) { dir_rec_t test3; EXPECT_EQ(0, get_path_from_string(&test3, "/data/app/")) << "Should be able to canonicalize directory /data/app/"; EXPECT_STREQ("/data/app/", test3.path) << "/data/app/ should be canonicalized to /data/app/"; EXPECT_EQ(10, (ssize_t) test3.len) << "path len should be equal to the length of /data/app/ (10)"; free(test3.path); } TEST_F(UtilsTest, CreatePkgPath_LongPkgNameSuccess) { char path[PKG_PATH_MAX]; // Create long packagename of "aaaaa..." size_t pkgnameSize = PKG_NAME_MAX; char pkgname[pkgnameSize + 1]; memset(pkgname, 'a', pkgnameSize); pkgname[1] = '.'; pkgname[pkgnameSize] = '\0'; EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0)) << "Should successfully be able to create package name."; std::string prefix = std::string(TEST_DATA_DIR) + PRIMARY_USER_PREFIX; size_t offset = prefix.length(); EXPECT_STREQ(pkgname, path + offset) << "Package path should be a really long string of a's"; } TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) { char path[PKG_PATH_MAX]; // Create long packagename of "aaaaa..." size_t postfixSize = PKG_PATH_MAX; char postfix[postfixSize + 1]; memset(postfix, 'a', postfixSize); postfix[postfixSize] = '\0'; EXPECT_EQ(-1, create_pkg_path(path, "com.example.package", postfix, 0)) << "Should return error because postfix is too long."; } TEST_F(UtilsTest, CreatePkgPath_PrimaryUser) { char path[PKG_PATH_MAX]; EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 0)) << "Should return error because postfix is too long."; std::string p = std::string(TEST_DATA_DIR) + PRIMARY_USER_PREFIX + "com.example.package"; EXPECT_STREQ(p.c_str(), path) << "Package path should be in /data/data/"; } TEST_F(UtilsTest, CreatePkgPath_SecondaryUser) { char path[PKG_PATH_MAX]; EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 1)) << "Should successfully create package path."; std::string p = std::string(TEST_DATA_DIR) + SECONDARY_USER_PREFIX + "1/com.example.package"; EXPECT_STREQ(p.c_str(), path) << "Package path should be in /data/user/"; } TEST_F(UtilsTest, CreateMovePath_Primary) { char path[PKG_PATH_MAX]; EXPECT_EQ(0, create_move_path(path, "com.android.test", "shared_prefs", 0)) << "Should be able to create move path for primary user"; EXPECT_STREQ("/data/data/com.android.test/shared_prefs", path) << "Primary user package directory should be created correctly"; } TEST_F(UtilsTest, CreateMovePath_Fail_AppTooLong) { char path[PKG_PATH_MAX]; std::string really_long_app_name = create_too_long_path("com.example"); EXPECT_EQ(-1, create_move_path(path, really_long_app_name.c_str(), "shared_prefs", 0)) << "Should fail to create move path for primary user"; } TEST_F(UtilsTest, CreateMovePath_Fail_LeafTooLong) { char path[PKG_PATH_MAX]; std::string really_long_leaf_name = create_too_long_path("leaf_"); EXPECT_EQ(-1, create_move_path(path, "com.android.test", really_long_leaf_name.c_str(), 0)) << "Should fail to create move path for primary user"; } TEST_F(UtilsTest, CopyAndAppend_Normal) { //int copy_and_append(dir_rec_t* dst, dir_rec_t* src, char* suffix) dir_rec_t dst; dir_rec_t src; src.path = (char*) "/data/"; src.len = strlen(src.path); EXPECT_EQ(0, copy_and_append(&dst, &src, "app/")) << "Should return error because postfix is too long."; EXPECT_STREQ("/data/app/", dst.path) << "Appended path should be correct"; EXPECT_EQ(10, (ssize_t) dst.len) << "Appended path should be length of '/data/app/' (10)"; } TEST_F(UtilsTest, AppendAndIncrement_Normal) { size_t dst_size = 10; char dst[dst_size]; char *dstp = dst; const char* src = "FOO"; EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size)) << "String should append successfully"; EXPECT_STREQ("FOO", dst) << "String should append correctly"; EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size)) << "String should append successfully again"; EXPECT_STREQ("FOOFOO", dst) << "String should append correctly again"; } TEST_F(UtilsTest, AppendAndIncrement_TooBig) { size_t dst_size = 5; char dst[dst_size]; char *dstp = dst; const char* src = "FOO"; EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size)) << "String should append successfully"; EXPECT_STREQ("FOO", dst) << "String should append correctly"; EXPECT_EQ(-1, append_and_increment(&dstp, src, &dst_size)) << "String should fail because it's too large to fit"; } 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 = create_primary_current_profile_package_dir_path(0, "com.example") + "/primary.prof"; EXPECT_EQ(expected, create_current_profile_path(/*user*/0, "com.example", /*is_secondary*/false)); } TEST_F(UtilsTest, CreatePrimaryReferenceProfile) { std::string expected = create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof"; EXPECT_EQ(expected, create_reference_profile_path("com.example", /*is_secondary*/false)); } TEST_F(UtilsTest, CreateSecondaryCurrentProfile) { EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof", create_current_profile_path(/*user*/0, "/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( "/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_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); // 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); } } // namespace installd } // namespace android cmds/installd/utils.cpp0100644 0000000 0000000 00000105626 13300556574 014233 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 "globals.h" // extern variables. #ifndef LOG_TAG #define LOG_TAG "installd" #endif #define DEBUG_XATTRS 0 using android::base::StringPrintf; 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)); } /** * 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); } 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); if (ce_data_inode != 0) { auto user_path = create_data_user_ce_path(volume_uuid, user); DIR* dir = opendir(user_path.c_str()); if (dir == nullptr) { PLOG(ERROR) << "Failed to opendir " << user_path; return fallback; } struct dirent* ent; while ((ent = readdir(dir))) { if (ent->d_ino == ce_data_inode) { auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name); #if DEBUG_XATTRS if (resolved != fallback) { LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode << " instead of " << fallback; } #endif closedir(dir); return resolved; } } LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback; closedir(dir); return fallback; } else { return 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); } int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, const char *postfix, userid_t userid) { if (!is_valid_package_name(pkgname)) { path[0] = '\0'; return -1; } std::string _tmp(create_data_user_ce_package_path(nullptr, userid, pkgname) + postfix); const char* tmp = _tmp.c_str(); if (strlen(tmp) >= PKG_PATH_MAX) { path[0] = '\0'; return -1; } else { strcpy(path, tmp); return 0; } } 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); } /** * 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_obb_path(const char* volume_uuid, const char* package_name) { return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name); } 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.path, 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.path); } 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.path, 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 PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT; // 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& 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, location); return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); } } std::string create_reference_profile_path(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(location); return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); } } 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 == NULL) { // 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, NULL))) { if (errno != ENOENT) { PLOG(ERROR) << "Failed to fts_open " << path; } return -1; } while ((p = fts_read(fts)) != NULL) { 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; } int create_move_path(char path[PKG_PATH_MAX], const char* pkgname, const char* leaf, userid_t userid ATTRIBUTE_UNUSED) { if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) { return -1; } sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf); 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 == NULL) { 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 delete_dir_contents(const std::string& pathname, bool ignore_if_missing) { return delete_dir_contents(pathname.c_str(), 0, NULL, 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, NULL, 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 == NULL) { 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 == NULL) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(fd); return -1; } res = _delete_dir_contents(d, 0); 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 == NULL) { 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 = NULL; DIR *dd = NULL; ds = opendir(srcname); if (ds == NULL) { ALOGE("Couldn't opendir %s: %s\n", srcname, strerror(errno)); return -errno; } mkdir(dstname, 0600); dd = opendir(dstname); if (dd == NULL) { 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; } } /** * 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 dir_rec_t* dir, const char* path, int maxSubdirs) { size_t dir_len = dir->len; const char* subdir = strchr(path + dir_len, '/'); // Only allow the path to have at most one subdirectory. if (subdir != NULL) { ++subdir; if ((--maxSubdirs == 0) && strchr(subdir, '/') != NULL) { ALOGE("invalid apk path '%s' (subdir?)\n", path); return -1; } } // Directories can't have a period directly after the directory markers to prevent "..". if ((path[dir_len] == '.') || ((subdir != NULL) && (*subdir == '.'))) { ALOGE("invalid apk path '%s' (trickery)\n", path); 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) { size_t i; for (i = 0; i < android_system_dirs.count; i++) { const size_t dir_len = android_system_dirs.dirs[i].len; if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) { return validate_path(android_system_dirs.dirs + i, path, 1); } } 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, bool validate_package_path) { 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; } if (validate_package_path) { // If we are asked to validate the package path check that // the dex_path is 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) { return false; } } // If we got here we have a valid path. return true; } /** * Get the contents of a environment variable that contains a path. Caller * owns the string that is inserted into the directory record. Returns * 0 on success and -1 on error. */ int get_path_from_env(dir_rec_t* rec, const char* var) { const char* path = getenv(var); int ret = get_path_from_string(rec, path); if (ret < 0) { ALOGW("Problem finding value for environment variable %s\n", var); } return ret; } /** * Puts the string into the record as a directory. Appends '/' to the end * of all paths. Caller owns the string that is inserted into the directory * record. A null value will result in an error. * * Returns 0 on success and -1 on error. */ int get_path_from_string(dir_rec_t* rec, const char* path) { if (path == NULL) { return -1; } else { const size_t path_len = strlen(path); if (path_len <= 0) { return -1; } // Make sure path is absolute. if (path[0] != '/') { return -1; } if (path[path_len - 1] == '/') { // Path ends with a forward slash. Make our own copy. rec->path = strdup(path); if (rec->path == NULL) { return -1; } rec->len = path_len; } else { // Path does not end with a slash. Generate a new string. char *dst; // Add space for slash and terminating null. size_t dst_size = path_len + 2; rec->path = (char*) malloc(dst_size); if (rec->path == NULL) { return -1; } dst = rec->path; if (append_and_increment(&dst, path, &dst_size) < 0 || append_and_increment(&dst, "/", &dst_size)) { ALOGE("Error canonicalizing path"); return -1; } rec->len = dst - rec->path; } } return 0; } int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) { dst->len = src->len + strlen(suffix); const size_t dstSize = dst->len + 1; dst->path = (char*) malloc(dstSize); if (dst->path == NULL || snprintf(dst->path, dstSize, "%s%s", src->path, suffix) != (ssize_t) dst->len) { ALOGE("Could not allocate memory to hold appended path; aborting\n"); return -1; } return 0; } /** * 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 char *path, int maxSubdirs) { const dir_rec_t* dir = NULL; if (!strncmp(path, android_app_dir.path, android_app_dir.len)) { dir = &android_app_dir; } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) { dir = &android_app_private_dir; } else if (!strncmp(path, android_app_ephemeral_dir.path, android_app_ephemeral_dir.len)) { dir = &android_app_ephemeral_dir; } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) { dir = &android_asec_dir; } else if (!strncmp(path, android_mnt_expand_dir.path, android_mnt_expand_dir.len)) { dir = &android_mnt_expand_dir; if (maxSubdirs < 2) { maxSubdirs = 2; } } else { return -1; } return validate_path(dir, path, maxSubdirs); } 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 append_and_increment(char** dst, const char* src, size_t* dst_size) { ssize_t ret = strlcpy(*dst, src, *dst_size); if (ret < 0 || (size_t) ret >= *dst_size) { return -1; } *dst += ret; *dst_size -= ret; return 0; } char *build_string2(const char *s1, const char *s2) { if (s1 == NULL || s2 == NULL) return NULL; int len_s1 = strlen(s1); int len_s2 = strlen(s2); int len = len_s1 + len_s2 + 1; char *result = (char *) malloc(len); if (result == NULL) return NULL; strcpy(result, s1); strcpy(result + len_s1, s2); return result; } char *build_string3(const char *s1, const char *s2, const char *s3) { if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL; int len_s1 = strlen(s1); int len_s2 = strlen(s2); int len_s3 = strlen(s3); int len = len_s1 + len_s2 + len_s3 + 1; char *result = (char *) malloc(len); if (result == NULL) return NULL; strcpy(result, s1); strcpy(result + len_s1, s2); strcpy(result + len_s1 + len_s2, s3); return result; } 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 << " but expected " << gid; } // 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, NULL))) { PLOG(ERROR) << "Failed to fts_open " << path; return -1; } while ((p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DP: if (chmod(p->fts_path, target_mode) != 0) { PLOG(WARNING) << "Failed to chmod " << p->fts_path; } // Intentional fall through 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; } } // namespace installd } // namespace android cmds/installd/utils.h0100644 0000000 0000000 00000012754 13300556574 013677 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 #define APPLY_HARD_QUOTAS 1 namespace android { namespace installd { struct dir_rec_t; 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"; int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname, const char *postfix, userid_t userid); 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_media_path(const char* volume_uuid, userid_t userid); std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name); 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, bool is_secondary_dex); std::string create_reference_profile_path( const std::string& package_name, bool is_secondary_dex); 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); int create_move_path(char path[PKG_PATH_MAX], const char* pkgname, const char* leaf, userid_t userid); bool is_valid_filename(const std::string& name); bool is_valid_package_name(const std::string& packageName); 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 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); 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, bool validate_package_path = true); int get_path_from_env(dir_rec_t* rec, const char* var); int get_path_from_string(dir_rec_t* rec, const char* path); int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix); int validate_apk_path(const char *path); int validate_apk_path_subdirs(const char *path); int append_and_increment(char** dst, const char* src, size_t* dst_size); char *build_string2(const char *s1, const char *s2); char *build_string3(const char *s1, const char *s2, const char *s3); 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); } // namespace installd } // namespace android #endif // UTILS_H_ cmds/ip-up-vpn/0040755 0000000 0000000 00000000000 13300556574 012401 5ustar000000000 0000000 cmds/ip-up-vpn/Android.mk0100644 0000000 0000000 00000001533 13300556574 014311 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_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 00000010466 13300556574 014404 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)); return 1; } /* Set the address. */ if (!set_address(&ifr.ifr_addr, address) || ioctl(s, SIOCSIFADDR, &ifr)) { ALOGE("Cannot set address: %s", strerror(errno)); 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)); 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"); 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 13300556574 011651 5ustar000000000 0000000 cmds/lshal/Android.bp0100644 0000000 0000000 00000002743 13300556574 013557 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-utils", "libvintf", ], srcs: [ "DebugCommand.cpp", "Lshal.cpp", "ListCommand.cpp", "PipeRelay.cpp", "utils.cpp", ], } cc_defaults { name: "lshal_defaults", shared_libs: [ "libbase", "libhidlbase", "libhidltransport", "liblshal", "libutils", ] } 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: [ "android.hardware.tests.baz@1.0" ], srcs: [ "test.cpp" ] } cmds/lshal/DebugCommand.cpp0100644 0000000 0000000 00000003006 13300556574 014676 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" namespace android { namespace lshal { DebugCommand::DebugCommand(Lshal &lshal) : mLshal(lshal) { } Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) { if (optind >= arg.argc) { mLshal.usage(command); return USAGE; } mInterfaceName = arg.argv[optind]; ++optind; for (; optind < arg.argc; ++optind) { mOptions.push_back(arg.argv[optind]); } return OK; } Status DebugCommand::main(const std::string &command, const Arg &arg) { Status status = parseArgs(command, arg); if (status != OK) { return status; } auto pair = splitFirst(mInterfaceName, '/'); return mLshal.emitDebugInfo( pair.first, pair.second.empty() ? "default" : pair.second, mOptions, mLshal.out().buf(), mLshal.err()); } } // namespace lshal } // namespace android cmds/lshal/DebugCommand.h0100644 0000000 0000000 00000002417 13300556574 014350 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 "utils.h" namespace android { namespace lshal { class Lshal; class DebugCommand { public: DebugCommand(Lshal &lshal); Status main(const std::string &command, const Arg &arg); private: Status parseArgs(const std::string &command, const Arg &arg); Lshal &mLshal; std::string mInterfaceName; std::vector mOptions; DISALLOW_COPY_AND_ASSIGN(DebugCommand); }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ cmds/lshal/ListCommand.cpp0100644 0000000 0000000 00000071230 13300556574 014567 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 "Lshal.h" #include "PipeRelay.h" #include "Timeout.h" #include "utils.h" using ::android::hardware::hidl_string; using ::android::hidl::manager::V1_0::IServiceManager; namespace android { namespace lshal { ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) { } std::string getCmdline(pid_t pid) { std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline"); std::string cmdline; if (!ifs.is_open()) { return ""; } ifs >> cmdline; return cmdline; } const std::string &ListCommand::getCmdline(pid_t pid) { auto pair = mCmdlines.find(pid); if (pair != mCmdlines.end()) { return pair->second; } mCmdlines[pid] = ::android::lshal::getCmdline(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()); } 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. mErr << "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)) { mErr << "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; }); } // Must process hwbinder services first, then passthrough services. void ListCommand::forEachTable(const std::function &f) { f(mServicesTable); f(mPassthroughRefTable); f(mImplementationsTable); } void ListCommand::forEachTable(const std::function &f) const { f(mServicesTable); f(mPassthroughRefTable); f(mImplementationsTable); } 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)); } } }); // use a double for loop here because lshal doesn't care about efficiency. for (TableEntry &packageEntry : mImplementationsTable) { std::string packageName = packageEntry.interfaceName; FQName fqPackageName{packageName.substr(0, packageName.find("::"))}; if (!fqPackageName.isValid()) { continue; } for (TableEntry &interfaceEntry : mPassthroughRefTable) { if (interfaceEntry.arch != ARCH_UNKNOWN) { continue; } FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first}; if (!interfaceName.isValid()) { continue; } if (interfaceName.getPackageAndVersion() == fqPackageName) { interfaceEntry.arch = packageEntry.arch; } } } } void ListCommand::printLine( 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 { if (mSelectedColumns & ENABLE_INTERFACE_NAME) mOut << std::setw(80) << interfaceName << "\t"; if (mSelectedColumns & ENABLE_TRANSPORT) mOut << std::setw(10) << transport << "\t"; if (mSelectedColumns & ENABLE_ARCH) mOut << std::setw(5) << arch << "\t"; if (mSelectedColumns & ENABLE_THREADS) { mOut << std::setw(8) << threadUsage << "\t"; } if (mSelectedColumns & ENABLE_SERVER_PID) { if (mEnableCmdlines) { mOut << std::setw(15) << serverCmdline << "\t"; } else { mOut << std::setw(5) << server << "\t"; } } if (mSelectedColumns & ENABLE_SERVER_ADDR) mOut << std::setw(16) << address << "\t"; if (mSelectedColumns & ENABLE_CLIENT_PIDS) { if (mEnableCmdlines) { mOut << std::setw(0) << clientCmdlines; } else { mOut << std::setw(0) << clients; } } mOut << std::endl; } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { for (vintf::Version& v : hal->versions) { if (v.majorVer == version.majorVer) { v.minorVer = std::max(v.minorVer, version.minorVer); return true; } } return false; } void ListCommand::dumpVintf() const { using vintf::operator|=; mOut << "" << std::endl; vintf::HalManifest manifest; forEachTable([this, &manifest] (const Table &table) { for (const TableEntry &entry : table) { std::string fqInstanceName = entry.interfaceName; if (&table == &mImplementationsTable) { // Quick hack to work around *'s replaceAll(&fqInstanceName, '*', 'D'); } auto splittedFqInstanceName = splitFirst(fqInstanceName, '/'); FQName fqName(splittedFqInstanceName.first); if (!fqName.isValid()) { mErr << "Warning: '" << splittedFqInstanceName.first << "' is not a valid FQName." << std::endl; continue; } // Strip out system libs. if (fqName.inPackage("android.hidl") || fqName.inPackage("android.frameworks") || fqName.inPackage("android.system")) { continue; } std::string interfaceName = &table == &mImplementationsTable ? "" : fqName.name(); std::string instanceName = &table == &mImplementationsTable ? "" : splittedFqInstanceName.second; vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()}; vintf::Transport transport; vintf::Arch arch; if (entry.transport == "hwbinder") { transport = vintf::Transport::HWBINDER; arch = vintf::Arch::ARCH_EMPTY; } else if (entry.transport == "passthrough") { transport = vintf::Transport::PASSTHROUGH; switch (entry.arch) { case lshal::ARCH32: arch = vintf::Arch::ARCH_32; break; case lshal::ARCH64: arch = vintf::Arch::ARCH_64; break; case lshal::ARCH_BOTH: arch = vintf::Arch::ARCH_32_64; break; case lshal::ARCH_UNKNOWN: // fallthrough default: mErr << "Warning: '" << fqName.package() << "' doesn't have bitness info, assuming 32+64." << std::endl; arch = vintf::Arch::ARCH_32_64; } } else { mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; continue; } bool done = false; for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { if (hal->transport() != transport) { if (transport != vintf::Transport::PASSTHROUGH) { mErr << "Fatal: should not reach here. Generated result may be wrong for '" << hal->name << "'." << std::endl; } done = true; break; } if (findAndBumpVersion(hal, version)) { if (&table != &mImplementationsTable) { hal->interfaces[interfaceName].name = interfaceName; hal->interfaces[interfaceName].instances.insert(instanceName); } hal->transportArch.arch |= arch; done = true; break; } } if (done) { continue; // to next TableEntry } decltype(vintf::ManifestHal::interfaces) interfaces; if (&table != &mImplementationsTable) { interfaces[interfaceName].name = interfaceName; interfaces[interfaceName].instances.insert(instanceName); } if (!manifest.add(vintf::ManifestHal{ .format = vintf::HalFormat::HIDL, .name = fqName.package(), .versions = {version}, .transportArch = {transport, arch}, .interfaces = interfaces})) { mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; } } }); mOut << vintf::gHalManifestConverter(manifest); } static const std::string &getArchString(Architecture 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 ARCH64: return sStr64; case ARCH32: return sStr32; case ARCH_BOTH: return sStrBoth; case ARCH_UNKNOWN: // fall through default: return sStrUnknown; } } static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { switch (a) { case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT: return ARCH64; case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT: return ARCH32; case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough default: return ARCH_UNKNOWN; } } void ListCommand::dumpTable() { mServicesTable.description = "All binderized services (registered services through hwservicemanager)"; mPassthroughRefTable.description = "All interfaces that getService() has ever return 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.description = "All available passthrough implementations (all -impl.so files)"; forEachTable([this] (const Table &table) { if (!mNeat) { mOut << table.description << std::endl; } mOut << std::left; if (!mNeat) { printLine("Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD", "PTR", "Clients", "Clients CMD"); } for (const auto &entry : table) { printLine(entry.interfaceName, entry.transport, getArchString(entry.arch), entry.getThreadUsage(), entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid), entry.serverCmdline, entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress), join(entry.clientPids, " "), join(entry.clientCmdlines, ";")); // 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". if (mEmitDebugInfo && &table == &mServicesTable) { auto pair = splitFirst(entry.interfaceName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(), NullableOStream(nullptr)); } } if (!mNeat) { mOut << std::endl; } }); } void ListCommand::dump() { if (mVintf) { dumpVintf(); if (!!mFileOutput) { mFileOutput.buf().close(); delete &mFileOutput.buf(); mFileOutput = nullptr; } mOut = std::cout; } else { dumpTable(); } } void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { Table *table = nullptr; switch (source) { case HWSERVICEMANAGER_LIST : table = &mServicesTable; break; case PTSERVICEMANAGER_REG_CLIENT : table = &mPassthroughRefTable; break; case LIST_DLLIB : table = &mImplementationsTable; break; default: mErr << "Error: Unknown source of entry " << source << std::endl; } if (table) { table->entries.push_back(std::forward(entry)); } } Status ListCommand::fetchAllLibraries(const sp &manager) { 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(2s, 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 = "passthrough", .serverPid = NO_PID, .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = ARCH_UNKNOWN }).first->second.arch |= fromBaseArchitecture(info.arch); } for (auto &&pair : entries) { putEntry(LIST_DLLIB, std::move(pair.second)); } }); if (!ret.isOk()) { mErr << "Error: Failed to call list on getPassthroughServiceManager(): " << ret.description() << std::endl; return DUMP_ALL_LIBS_ERROR; } return OK; } Status ListCommand::fetchPassthrough(const sp &manager) { 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(PTSERVICEMANAGER_REG_CLIENT, { .interfaceName = std::string{info.interfaceName.c_str()} + "/" + std::string{info.instanceName.c_str()}, .transport = "passthrough", .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = fromBaseArchitecture(info.arch) }); } }); if (!ret.isOk()) { mErr << "Error: Failed to call debugDump on defaultServiceManager(): " << ret.description() << std::endl; return DUMP_PASSTHROUGH_ERROR; } return OK; } Status ListCommand::fetchBinderized(const sp &manager) { using namespace ::std; using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; const std::string mode = "hwbinder"; hidl_vec fqInstanceNames; // copying out for timeoutIPC auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) { fqInstanceNames = names; }); if (!listRet.isOk()) { mErr << "Error: Failed to list services for " << mode << ": " << listRet.description() << std::endl; return DUMP_BINDERIZED_ERROR; } Status status = OK; // server pid, .ptr value of binder object, child pids std::map allDebugInfos; std::map allPids; for (const auto &fqInstanceName : fqInstanceNames) { const auto pair = splitFirst(fqInstanceName, '/'); const auto &serviceName = pair.first; const auto &instanceName = pair.second; auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); if (!getRet.isOk()) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager:" << getRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; continue; } sp service = getRet; if (service == nullptr) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager (null)" << std::endl; status |= DUMP_BINDERIZED_ERROR; continue; } auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) { allDebugInfos[fqInstanceName] = debugInfo; if (debugInfo.pid >= 0) { allPids[static_cast(debugInfo.pid)] = PidInfo(); } }); if (!debugRet.isOk()) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " << "debugging information cannot be retrieved:" << debugRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; } } for (auto &pair : allPids) { pid_t serverPid = pair.first; if (!getPidInfo(serverPid, &allPids[serverPid])) { mErr << "Warning: no information for PID " << serverPid << ", are you root?" << std::endl; status |= DUMP_BINDERIZED_ERROR; } } for (const auto &fqInstanceName : fqInstanceNames) { auto it = allDebugInfos.find(fqInstanceName); if (it == allDebugInfos.end()) { putEntry(HWSERVICEMANAGER_LIST, { .interfaceName = fqInstanceName, .transport = mode, .serverPid = NO_PID, .serverObjectAddress = NO_PTR, .clientPids = {}, .threadUsage = 0, .threadCount = 0, .arch = ARCH_UNKNOWN }); continue; } const DebugInfo &info = it->second; bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR; putEntry(HWSERVICEMANAGER_LIST, { .interfaceName = fqInstanceName, .transport = mode, .serverPid = info.pid, .serverObjectAddress = info.ptr, .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{}, .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0, .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0, .arch = fromBaseArchitecture(info.arch), }); } return status; } Status ListCommand::fetch() { Status status = OK; auto bManager = mLshal.serviceManager(); if (bManager == nullptr) { mErr << "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) { mErr << "Failed to get getPassthroughServiceManager()!" << std::endl; status |= NO_PASSTHROUGH_MANAGER; } else { status |= fetchAllLibraries(pManager); } return status; } Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { static struct option longOptions[] = { // long options with short alternatives {"help", no_argument, 0, 'h' }, {"interface", no_argument, 0, 'i' }, {"transport", no_argument, 0, 't' }, {"arch", no_argument, 0, 'r' }, {"pid", no_argument, 0, 'p' }, {"address", no_argument, 0, 'a' }, {"clients", no_argument, 0, 'c' }, {"threads", no_argument, 0, 'e' }, {"cmdline", no_argument, 0, 'm' }, {"debug", optional_argument, 0, 'd' }, // long options without short alternatives {"sort", required_argument, 0, 's' }, {"init-vintf",optional_argument, 0, 'v' }, {"neat", no_argument, 0, 'n' }, { 0, 0, 0, 0 } }; int optionIndex; int c; // Lshal::parseArgs has set optind to the next option to parse for (;;) { // using getopt_long in case we want to add other options in the future c = getopt_long(arg.argc, arg.argv, "hitrpacmde", longOptions, &optionIndex); if (c == -1) { break; } switch (c) { case 's': { if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) { mSortColumn = TableEntry::sortByInterfaceName; } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) { mSortColumn = TableEntry::sortByServerPid; } else { mErr << "Unrecognized sorting column: " << optarg << std::endl; mLshal.usage(command); return USAGE; } break; } case 'v': { if (optarg) { mFileOutput = new std::ofstream{optarg}; mOut = mFileOutput; if (!mFileOutput.buf().is_open()) { mErr << "Could not open file '" << optarg << "'." << std::endl; return IO_ERROR; } } mVintf = true; } case 'i': { mSelectedColumns |= ENABLE_INTERFACE_NAME; break; } case 't': { mSelectedColumns |= ENABLE_TRANSPORT; break; } case 'r': { mSelectedColumns |= ENABLE_ARCH; break; } case 'p': { mSelectedColumns |= ENABLE_SERVER_PID; break; } case 'a': { mSelectedColumns |= ENABLE_SERVER_ADDR; break; } case 'c': { mSelectedColumns |= ENABLE_CLIENT_PIDS; break; } case 'e': { mSelectedColumns |= ENABLE_THREADS; break; } case 'm': { mEnableCmdlines = true; break; } case 'd': { mEmitDebugInfo = true; if (optarg) { mFileOutput = new std::ofstream{optarg}; mOut = mFileOutput; if (!mFileOutput.buf().is_open()) { mErr << "Could not open file '" << optarg << "'." << std::endl; return IO_ERROR; } chown(optarg, AID_SHELL, AID_SHELL); } break; } case 'n': { mNeat = true; break; } case 'h': // falls through default: // see unrecognized options mLshal.usage(command); return USAGE; } } if (optind < arg.argc) { // see non option mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; } if (mSelectedColumns == 0) { mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS; } return OK; } Status ListCommand::main(const std::string &command, const Arg &arg) { Status status = parseArgs(command, arg); if (status != OK) { return status; } status = fetch(); postprocess(); dump(); return status; } } // namespace lshal } // namespace android cmds/lshal/ListCommand.h0100644 0000000 0000000 00000007477 13300556574 014250 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 "NullableOStream.h" #include "TableEntry.h" #include "utils.h" namespace android { namespace lshal { class Lshal; class ListCommand { public: ListCommand(Lshal &lshal); Status main(const std::string &command, const Arg &arg); private: Status parseArgs(const std::string &command, const Arg &arg); Status fetch(); void postprocess(); void dump(); void putEntry(TableEntrySource source, 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); struct PidInfo { std::map refPids; // pids that are referenced uint32_t threadUsage; // number of threads in use uint32_t threadCount; // number of threads total }; bool getPidInfo(pid_t serverPid, PidInfo *info) const; void dumpTable(); void dumpVintf() const; void printLine( 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; // 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); void forEachTable(const std::function &f); void forEachTable(const std::function &f) const; Lshal &mLshal; Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; NullableOStream mErr; NullableOStream mOut; NullableOStream mFileOutput = nullptr; TableEntryCompare mSortColumn = nullptr; TableEntrySelect mSelectedColumns = 0; // If true, cmdlines will be printed instead of pid. bool mEnableCmdlines = false; // If true, calls IBase::debug(...) on each service. bool mEmitDebugInfo = false; // If true, output in VINTF format. bool mVintf = false; // If true, explanatory text are not emitted. bool mNeat = false; // 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; DISALLOW_COPY_AND_ASSIGN(ListCommand); }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ cmds/lshal/Lshal.cpp0100644 0000000 0000000 00000021104 13300556574 013413 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 "DebugCommand.h" #include "ListCommand.h" #include "PipeRelay.h" namespace android { namespace lshal { using ::android::hidl::manager::V1_0::IServiceManager; Lshal::Lshal() : mOut(std::cout), mErr(std::cerr), mServiceManager(::android::hardware::defaultServiceManager()), mPassthroughManager(::android::hardware::getPassthroughServiceManager()) { } Lshal::Lshal(std::ostream &out, std::ostream &err, sp serviceManager, sp passthroughManager) : mOut(out), mErr(err), mServiceManager(serviceManager), mPassthroughManager(passthroughManager) { } void Lshal::usage(const std::string &command) const { static const std::string helpSummary = "lshal: List and debug HALs.\n" "\n" "commands:\n" " help Print help message\n" " list list HALs\n" " debug debug a specified HAL\n" "\n" "If no command is specified, `list` is the default.\n"; static const std::string list = "list:\n" " lshal\n" " lshal list\n" " List all hals with default ordering and columns (`lshal list -ipc`)\n" " lshal list [-h|--help]\n" " -h, --help: Print help message for list (`lshal help list`)\n" " lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n" " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n" " [--sort={interface|i|pid|p}] [--init-vintf[=]]\n" " [--debug|-d[=]]\n" " -i, --interface: print the interface name column\n" " -n, --instance: print the instance name column\n" " -t, --transport: print the transport mode column\n" " -r, --arch: print if the HAL is in 64-bit or 32-bit\n" " -e, --threads: print currently used/available threads\n" " (note, available threads created lazily)\n" " -p, --pid: print the server PID, or server cmdline if -m is set\n" " -a, --address: print the server object address column\n" " -c, --clients: print the client PIDs, or client cmdlines if -m is set\n" " -m, --cmdline: print cmdline instead of PIDs\n" " -d[=], --debug[=]: emit debug info from \n" " IBase::debug with empty options\n" " --sort=i, --sort=interface: sort by interface name\n" " --sort=p, --sort=pid: sort by server pid\n" " --init-vintf=: form a skeleton HAL manifest to specified\n" " file, or stdout if no file specified.\n"; static const std::string debug = "debug:\n" " lshal debug [options [options [...]]] \n" " Print debug information of a specified interface.\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"; static const std::string help = "help:\n" " lshal -h\n" " lshal --help\n" " lshal help\n" " Print this help message\n" " lshal help list\n" " Print help message for list\n" " lshal help debug\n" " Print help message for debug\n"; if (command == "list") { mErr << list; return; } if (command == "debug") { mErr << debug; return; } mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help; } // 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, std::ostream &out, NullableOStream err) const { using android::hidl::base::V1_0::IBase; 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; } 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) { static std::set sAllCommands{"list", "debug", "help"}; optind = 1; if (optind >= arg.argc) { // no options at all. return OK; } mCommand = arg.argv[optind]; if (sAllCommands.find(mCommand) != sAllCommands.end()) { ++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 = ""; return OK; } mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl; usage(); return USAGE; } void signalHandler(int sig) { if (sig == SIGINT) { int retVal; pthread_exit(&retVal); } } Status Lshal::main(const Arg &arg) { // Allow SIGINT to terminate all threads. signal(SIGINT, signalHandler); Status status = parseArgs(arg); if (status != OK) { return status; } if (mCommand == "help") { usage(optind < arg.argc ? arg.argv[optind] : ""); return USAGE; } // Default command is list if (mCommand == "list" || mCommand == "") { return ListCommand{*this}.main(mCommand, arg); } if (mCommand == "debug") { return DebugCommand{*this}.main(mCommand, arg); } usage(); return USAGE; } 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 00000004251 13300556574 013064 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 "NullableOStream.h" #include "utils.h" namespace android { namespace lshal { class Lshal { public: Lshal(); Lshal(std::ostream &out, std::ostream &err, sp serviceManager, sp passthroughManager); Status main(const Arg &arg); void usage(const std::string &command = "") const; NullableOStream err() const; 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, std::ostream &out, NullableOStream err) const; private: Status parseArgs(const Arg &arg); std::string mCommand; Arg mCmdArgs; NullableOStream mOut; NullableOStream mErr; sp mServiceManager; sp mPassthroughManager; DISALLOW_COPY_AND_ASSIGN(Lshal); }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ cmds/lshal/NullableOStream.h0100644 0000000 0000000 00000003557 13300556574 015062 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: NullableOStream(S &os) : mOs(&os) {} 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 { 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/PipeRelay.cpp0100644 0000000 0000000 00000004444 13300556574 014252 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 namespace android { namespace lshal { struct PipeRelay::RelayThread : public Thread { explicit RelayThread(int fd, std::ostream &os); bool threadLoop() override; private: int mFd; std::ostream &mOutStream; DISALLOW_COPY_AND_ASSIGN(RelayThread); }; //////////////////////////////////////////////////////////////////////////////// PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os) : mFd(fd), mOutStream(os) { } bool PipeRelay::RelayThread::threadLoop() { char buffer[1024]; ssize_t n = read(mFd, buffer, sizeof(buffer)); if (n <= 0) { return false; } mOutStream.write(buffer, n); return true; } //////////////////////////////////////////////////////////////////////////////// PipeRelay::PipeRelay(std::ostream &os) : mOutStream(os), mInitCheck(NO_INIT) { int res = socketpair(AF_UNIX, SOCK_STREAM, 0 /* protocol */, 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() { if (mFds[1] >= 0) { shutdown(mFds[1], SHUT_WR); } if (mFds[0] >= 0) { shutdown(mFds[0], SHUT_RD); } if (mThread != NULL) { mThread->join(); mThread.clear(); } CloseFd(&mFds[1]); 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 00000003045 13300556574 013713 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; std::ostream &mOutStream; 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.h0100644 0000000 0000000 00000005657 13300556574 014105 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 namespace android { namespace lshal { using Pids = std::vector; enum : unsigned int { HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list() PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient LIST_DLLIB, // through listing dynamic libraries }; using TableEntrySource = unsigned int; enum : unsigned int { ARCH_UNKNOWN = 0, ARCH32 = 1 << 0, ARCH64 = 1 << 1, ARCH_BOTH = ARCH32 | ARCH64 }; using Architecture = unsigned int; struct TableEntry { std::string interfaceName; std::string transport; int32_t serverPid; uint32_t threadUsage; uint32_t threadCount; std::string serverCmdline; uint64_t serverObjectAddress; Pids clientPids; std::vector clientCmdlines; Architecture arch; 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); } }; struct Table { using Entries = std::vector; std::string description; Entries entries; Entries::iterator begin() { return entries.begin(); } Entries::const_iterator begin() const { return entries.begin(); } Entries::iterator end() { return entries.end(); } Entries::const_iterator end() const { return entries.end(); } }; using TableEntryCompare = std::function; enum : unsigned int { ENABLE_INTERFACE_NAME = 1 << 0, ENABLE_TRANSPORT = 1 << 1, ENABLE_SERVER_PID = 1 << 2, ENABLE_SERVER_ADDR = 1 << 3, ENABLE_CLIENT_PIDS = 1 << 4, ENABLE_ARCH = 1 << 5, ENABLE_THREADS = 1 << 6, }; using TableEntrySelect = unsigned int; enum { NO_PID = -1, NO_PTR = 0 }; } // namespace lshal } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ cmds/lshal/Timeout.h0100644 0000000 0000000 00000006366 13300556574 013460 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: 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 NULL; } 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, NULL, 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, NULL); 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/main.cpp0100644 0000000 0000000 00000001365 13300556574 013303 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 00000013265 13300556574 013340 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 "Lshal.h" #define NELEMS(array) static_cast(sizeof(array) / sizeof(array[0])) using namespace testing; 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_death_recipient; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; 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 LshalTest : 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; })); } void TearDown() override {} std::stringstream err; std::stringstream out; sp serviceManager; }; TEST_F(LshalTest, Debug) { const char *args[] = { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar" }; EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager) .main({NELEMS(args), const_cast(args)})); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar")); EXPECT_THAT(err.str(), IsEmpty()); } TEST_F(LshalTest, Debug2) { const char *args[] = { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux" }; EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager) .main({NELEMS(args), const_cast(args)})); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux")); EXPECT_THAT(err.str(), IsEmpty()); } TEST_F(LshalTest, Debug3) { const char *args[] = { "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist", }; EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager) .main({NELEMS(args), const_cast(args)})); EXPECT_THAT(err.str(), HasSubstr("does not exist")); } } // 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 13300556574 013523 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 00000004450 13300556574 013162 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, USAGE = 1 << 0, NO_BINDERIZED_MANAGER = 1 << 1, NO_PASSTHROUGH_MANAGER = 1 << 2, DUMP_BINDERIZED_ERROR = 1 << 3, DUMP_PASSTHROUGH_ERROR = 1 << 4, DUMP_ALL_LIBS_ERROR = 1 << 5, IO_ERROR = 1 << 6, NO_INTERFACE = 1 << 7, TRANSACTION_ERROR = 1 << 8, }; 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 13300556574 011666 5ustar000000000 0000000 cmds/rawbu/Android.mk0100644 0000000 0000000 00000000463 13300556574 013577 0ustar000000000 0000000 # Copyright 2009 The Android Open Source Project LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= backup.cpp LOCAL_SHARED_LIBRARIES := libcutils libc LOCAL_MODULE:= rawbu LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) cmds/rawbu/NOTICE0100644 0000000 0000000 00000024707 13300556574 012601 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 00000051376 13300556574 013650 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 = NULL; 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 }, { NULL, 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 == NULL) { 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 == NULL) { 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 != NULL) { // 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)) { int i; 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 != NULL) { 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 != NULL) { 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 = NULL; int srcLen = strlen(srcPath); int result = 1; int i; dir = opendir(srcPath); if (dir == NULL) { fprintf (stderr, "error opendir'ing '%s': %s\n", srcPath, strerror(errno)); return 0; } for (;;) { de = readdir(dir); if (de == NULL) { break; } if (0 == strcmp(de->d_name, ".") || 0 == strcmp(de->d_name, "..") || 0 == strcmp(de->d_name, "lost+found") ) { continue; } if (fullPath != NULL) { 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 != NULL) { 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 == NULL) { fprintf(stderr, "unable to open source file '%s': %s\n", fullPath, strerror(errno)); result = 0; goto done; } int copyres = copy_file(fh, src, size, NULL, fullPath); fclose(src); if (!copyres) { result = 0; goto done; } } } done: if (fullPath != NULL) { free(fullPath); } closedir(dir); return result; } static int backup_data(const char* destPath) { int res = -1; FILE* fh = fopen(destPath, "w"); if (fh == NULL) { 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 == NULL) { 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 = NULL; 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 == NULL) { 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, NULL); 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/service/0040755 0000000 0000000 00000000000 13300556574 012206 5ustar000000000 0000000 cmds/service/Android.bp0100644 0000000 0000000 00000000555 13300556574 014113 0ustar000000000 0000000 cc_binary { name: "service", srcs: ["service.cpp"], shared_libs: [ "libutils", "libbinder", ], cflags: ["-DXP_UNIX"], } cc_binary { name: "vndservice", proprietary: true, srcs: ["service.cpp"], shared_libs: [ "libutils", "libbinder", ], cflags: ["-DXP_UNIX", "-DVENDORSERVICES"], } cmds/service/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 015326 0ustar000000000 0000000 cmds/service/NOTICE0100644 0000000 0000000 00000024707 13300556574 013121 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 00000030342 13300556574 014351 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 != NULL) { 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 != NULL) { 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 == NULL) { 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 == NULL ? ": 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 != NULL && 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(NULL); } else if (strcmp(argv[optind], "intent") == 0) { char* action = NULL; char* dataArg = NULL; char* type = NULL; int launchFlags = 0; char* component = NULL; int categoryCount = 0; char* categories[16]; char* context1 = NULL; optind++; while (optind < argc) { char* key = strtok_r(argv[optind], "=", &context1); char* value = strtok_r(NULL, "=", &context1); // we have reached the end of the XXX=XXX args. if (key == NULL) 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 = NULL; int categoryCount = 0; categories[categoryCount] = strtok_r(value, ",", &context2); while (categories[categoryCount] != NULL) { categoryCount++; categories[categoryCount] = strtok_r(NULL, ",", &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 13300556574 013541 5ustar000000000 0000000 cmds/servicemanager/Android.bp0100644 0000000 0000000 00000001675 13300556574 015452 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_vendor"], init_rc: ["vndservicemanager.rc"], } cmds/servicemanager/bctest.c0100644 0000000 0000000 00000005251 13300556574 015171 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 00000041527 13300556574 015156 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) { return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); } 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: { struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; if ((end - ptr) < sizeof(*txn)) { ALOGE("parse: txn too small!\n"); return -1; } binder_dump_txn(txn); 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); res = func(bs, txn, &msg, &reply); if (txn->flags & TF_ONE_WAY) { binder_free_buffer(bs, txn->data.ptr.buffer); } else { binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); } } ptr += sizeof(*txn); 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->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->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->type == BINDER_TYPE_HANDLE) return obj->handle; return 0; } cmds/servicemanager/binder.h0100644 0000000 0000000 00000005753 13300556574 015164 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 *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 00000024613 13300556574 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, uid_t uid, const char *tctx, const char *perm, const char *name) { char *sctx = NULL; const char *class = "service_manager"; bool allowed; struct audit_data ad; if (getpidcon(spid, &sctx) < 0) { ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid); return false; } ad.pid = spid; ad.uid = uid; ad.name = name; int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad); allowed = (result == 0); freecon(sctx); return allowed; } static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm) { return check_mac_perms(spid, uid, service_manager_context, perm, NULL); } static bool check_mac_perms_from_lookup(pid_t spid, 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, uid, tctx, perm, name); freecon(tctx); return allowed; } static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, 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, uid, perm, str8(name, name_len)) ? 1 : 0; } static int svc_can_list(pid_t spid, uid_t uid) { const char *perm = "list"; return check_mac_perms_from_getcon(spid, uid, perm) ? 1 : 0; } static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid) { const char *perm = "find"; return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0; } struct svcinfo { struct svcinfo *next; uint32_t handle; struct binder_death death; int allow_isolated; 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) { 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, 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, pid_t spid) { 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, 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->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 *txn, 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; //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); 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); 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; if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, txn->sender_pid)) return -1; break; case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); if (!svc_can_list(txn->sender_pid, txn->sender_euid)) { ALOGE("list_service() uid=%d - PERMISSION DENIED\n", txn->sender_euid); return -1; } si = svclist; while ((n-- > 0) && si) 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); cb.func_log = selinux_log_callback; 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 00000000707 13300556574 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 writepid /dev/cpuset/system-background/tasks shutdown critical cmds/servicemanager/vndservicemanager.rc0100644 0000000 0000000 00000000307 13300556574 017567 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 13300556574 013742 5ustar000000000 0000000 cmds/surfacereplayer/Android.bp0100644 0000000 0000000 00000000052 13300556574 015637 0ustar000000000 0000000 subdirs = [ "proto", "replayer", ]cmds/surfacereplayer/proto/0040755 0000000 0000000 00000000000 13300556574 015105 5ustar000000000 0000000 cmds/surfacereplayer/proto/Android.bp0100644 0000000 0000000 00000000256 13300556574 017010 0ustar000000000 0000000 cc_library_static { name: "libtrace_proto", srcs: [ "src/trace.proto", ], proto: { type: "lite", export_proto_headers: true, }, } cmds/surfacereplayer/proto/src/0040755 0000000 0000000 00000000000 13300556574 015674 5ustar000000000 0000000 cmds/surfacereplayer/proto/src/trace.proto0100644 0000000 0000000 00000010411 13300556574 020051 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; oneof SurfaceChange { PositionChange position = 2; SizeChange size = 3; AlphaChange alpha = 4; LayerChange layer = 5; CropChange crop = 6; FinalCropChange final_crop = 7; 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; } } 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 LayerChange { required uint32 layer = 1; } message CropChange { required Rectangle rectangle = 1; } message FinalCropChange { 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; required int32 type = 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 13300556574 015565 5ustar000000000 0000000 cmds/surfacereplayer/replayer/Android.bp0100644 0000000 0000000 00000002372 13300556574 017471 0ustar000000000 0000000 cc_library_shared { name: "libsurfacereplayer", clang: true, 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", "-std=c++14", ], 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", clang: true, 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", "-std=c++14", ], } cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp0100644 0000000 0000000 00000006513 13300556574 022350 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 13300556574 022014 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 13300556574 017014 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 13300556574 017347 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 13300556574 017021 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 00000006544 13300556574 017163 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] == NULL) { 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 13300556574 017053 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 00000060342 13300556574 020056 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.kSurfaceDeletion: { std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), 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::openGlobalTransaction(); status_t status = NO_ERROR; status = doSurfaceTransaction(t.surface_change()); doDisplayTransaction(t.display_change()); if (t.animation()) { SurfaceComposerClient::setAnimationTransaction(); } event->readyToExecute(); SurfaceComposerClient::closeGlobalTransaction(t.synchronous()); ALOGV("Ended Transaction"); return status; } status_t Replayer::doSurfaceTransaction(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: status = setPosition(change.id(), change.position()); break; case SurfaceChange::SurfaceChangeCase::kSize: status = setSize(change.id(), change.size()); break; case SurfaceChange::SurfaceChangeCase::kAlpha: status = setAlpha(change.id(), change.alpha()); break; case SurfaceChange::SurfaceChangeCase::kLayer: status = setLayer(change.id(), change.layer()); break; case SurfaceChange::SurfaceChangeCase::kCrop: status = setCrop(change.id(), change.crop()); break; case SurfaceChange::SurfaceChangeCase::kMatrix: status = setMatrix(change.id(), change.matrix()); break; case SurfaceChange::SurfaceChangeCase::kFinalCrop: status = setFinalCrop(change.id(), change.final_crop()); break; case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: status = setOverrideScalingMode(change.id(), change.override_scaling_mode()); break; case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint: status = setTransparentRegionHint(change.id(), change.transparent_region_hint()); break; case SurfaceChange::SurfaceChangeCase::kLayerStack: status = setLayerStack(change.id(), change.layer_stack()); break; case SurfaceChange::SurfaceChangeCase::kHiddenFlag: status = setHiddenFlag(change.id(), change.hidden_flag()); break; case SurfaceChange::SurfaceChangeCase::kOpaqueFlag: status = setOpaqueFlag(change.id(), change.opaque_flag()); break; case SurfaceChange::SurfaceChangeCase::kSecureFlag: status = setSecureFlag(change.id(), change.secure_flag()); break; case SurfaceChange::SurfaceChangeCase::kDeferredTransaction: waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock); status = setDeferredTransaction(change.id(), change.deferred_transaction()); break; default: status = NO_ERROR; break; } if (status != NO_ERROR) { ALOGE("SET TRANSACTION FAILED"); return status; } } return status; } void Replayer::doDisplayTransaction(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(change.id(), change.surface()); break; case DisplayChange::DisplayChangeCase::kLayerStack: setDisplayLayerStack(change.id(), change.layer_stack()); break; case DisplayChange::DisplayChangeCase::kSize: setDisplaySize(change.id(), change.size()); break; case DisplayChange::DisplayChangeCase::kProjection: setDisplayProjection(change.id(), change.projection()); break; default: break; } } } status_t Replayer::setPosition(layer_id id, const PositionChange& pc) { ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y()); return mLayers[id]->setPosition(pc.x(), pc.y()); } status_t Replayer::setSize(layer_id id, const SizeChange& sc) { ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h()); return mLayers[id]->setSize(sc.w(), sc.h()); } status_t Replayer::setLayer(layer_id id, const LayerChange& lc) { ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer()); return mLayers[id]->setLayer(lc.layer()); } status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) { ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha()); return mLayers[id]->setAlpha(ac.alpha()); } status_t Replayer::setCrop(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()); return mLayers[id]->setCrop(r); } status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) { ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id, fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), fcc.rectangle().bottom()); Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), fcc.rectangle().bottom()); return mLayers[id]->setFinalCrop(r); } status_t Replayer::setMatrix(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()); return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); } status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) { ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode()); return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode()); } status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) { ALOGV("Setting Transparent Region Hint"); Region re = Region(); for (auto r : trhc.region()) { Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom()); re.merge(rect); } return mLayers[id]->setTransparentRegionHint(re); } status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) { ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack()); return mLayers[id]->setLayerStack(lsc.layer_stack()); } status_t Replayer::setHiddenFlag(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; return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden); } status_t Replayer::setOpaqueFlag(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; return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque); } status_t Replayer::setSecureFlag(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; return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure); } status_t Replayer::setDeferredTransaction(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 BAD_VALUE; } auto handle = mLayers[dtc.layer_id()]->getHandle(); return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number()); } void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) { sp outProducer; sp outConsumer; BufferQueue::createBufferQueue(&outProducer, &outConsumer); SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer); } void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) { SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack()); } void Replayer::setDisplaySize(display_id id, const SizeChange& sc) { SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h()); } void Replayer::setDisplayProjection(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()); SurfaceComposerClient::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::deleteSurfaceControl( const SurfaceDeletion& delete_, const std::shared_ptr& event) { ALOGV("Deleting %d Surface Control", delete_.id()); event->readyToExecute(); std::lock_guard lock1(mPendingLayersLock); mLayersPendingRemoval.push_back(delete_.id()); const auto& iterator = mBufferQueueSchedulers.find(delete_.id()); if (iterator != mBufferQueueSchedulers.end()) { (*iterator).second->stopScheduling(); } std::lock_guard lock2(mLayerLock); if (mLayers[delete_.id()] != nullptr) { mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle()); } return NO_ERROR; } void Replayer::doDeleteSurfaceControls() { std::lock_guard lock1(mPendingLayersLock); std::lock_guard lock2(mLayerLock); if (!mLayersPendingRemoval.empty()) { for (int id : mLayersPendingRemoval) { mLayers.erase(id); mColors.erase(id); mBufferQueueSchedulers.erase(id); } mLayersPendingRemoval.clear(); } } status_t Replayer::injectVSyncEvent( const VSyncEvent& vSyncEvent, const std::shared_ptr& event) { ALOGV("Injecting VSync Event"); doDeleteSurfaceControls(); 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 00000012462 13300556574 017523 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 deleteSurfaceControl(const SurfaceDeletion& delete_, 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(const SurfaceChanges& surfaceChange); void doDisplayTransaction(const DisplayChanges& displayChange); status_t setPosition(layer_id id, const PositionChange& pc); status_t setSize(layer_id id, const SizeChange& sc); status_t setAlpha(layer_id id, const AlphaChange& ac); status_t setLayer(layer_id id, const LayerChange& lc); status_t setCrop(layer_id id, const CropChange& cc); status_t setFinalCrop(layer_id id, const FinalCropChange& fcc); status_t setMatrix(layer_id id, const MatrixChange& mc); status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc); status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc); status_t setLayerStack(layer_id id, const LayerStackChange& lsc); status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc); status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc); status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc); status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc); void setDisplaySurface(display_id id, const DispSurfaceChange& dsc); void setDisplayLayerStack(display_id id, const LayerStackChange& lsc); void setDisplaySize(display_id id, const SizeChange& sc); void setDisplayProjection(display_id id, const ProjectionChange& pc); void doDeleteSurfaceControls(); 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 13300556574 020402 5ustar000000000 0000000 cmds/surfacereplayer/replayer/trace_creator/trace_creator.py0100644 0000000 0000000 00000021166 13300556574 023574 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.type = int(input("Enter type: ")) 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 13300556574 011175 5ustar000000000 0000000 cmds/vr/.clang-format0100644 0000000 0000000 00000000231 13300556574 013541 0ustar000000000 0000000 BasedOnStyle: Google DerivePointerAlignment: false PointerAlignment: Left AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false data/0040755 0000000 0000000 00000000000 13300556574 010531 5ustar000000000 0000000 data/etc/0040755 0000000 0000000 00000000000 13300556574 011304 5ustar000000000 0000000 data/etc/android.hardware.audio.low_latency.xml0100644 0000000 0000000 00000001634 13300556574 020662 0ustar000000000 0000000 data/etc/android.hardware.audio.output.xml0100644 0000000 0000000 00000001661 13300556574 017702 0ustar000000000 0000000 data/etc/android.hardware.audio.pro.xml0100644 0000000 0000000 00000001625 13300556574 017142 0ustar000000000 0000000 data/etc/android.hardware.bluetooth.xml0100644 0000000 0000000 00000001464 13300556574 017250 0ustar000000000 0000000 data/etc/android.hardware.bluetooth_le.xml0100644 0000000 0000000 00000001476 13300556574 017733 0ustar000000000 0000000 data/etc/android.hardware.broadcastradio.xml0100644 0000000 0000000 00000001470 13300556574 020221 0ustar000000000 0000000 data/etc/android.hardware.camera.autofocus.xml0100644 0000000 0000000 00000001643 13300556574 020501 0ustar000000000 0000000 data/etc/android.hardware.camera.external.xml0100644 0000000 0000000 00000001645 13300556574 020315 0ustar000000000 0000000 data/etc/android.hardware.camera.flash-autofocus.xml0100644 0000000 0000000 00000002034 13300556574 021567 0ustar000000000 0000000 data/etc/android.hardware.camera.front.xml0100644 0000000 0000000 00000001555 13300556574 017623 0ustar000000000 0000000 data/etc/android.hardware.camera.full.xml0100644 0000000 0000000 00000002025 13300556574 017426 0ustar000000000 0000000 data/etc/android.hardware.camera.manual_postprocessing.xml0100644 0000000 0000000 00000001662 13300556574 023111 0ustar000000000 0000000 data/etc/android.hardware.camera.manual_sensor.xml0100644 0000000 0000000 00000001640 13300556574 021334 0ustar000000000 0000000 data/etc/android.hardware.camera.raw.xml0100644 0000000 0000000 00000001620 13300556574 017255 0ustar000000000 0000000 data/etc/android.hardware.camera.xml0100644 0000000 0000000 00000001555 13300556574 016474 0ustar000000000 0000000 data/etc/android.hardware.consumerir.xml0100644 0000000 0000000 00000001560 13300556574 017426 0ustar000000000 0000000 data/etc/android.hardware.ethernet.xml0100644 0000000 0000000 00000001502 13300556574 017052 0ustar000000000 0000000 data/etc/android.hardware.faketouch.multitouch.distinct.xml0100644 0000000 0000000 00000002014 13300556574 023220 0ustar000000000 0000000 data/etc/android.hardware.faketouch.multitouch.jazzhand.xml0100644 0000000 0000000 00000002117 13300556574 023214 0ustar000000000 0000000 data/etc/android.hardware.faketouch.xml0100644 0000000 0000000 00000001637 13300556574 017216 0ustar000000000 0000000 data/etc/android.hardware.fingerprint.xml0100644 0000000 0000000 00000001502 13300556574 017563 0ustar000000000 0000000 data/etc/android.hardware.gamepad.xml0100644 0000000 0000000 00000001547 13300556574 016643 0ustar000000000 0000000 data/etc/android.hardware.hdmi.cec.xml0100644 0000000 0000000 00000001502 13300556574 016706 0ustar000000000 0000000 data/etc/android.hardware.location.gps.xml0100644 0000000 0000000 00000001656 13300556574 017646 0ustar000000000 0000000 data/etc/android.hardware.location.xml0100644 0000000 0000000 00000001665 13300556574 017056 0ustar000000000 0000000 data/etc/android.hardware.nfc.hce.xml0100644 0000000 0000000 00000001570 13300556574 016545 0ustar000000000 0000000 data/etc/android.hardware.nfc.hcef.xml0100644 0000000 0000000 00000001573 13300556574 016716 0ustar000000000 0000000 data/etc/android.hardware.nfc.xml0100644 0000000 0000000 00000001631 13300556574 016005 0ustar000000000 0000000 data/etc/android.hardware.opengles.aep.xml0100644 0000000 0000000 00000001546 13300556574 017624 0ustar000000000 0000000 data/etc/android.hardware.screen.landscape.xml0100644 0000000 0000000 00000001503 13300556574 020445 0ustar000000000 0000000 data/etc/android.hardware.screen.portrait.xml0100644 0000000 0000000 00000001501 13300556574 020355 0ustar000000000 0000000 data/etc/android.hardware.sensor.accelerometer.xml0100644 0000000 0000000 00000001470 13300556574 021362 0ustar000000000 0000000 data/etc/android.hardware.sensor.ambient_temperature.xml0100644 0000000 0000000 00000001504 13300556574 022602 0ustar000000000 0000000 data/etc/android.hardware.sensor.barometer.xml0100644 0000000 0000000 00000001446 13300556574 020533 0ustar000000000 0000000 data/etc/android.hardware.sensor.compass.xml0100644 0000000 0000000 00000001444 13300556574 020216 0ustar000000000 0000000 data/etc/android.hardware.sensor.gyroscope.xml0100644 0000000 0000000 00000001446 13300556574 020565 0ustar000000000 0000000 data/etc/android.hardware.sensor.heartrate.ecg.xml0100644 0000000 0000000 00000001507 13300556574 021265 0ustar000000000 0000000 data/etc/android.hardware.sensor.heartrate.fitness.xml0100644 0000000 0000000 00000001502 13300556574 022175 0ustar000000000 0000000 data/etc/android.hardware.sensor.heartrate.xml0100644 0000000 0000000 00000001466 13300556574 020534 0ustar000000000 0000000 data/etc/android.hardware.sensor.hifi_sensors.xml0100644 0000000 0000000 00000001456 13300556574 021247 0ustar000000000 0000000 data/etc/android.hardware.sensor.light.xml0100644 0000000 0000000 00000001460 13300556574 017656 0ustar000000000 0000000 data/etc/android.hardware.sensor.proximity.xml0100644 0000000 0000000 00000001457 13300556574 020621 0ustar000000000 0000000 data/etc/android.hardware.sensor.relative_humidity.xml0100644 0000000 0000000 00000001477 13300556574 022306 0ustar000000000 0000000 data/etc/android.hardware.sensor.stepcounter.xml0100644 0000000 0000000 00000001462 13300556574 021124 0ustar000000000 0000000 data/etc/android.hardware.sensor.stepdetector.xml0100644 0000000 0000000 00000001453 13300556574 021256 0ustar000000000 0000000 data/etc/android.hardware.telephony.carrierlock.xml0100644 0000000 0000000 00000001505 13300556574 021545 0ustar000000000 0000000 data/etc/android.hardware.telephony.cdma.xml0100644 0000000 0000000 00000001563 13300556574 020155 0ustar000000000 0000000 data/etc/android.hardware.telephony.euicc.xml0100644 0000000 0000000 00000001440 13300556574 020333 0ustar000000000 0000000 data/etc/android.hardware.telephony.gsm.xml0100644 0000000 0000000 00000001561 13300556574 020035 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.multitouch.distinct.xml0100644 0000000 0000000 00000002064 13300556574 023576 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml0100644 0000000 0000000 00000002170 13300556574 023564 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.multitouch.xml0100644 0000000 0000000 00000002013 13300556574 021750 0ustar000000000 0000000 data/etc/android.hardware.touchscreen.xml0100644 0000000 0000000 00000001615 13300556574 017563 0ustar000000000 0000000 data/etc/android.hardware.type.automotive.xml0100644 0000000 0000000 00000001500 13300556574 020406 0ustar000000000 0000000 data/etc/android.hardware.usb.accessory.xml0100644 0000000 0000000 00000001717 13300556574 020027 0ustar000000000 0000000 data/etc/android.hardware.usb.host.xml0100644 0000000 0000000 00000001544 13300556574 017007 0ustar000000000 0000000 data/etc/android.hardware.vr.headtracking-0.xml0100644 0000000 0000000 00000001536 13300556574 020452 0ustar000000000 0000000 data/etc/android.hardware.vr.headtracking-1.xml0100644 0000000 0000000 00000001536 13300556574 020453 0ustar000000000 0000000 data/etc/android.hardware.vr.high_performance.xml0100644 0000000 0000000 00000001561 13300556574 021167 0ustar000000000 0000000 data/etc/android.hardware.vulkan.compute-0.xml0100644 0000000 0000000 00000001542 13300556574 020350 0ustar000000000 0000000 data/etc/android.hardware.vulkan.level-0.xml0100644 0000000 0000000 00000001541 13300556574 020002 0ustar000000000 0000000 data/etc/android.hardware.vulkan.level-1.xml0100644 0000000 0000000 00000001541 13300556574 020003 0ustar000000000 0000000 data/etc/android.hardware.vulkan.version-1_0_3.xml0100644 0000000 0000000 00000001610 13300556574 021017 0ustar000000000 0000000 data/etc/android.hardware.wifi.aware.xml0100644 0000000 0000000 00000001505 13300556574 017273 0ustar000000000 0000000 data/etc/android.hardware.wifi.direct.xml0100644 0000000 0000000 00000001513 13300556574 017445 0ustar000000000 0000000 data/etc/android.hardware.wifi.passpoint.xml0100644 0000000 0000000 00000001515 13300556574 020215 0ustar000000000 0000000 data/etc/android.hardware.wifi.xml0100644 0000000 0000000 00000001475 13300556574 016203 0ustar000000000 0000000 data/etc/android.software.activities_on_secondary_displays.xml0100644 0000000 0000000 00000001405 13300556574 024112 0ustar000000000 0000000 data/etc/android.software.app_widgets.xml0100644 0000000 0000000 00000001360 13300556574 017601 0ustar000000000 0000000 data/etc/android.software.autofill.xml0100644 0000000 0000000 00000001355 13300556574 017116 0ustar000000000 0000000 data/etc/android.software.backup.xml0100644 0000000 0000000 00000001353 13300556574 016542 0ustar000000000 0000000 data/etc/android.software.companion_device_setup.xml0100644 0000000 0000000 00000001373 13300556574 022021 0ustar000000000 0000000 data/etc/android.software.connectionservice.xml0100644 0000000 0000000 00000001530 13300556574 021012 0ustar000000000 0000000 data/etc/android.software.cts.xml0100644 0000000 0000000 00000001510 13300556574 016061 0ustar000000000 0000000 data/etc/android.software.device_admin.xml0100644 0000000 0000000 00000001361 13300556574 017703 0ustar000000000 0000000 data/etc/android.software.freeform_window_management.xml0100644 0000000 0000000 00000001377 13300556574 022673 0ustar000000000 0000000 data/etc/android.software.live_tv.xml0100644 0000000 0000000 00000001354 13300556574 016746 0ustar000000000 0000000 data/etc/android.software.managed_users.xml0100644 0000000 0000000 00000001513 13300556574 020110 0ustar000000000 0000000 data/etc/android.software.midi.xml0100644 0000000 0000000 00000001351 13300556574 016215 0ustar000000000 0000000 data/etc/android.software.picture_in_picture.xml0100644 0000000 0000000 00000001367 13300556574 021176 0ustar000000000 0000000 data/etc/android.software.preview_sdk.xml0100644 0000000 0000000 00000001474 13300556574 017623 0ustar000000000 0000000 data/etc/android.software.print.xml0100644 0000000 0000000 00000001352 13300556574 016430 0ustar000000000 0000000 data/etc/android.software.securely_removes_users.xml0100644 0000000 0000000 00000001372 13300556574 022112 0ustar000000000 0000000 data/etc/android.software.sip.voip.xml0100644 0000000 0000000 00000001560 13300556574 017044 0ustar000000000 0000000 data/etc/android.software.sip.xml0100644 0000000 0000000 00000001474 13300556574 016074 0ustar000000000 0000000 data/etc/android.software.verified_boot.xml0100644 0000000 0000000 00000001361 13300556574 020114 0ustar000000000 0000000 data/etc/android.software.voice_recognizers.xml0100644 0000000 0000000 00000001407 13300556574 021014 0ustar000000000 0000000 data/etc/android.software.vr.xml0100644 0000000 0000000 00000001465 13300556574 015730 0ustar000000000 0000000 data/etc/android.software.webview.xml0100644 0000000 0000000 00000001354 13300556574 016746 0ustar000000000 0000000 data/etc/car_core_hardware.xml0100644 0000000 0000000 00000007375 13300556574 015471 0ustar000000000 0000000 data/etc/com.android.nfc_extras.xml0100644 0000000 0000000 00000001452 13300556574 016355 0ustar000000000 0000000 data/etc/com.nxp.mifare.xml0100644 0000000 0000000 00000001456 13300556574 014655 0ustar000000000 0000000 data/etc/handheld_core_hardware.xml0100644 0000000 0000000 00000011255 13300556574 016463 0ustar000000000 0000000 data/etc/tablet_core_hardware.xml0100644 0000000 0000000 00000007306 13300556574 016171 0ustar000000000 0000000 data/etc/wearable_core_hardware.xml0100644 0000000 0000000 00000006571 13300556574 016503 0ustar000000000 0000000 docs/0040755 0000000 0000000 00000000000 13300556574 010550 5ustar000000000 0000000 docs/Doxyfile0100644 0000000 0000000 00000237033 13300556574 012263 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/include/ndk ../../av/include/camera/ndk # 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 = NO # 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 = NO # 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 = NO # 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 = # 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 13300556574 012210 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 13300556574 012721 0ustar000000000 0000000 docs/header.html0100644 0000000 0000000 00000000553 13300556574 012666 0ustar000000000 0000000 $title
docs/images/0040755 0000000 0000000 00000000000 13300556574 012015 5ustar000000000 0000000 docs/images/camera2/0040755 0000000 0000000 00000000000 13300556574 013327 5ustar000000000 0000000 docs/images/camera2/metadata/0040755 0000000 0000000 00000000000 13300556574 015107 5ustar000000000 0000000 docs/images/camera2/metadata/android.colorCorrection.mode/0040755 0000000 0000000 00000000000 13300556574 022617 5ustar000000000 0000000 docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png0100644 0000000 0000000 00000062003 13300556574 027364 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 13300556574 023614 5ustar000000000 0000000 docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png0100644 0000000 0000000 00000031267 13300556574 026754 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 13300556574 027425 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 13300556574 027452 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 13300556574 026577 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 13300556574 021747 5ustar000000000 0000000 docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png0100644 0000000 0000000 00000075406 13300556574 025273 0ustar000000000 0000000 PNG  IHDR7?{sBIT|d pHYsaa?i IDATxyxT>{dF E$ Z(bQ[]Pq_[tSi-uAQ*P7?&$jbXž}IyČ $sΜ?u%gsɝC=N!@DDDz """HlnHS!"""MasCDDD憈4 i """67DDD)lnHS!R?P|")**¿/p/B24[o^ݻ.H3 7O;~쫯µ^. w?~~)~a߾}}:ΝKbȐ!JBF Ң{/ _WϿ[V^ 466.\ Æ v{]ԩSBkꪫpUW)]ph+_W}믿رcZ3`ʕ _Dee%|;!""hPr-Bzŋg},88X;V-111=n?wÇ !(//wqHHHfY 6L\{GHzpx`Pk_|!-Z䱼c6رcBӉٳgk N'򗿸uo}Ns- 33S;BӉoٵ{:N\m}ts[ޱ,Z>eDZZí*t:OB\r%l6"~BӉ xWjjv-_~tn3$}{3gnze]&t:LSkk6mtb̙'7xt_v[~wN'֯_/h.gnΝZֱ<.p-eY$$$4ײDcsCtC{W,\Pĸ}7ֹ͛EfffmݺvmbǎO$^g67B׋Z![nckkk]fpp7n\uL2EFZֱ_nc}Q /otW^BqF{WuuG^3g N'?ǎMOUTT^/O>;vUscEPP8s<^;txꩧW_}%8fΝ}gN|k͛NO?Cccc_/"ٲe PTT@@UUM'|QQQزe MrڊoYYY=-))+tR\x}~k.i<^tg1FrkK.ضn,سg&Oڈ#>8yzn-{v9y$vڅ۷;vȲQ̙3=G?͛k.jݻz,??Bdffz;q^^RRL4aÆ7MN퇝,Y5k'? CRv?4(77UGXd ֭[T\r% VO?uNkk+n#455tf|W>|Ç]w݅W^yfB455\u]ŋcB@c׽?pʷ~wʕ+__OMwɜ;wnc^uƺ5&yfϋazt<ꫯ]|9ۇ+χ9pף_^۲e |A_db:umyMM }QXoɒ%nMúuUW]{61{l|O][[[jþ}j*מyp5hs?kn>K/}>#\tEn׼?pرc1rHn6s+66mڄz 3gte$ɭqںu+f̘9s>]:t(bʕ:u*.]cƌA~~>lقÇ㥗^q/=>CG?ѣGfo5 'N֭[1}tL<UUUX~=9-۷oƏ/G둘?{^|Edff";;sEBB6n܈jpݟ;˗/qw7ƌ\l߾sŵ^ ۰b ,^ fNw7zs R(//aaab˖-=}7Ř1cjW_BVeee\r8sNz^Ĉ3g~mSzT7""Bz׶N׿kŐ!Cڗh"&BBBĦMov DZZx\w[^3Bt:q->HKKbСkwK ^?K_]ze'O .IIIbs=W,[LvqUW ^/JKK?\矋y`'nv%2۷O\uU"<<\lgDvv6:sIqwDa2DjjXd?'OVUʼn{GՉpKg̘e㧳",,LvyIw_Tss!}4zjn֬Y#t:x.<~x?Opy饗7ˠ^hX:{|Bn7PZZ("!ka„ Cz)̟?0{ld/TLjWUXvm\;`:?f׮]{pB=?t=.7oJJJ~mNGzzҥhGyB$5NǸqCGEVV^y\z饘7oe)'N(6lJJJЫ+<;w츓@zz뮫;q[on3N5o<]V2WWXmR+uhvx>?Ccc# z=j*;N8t(,,Ĉ#\O "** W^y%֯_k֬v[s&L f+++ 5-UhnkD$B:sI|P_W<,0yd|'er ^yܹsm6曮礐4++=!ZP^[#uehn9?d]~w/𹙛3f@eeNc]w݅[ p^Z[^7*-W^| w|!"""NZڪ ;^hXV$H*/ۿ>5!ո+.1e1e1IjkmA0K"a2^6 #Fcʋ+V<熴飏>R&T1@W^g@d` G#6$l!Xt%5,,hAmKzQoBD`4,딛?asC0@n^ɌR1q m DF#$ k"""?y& `ڬ)q~'k@S*lnHq`0bf)OmӗFRrb6"20SWj+PLj*Kk_Y_Yn^p{n3ƹ^+*^ǕO)#`sC*t ~++_hɚ hDL c'zh !!0)xXTcŊJטCvVz[ vjq56F#V+!9C%zA"""rmiD`2v$58$nko~-' """"ҘVɎ GQVsZWcqYsk$ $aSSҼ_ F6ԴTv{c"OSPqL̘4ܐjdgg+]_cbm?(i>eMnͼk12mԠ;8sCh"Kk_Y_Y6G3[*as4z0\u<\0G*/J\w(NMIČI>lnHE̙t ~++˗B5-K^j@5u)wS B5-hZ{^2 uiCn::HDF7 rI5֭[t ~++K;ujcjl:="c9 1q~lnHEV^t ~++KM;-U(91 ޿WO{)]_cbR"A١R9`2Ñ04W0ꍈ y훰v+*PPPi`|]oқPK8tlj222 &(] 7a|}ڬ)q~~X` n`CM_":&IɉnĦF_f5=FlMo ŤEEEJטB^et46%ť0Ț Wc'h T$!lnH5qKk_Y!ڀõqh4jmIȂMa)R_~YWmMj>VZf2v$h4׿%@$mv@9Ӏ /UWX6G ԕX!?{Hx97,RSҼ^7 fJvT5Bs[ccFMw{q*ۍ=WKČI^/ k[q춱D̸ǮeYFsS[c3/Z>ۇ!x.1e1#jbd^Xbcyab̘: `a)RKk_Y̿od'u |}΀Y#׵]?2mkvFj^_:% )dԶIzhDXP!@DD~)ՠ{(DFà:8V]PR:aHDh0 ruVX^UU 1m8uVD A):n@ yUq`0x<`ۦ/6c6 *h!Ԑ6!""f5=FʂHJNQoBtP,BPbRUV)]_cp Jhhlh4"kz&_M'DLpt(\c챹!Xz%5,5/@sk#ĩ㐝R:b9!a>pK5OR%5,%/.9ܼ*E f0TD'D:[masCD6o߄>X^QUoƴYS0>. Ct {Qo!"3%mDtL4ֵ՚ q}<>cgh4\YPTܘ  sOSC'j,\PW7/9T}Ʀш58v 1mlk gnH5xPe1ey#Ivljq56F#V+!K2GO TcJט3pVJdmvHHYQ_46a)""BkOOȲ[s I8Fe@jJ2gn4MjEE 8<^=6-ƞB`b$K48sC}vKk_YQtj;ml )9fMaYFsS[c3/ZL5 5TcٲeJטsjqjm=6y:s2/Blt, cYp"xw.1e'M'*z` 68VS C#ǜj masCt ~+l︴DZACjja> ^x!N˖-èQ .k֬RDD)iDYmqpkR"G!ƆO47;v@vv6Ǝ?7t~_᷿m7/KtMX~=pcڵ^htuiwWM1>ng o`Μ9o~{ ?EDT**{nL53tpkcy-?0-|ϻk߿0eӧO۷ː/++ ֭s#;;cVr[lTUU-_d ^xeFQQ^zldgg{<eXpGm]wo]_~?qM|ulٶ o յ/ƞo;6}`k79nvyo_V1c&?V\5==FDO}Tࢋ.fÎ;\˞x 8~8ƿqF̞=۵;|W GFF0a„SbAA{Z/~1Y3Qqq1ع5y;dLz3bd!A_9W੧_c…رc/_e˖! (,,Ĉ#s .7ވgys=lƆԁ?XCeWy dMτhDBR< kz&vSvıpkd'̜9k׮1w\^˗/w]ɓ'O>Flٲr ?+[o~|AwoED4hJ`omj7hjYn37pWꫯ3ft ypp0-[dSDd26;$Irkp$I<7FD=gG8T[jl >1@m+vń$IX*H[s 2 5%MmҾKk`mh:6G&_R{ <8$3&y^_[|j)oRD4pV t ome_Sg FDWKfT4G8thl?"C%hsl2#5% 3&adڨnAD]csCDOSBeI4zh BlH̆DEhbR3INz{-tFą$!1,fUP_Y_[ܐjvmJטߴI8Z_S ۱aHDD%; 3e1ma)RK*]_cNԴT a0X)2e1masCԔ{ֈp8ۺQAFYP_Y_[@r:Pt= 2 68& QguT5S8kԛP/UGD] ŤVR%ԕDM5 )#0e1masCt ~s M'P^[dvhŰ4C3} OqP)m0AQHJNr=N8Y"z}0u_ " o x,¾aڬ)q!a QoFDtxXNq`0 (8z?mGˏ>7H СllT37Dwrr\0$ ; o7p?u{*Fvv%5_egl:0Ț9yR"F :hȠ76Z1masCh"KkdǑR[jl:FXVS0=r9!5cRsQU&S93v$58$nko~޻˰_[8sCD*Q^WS?2>1 2vń$I:F۟򝚒LD/!"M:stǦ㻽ESP݈'fLjD408sCn:KkZʿٚ%%'bڬ)eYFsS[c3/ZLzm!Xz%5-/@usזG'̜: zf!6:aልEYx1c,/UN 2-|"*q(Z%{L3B`5z2"@Dt!jZ*QR trthDf|D憈|]Tgk̆ Il `sCD>G'[*QRXo'Xp%5_lTDc+kܐjR{}F#fkԞ1masC`KkjdQɭݎ3_[j95 Ad`O`sCD5D48'ە.%p$Jzll"c0,"MZW_[8sCl2L:U2@n^Ʉ!15f$\/`DM45+k Rw}W߼}ncyUU% ôYS0>|ףc έ,-@>-0wU7/.9 AoGҶM_h1F E!*(Vs _i_[8sCD^x|8dMτh$Iع5yE﯄tJLD>D{*>chDL NacCDTS[t0ZOvJFWRe1ma)RdKkSȨh:{&nC$G$mO0&5,-!ո. fv kK`'dYέ9$ \Ȳ HMIԆp憈*T5r[>zl:[(ۍ=X-lpN׸Z/ignhP8dhl )9fMq}.2ybd(JD™R"=Z2@ڀrc&\xF ;{ Qzm6Mfab$kl+k R6lP 5;M'PovhE|P΄~VpW67/%ol8S#c֜!क़:UDzC`5m_Y_[Yl8GSB1$8K?csCDgBa Gh@8CRpR^xAZow '*xn"f`c 37---Jzdlj#=4aFtP,t:[E' @? a^Jt:!5l:I!I4{:"e37D-)T145v;.DBDO!"Rs[N6씺10ZQqW"Xt%A١R$E~hN1茈 MD9je1masCԼkMXTUW -´YS0>|u#OpPq`0 (8z?mGˏallHS>1Y3a4!IvnAAn@Q~OE|P[CD>37VRQv@MGchDL ΩHHcc F~~%6-X-0'p0Z섞P bR+VPklmƣnC$F,y)$IɬXO2"? @UIh(S8e;@oq΍,?;5%Mgn4NvJ8p-fײc"@An)(jfSvM̘z37DfwpĭDL5,hnjvkle_iV+@asCt RoőRHNG87t&]Xl cY^ؿqW"Xh%h^kClp)dlɛO(&Xp%RGjKmlB14<_ WWܐjp(D,,-lnH5,Xt !o8ވNp!Rfh<)u9j 1 &/VFD)rrPvmm0 HJNtpkb". ş}.B!%%?||ǘ8q"1tP,^--]O󓲶o߮t ^y&?#TTUno>|o= vA$o Whnv؁l;~!n&Wo. w>7  mcތ4Z&_agy&LovÁ?0,x!/^kV̘1,_DKK $L8%xMn^5=F$aGtą cΖ?F_Y_[T?sڊ-[kq[>o<455a۶m xdcR});T }Ʀш陮H6 _W77hkkèQܖ1P\\NAAb+@`` "##u&V;bFV K2O&"#77uuuP!!ihhXsy睇O?/ꫯ[n䊉&@e 6;$oI`7?f@DWonݾ{~ mmmk=,Y5kR+c=t )8P:{ ,cWq΍,Ԕ4էՎ+kO( 466-u\qn/]<s򒓓.aHN .Ǧ㻽ESPt71cjr+kgn`0pA3c#G5G;ge]lYYYXn۸?w}+:#;;UUUn˗,Y^xmYyy9QTT^ˢgXzuTu\p:~ֿp׹HJND^n>J475ߟe_iutW__ߏ:Xrt̛7c=Q.6 ;vp-{'ꫯp܌8dggv-駱l2?~QQQn###yyy0a~A7l? Yȝ~8#hsl2#5% 3&""-߿?,O=.b̟? .Ď;|r,[ hllDaa!Fhg#<̝;;vez46D'@:2M ^JD4TX fΜkb;w.V^˗GaO\qXxXJyذaeU'QkrhEB08B++oKx嗕._‰GyAć^IS_1e1masC˗bN a~|%rО=|9-`b憨rCnrLtP"Qml IDAT Q?6?:&hp?3oHvM 8RWec35_Y_[8sC*jSgAE._7MH ŋU/E_Y_[x)x)8FާPkrLтa0L^7Rp"91A/6x2"":^27hrL% "HadԛHxB1ƪU~o‹3rAEUPSS֣ oЀp$hv+k R|Wqs ?ضK-o?&20CB}x3H5VXq}<>cgh4B$ܚ#Y#VR?ybb™KeJt46`45=5Sy/""-asC~|t0Z!y6""67wrsg6;$ɽ$ v`6^RAD!.! 2vnq58Ȳ HMISטbRE ;no o7bf)𸅉&5{_Y_[|;ğgM͙3gжp1m,٭}-Ff01e1m𙛲2,Yv}7MtVG}ca|Fwqx%6Mfab$jld3g"))i7KtVGpuzZj~|Ś0?Rg^yB[֭Iyc` IÏS諁Ο+kˀ770{lwCnn.N믿@%iիl[CGcs; dw_Y_['~3;6m?>}:f͚Yfa޽ހlGrvؘ $9@Og+kˀ܌1<>cTWW>CVV>cL<ӟ-\$~pM!"Ҵ 6n4"++ YYY/Vr-D:2ɭn6 )|8""b띞Xc[1!"ҼonV^ɓ'^niߒ4b…gq`b2͟g5 wyg [FBϱ龱Iac C O_4iM~ Ҩ iMw6H KQo4bb2}݇_K/͆\#?%;%?naa4!"7<3J ; z@%Wc#u7cƆ_ 97 s9}7ޒ|{#;}{ccbcgɟWזon/_kSLA^^@%iIJe˺}Ƅ66gip1e1mf-I#}._uz{c3&yӼecǎoFxx8.R,_p:Fii@%iD```ec46F66; xsȑ#{tT>gL.r"&XP$@cOQZ^ b唈K ,+ЊGZAZ  r{ -iFC̼3{?y\׽v; %%%ͅjM7݄Yf-IZUkwn~pǜ9szQUUݻ`0-I^| KDzՊlѣs_OnP|,/Ewcr1u {s/c޽_&Lŋ1f\x1oG*ү_?Wxu#vNMXOr0"/8|,]ot?8&*//Gvv6v;dY^חOѥF\: փH "37 3x'OSߒT9yƦ""O(njRޝ<~KcGn{8FC3o>#""Ç֭[ߒ?J{opi\x _~q[6 Dei_..aonx<8~o&oIq&w_⢝<}};y.AӘ\_]}YɓXn0j(u]~{EaԨQOӧcذax7qĉpNql7e߈1#{lѳ;v(C}ԑa\EVzj%hK/r .]x瑛/HLL=[oEQQ~_aѾnT:ų'0 3F}_ +Kw?~"Rq8w$Z`454HLLD}]=. XiWs/ܼxڽދgy6 eee9rds aÆZR7kpn7͏ą}uO>ƦEZZ֯_7x#:yͫӳwvvvc׎2x< jJAA4ե]gn,剝FCkD-8Ss .C3/o?+c.;\n9#oUa\_]6d2H՟Eз_:1ǃ756mwaPשUO?4ե]gnۇ-[[oEZZZj"xݔ=uOÑ/BU98]NMf HQHܜ9s'O 4Fs˖-4iR+7ugO173DD,5`b֬YHLLī|??~pgNf9פcr1Ms`HJJBzz:.]n1P‹˧H99"Bbr1EsSZZ ͆LlٲӦMSO=ŋK.޽{9?BTUnP<ENצ1\_] h~YYYxW4w. K.c=%ӟ9,Y={Fܸra춗cp497ѫOO hJB^+%""j?sԄ۷;];"$A1\_]bt 55o<%%PSܞ={sa0gOm/M7= ̘y?|藸)F߱O#_8-[,*Cʘ\_..1ܴvE?}Q >c3f$33F9cF>u:j5mذ!jE\_..1t P[[7{Z`X`n7n7zx7 $Z|M шDE7)t_./Won222`0W_>lذl޼Brr2f3f3cL&A?1l6ONNn>6-fºuaP]]7pB:u 6 _jΝ7pPqA)S>|ЁxY~s;/}"<99999^z%!C`Q:3mo64667O_Fee%_> t:֮]tt9ΆnGVVVd?P *><ҔƮe&T""RH|:7 , p= //Xb-[b8p p 7Frr2t:&ָ<.\7?+ÒhAcc##oZ]sˣ~b 77wiҤI(,,Ċ+v;FA_Cq;Ww}[xHM茞}%UFDD>qqYB}nzi{ԁ͍F4Xx{50Fa{\y"EՅ͍!.GHtrE͛'Mcr1рKht5(&wEzj%hՅ͍ynTםU<%1-jboŔbFO˨7[Rw EktգqQX^ Q(ܨ*O"N2 9!5M(\_..lnTby8=M:#ZР<院bMM/Z64H4JJJ0zhehՅgn$GI^XchDΘ38NV#Q""bsaAcs`֭K4/ SSʠ37K&.AӘ\_..ln"+p(.g} 6n(Mcr1Ms`HJJBzz:zIDAT.]z;N,YCErr2gy.WHq").Rl6dffb˖-6mz),^8sy<쳘1c{=DAAfΜ憈(r h~YYYxW4GrtRU|xh;?Gx%%%@|y=Z#<^z%!C`a"?^͛7OtU8̟?_t:㏷=v v{u\s|_[%h oF`L0sPZZ+V`ٲeHHH@mm-ߏ"-- (((~}(++{L߄Hfٰa4bMnn.6oތ bҤI۷/VXG}`1~x_ӧOo ػw/rrr^Ka۶m3fLUZXlH{A[.R0\_]tBr;F#;;vYYY!_UVS2vN`DDD n hl^"""67a|]9b&̂L2[ӯ_?%hՅM !Ш0yL˜9sdi_./W67a$EDD-lnˆe&x_\d8EՅM)-gaGgx7o<%hՅMx4:NBEgղK4/ 0it7*s2qVL\_..ln„67a4X\(܄Bq2Ŕ6PPP Mcr1  ^ O4%I&~54(_ڣ`r1[XkPG\xUuS#9!%\%NGᙛ0ht+'rL""csJwJ 0!""667ƹTUWW.AӘ\_..ln:Iό3di_./W67t>LnK4/ R:s`2%T5KBbr1uas^CaDnIDD$ hr7\&mas&[8&$֭]1\_]trs\v br1uas"!Rzc Ś5kdi_./W~ q#<\H.67!jts>""X&D\(6 db$ulK4/ x8=Mo1gϖ]1\_]܄eFĉei_./W67!pp21Qbs20,!""bsNKfuV%hՅM;5 er2q.AӘ\_..ln)d(W>7n]1\_]ܴS#7$""lnکU0`7H\'^w867"+//Ov br1uas\/B\_./W67tƠ7¨f0uT%hՅMyMnGxb6ryI(i`yT.AӘ\_..lnڨQa2:X,3\-[&Mcr1ie1pٰa4bo6pzހqje21\_]ܴ#""ln@憈(i&QB55w\%hՅM+^7\g8ڄ_~di_./W67zInΜ9K4/ Vp>""J͍^gِ """j /܁{J%Yf$ηj%hՅ͍;t#hPv̘1Cv br1uass6Lf/Mcr1o8D^fSh\_..lnt;yQ|`ssG憋67WhRhn̆ h˺udi_./W67Ww/IEGyy4b Dǚ5kdi_./W67m;VtfeQiE)67%lK4/ Vp2q̞=[v br1uass:YfM8Qv br1uass Du(7|Ç#)) Xtiyא ՊC{J^"""?qܔf!33[liSOaA[oaO;?~<xm~_67ѵuV%hE'h͏~#\|eee`΋2dcÆ {vG x|yy9y0nCa#HINNv% br1<-߿vYYYay͘?sԄ۷;jkkVfCQ|ss%@jjxJJ &,iw7ŞonZf#i1ޟ}jJDDDKb~RIN4_.[w9-N?yx^,3Cf~)eY_./e*IX(FXb~*t:رcGs9"t:شio)t:8uTs*++E?ş޽{ʰqq+m݆Fƞx ˨DBBBs222V)S`߾}A3g̙3DDDT^ЫW^_ `„ {RX˖-CBBjkk~ 8iiiwCnpww[o7|3;\"""8s4pB:t}ŬY裏`X~=O{K/+VO>OG ""(憈-bVp"""Ls#cWqV{w:Xd d :< \.W*VPp1brss#Xᅬ#Fjk#<(T>bٲeΝ;cǎ>߿a*ܹSL&1}t ^/}٠im}9st_bJ3gIII@˗$_"C(3<#t:͍pJ0 bƌb۶mb"55UL:5C( -Z$>\^ĉbȐ!B׋۷_Ds3qDq7="%%E466*>gbʔ)~cSLXj^XhBӉ֫6oQQQ!Vի7+ ߟ"##CG^ ߾}FBzş'ѵkWѭ[7ZmnRUmm-fΜ 7>h ͛Rۄ ӉӧƐ!C"]*EE?9s?Cꫯ`Z#Zַanݺ… U>s׿F^^LJWMw'䟞իWo6fskQpbѢEx̝;~).\*UB|r=z~oU^]tA.]p}gi8ý8 G6m}݇qƵ. -Á>(4!N';Ē%KcǎœO>Eew܁}aڵ:t(")) ?|%WUu4˕+W{رc{d2EP % @ vvC U$['?~#smBw7x<nV /`/\&##!`RÆ xNf"Q*[UX,37oߴit#FFjo}tbĈLڵ罹Flj;رcŸqUʕ+NfXh0bܹ>Fjo.Kdee=z?񏢨H,YD$''2?J۶m[:7Ds#[l7xHHHbʕc-+~Yv4hX,"33Sk.[5ړN'zt~?mYݒƍ: ?nA$$$KFlhobܹbРAjLQPP \.Uc۶m /w'"""UQ"""67DDD*lnHU!"""UasCDDD憈T  """R67DDD*F)q:~ÇرcիnF_}{XhzQ,RD6n܈Sb͘4iot?9g$XRDJJJz߸lF^^.]u(b"i4h]067D._5j۷@DMln(fBؼTWWcÆ ]w%:"Uln(f<3sal6L8_dFD1Q*..dªU. V+Wȑ#%WHDQLjjjBN0j(./KQLڳgN'ƌc= @#//Jl6c71cŋG3ǏojjlC=^x7&憈bɓ'Łpma˖-W}Ν;._nfiPUU>^7 &ѿoʠ1|o`Z=vLDgn(aرfٳgׯ5=D>ln(r5<}ŤIpqLhHDQRDuVڵ ömېO?VBii)wGy%%%&H:7DDD*,EDDD憈T  """R67DDD*lnHU!"""UasCDDD憈T  """Rr‘FIENDB`docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png0100644 0000000 0000000 00000054242 13300556574 025657 0ustar000000000 0000000 PNG  IHDR7< msBIT|d pHYsaa?i IDATxwxTu{B 4)" %@$ ` +E(ફ*"J] ۊ(jE" *$ <"(XQ7* 59YIHsޯ̙{)˲,8DrrrrrrrrrGڱcԣGӣT؞={4k,eݻwWTTch*iz׍p ƍM8xLPazg*--MFfڸq:uիWy}7۷tI'ppO?͛->|i_uA38UW]@.HBk3yd:sN:I7|~IҡCԤIlٲ_([η,//OW_}7n8u%`IRjj&O aÆzu+**JSL|( zԹsgծ][ZҸqo߾ל5k:w:u}z衇/***J~aygϞ}>wݻSNю;Էo__W~~vڥoYM4QƍuUWz/wﮆ *66V-ZА!C}v .?YuQVM[lZ_/\Sf4|p}~=vG{t9nݺjٲnu<#<38CuQ֭5l0رw4EEEiѢEWÆ }?ϙ3GQQQzWճgOժUKꪫ˗<ǚ5kQFٳG'NTUV-lRvr@_}x=z,KNNښ8qեKX_|ᄋvxUV=C&MXeK,bcc X#F&L`y晖nf{<뤓N4h`qր5kXiii㱚5kfMn<bM<-˲3fXjܸj9jӦx[n%=wي."k„ օ^hy<-˲rss Xjղ d}֟'X^zU\\oݭMZ-ZRRR &X)))=99ܹu]wY}}G;-cuź;cZɖZli۷wx]vVFdq[{QFV۶mDkW\{wÇ[ڰaeY[O-˲{1X-Zn6/UN]v^x2?c˲"w޾; dըQ:}N4x<֢EM6VÆ }??u$뮻-cxq[裏,˲]vY~x޽{['N:+::ڊvywWn<k:t-˲XG{ޥKZǚ2eeY['xռysoݯ߿xK{<+**Zxq%dVV֬YݻMZqqqVaaeYyqrs3Ʒl֭Vttծ];_)γ<o/c͘1/X+Ve֩S'N:_%.XO?_6ׯoÇ}%Ou!m=z<gYeY}muݯ4Ye]y啖~m߲?fXiii{}qX7poYY"7|cժUj߾O?ǪYfٳ-c 4:|oK/Wܴnگ Ze%&&Z7l-ZX۷-+Ƴ>ŋ[߿q܍:۷W~|?רQCzt+IJIIQvko2ol=Oȩgo XJ[ڶm-Zȷ +t%–-[|;|-SNg Ȳ,͛7O5jP}[$iŊ*..STTdȎ{^z,K?Cm%7n\-+3ˢ2Le]V]w<_~Y_}vڥ{O]tQ)h4HQx<~x&MT-*g\n]zP2[!Ch׮]zb d]6lXBpWu8 Y|M]|~:,I;,ݻ5vR]On[vmϺt"~Uaa:uTiÇ5a=䓕~޲deeɲ,=8pڴimӦMo޼9`??to"tx_oUE׮]%9X8 }駟nܹ%I5k֔k׮}ӿhѢEzWn?SXO>_=(7q 0@5j]wݥݷO@}~JOO8/]wݥG}Էlyɴ2RSS%IwV_ .~?%kРAִi|;JGw!ȿ+ .Pv4{l}~롇*UUW&?{WH<߯{eY{TPP#FƄ k۶m>|xP. 2x`լYSSN;ͧ~ZyJ3dY &ط|޼yڱc.bIi&Iz7s7jH}ŋxb﫵X 0@6m?ؕ+Wos=FUslj'/XK.UݺuuW^~}͞=[ ҙg}yZr>%''뮻kjJ7o֨QԧO]q~v;O_3謳R>}O?_WÆ wtLY3IG~iر2224vXuEW]uj׮ŋo-R JN8קOOW_mjݺuziӦ=HG#g֭}G<䓾ƌ7|Sgq^{5Kjٲ駟>c]wuS~i͚5~;MEyZdYfɲ,-_wK=G視X<~gM EĖ˲+))I}}?/XO=zM4) V&M|'+}v-\P:u ;0gŊj޼y3g5j3跼_~?mp/iSo[ov믿Çk̙Gt#.RiFSL'|O?].\_U/bbbB>`7Qnڴi۷Eʽ͛%᳤|8'O>DӦMӊ+oaÆӧnߡרThN:裏ŋSzԵkW7RZQn6l V%g< ՓT}={1᫯}NYfzG#5}n*8CDlC=x%kΝڹsgh~7ok9ܔ.|۶m~G cǎٹsN=-Q{v~-ZкuVpYnԶm[ׯokDYWsN]ml~zi-֯_?-XEfYo͛5dܹrsmڴI v<JMMUƍuW.6>W|6ڽkx꾧~NlwZV15C^ܬf͚JJJ2=kYo;KDY{!֭,Y[6|p=3zwԷo_ZJu܍O픨CŇݮt`p|3,7%t]EEEˎmoY7|s繠jdI`oWnB tzN:cؔ`c<yn(p;'t #EfD>7UC-OWltRogXx\"Y(7e_ZZ\"Y(7A /4"Y(7Y(7@ rPn*zGp57"gT'LjoE,2=)99YXVT|HRLT *rSl(7UD(7@~(7D kzW#,wMPpcԨQGp57"g zmzW#,wMQp0rd̢܄jMjoEB  Neee,7rBʙ7o\"Y(7!F (7a@ |(7aB <(7aD9T#EfPnŒS6jEfPn nGp57"gB 4(7Qp>ʍaYz\"Y(76@9"##FfY,Hs5=Yo; F^pLjoEBrcCrcSn,8ƍ3=Yo; VpZnmzW#,weY! //OURRqwv@RoQ*&f'zB-7m[pMP1e#EfPn" MjoEB@N-83g4=Yo; &B9p(YoEB`N,8T&QpGqt#EfPn FfY,\~vBep@$ (P܌rPXpMjoEBqH+8#F0=Yo; "थ,7rRp"(5' ,wʍKDJ(7.Be\p2337"gܸ] N^^^X_,7/)_ .0/ ܸ4PpB${F,7rgԨQ!y^T EfPndݻwПGfY,0ܠT@ܠL.8AyT EfPnp\,8YYY~TEfPnPpyU7"gܠB)(70  PnP)QnPi*8UDfY,TI( g5"Y(7`s3U\522hѦGp57ʳk.IRlٳgO{衇}v]z饾e7xƎIQݮ:Pub'ULtM[n,:QQoHW^yrss5k,\R+;ըv2M $هh%?~7xC^ytM t=衇?Omڴ)Le[D1e˖-Gp57}ֶmܱcǀ|璤?ogIg}V]vez~RRRw˗<~ȑ['׫|&MRzz߲) IDAToF^7'x"`oByހkdee)555`}]p_ֽc&w|BonqH<$ix%"}?Cϣ{xv<}Y߯ׯ_sT*{ի߲ۧ &hwW_}Uo/ط|֬Y[k׮~Srrrss7þ}p~nZlw{LT 7|#YoN(~~bItEWjjrrr4c edd(66VڴiԤIW]tѐ!C4yd%&&jڵ?!Pl`;[l$v2'b7"gRԣG-X@,͘1wSnnu%KHjԨ+WjzGtW襗^߯^{[A)LT8yGEU ߈rwl1؝^o; QXXWT,w_K _Q;\-8(86=D GY,ƈ#J]N GxY,FZZZQpBx#,w l)8QfY,D <D x(7 ߗ|GfPn`yyy?'*?"g  K5@ @)؂8@1(8rz~ N#TEBm5*(C`只!Y(7޽{(8Qyo; Ew(8>FvvvHS1CfPn`YYY!{n NB?GfPn` Sp/,w \GPp(7p% 8ף wGfPn`&JjEBm 8Rp0? Y(7(8$p lcզGpuCnFfPn`Gނc݊"g6Νkz7;Fo; gz?n+8vm,w pn+8,ƸqLP&7;o; Ѻuk# w:7cYez;SrrrssdzXQa}k/l͝;׷jƍ{L^^о!0sAwv@RoQЁoYoN(~~́rJ]s5~맽{jժUY~+=o7m۶ئ\B[pbbky2,wۗ/RT'$$HnKjժ+BqqqjԨv2&/7v$կ_oyz$I{ x_IR߾}չsg-]Tw}fϞÇxb(8妼]Grk4m4]x7n&M/ɬqƙWpo;M $I~K~.h%[u \r$iÆ A׺uk#BYEf؂c Y,/7۷Wttm淼;9_iKe$i_>8Z5cQ"?I=z#އ;ag~]tE땚͘1CUAA6mڤ5iDuє)S4vX5lP}UNN222tJUpJ$ZJz ?W߾}3fhر\uMK,=f̘1zrJ]~3gLY(mMK}pƒ,wZ*Z<׫7xuWj !f9|8O 6=+ NX"g z6m.bM6Mk׮Uqqs K!Lj̟,wAO>Zzz +ԣ> 6O? K!͛gzW wf$$$hرz뭷/hٲeJII[onݺG KPrm6רQC))){_u](8-f֭3gN;ƪSN~I ^nԭ[7=>t`$"55f:;Kc=:覛n*`$3e\p쐿Ι3G 6Թ瞫 .@~ 8Mjvɿ|kg}F{ yO$Qra}駪Y_ Ѓ>(׫5k xLǎձcL @ܩRQN>d_)ѤI͙3GJP՜d#;KMZСCe YN? ,A&&&&XOѪUj*䨠 xLjnNqcvBY&:N:*vd*))8¨pGbb8"k!ĨM6| 6}EzB~ٳg%(8sܰizW#"gݰ<#aY,!ݡ8C11C1D*"@"D lȿl(8o; 1j(#_ EBmF e!Y(7P *mzW# v!Y(7,#W^0 EBm̛7FUCfPn    l#55FQՂCfDLYlv:u(>>^ӧOc>s9G=zᄨ.jOU E&''G^W:u… 5tP{z:uj?}t[N'ē:hzW#l!Yj"&O$ 4Ci;UVyH҆ 4m45k,\vЁ'ULtMd-7ʕ+u5-ׯݫUV؃jذaەQ ;gr_СI֭[|)STTT4Y9Q}W6=T,\ g}ٵk$~~ի'IڳgOOkΜ9Yͽ ##FU^y g})oKTT[ؿ1cƨk׮ A6w\# Σ2 g}iР$oy%eYta>|XeXEEEUgzW#(Ԏ-}pLawۗ+::Z۶m[^sǎ`}[j֬5kjժU_|׻z(;;~˗/ xȑ#,//O^W~'Mte|^l'иq|WU }>l>>Fnog ߇S>Gdz>511Q xXm^o>M0Ag?X:x,Kr<f͚x5j1yyyJNNVnnB@RQ2HGfZvz-]Tf̘{G*((К5k|OWRROrr֭u*)))‹诨‡Y"US߾}3fhر\uMK,)9<g(֭[ߌӺURo뿳DRRL+*kL@Pn&(8@pPn`"J,MjoSpY(73g߬c뿳Pn`iUZaw <  l؋!߬뿳Pn`Gp57"SpBYH$\~@lF~~\ͪJaw lcĈGp57SpY(74#U)8,GEfU7 N; AHff\ fcw l#//Ff; N; __D\v Pp&؆5=YȟS6g6FezW#•?tBmFf3 N g Qpdp) r6=Y,FVV\2?i(7y,EH9(7 rCA6RSSMjow[[ l3Ef17;揪6hzW#욿[ ]GPn喂EA$6V^mzW#"!'HGmddd߬Hߩ'RGPn`s5=Y N$Qn`qqqGp57+wZq|@89(7*(7qƙ߬H 'G lu֦Gp57+ßDz,vd*))8];thDŨ m]3̓B-7-8p (80rزe\rZVpQn`Ǐ7=YN? w3 lc̙Gp57˩GJqjnEmp(Yo󏄂݈rH(8p ,(8 l#==Ff%Faa\rSv,8n jb-8p  Br7=Yn;1b#M4FZZ\"r(5,?T!glWT|PpPFff\"@,8,F^^\"҅p?p/.\\~J샃ʠ"ED *rzGp57+.r5j\" v!g6zmzW#ȿYpY(7>8( (88mzW#ȿz[pY(7,#EWCBm̛7"K,IDATFfpTDLYlv:u(>>^ӧO?;?1Bo(==]zk'@qɓ'+))I/##8t萦O;Sjf͚ ;VԣGI㕞ƍMªu5ĝ[n8+Wk[ޯ_?ݻWV xLAAnրir)/2tRSSMjoFE ax'C(پ|:x:t8>7[p"RPp"q)^oUVDm?FfY˟y(73g,7)8rPX,7"Sp" D @? l#==FfY͟coFaa\"?Ǿ*TCpl-8C(8Bm,7+Spr1b\"?(74#Ef3 yGEfYΟcC D(8fPn`Gp57 e6Ljo)8ƥq"[pƒr@QpBr@QpBrzGp572?'t(7QF,7TР6zmzW#,Spra`6Mjoe)8Amdee,7NSprۘ7o\"?z(7(7j(7(7T#Ef= NPn`,7͊)8Gm 8FfY?b(7D N(7D Qn`W6=YoV$O)azW#߬H͟S: lcܹGp57͊)8(78#EfEz?s1n8#Ef9) 6Һuk#Ef9-eY! //OURRqn(_1Q1jyB[D d߿lܺrPn`1=Yow[6ƏozW#,7廬Cm̜9FfYn-rpڡ"ܔ qzBN.8Fzz\"ܚS QXXhzW#,7ĂnfR \~Pn$ l#??FfY?N(8ƈ#LjoE"Pn`iiiGp57"@\p(7 R3"ȿtZp(7LXp(7"Pn`Gp57"ER6LjoE)//P1T_} T T] zMjoEUgǂCm5FfY_=v+8F޽MjoEgCAa1fٲeڵԩxM>ǼKԩtꩧrJB'"MNN^:u꤅ jС{5u2ꫯjذaӧ-Z={ꦛn+Q٦Gp57"2]p" ŗ\rvޭ5kM8QO=~gժ^bbt颹s 0@ںuk9Cy)))裏LZoEQ3]:uhʕkO{ժUcmݺl߾]۶m ̨O<FfYۂ7k[o_/ ھ|:x:tmo~UtT@;:arӾ}{EGG\sǎϙgY4o\-ZА!C6;*/99FfY~YsK6;Q͛7FġzҾ}[6a͞=[?bccӾ}{}~_7n,sΝ;sP͛~ˍ$w}袋t+55U9991c222mڴI jҤ$Pjj7n+R-ҫ:_Dl=rҤIղeK9Rcƌ$}ٳ̙aÆjƌoվ}{}>ϻ HHH@zz:֬Yj1rFVV fGVVl6ƌ"\p!i3~?~Ǐ͟?_RSSM˩ֿC,_>h\4MS߫Ul65͍Aqƅ|lذARRR䯿25jGSo/$%%ȑ#EӴ>p̿_e;wnxZZKR/׋<<쳁ckk+Μ9+V~f35J'a#G7-[,_>\ W.#;y@3O>!H{9NԿ9sf!)) 2?Byyyرcjkkى]v-2=jֆspͿ!~ׂGWjYUU;wkQ0onnƛo ] 0#?w 77 .Dqq1=իW=tuFraفK7)5g('W7W8jYYY "''g@!cx"/^{'O6+ڐ`^c `/te?OO~a8q[lAzz:n7֮]x[f%oU~ZkYQQ'xvj5'Կ"tww"ߏC+H{C3gNC== ?F_SS`XlfΜUV7no~!,\͵*}ԽX/͛FJԿ NBBBt]p8tV+v*0R{{M3s]bN{A'O'\M\\QUU4^YYDdee쓚cb޽!L0cƌ15JVZ~+WĞ={x #߷oZZZ?ĤIrFHv;{XVL6*1Ry\CC`ر&% o>x\.X,2o<q}Jgg455ɹsl۶M4M˗_|!O=h&{ZǏi%GhA_v]rrr"Y)F_QQ!O?-)KqqqƠ5|>̔[oUyq\RVV& #D z!Ϲ1k͍HuuL8Q.)))RQQx۷og˖-&qqq!;wtle h&E4M -)rrrk`[nK.ƍ_=ұ1{<)..4l!֭ʨ 7kDDD!"" ) ) ) ) ) ) ) ) )h "׋G}OƏ?QFaĉ/_~w Ӊ;3i(pm)"i},X*ƽ^/|I\.?~QLID(>|0s̠q]ב?^4QbsCD1v#-- 7|skF:067DΟ?ӧO+NDC"Y6/p8G!*67Dn733Oܹs1k,|gшFD1_'vaZi&h|lԩSb ND1 7x#O8D4Ťfx^dggm[[JJJ`X={~WBuK7,YxL{D~SWW/d 7n _p߿67D3~g<Ȁɓ'q|>444\E_~%~"R(f$''믿~GbɓCƧLlXl6l؀$L4),(  z. vKGڱcǂgy. ۶m 4=D67D4]U>/=z4rssqXHF$e)"n7>S455oG}}==M6r /^اbj"2sCDDDJe)"""R """R """R """R """R """R """R """R """R """R """R """R?cݑӝ IENDB`docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png0100644 0000000 0000000 00000052476 13300556574 025465 0ustar000000000 0000000 PNG  IHDR7< msBIT|d pHYsaa?i IDATxytT$H !!,QI7+ ,ZF   lи&*h` 63;>2/ܹg1p8        8⋕k"kÆ ?1\kիmxN-N*OffԮ](L9WVΝu-w޶q^ze˖:lx;Ow1jl֭*))=]q+lxKuscn-ZX,[nJNNVÆ կ_?}w!ϱ~z]:TNo^'N޽{Cb [԰aCCK, _nt'_Wf͔}rw.Iz'> poݻ+99YIIIܹ^xᅐzwM[ƍիW/5h@GuBm۶MuGQF+}_|QݺuQG5iD_6lts9Zr=\%%%Yf?֬Y3}w˔ƍkРA{177nT\\Ə^{M;wVbb;8O?:wziӦ;v{J#w^=c:S͛kڸqc>mfsQG~>}4w\u]uQJJ w y>Hqqq6lX`5fjJuQӦMu-_Ti222L]we.bLFFFy;;ӌ1dddg6mjvoJJiٲiذZO>ر#h O<Ѵm֌5\~A3V2f7W^y3fر| / Z|>ӤIs-?&))ɴlٲR}L=o\{VZNZ6]E樣 /g;@VW_}O37pCs >|>cٶm9䓍3=z0cƌ1W_}7)))f˖-]/2(7pg&Otߋ.|>dc1&--$%%u]em۶5G}[V@ƎXVf|]wUj.]j|>;֭[gM˖-ec~sgg^wٺ'l޽ug={n4>|c~ouT1gϞ~;yʠq8 k׮'x|>3xCg^}x߰a1ƘM6:uVZ4k׮:uT̵^kX>cƌ}uM̓ 1Fmg4ibZjXv-g{/^|>ӷoîP|,Abb/$[N/ 7ܠSN9%٪]yI8q^~es1A̔$ѧOj+R7N͛7,_}QIҴiӂ>ЁO?]t뭷V߿ ѹsgIҦM$IuՌ3O|[nBף>t &~9sfl'NTBBB[oU-[ԼygϞUVAZjϗ;I3gvޭ{G{lྦྷv|/8=裊,tw묳ΪK.Q:u]wuںu}˗k˖-k%쥗^Rt7=.S׮]5o<رڳߖ¢EAoԠAIݻ%Igggf߾}A|:e}9vg(>>>-^$I!X&[ze?ח_~ 6hժUcήiӦjٲe\m۶g}]v^VZ:ssjÆ =U@6mBg}&I:3C۵kW!իլY35n8_?믿^ƍӜ9st%Hf͚M+ڳgO®]o>^Z]vь6 W|o$m۶M[o魷:W%%%_n6-[LTvm3_]7֭[uؾ}|>ׯr[||=X-z-++56|3&'~Pǎ;dw '<71FK{챊 ]V"~V =GN&Me~goUxҥ-Z8H|޼ymaڵ0aBB(W$WiiiϾ}o>%%%H^x>_n-"%''͛7VߨQ*?oU;>c]}*))̙3j۶mzu;w_~ ZW>2fCn+oYyիro_bulEzm۶Mo,YB]wuA3Iʶ.J rDi&w^=Z_$I~G 6Lwu{ >sI5;GMygcرI,??_JKKkUe!77WUբEmk֬ݗ_~rǮ]S;v :P^^9昐SN`qSNw}!uAm۶߶m[uN߾}uGhѢE;wԿڵSBBBʃ/у>[VuQnԤ,T9眣-[jԩOn4iy}駒~'~6m PсSf}s\{?88WڿGo*|pvv=[EEE2dHk=:.zWg]֯_?=c[l25nXWֻᆱ=zo[HvkԩSg3?Pݻw7K,QFFw/RN>d՞@͛7ׄ 4fviٳ5o<խ[WEEEA߀* 7ܠy饗^իխ[7ּyԲe@yԤI͙3G۷oW5k֨CAE2@Q:#2<;Ђ t~Uvo寞ئML ̲e*/lڷoo֭kڶmkN k3qqq!?e˟x}Bsӱcǐ>}zc|k5wSiݺ>|xL1fO>3u5:u2=)--5zIJJ21f/R~1cΙxIׯovjf̘He&...\9M{=sYg #<\|fɒ%Ǜs=7p-Zc93<ԭ[פ &:L\\Oͅ^hM͝wYeիWHf孃1+sꩧ:u꘦M<hڵkg̱kru֙޽{ 뛞={/\yA9@v2 40lݺu]UV&!!4k\uUfʕP>cb2}."[NK. :Ν};_ gь3\ݺuUTTĈʹuVٳoK7N<>917 JOOWaaavY{コk裏 /O?kFƍP}K,uBM>] 6ԩji:bܬZJr/Wx7jݺuݻw>}hÆ ~*#;/hB&LPϞ=5vXORZZ6oެ'|RGqDbULPܢE mذAM4 _J =hO됳|PIJJ҇~-^XթS'92p 勉rsQG騣΂yV%U$Xo}.]I5nX={챨&1TUU۸Sw=7UUvO=^{۲el@?>Bse);냾QPv qCeo>cVp\YnRSSu'jܹAg}WնmYem۶M3fէO͛7Ev]yf~,ڢ.8w6~FNoESVlv-$K=5°f֭ޖ? b#IMsK!IWwM\W^)sZƩ:aއam /=]oኍ$R[=sa}<xb#xEvT4kpjJ 8Fvv<"?nz饗ok9s~m 0 3znV#xEv}oNMlPm۶Iׯl}#< 6K. ,4bĈM %c^/7Ƙi߾}ٳV^g}Vڵ*))I?x94h@chT{z{S%Is!QS>S>pkΝ ,=zN͛7+!!]ϝ;W}o /0g7߬O>D:u zLAA222Ȯʵi&1bEw6E͚7cxuRt .5\,iQBBf裏V^ԱcG]?~ڶm?X>~H3]oGVEPlKIRff͛JzRnn&OS~~v7|STV--[L c=/\3f}ݧW_}'&>>8_$cb ]o_U Q\\?@ë{l]X7|,GQ8 $ %(6(Cc|NDE5SbCBc 2Fvc m{O#ȿzQ "ȿy s8xC *BcL6FvDؐPnG4+=6.\~7\~܋/w1za{O#j?Ŧf݅r1b@ (7p ߮O w1rssmioWU؄ۿPng϶=]Uɟb~lBB*FA*r1bTe{O#?&݅r v]ʟblBcFv?&z݅rDrCj&f[oN:)))I)))8qby7Թsg%&&YfU\\?oŊG4,l&//O~_iiiZ` {G=!+Poj̘1z?!*rrrlioWNN"wc!*rE_~G}X6f=UNcO?]f ,'OjJLL zLAA222ȮU\\wA];~-%XoO$fZlzO>ڱc/_+Woխnـ]oOG]_nM6ASSS%I֭ yʕ+%Iu_D5lPvv( ?Ǘm۶Iׯ<99Y}$K:t7;VSNՠA"<1T Ǘ ]IR޽ȑ#5n8͙3G_uDfE͌9FuphbwǗ H^v\~A/"IҪU>'jyG44i8)6./7ZR||֯_ۇhjԨQc*8H T$kY"{\kFYYYɓi͚5JMMG$M0A#FQG^z)//O999>|xHH$)33SW_}^z)77W'Oֈ#$Iڵ|c=Zl.2M>]&L,vTDGNeͯ[J(6KL|, |,ekf{ "Ȩz_M3;Lb{O#GQo W1"16o Œ(7F> R!ȿjRl.w 7DLMؐ].|7|@uQP}|b8b8qUo]_5.6o 1d#xW^$ؐ].8Fvv<+'RE].8R+cl.w Trrǘ6m<bCvPnG4=6o _ _p >/@PlFPlGPlw1~<.6^6w cذaG4/oH ]j oFSOUfff.֣G#xWwBS}ͷ~J͚5 S@X9TӦMV¾f޽~Jx… mi^߉K;K? /P?>c>}p$\"77Xl$T.aXkUZZ,Y'J=\u]ݻwK̞=Zl$od.as#F7O?zK]to]s_*b ¾f?yZҥt颻[w֠ApX[¾fݺu>}z'$$(---/ D'&77W]v /P K%lin?%.a/7?ڴioSRRp jb#/XCcnO:Jgq9%$88_min?֊cKСC5vX}zꩧsN%''_~jܸq_b^a/7Ǐ$H4xp$H/ܔ'99Y7tZnŋG%VXa{Ocz%fɇR~~~_.c{OcH,ZHK.UIII|p$\b֬YGX F݂%#\ueiŊTbbN;4 :4/ TlWؿ-5|pII'N:IzJKK?cQ|||_PlND-3(%%E3}c-_-))5|pm6ң1bM_nM6ASSS%I֭;c'L});;[ƘΉ[b<,?D`f۶m-ONN$m߾}zG5}tծ];C",rrrl9BnDۿ]./7q ]]viРA;ԩSH05khy۷y̼yW_^z]j׮˗>G^z鐯w饗tE . ;#CjڴiA UXX|ܸq4iRвM6kڵA˟z)92hYqq~gŹ垐o߾Tl^:S oɮ=>.}ѧ^߃`==\k۶mէO)#m?|ܹSyyyeGԩSyf%$tM|zg =@WzzzdWoEpH:~ύc?X\sɓuw+!!AEEE裏OVzzz'##CSzRl «b?Sl,b%&Mfffࣦ^z)77W'Oֈ#$Iڵ|C> ׼ys#VeشO=bcۿ].1T4܊8g?P=^D\b(7pN!.w cԨQGp!.w cʔ)Gy5("](7p Y35=Ɔ"](7 p0r8 1*bCv].8Fqqo(_ؐ]o _ _@,(nPl Plb8Faa-ņ"](7p!Cdž"](7pl#8R>".w o16oEBz(7Ql(7piӦl"w1 l`=6oE~`E".‡rXFQl (7p o{rZZNCvPnÆ =B8H߉.w GG ';;EBȩ܄rD rXp"&o k{b#7XAvPng϶=BJܙ,!](7@R7@{(7@Ql. #++5 2݅rLj3rb?XGvPn=Bzb;7 ](7@ PnPn+V=BZnCvPn999G4)w#݅rǘ5k*ōF݊"w1mP!)6w3݅rT   cȑG(WS ݅rh޼BxHK.w1 |bD=7@9(6(7A(6(7pk {EBc5{H:݅rǘ2ez݅rǰUL~|.݅rOPnYp' cҤIQ{-MhPo Q\\~7M?Gvppop(6 xr(,,Rl*'Rr.w cȐ!aNME"TEBcdgg(6UQ5o oQlo EvPn:6 \b1MVSljf.w ڏ\MG͑].\~7\~!vQl vq (7Y@y(7IPn~RDFeGd].8ưa*&r*?""w1zq)6UQ,݅r@TGTpe)/DEBcNGt].8ٳb}#]bܼ[ԩ'%%%zծ];իWOڵ<={DibTP1Qn h{C1~|A 2D?hҤI曣89bZ+==]/#سg&N;Su O?g}V9991b$)33S4j(M4I5JB@88~ݻl2;hy>}c-_<1EEECNݺukI7|Q-f]חbcQVV<"wq|oTRR6m-OMM$[.1)))2eJ̔?j׮\l͙u.vMtpV.wǗm۶Iׯ<99Y}J=ϼy4c :T 4?!SlG4s*^W_}U^{uVᷬ=cǗ,EEEA~h/c=~믿#8"2J(6Hq|iժ~eo߾ct뭷ꮻW_7xCޥ^*ӥKSs;!,KСC5mڴe*,, Z>n8M4)h٦MvڠO=FX~_+VZ[q}uzu׈bx4V|~ߋͼn{Gܱebm=VXbqgb=x_۶m>}ό1Ro$ϋCWlں|B~2/f~iv)oٳ<"{"繹{ukQVV4ydIDAT(!!AEEEZfRSSuGkʕ4iN?t]}裏/---p@2(ʢYf.w(77oƍ^ziӦS.]iҥ:s^VclߚbcQe>E䐿].>c*:G(##CJOO=p0P"(6h bZl>Ev].DDu4o1c駟֏?:uD,"{% +p;w4j2'OZ'g>Ǭ[|>ꫯ-3g|fӦM!ټy91F??ş&M͛7;W?|ܹSyyyeGԩSyf%$~lԪU+~A_۷V^}Ȗemٲ%+sRt .5\,iQBBf裏$RFԳgO-ZHs՜9s:D_L칑=rܸqꫯԴiS :Twq$wӧk [߰}#8 8м{5qqqIݩ:k&>> 2,]L2ԯ_?Cu6f„ EPy7n4m۶5qqqfٲeo8=Qnza8㌠eG6fΝ>M6o߾AkRSS#6[U5BrnGy|>SXXyݦ:+WDsSn-[OVZ_5MuM-;3@54l4j| M8]T*`ɿH7|~֭[K1UTNu/SRRjj۶mGurJ}[vmZ~#:Tw/** \LFuֈVV-ܢ,?\○/7Ѿ8U'M2%Pf̟?_ky.Zu/3a۷O^ N+W$թSG_~հaCvmڽ{wvnK/~[۷o̙3okmZh 6hɪ[noLį&}Uq Wӌ3ۻӐ6 LQF!JL0P4V6# !ڰE(3 ,(?DmqHP(H3~;t`>y>7皳`HipUȲ I\[)OO .. HMMEcc#222pu=ltuua˖-ccĩS\TvgO6CO8'h2zYRRL*R$&&ĉ sUi%l]v!++ `6122g"33{/IIm6!//˖-,˸x"p5WKSTqro{iXgfTTT`0PRt!! A h Qo[nu߼y30(yy9*++QXXdDGG#-- 7o\__l71cIH?XiɓC?~1>}x1OT@IKKKoooHI 2^| wNIZ{<AI;::k׮u7LvWJ5Y_͇OOOL&:Nsb9K.ŢE\Z(?ƍ8y$F!%@ssث !!! Ess5%7Baaxyy9 """\Z(鿿?8hj VV">>^4E߅ᆈ4ᆈ4ᆈ4ᆈ4ᆈ4ᆈ4ᆈ4ᆈ4ᆈ4e "͆;wo߾ł  Ǐj*dffbnԄϖ""U{݋Rōl6۷VucD&<,EDVSSv$ III+n߾҈HnHdYF`` ̙^?۷oS] Voo/^~ȟ Dwb!"ժ˗/>bcc{n7TGDjpCD%2=3ؾ};6mڄG4"R1^ ND%2 rssvFk֬qsDFTippfBdd$V!iH``2lWWӡ둔"OpH3gΌ-g8ptց܃{nHp9TUU!66˷d23g{ss3p-|Fֆnlذe@D=7DJ,C$DDDkyՊ(`^BFFϟZDEEMjD 7D:vuuu TUUaݺuNッ$ ǏGNNhza!"x=6n܈ ׯGYYov:$IϞ=Ȉ"uj^0իWF#qu̞=R3ѴgZa6;Kkiiq1GjE~~X!"a!iWWTvpBݻw0 SY"M!"iKe<|Xx1F梮sEbb؜Ըj"r5熈4HSnHSnHSnHSnHSnHSnHSnHSnHSnHSnHSnHSYIB; IENDB`docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png0100644 0000000 0000000 00000077200 13300556574 025214 0ustar000000000 0000000 PNG  IHDR7< msBIT|d pHYsaa?i IDATxy|T?ϽdNBaQ@B,jզE,j( XQ*Z[ԥb."*?Po@!@d_g&31΅a;w9s'7s=c1B!/wB!}B! 7BQ*n!*TB!DU!BPqC!UB!B !BT:6l3LHNNƍ7ވ|Ck׮.>߹V+|Idffl6cĈ_CRRf3FW^y.W\.꫰Z^ <ٳg !O+wiӦaڴi^mMMM֭[;wɓ 8-Y뵟yq[n]q\y啌yB]"AVE~~>vt롇ɓ'/#..Nj_nz={1Mⷿ-cx뭷 `˖-+W">>/ba.!Dᨸ!AI!<<Ν;1k,#$$'NĖ-[:}ݻwGtt4"##1ydl۶4cڵHOObW\>97wy'xGSS/^xL&;{8s $%%b`ԩطofΜt9x nf 0aaa;v,6lՇy\q>zr/Kmiiiꪫ׿ … 0qNckpB~/ RDuzsYoaDZT8 q{gیyϟϖ/_Βq쭷r3ڏ98czkeeec^;v,8}]Ʊzj&]{c9sFɓ?nƎˌF#KJJb]|=Kf4hdr [b:t(8=SR?W\s'7/Ԗbcclf{/[p!裏u]xg^Q[[˴Z-k $1ƨ!{c\.m7ߔ?q3 .cRngSLa>|1.XBB+--ֲDQQ\\8cSNyn߾}8xN7ny^aРAh42~eeeL3ۻ97gyƫ| }{xA:rܼy38qrI,&&0;[@t:VXX(}7q>m׮]86w\{a<ϳ;vxO}#""XBBka}geUUU /nxg^}V+ eƍj6mX,1vaxЭDν 6m‰'0o<0 ^ _~%<\[[HL0?].}$%%nyd2Gwtt8f}݇cǎ_|ܹ9s&?J}{]J?i$ر¨Q!C r k׮[\\ (**u]'gffzX,33ܹV&M¤IЀ";v G{{;DQyG?яϏ_ۓsv|R477ꫯ?%%)))=~3gb̙=FW L&fϞwyǏQ]]ݻwo~~<!T՚6mz)իquwAdd$?yojjkqhhhD ?C^^lٲN477w|ss3BBB^z%}رc\.&Ocbӓ] 11|;ܹe܅Q3tI?χ:~kllă> 8ddd`ڴi8tPw%%%yrxB=pkuuutz|bb"yN?o;#͛7cŊؼy3c^k)s*nH0ؼy3Fk.w-=)ʐky|87`0|sVWwYy>p/uw~QQQq^o9-Z;FFNl>mB%&&^ OA)8U;A^:+.t3f !!~!VXM6!&&^{O\$ăn'A%..N]y+++#<";rHt 0LسgEǙ^%QXXu98͆nCrr啒>|3f̀Vu<YYY0  s+#}Ό='N@MMsSOrNo455?Ǐ~#k0aT?~V~?^Cb>555ȑ#=Fw{$Z|;8v6իW3[sO5kyٳǘfΜ8cR۝w8cOM8wBl{{;h4l^jJ'%%I+::H%%%,66 ۷o,++JZw<{`}q^DQd/{}Tjj* &3枀=zhf0ةSc5@j8c111쮻b˗/gÆ !Aun]wŖ.]l֭R?lժUl׮]ɧ(lҤI86k,裏QF1#<շ1N͛~a6dq[reZ`8J뾜ɓ˥unbcc;]7 c͞=qdžʖ.]/^,%s&Z0ܹsrVX/^̒Yff&bfY%s~EDDvw pf4ٿ/"""hd~;[x1KHH`_x/n؃>ƎBBBlf>q;v0f37o{Yff&yޫ`yWq,..=lѢE,22M:>|L@g.d쫯;ÆL&Zӄ׆ 7uٌ3Xxx8 aFb/sر͘11&OQ(--eDZ_=mmm4L/g_ֆ8u;Xrr2 c999lͽ1'L :t^__|Az=KMMe{/;qWfg^~BQŋ _~9,YG}f¯k5 K.;DB& FnR$&&b׮]=Yǣ }K+ߏD͛ֆbʕXr5]@7ҊY6=fA+B1^6m\.W?Gtٳ1{l}?B* .K]䜿 !B# Fn.g2ˈ{;\=B!>{=U7YYYKg"{f%-B!G\|, Y&336裏cԄw}Æ gs-[#hQE0`sބg uE4Ff (Haa!5ʿ( ;EjN]h4L}G"gwiP]u8#102Ii0C|v P",Ecݺur(Kh֡ t:FQ*lwKsr(+Pb.4QPӭ!95 )=Em^͜ܛ08cH4rCcɒ%r(+/4d^ÁC) h y_((V(q! rWBPˋ/@ɿStVf[#\=4ќ3Aس+QqC!(\`G-?Z^HS M9emߨ!B洢Z]O]k 4 L#|Dn*wA//ʿ;Zq 'z]&$b`d&=B7n~3ZyQ%G e҄dd^Y(S,L:s@lc";;3fBQ]{wz})'atn_#(s ZS_WK#7B--*l4"z{v~$&y2F׸ 7B}_ztHignEŇH 0E#=S6"BHt k?##6pȝ3ux441x Be6D Q BPˋ/̿Pve%ht&׎܀1sh\ QCe:5J@BZQEW_hաt:f ^ >o5z!5ʿ(;EδByC l^ $$poXwAi΍(g1!BCtZf{WY:" W (*8o-wȪE#7B%p8r %h7S0c (hok*lޤM-Fnbݻ'O;E_^k-Z;{}L> QX uF  QVQ*P<(-Sas!f.#ʿ(w6Էעw& ( BEL?(| wA//ʿzӆzkj@9zR#fY_^yuӊzk ^g9PqC!thGVg{ӾOA !NwQcsZ{u (Db9Bt+8Q+VBPˋGKK7kV#g GuzgHS44"O(ҥK!QE%^FĩV޳k 95Iw6T4*n! >Gz<:{$rNVv**8(>|ɩI9 "Mш0ESQ h !`uds9J+JGl< hZL^h<]u1x h' IDATK6r(ӊM8\ 0RaSz 1LDcsT*nb<#r(=ӆ h*ZFsd!gv`Z5!5ʿ(}CTs%*Jɪ IܻqߝAWJsnDQ JkܤЄbt+(_:ЁzkMts_,*8oh2z-k !8rG{,l u`*Z(hok*lD;v0! Z4{՟xDbaFa2p(䣬z20.{<6Fnb #3rɅ |W;!zjCjyS`˶g=;FLl SCmQgӘ)t( '}=:{$rNVv**8(>|D*n: -`ҙ%`0RB洢VkMUaҙ~+l !~:P~m8d V ^O 8NIׯ;FZ/i=ƣhst:zv xmvQo[a+*nbBPKmwDԵAy} ^%$pƽwTx܈{mAi~WmvtY(ƺu!Q奖3lo@}{ .:" W (*8o-%f@='n4rC!1joFEQԴUwY@rjN"=E96g 97 !Kbu :[/zs0Eb?8PRiAi= rI!rQ:;jOhUPC8-qk+ BE tY(Fnn!5ʿ)Nс-'qX ΂Ԉ V K*?RIh(ƒ%K!Q]h5ߠ1"&d̺ůU'G QZ_^JοdG.걿!2p5J?pTBc -Mo?%؟46"YD>TBCug;zρC)QXhx"${Tغu+~FТK--A~A+ʤ[SSS9lbczaHD9E翺и!Q7BPߵw'־'457~یoчb`d&CW!i&Cjy3GKKeGcF_#&6ɩI^&Xa[?.TBH/ȓ=9S'@JW>"7:1xC(!Jˏpx jș:P]u^DEF! hBD`GM[5l=&TxhZL&CDG ۺI3(Ƃ !Q՟\δVx1؜6;{A`&)h :%8ZhPyQg̅k*Jlo(b<̹E0(-cR*:Յ.KŸ[!Q՗,W~NYb@Q!|[tn)=bR::ՅFn!DE:;+pS0e$(hok*lބC=^BB .u5h7ؗSfbTf6)/*Je`\x*lH@(޽{1ydZy]lch5Z{bC%޽nMdepUuRD1֬Y#wA/Ơ5"9<aij.4rC>;Fׅ!t펶j8 b,fA翺PqCl6BP˫7]"5hؗS4̱QW, cǎbAZZ{n\.YC Ahh( l޼OBHqϫGyCI >2O JQ۷1b>̛7?8}.o~+Wb޼yO[n[lcri6o<5$!)| "$Dyy1f۸+Vs=1o&n6<䓘>}:`Xn'b Cjy؁8\ND ̄Yҟa⦣_}nF9s栭 {VzEGG5 W5bbc #]~"'/nМmۆ;v`ǎ>}:`ʔ)%K_#FɅ*..С\(Mș:+10=w硨$&!hs4d,:E322hp1vÇsIڧN 됛/''[n_ 77ׯj+,,Dnn.WZޫ(..j3j"77{j߸q#,X7߬q]w}GQP9??ގLV#6n؄wqi>s b b<ו}yxǴiT>x7>_0g׸T麏̘16 }Q8u  u~!nflc8pƎuLaa!QPP1c";dD_]Ϡވ^}mXp|HLN"}o@q>9s&Ν `߾}xf DLL fϞ+~;~idee!??Se?,Z:Pvt:f jRa#6]=mp/t/KUW]-[ȑ#={66n܈_|Q󩠠'NZ_}ϟ^z 7p}]<#߻!?tvl*Ǚ*x΃ ܅yEwAiR@!}DԷעV'+ɦOǝ-g %Q ΟGchhAEñ. HNM铥Ǣ(bUɽ ?_]b VU8Ejڪ{eEͺ Ł|UB .&20.{<6~F翺ee)B`̅k`ϩ!.$!nLn)BQ" 5m= D[s4F !\ %=5̈ IAkCd&9N_1dGECI >4 *l( 7D1.\(wA=ӆʦ2ԴU\ 7F"-j0Œ//ʿe)W;F6Ǿq!0}( 7D1.5yQ׬iFmiL/6@)#5ˋ.TByBδcPC8b-jt~TB䡼 zɉHJCrjb4zą$¢Sޢ D1֯_/wA-kN}O&5u5hjnBM] ɦ(*8q8D002O `˿PՅrԂ)GKKeGcFK=;*̺ )JDW,EcݺurԂ)yףG"ghZPTpP|Sma=a'% BNYE)Vӈ0E#=r0B VB7CV{h(6Z&_d Ik+!e)BHPp] }۶f ^  KKC#7D1rss!514ZplNMBR<@EߝAp/s#"`PZTk_]h(ƒ%K!1gZ`l^CGdEmaMFl6ijFX՘@BW!qWBPSSch֢ԧ$L>Iz,"ڽ 97ap +.4rCQӭU褨9Űp!UJ+J~-l!}B*xFkꭵX}ÍQ >ZˇORD1n*wA-owPXzk +ltS݁5 7D16n(wA-b.ԵAeS):D{}#L .L _M(BblڴIZtkbG&3)hWʿPqC (.B}{ mu=4&e0lN+NSttO1 >4 FB7̭i7(s,跟(Ƃ !)5VG;7뱰1hH@e@@6J ŠB奴ڶ3h7ГC5%ʿPqC[o;;p m?ք$F?E`DW*n!9ZZ }F0jMM^kGxjݻW?īh4XkIڳkskGJ U6tˋ.TXf!5? OztH,Zv.{ݍ#tf DY_]!BPg+GlrNVBj3u4S]uq! HO^њs//ʿPqCld;X;(6Z& DSjGkE翼(B !~C 5mRAc!W#6Gk!Fn!Ntn=3mU`p!!).ؿ; p6wAE b&.*nbXBZhchhچȒ.*87֮[׾CsKLJD翼(B QTCj}dsJɩI2}XE%97apƐ>I_]8;%(,,Dvv6 0f!$9n9 `_B^Ǡ T !?>iB1!0ф3m`e? >4 Cb0~ ! %Z;gY F!(..;v)9ݓ/l8X )< N//ʿPqCG;v1L>;i\:e . ]"+BP;E[N.XfD\Hx/:EW*nbЭW>W&" 7^sOFkGSL: C!E !WlN+N5cHS ͭ!Ȇ.x!ukpFB/ ŰZJWgwn= IÆa^_ m~ofia0!I5ҤaBš!ܓ[=L֚L !C !Ahi +u:b1d`$&uy\9Buuu;kNlfd6L٣:ć3;\գ_^ub,\PF{kIسk‘IM?_^uW;_'}=:{$rN3 kߝCG:0q!3FnP_^utWpLV#C"gi3H̤_]!$ȸ\"l6dV=ja2}= E 0TDB*ʠѺ۳  BH(A:ZPT ؁x(ؿ; > ؿ;(erP_^u(Faa!c ugpRZmx,CxczlyMi21x 7D1֭['w#DTG֫=95 SO:O1iZ\٭ބCk_^u[ QSǻ{t($'qT^Ǡ O !$`QqC ؛p ]f@2p !B !*Cmi4pc EQ%/Q\ChKn  EJD:"L> _^yQՅFnb,YDiEuK%e֌İh5Nˋ//ʿPqCꫯ;dk@m[uk"Q u=XK_^u←b.ԴUeBncd"/*n @Nс .hyRaԙ!ȏ&غu! 6f#3.ˋ//ʿPqCcƍrh14Zp"iARx4 RE_]QM6b gZePCE}ʿ( 7(CJ8Ď.4z$ 512BQ&*nQVn=!>?FF!E ! C֚nEem!4(Ƃ ADS-6;:aG&TxhZL&,W*ʿ(|W̙6ٳ瘢"cҥ^˖-ñcǨQ`ڛQ\=c!WA`z]ݝ`ȿQEW7eeep82dW{ff&ѣ>F#n% GJ IDATfDEEaٲezBSխ'EwI ؿ;{PZlBHRe&@XXW{hh(ZٳqmaŊ8pVZ~$~C]i4ꥶ#wC0&#l6\}e{?ry} {7ވ+b Z 7oFIIIJ.͊+Ϲ [Oz6)'IEQD{[Wa3'& Oj ˋ. z{{?gTnjkc>RSSOy9;}~t('‘EyEN:=e`\x6h(Gn222hp1vÇ3x`_t:&wu!77_NNn/@nntGaa!rssQWWվj*<^mEqqWgYXVϰqN7> U{):q 6g;v< ]soGOĦw?)l><v^Xt*?/RC?7x5++ syKű(3`ٰo>GśoSN`0xoooG||$֬YSN!::Bdggcƌ7DTCKyCju$`,O<fΜsbطo^|EY8|0233łgy="##1{l۷k֬ MbL> G5l!$g!hiWah` לسk"%b6 !~z}<#h]Xx>Yv7Fg+1Dd|?E_]!QXXU^Q=b3uZZVڠYϟ'( 7D1֭[ewFQ*lg삄 %@ oMR x Q[HE_% ЪPH $E " AC3erg~?9sg~%2?}}0r̵M=.\il&݌~VS g2/W PԘ5kVX^UBWK.8NNV}30${ |S0Z4onML\uUIaƍ5:8ދFe"7k k‘?ERG7ߌ^ziDmpDQx'$[ .IƆH1Ejj/K&N''HchMFT66DD !j 6h:N'w1ll.U/W͢E-Ž;uXroI(,,lkcڴik!+<8y+8u[LVv &#Wi-ޡU./W\;vСC1rHl6߂5iҤV=+8yݵ[V\36im_.9s&~aر˖-C]]pm{ZWxqWs]l C:,&K+#"hysc@URP]]UVfWoI:!6hccFj`1Y#\E#)) ӧOG~{E-)&d4#52X؄)\_-77K. kEYYoIXdIq!>gU& )鰚9=ObjѼyyf8o )իW !MIT;h0!C:lf{S^)r\_-77'OĔ)SСC?ҥKk.x^;w-I>? !po49.))\_-77s?27JJJx\}՘9soI Bli.v)[/ͯ{_~9.r̞=^gΜA׮]a2n}PXX3g"..*O/1c`ؼy3S# &\_AbjiSsӿL8˖-î]bƍx衇0db…ѡC >eee᪝Wxqp{^|:uj$J \_.毖6ڰa)))p 7hXO?Eqq1QZZj 7'p%$X,Xʨ9_./WKqs1͆#F`Ĉxrٮ)T}j%ඬpE-`r1Z4_, zη(S뼀Nf5=(K/(0XmF&h0E*""қ77bjasCͪwH}֬YPh\_.67 i0*#"" 08^W$] %.U TqԺ.!3#\Q밹!?]]EmqxtMR0\_-1ܼ(|Vxbܹ3l_ʪx=8u~ 0Gra@aԠ8NSL=܃Ԙϒ"gcv8T~W/5ocWg{'q1ņon> Ӊp.\ ,jѳ`;k~6LHHLDq8s#sԫ$"C1\_-QTVV}ƓUUUOOcʕZ-2(Wg_.Lq'k\}eӶ#_%KD}(0/%ꛛwy'> <8\Ŵ#Gh8b3jWf959Ցմzꈽcr1ZIIiXmg?4o<!0olذy~!9s&VX3V^^e˖aΜ9>c;W\XXT'NlVKé;f/ѩS'}ˑ2deeE^Į]4̹i<5vmv* 68w:YfDDp|}n͛c[oE~~>JKKtR,Y6 ػw/.]+{D ]4.-q{\30U޵8;|!C#Vל9sSOE 77TQXXKa5 PA^bk<.\il&݌~DXE"A/\8^yNb8}kjC ƆIHB93i=dKA^*#""NL"mT9*p`DEDDj`s59+&K+wY_./W ,<7n1Z!͝;Wv br1ہ*nՒ˗.Aט\_.毖V:stxK"I&8^)Fq5j:kn؝!""尹QWxq6Qqa3#\QQXe9j$""E\ðcgä]e]{a^yC*1ۇ.C\_._F䎽gǃ 5| y7_F 1w\%#7xW\3:`8}k\.ج63Cꪱ˗.Aט\_.67Իi=ֳikBwt",x)\_./W OKIR 8np}""`s#Iߘq!""R s-0 *K5/͍w=TmmSu_./WA!d ˑ2dee;5{F5)MDDM#7oviM !^)e3a2$TDDD67y 8PQQ!]cr1ZDX}21L:Uv br1067-X@v br1@F ,&jKR1\_-ln"q vDOIr67 VX!]cr1ZDPȍ`dPM)//]1\_-ln"+wX,Ӭ2瞓]1\_-ln"ᮇ2^<%EDD-67DDD&B.憈HSln"@Gn8 #h']cr1Z.Q_f͒]1\_-ln" ))η5n8%&, H}ln" ЕRfYB5DDDjcsfn.oc>tbѢE8p 1p@t{O<Nw}QPP3fDnXfeKL\c!++ \.,^?vϝ;^xK,<̝;ܹs 86DDDGnlقng|„ Aqq>՘1c8|p 7|@ek_./WK77D} 8x>X|yS3護ނj{p 4lf{g*Mee% 99g<)) PUUժYn^{5̜9)))L24itbjoY!DۍƖ?ڵkqk*+8=xoeW77GY}n(3QPPcǐ}/[ sE^^JJJ| N8qb?#//>Z}?J|Xh;?GX%%%J| 6cs:uy衇`<@ޣLeeeW!UqM^[U7xtbh+1qyaر[R,]K,fCuu5݋}K.صk r ozMq2OI5oղK5/%&\[㑚Ka̘1Xr%Lz sN`͛1jԨ2CӚӆ>_./WA.G҉rdgg YYYz3էPYxzrM)""&Z~6 ű.mLF3,\EDDgln4zps̖]<"bjas@w%83$--Mv br1X\,%gϖ]1\_-ln4xL#$""67B>@sg|""as!WsֹY_./W  _,mZcܹK5/͍N&6Eش|r%F#B7ﳙ0M*=S./͍F\^'<^8ד""",67 tę9߆(h$|^)zK5/͍F57fl "EbBߘEڳơsHHGr/J$""RN{ p=)""Fη!""467 FXM6 Į %榝 o<2hԩK5/M;9u4T[-X@v br1i'N&֎V)4_./W v `pL"""ܴ"`sc7a40Z""" N^)ЬXBv br1iF[K5/M;[ e瞓]1\_-ln!Б ,"""܄qu\lnBTmL;,//Ov br1 Qd0bJF f͒]1\_-lnBlL;lq.Aט\_.67!v 8&yQbs@͍v ըcÆ K5/M !P2p%..K5/m+~o~k֬]1\_-lnڈ!""nlnڈeE767m @߸dhP]M.nqηF~~tbjasMxr1\_-lnڀMxM4Iv br1i@mF3Fj(67z8, \,(i%^~%%%K5/M+Xrd%榕ͷ1qLͬ^Zv br1i!woxbjas zq^NDD}ܴoCDD;ܴ9s.Aט\_.67-Xi)--Mv br1۹.))͞=[v br1iA۰!""JlnZE+67-th2o>%n.o<Β ͝;Wv br1i/˗.Aט\_.67"bbr1iF2Ml!""`sWxQpL""h&UXf$.Aט\_.67A}W[8s / ?>K/NҬwKzաiK/SNf퓑G>O8w%>}OQP=z@=4{?-رcq뭷"??Xt),Y͆jݻ}E.]Ν;oo7|oF:\"""8r4=rؿ?RSS1sLw}3f V\)S4/bҥ8~8222;(b!"""j-tXUN'-Z"11?#ۍ!C 777-7n܈!C >>z½ދTzdIIIkIsqt[nm캫( "L">1o}?Qddd .^Մ*L36l0?+׿N:Ν; bs"8 %j̘1yyy>а*N(7r:2e  0 ܥ*)wڅ#G`>w}7KLJf_]]ݴaΝ;o [MW'_䟞˗753z-XVעBɿ… x`kB׮]ݎ爏GNpwph?ePUU_|&OUӻwo:tK.E\\\7&n^U|iuk{mvm0j~)~ijHٳgǏw܁9sO>qSBꩧp!p Mcwux0U;cǎ~V77-SUɗY]wq6]C___;wt!N'ছn¢EG? e+7ވݻw^Q\\'x xgU.A_忥#8jo< n6=.,Kx UT(ϛ7B̛7nnBx^x<Pos'0P넒;#M6aժU6mFGyO=?a޽/\ǴUȀdAi<ٖ}(Pٳgwnlܸ)CJ֭ ՊblݺjDjWA(75x~M=Z3o^{Qy8JWncԨQXnڵkѱcG 2o}.ÛoπЫW֬PGy=~MBwΝ;~);wQBHHHU|yX,䄵fFFh?\vea [z***FQr-o[MRUU%m&Ξ=۴ʕ+`owF o1_A 2Dl߾]l۶QUU%ĜP~/6zhquEd3<# 9sشiXpZbΜ9>Fjk.Kdeenݺ/H,ZH$&&_2?Jۼy}nF!֯_/JaDFFxg5+ /~ .233k鲕іW a4`y/xv%_~Y\qf>}ŋGle5j1gѯ_?/233EAAp\2W͛/W'"""(?熈 ) ) ) ) ) ) ) ) ),"@N'~_8|0z+P__'O⪫… qK ז"fL4 ֭ƝN'nv_z-J"&<-EDQ0rHqՊ|TVVbŊ2J#(憈Zqq1.]mD,"bln(j?1|۷lA>!UZZ !D楢WFnn.nf QbsCDQ //ƍ{'4"bVqq1, -[p\3>-> Fa6}ΝX~=y?~ݻw̙3;vl>#7DaZӪaĈ> رG+41b5Qt`sCDQrYYY磏>˜1cV+nOjmkQlasCDQ㫯cdff9z_\.>cWnV0yd9s>^"R&ѻwoh~۷ohƇ Ǵi?:uBvv&5Q"yEEE=z4F߿|Yf+WljzH=ln(r4<?~<9(xZbVqq16l؀m۶!-- 7oFnn.>,[ ڵ+Φ}^H!""") ) ) ) ) ) ) ) ) ) ) )ȝp8IENDB`docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png0100644 0000000 0000000 00000077073 13300556574 025150 0ustar000000000 0000000 PNG  IHDR7< msBIT|d pHYsaa?i IDATxyxSe?ɞtIWhiiK[(@iy,":LeyQauP~:̸Έ(n ¸,BTtPZoYGL$$纸9ɝrgc B!>B&vB!݉B!*n!S!BOB!>B!*n!S!BOB!>B–-[ jYf!;;`/~; CFF^x477nݺ6S#-- /2xž})s'NL&CSSءb@78q"&NrYYYصk ߿7tKz}/iӦ!>>իoowƌHMMF:tVѣG`xW0ykOi… 1i$jC!/QqC58q"֬Ys֭ó>+Wȑ#1q8|0x+t.Xx1̙'OBp+:c {n9c ڵ ~-ncthK`C įm)BիP( <[oØ>}:6nV?_|9TB|7ZXn @[oq)JzC`[ PPP۶mL&Æ *++?h4HLL?N?!]!`DZo=졇b!!!L.;۽83ϴZǏgDZ8籒qKHH֮]8cs6qLTӧOw:vb3f`DZn=31ؒ%Kq,66=Cg8 /}t&ٌ3?F8cSLqyϽ{2Zz=[h[j>|88-^إmBBKJJbAAAlڴilʕn`DZ9sx6frJ6vXq9sk̚5qƏV\ɖ-[RRR1_86tPV7VXq[na6%D٪U؄ qn`---ζ&L`25662oqlȑLRZ%''3<ף>8cGew}7S*,))q8﹥ř㌌ c;kss3c 0؏?DZ#F8;޽{;s?qǞ{9׸;\.g1Ν;bcc\.g3gdO<q,-- ?!]E { L&'w)ͺu\l6VWWvd2۴i}1ӯ)Fˌ3ڵkڵkٚ5kو#Jb7nk9>k<VWW<^^^Β\.w~:ا~lkZHQQc1"##Ytt4;KΝ8cqlc , qΝ<c .ҥKqw8zgPZZd2K/T||?㏐;vs7x#0`[+~[έL&C^^1R} qׯqF~111?>xGnn.Μ9gĉoا_nĈv9DcHIIa61+9;~88CFF[۱coiXbL0J-_|;s㏱vZ6s`wm^ * .\ŋѧO늑*nhHOO;#Xz5FwyÇwiTuo|P._n rL&9 wD"-- ;wDbb"lق/ю455AѸM=1.숙2-ǹ|m"=qF<쳨`/,Gѣnbbb|(Ae+9A@@Z[ۮ|;ye]ڊkĉu7طof̘q-dee!++v\TܐEP(|rO8w6mڄSĉ>}zt:|ݻ7^ulڴd 48uTǯT*.?UȎ/KG ߿6 m'|~عs'.^jٳeښr[)GW lvaХ9ow 󕅕Õlg8üypi:u ;v ZfMׂz2dU7!mZ {-—_~o(--uzӬ/b =Xse/NW6G{1bc8tsiXMn566G}tկkmݺwq9+ۊeee=zӧOMY68qFf9r$x?\Gz8z"//V-HL6 Vt٫{|gO7;3^~e TP(/駟bqX,FqȌ30k, g1?&M_;Y\Ӹ[oMuUvv6;aܸqu|_Yx|I.^իWC./fΜ`K(**ryO^y6o]+-J>8ܾ<ϟ?V˗/@fWv>f'Dss[kpBSO=SbΝ]]9|444z`61eAe-#͆/WСC1tPlݺ9s\c„ ػw/vrG}5k`Ϟ=n4Dشin~d/k:Ի_Ǿ}w^l߾su>k.,^۶m_|iӦhmm?l00ansN; SN/c ZRu<&Le˖_ǰaÜ>3ǰaú`lڴ wu3g"::?tOk]Җc9s&͛ ;v Gɓo>EUVoA~O{qUTشi?ѣG#;;GŤI_umC=z [q%Fhh(jjj ;|'|{?`ԨQ8}4`xGwu~i(..FBBk466"..?=܃UV@on gm݆̙3سgޏv2G:1^THVe))).r9^}Uz= dcǎe~K-[0L"~nGtts&\2o߾@fϞN8N88c{cm? 6iZ6d-xɎ9222Vegur}egmذ0Z͒kVZ8cns&rJ֯_?RXll,{Gr~q6e]w.\RSS]܅ \.wYJϟg/fLV~ M'zpy3ΝôiPXXoǏo'|sb[sN[q]wy0jBTd29ǎmBBFҥKP* s{nPUU6ؙ+sûヒ4twމ2e ;t@!R """ܹC †NxEq?.\>(,,ĬY\Ϟ=EEE4B\3.O@\h5-l*n_/.ȿU" E\.ǘP(7q ƌπLn/ *+C&Ɉ'@ t%@7|S_\qr[y35hnmXVFq6 Z^@(dW*x_ĄBj@KqR 0yޥyf}'oZ땅 @!CKkqĭ( 8z0 <6Gf9oLLH\;K2B!81ڀ:c# ?S)R0*}tܓHFff!5ʿ(ۘ Zk#`ǂ bp)lfgA='P %K_/.o˿``CtzmB0 wdȹBqbBFHԩSQE*XPoE O _6@ر+/BVތzc ~ 赡qQqC!HjDYOQE X#4Dv%v~/.ʿ 8P ].l4 -!>4zoӨH֭[1c [qQ%F bؾHޱ]~*aHh:8;)Ezz:rrr&v8B|w}qnDj_#HG.jB!ă \ \ޡ#"2q1.:6 E@)Wy4noB !AYǏ:NMܙ۱A^I@3F G6k{$mD!x`Qc3=6=f|drWsEy%2"Tt7D2.\(v~/.ʿz2GuK%k Pgjh{ @.2Ss$-+*ʿ(u4R 0yޥyfQi ׃F$c޼yb(;o,|%uh0պ6aуYycnWTbBRoBzc5 _ee9s?r-Gt>zn!`[Qtuh4ףbbp˔[ARΜ㳛Zz8|0n&[qQu-oͨ3VĀ)7`HPFqisĄ$JMuHwQE俕7PKS_߾p$ XT*ndl۶M_\qu%f ƪڡ;Hp]$T G7D2t:!5ʿ((&*--]~= *95bBib@ FKi'Bc F1Y]:6 (tM'b CkqQ=~>[cn3/?}ߥ†Pm@h*l$zndʼn_Yޏ?u>VjAMm ~M<ױCw6rRB$cҥb({NaQKa#1v| 8ZD@NYJ7Bc ;|> cg@P8v9 ?uq1qrj KИB!~h1Bc J{l ( XY^($@x@/*l7D2QE9&Jp&VjMQa1{j6E@QQm!rJW IDATCkqQjByc)7UTg5Mf<xWn+=""#rn*^(?PqC$f/.,ϩ0f| soSgōN*u=?m)B!cQPrQB_M*uClH+lB!=·p -&X,Fq6 Zbq!/F=7D26o,v~/._?oq"J bi(&3xwm0JWﯨ!+v~/._˿&p %h4׹<уY1Fk$&$y,^_˿RD2|MCkqJch4סPIy9'S)R0*}Gb|'Ďzn!\7͍(/DUKE `qǂ 2fvڝ\3!r]T.7u5m(fNRXN6K+'&$aTh*luB5iͨ6ThiR W4#LCkqySMpl 2q!I׿UBOQ %K_/x`봽ZAD`o蔁_O"SN;Foc64jQg:m)Aj)\=*n!1.񝶗qr"׆AѨ"*n!``iF,Bk9pц#L L 7D2vڅ3fߢK ;' %ΩqqqH>]z`M(u+{8ڞE׿o~C"[n;Fx}>*446 scG#/P!>4QA1^_t!}vCkqy2EرSc\#"2q1.jZDDA X@׿oBLvNa3> ¹qe^I@3F)W!" Eݖ"?CQYGa cg@&5TWB.SW`$G: BD+oFUKL=4ZqP(j0  iK$c…b(6q,LVJ` w]yMfV†W- B(3fCuh4;GDAуY1F@bBR$Ut-E$c޼yb(;6T*Xܞ8$OyfK[B|H+oFyc).6Y@l\ MXZ .94%'P !ƣPFs]m9p4n2'x(.-rPQ飩!^"M7$v~/k?c :`cB [)%\C׿oRD2֯_/v~/kɿҌsgQm贰Q+4C8(k g[HƶmQu56T`i鴭#"75_-Ttb(JZcL!D0]$2yw-^s[/ȑ#lX~=  1%cWS.6 ć%#20 ◼9r2331dܹիs{9<䓘?>>33;vx0rB>FK ՟EUKjTr5b C%W{(BB+gyiiix0uTBYΟSӇa +)C|8=Sgmu IlE׿o⤤$r={9s ot9>~xvoGff˟1c`׮].kdff?7ov9LԸ_v-^z%ceeeD~~nF#233qa[n… b;w?{XrO|~ՈNދƆ&B}[lLnKTw=G\^W9&NC꿏~5%%gv{űH-Ʉ#G8Z 6mŋVO0w\|W.7n܈~ǎȑ#]Ezz:rrrֳf,߳v_B=Z  .^@hp>j=/}OO|zESO=ɓ';… qlذׯZFss3N:dDDD`̙1b<3HIIAvv6ydff6DqQ{c M i"|J`6< yf}.6}oK7ߌ;v̙39s&n݊ 68g>`رػw/@PX`aOOg'BHOh͸PKc'?=   IIz B|FO|zE W#E~146l `A;RΜC[b FQX Z*eB*3Aj8"(rĄ$JM[/!Z0fCuj0tϩBDh;lG?R"EFK Z*:ݵBGiTBUm<[*i[R^}Vh<!DB\xs14jQZWia# A_ ʿ("-;FjBYC1Z*`c5H`Mh7[u։_M lPi[B^}U}(*nd,5qQ]׬iDuK%wVmX{jD߷PqC!Wr&ӶAj="+=!+!¢dd* }b_JbtxRB>Pz(ZBHWрb"7o;o~UdBUMPUS<ܾ y9?y^M/5B \Ck¢\.G@`d_I<{\(+w9O D|Xr,O"ʿoRD2|MCk,ϩ0f| xуY9 ?uq1mT<`3)!"Ga cg8{p*+ GR{!t/*n!~1zS-fY8( hZ{{W`4d2c%\;-E 0YP*Ӷ&3xw)pxdUjQb%\!)v~WCdDDAуYy"}17 <[P} Xd!5_+oƥryCRp|@^Iw & 6}FXoB-sC$cԩb|)1QV_V@l\ MXZ .94#oD-sC)hmA 7ɓ(.-rPQ=ZB7譩5V`kp9 !CdڵK7l5*tT(e*wP8iMʿoH֭[ycm̆%5U0w6DdTxc} ߷m)"۷o;m7[l.EhJFh:Evm-[!xPzSMmC=%!Dڨ!x Ո ۩jD@#BHϠ"y5 Nۆ"B'pBCkRͿb6jqI핅T/(znd ZmL@u%4:i!<[OZ ߷PqC$c޼ybפ۬S+ ZPd=GJGB !D4EBIi1,V Jc4bb=^Fxuo !gPqCwcOݎWT'1nҍHMwA"*(*aB#>b<¢F.# 02$=.;s!>Y/.ʿoHy29YΟSӇac >\3Rd|6]*ndl۶M'_RZ c3f| rB13=8X}?[s9E-Th5Y1y*V#@8 BV `C6g{k.G׿(B<1&sZ*d.0;|U[CsCqM@Ey\j) 1Qむ<{as`A$&$3!{QqC$cŊbz*FϢyly9'?7!/Q{$&)_\B 8Ckݝj pmظt 0`lc3ҀnI߷p1&vRt --MpjM`MlBUJ0*}_6ńnCsk.T1[dQA1Cz0BB?-hnm찝NX(JEF74HF~~!ɿj4qa!"7b TشqQ} 7D2V\)v~Z4|}rB]-E$7;v T4]7v.X^ѐq/.ʿoHM߾E:4;/.ʿoeAhnm谝V(*EF!%&Ma`l DF6BDC7dKb?c *o(QTK]!a4v<0o,lAayt[h_ j67RKy'e &\~l>hɠaQ4h"9T ܸRTWt$ ظv 5ɢHFMM """o|wx?vd4AӢ~/M]Qʔ Rp}]-E$cѢEb7 \ \A&CDžr qTE-sC$cݺub7s?Ø0yRуY9 ?uq}+5!b߷P 9%=6cg@P`ȰP(3>كSyBE-Tgl6V@@pU(j7bvwNM>(=sJaZRQ飩!x-*nAM\j.CC<|0ƥR.!ķPqCaPSmzFC !>F$#33SoqF.SoH?h ʿ(!dCZ&Mem|m4 Blyʿ(*ndL:UR- ф!20 ~g-_\qQ} 7x)Mvp+PFF!/d,h:3ojBD8hZFF!D2v%v^hiAY}QNФ*l([!uVC45X F F:f)[۷d٘ n!*(Aj5_\qQ} 7HEh2Xv(* Z`d"MT"a-ͨl>ͻ- DB.{02B.*n ը5Vu.\ aHFB.Cd,\P$A Taa##y<20!0_C!17 L_CDžr9b †Bk/#GD@@/vzΞ={0j(t:>(F%b19YΟSӇac >\鳈 MN1S/.ʿoȑ#Đ!CsN̟?W_vqw`Сػw/q=9ׯ;).-`3> BPʕɟ/EqQ}Wyg{}Tjŋ/4UYcxG1gl޼0qD_F:㟃tl۶mbF&Q8 BV Ck_(ܴ5kٳgr;'//%%%Xte˖ٳTH?^͍(o,Bi1yޥ 0{{w/eqQ}䋛bX, 0xrr2휼<Fӡe˖yIT4CtL@=,pxуY߉ IK!J'5 IDAT.ǃMMMnTWWfΜ+VcǰvZTUUQ#(j 7: ?rN⧼Sh50L #5*}%o'XdbS5k^xL0+VڵkGb%gŊbl̆ . `ܤARΜI==M(z@ssqcsL>iOtE'vʱGjh3-DIi1,V TJ0*}G m($%%A.ٳ.vNm|jhvoGff˟1c`׮].kdff?9C!77q9vZK.ʐ|ga4>֭[nܹ#FqaX+7d5˞rmO%sxz:ҥK}so|sHo|`nq8} [`2pUVaӦMx"jK{(dff⣏>r駱~z\x."==999HKKD|Z+oFyc)x8D"HH!'%[ z)L<wy'.\#G`Æ X~=j5q)$''#""xgc!443gđ#G~z<#n !h1b9ؘe }Sz82B-7|3v؁3g`̙غu+6l؀{ cb޽s/_wyo~lٲ>,B)aWvz66 }C%[x{_\yc} _\B oic6\j.Gskcm `o˿[!t j<!*nJVRX) B.ƒQBq~_9W.H%E-l8p땅7ߗQE-sC$h4B|} QE-44t܈ `hB@>jÑBwְ!B !5l!ğЀb"Wz+&BM:1)lD߷PqC$cѢEbMy Ն#:W,URɿ[u։_a#3ʿ("bR+,*@vNJJjiL!OIAl\K[b{Tb 1QAp`yGfA@bBhB64 ŀs``8$Oyj`2`lsF+\B!;} B$l_A־uZDE R[B-تG**V-Bb%)RPYdUe#Mt,;<9g1ϝ;/qs"p8ڵlhl 5'Fi >?l5EpO-cr1 EYfu\8y(_}tɯt9aX'=Cꪱ—?EfڴiUW]\_6n8_]{QchPLf`cM8cr1h~Zȑ#ѫW/_Mu8Qq^ =ɖKz!"RGnrss]I&{R*""i~{ֆ 4ylj `MBd66* EfѢEEaǎz}:\R$E5\'NT;HcoID^0xŴȟBbjߎLbQFa̘13f ٣["֬YӮ]NT =ΒKS`dcP{ar1h|߾}`ƍ8w}`ƍ>|8?hDp{8q\kAƆH4?r_~f3rrrGy;~K9׍Gkl37S+#""4grʀm6233~K1׃fgJ:LF66DDzysSPPÇ_}Z%)"//Mollu[M6iObr1h<߿?Mp{zzoIhBƒB6vɊd #;bjo+Wcǎ:t(F ͦ[&MԪy'C&vъԔ`fc&͟ƒբ3g?;v`ٲeERRn6t]# /N vHdpeDD-4on{1hJ j*l6W-I'tqmlHp,&k+#"h~$%%aׯ{H%Šۄ8]yUf\+5?EfҥA]{(---IK, 8.U'P d0!5%V3wG)2\_-77o66o 3 ոX!zj1!:1LH(/Wɓ'1et?OtRڵ ^ΝÇ~KRD||B>7HMIwqY_.͛{ǏGii)E||$F |p|[s63 49 Ĉ֥iiiK5/WA!|z/s:x駱i&lڴ)**++Cvv6JKK%8xvnǑQW_و=c`yiH%K!߿Ϲӧߘj? ]wݥR gq!ֽo |gFWg_܋ 槥n[_k:ؘL&$$&hWc8vݓRdKQ& ͛J8p Ν;kvno﫳}FwWg_ٴǑl Dٷotբys3m4L>G o-)9p3GɄQ`6?l6#g԰#8'Y̝;Wv bji󜛯 +V@uu5og{nݰpB >/0e 4Fo=U<=fB8] -_\v bjiSs_kEEE}YC\w9r$ #Fh+C)V !`27lƥ=nuu|G /ҦR1fXjO2W{_-[`Ϟ=x7i&ڵ \rVS,.`pm[gHbS|gسgֆMvmxp8}v 6oAaРATK1B}՜bO]]{a^C*bTٳiԥK\VҴ0RSUi=1r̵M<~TLp܌~#^^.Aט\_-m:rc555Dj N?y:*tٯa6ЭK7Ill"bjE"!uI8=7xLMk.F.?wc=&]cr1ݻ~zRw娮 -ɖq#iMGnN> &Ç7=~z?RLF7n$gT.JDDMGn郂̜9qqqxW1}t^~e3 ,͛QWpI\'NWh0Ҕ4 \_./WKcĉXlvڅo7nC=!C ._:`(-- Wż‹S{${ԩS#Qbr1Ԇ |~NII 7܀n@Ê~)PTTTUUiW-!:zw]].A5o|abr17l1bF~. *bOE?p[5ܖβ_./W櫂bA޽ejp۬&'s1WXx饗%& h04 WEDDzGnaqxL VXbE8JVbr1Zܐ8S} ڀ;wC-)++Ӻ4j/ }OgʐRN,koqTm $\y6DDP8yڥU3Ձ'[LVtOJecCDDBpL 69" D4N n ᪈PT:wMm"ɖt8-ځbjasCmv;6xk":w ug͚՞\_.67&uWFYVHqƵ4j'/ Ʃc&ÀKS8csC"cps1E967*g/|ZW  ɞذaC_Bbr1UU\mtID)((u(4_./W jVT lGr/fM^Bbr1<-M NNhPQpln( n$R-qeln( ߠu!qlኈZ ;jnģkBo^^^X^Zbjt,^n 2aP :|t=4@|1!T./%&8dffb˖-Ü9s|jjjp8!ĉ>pn0>s_4=/Z?GXx}_  &F{D ̴Q[[|/N:={ӧO` /ttg2dggYYY@Q`Ūk. snOMnlۺJw NY*),߿1qyaر[,]K,fCUU݋}K.+^#11AKK Lojnvƿw=ΎZx=oHЈ5g|8} ;Goгץ9ڦ1ǃ | /cr1ZbT$T+7|IۻO~ Ym蓞!C#>iC3է}Rz"5'l)KReDDDډR Իk 0k"""5 ׃onf%E,/͍N|[sYBEΝ+]cr1Z]jnՒ˗.Aט\_.毖V:stxK"I&8^)FqUqV5;CDDas0l6:fG"""csspyW6LMBEͻx8,/͍՜ K%0Me555K5//j/|]y~6i28׆B8yFA tK )͍b(ɖ8K|+""",67h"bjasXX?9SN]1\_-ln|(KԬ՜ .Aט\_.67hX?\m]{DQQ*Xbr1D7JQWGQDDDF\?{lnbJGXBv br1q~Tsdk_./W G5瞓]1\_-lnbXo of#QBEDDD򱹉Q\?(0671G&5~T(_?9Cv br11-e3k,%&Q7ntbjasCTX?(mCr(""UQ6l]1\_-lnbPh.Aט\_.671;֏jΚ5kdk_./W (QDDD&q(""asŚ_?&J !p"\Q.Aט\_.67Qu !T./MjnN]bzL4Iv br1Bͭ1b(ڇߔQ&Q ʯU\\,]cr1ZD׏R%K.Aט\_.67QB/G5gղK5/,;xvnǗG=zvHMQ͉]1\_-ln$gq!ֽo |gF?}׏"""j>dBBbH ?7'/IDDDdG:Jwa;1_+}Z3gtbjas#ɑ45 fsBٌQÚ>zHZ&]cr1ZHt98{Scl6#..yz0{l%F ԂF]m󈈈uHrYzۛۍm[gH(RpIfÎ wƿw=ΎZx=ަ *Ĉ۷o( br1X\IDATZxF~;=.T_il&8nFcܹK5/Gn$ f:tNqp\YmgdUc˗/]1\_-ln$sRz"5gxׄEVYRRL\_.Upn&DDDFZ1 $TCDD67!P0$Tek_./W  u7SR55OQd0\_-!]D4(++Cvv6JKKV ֤7Q4 /Hh >rCDD67&xdhPZD%@yytbjasao0uT%&`%&M&6-.Jbr1 oGm&xJ(DP]۰ib %&jGn#&jOYYtbjas!^E+bf}/C=tbjas!:/SRDDDbs!LLDDln"$bln4& pn?FCv br15\'<7Σ6f͚%]cr1ZD@SRokܸqK5/M Y"\ D@+,&+Fj&^7\x.a%&x+((]1\_-ln,bos5k.Aט\_.毖in} < HOOŋ}ĢE0p@$&&bxr"Tq@Gn,&kD ""ҋhnJJJp8cxGO{O:wp Q8Ezlٲ7t P]]"}0c ipo6.Aט\_.毖on> ӉpA}ӱ|f[oZh&!T./%ꛛ @rrxRRUn:k9s&RRR-2!ܙe7i$%߲Bf-kou]UVZqz^q.IDD^Q4eo0< n6=.,Kx $""#ꛛ L&|>?4(~B̞=p-`ƍo~5~7afΜ+V̷ٻsfl|{;fرcp8طoe0g87q6*++@yyϸV#V<(>Gqq?#F(9_~0`&L&b1cDNNܹsEND]]]}z!a0<Ъ(--DiiiBC3y9w@VՍ7(]cr1e0yf5*lrд!bˑt (--EVVV^L)T}7ީ?\S߿~Bq, p 12WjyԻƹXf.Obr1X@s[&]cr1Zh,db.ْٳg.Aט\_.67 XeE !Pq QPWsֹY_./W  _,mZcܹK5/͍N&6Eش|r%F#B7ﳙ0M*=S./͍F\^'<^8ד""",67 tę9߆(h$|^)zK5/͍F57fl "EbBߘEڳơsHHGr/J$""RN{ p=)""Fη!""467 FXM6 Įr%榝‹zwx%eԩSek_./W vw^,SRm`%榝8X;Z͒0\_-ln)psc2d`sB͍lhd7p;8= 8OIfŊK5/M;6*++]1\_-ln!J\,34=tbjasXLVf &dn .obsZ7oCDD܄(dbޙ8dCv br1 Qd0bJF f͒]1\_-lnBlL;lq.Aט\_.67!v 8&yQbs@͍v ըcÆ K5/M !P2p%..K5/mT暑Wx9ߦ֬Y#]cr1ZܴoCDDܴ$""nln@Zq$""""6p{]p{~o<% xBbr1Zܴ4itbjasۘf Q lnZ$pL""(榕x x.Aט\_.67T`7Ғ%Kdk_./W V 4h025zj%U7n7syL&/M+Թ y 8Qas Q`s R*2̙#]cr1Zܴ b6sƧ4%om۶.C\_./Ooii)4yͨ?rS__-[ছn0aQTTѣGq:t_~e۸}l{!NMuUv br1D}ssa8Ngo߾(_|A9p@{am:]QE}sSQQHNNOJJTVVjO#Hֽo(D}sҔ6#٧ф~l-THDDD,h8]C?7no> Fsz( P}'(++]n1\_Ʃ$QVftRO>D uV}<( Xvo! 8v>Nݺw>>.Rq)zGmm-JJJ|AK8ul6>я~s)ĉ{]ӧqi?գGC׋R0o<;z+PRRKbɒ%l޽{ѷo_tyyyܹ3nFx7o}%""ȋ#7@#ϟ#553g}?1c`ʕ2eJ>/".]Ǐ###?0Y" f"""ֈKB7͍U{mtbѢE8p 1p@FQ<Ail~1c0 _`j%3f/ SO=%]wJ? rss\Bw&IL:Ul޼Y,_\$''I&Er5 d .}Q^Zѣbh4-[4\-u܌7N :gIII6>'N8q۷oTU[///FFO=0 <&FvGlnB^ӧ?EFFpBXUM(bʔ)>cÆ !zԩܹ0 -67Z|*Z*ҫP򯪪Œ3p8|a1UjPot:1es=0`@KUR(ڵ Gٳ}n|嗈k* i-F;wƷ~ZUg᷿-->__四H*NB?==˗/ojf[Z~E `-F]v?N:F}}}VHSL>x`aY5{ơCtRŵ|c&~UɗVY[f#_駟駟FQQVkxTX(={0~xq3g>̟?gΜ:5/z  74v]wSر#:vk|sҿ8^U|iڵkqwkU>Zuuu;q}a*MBtn&,Z0zhx^ce]j WǮBa4-"5ĩ>*b۶mٳM\R Vo`oښ/a0Đ!CŶm|2?N bG]w]JVJ(?3`03gM6  *̙#cĬrDVV֭_" ŢEDbb/~!ļ͛7&\߿hnb+6Mdddgyi[c௼>/ׯ"33Sk.[m`FQ GknIB뮻70kPW\!l6ӧXxqVF[󯪪s"33S %|el޼p}rUp"""RsnH_RRRRRRRRR̲ " t◿%8ÇG+uuu8y$*,\_~j(pm)"jk֬In:?itoGaa!w$hRDՊ#GZCEEVX!4"Rln(_~ҥ߶*@uuu"(憈Ç}˖-t;"Z%%%Bl^˱zjoPE+67D9pƍ{OFiDx)8E"X,,[ rgyÆ \!E#^ NDQ)))>|8 eCD1(*}p:5jT=t͛ш<N: j⡇jz7|So߾x'H!h"<裏KKK1j(?fgw܉ؽ{7Μ9cdž3#3oVw}7?5Դ(!W_?1233QUU?_=֯_~. + M̬YPXX+W65=D67D]Qr|T?GbdDA<-ED16lmې͛7#77| -[twyg>{/%VMDRxZ憈憈憈憈憈憈憈憈憈憈憈sIENDB`headers/0040755 0000000 0000000 00000000000 13300556574 011233 5ustar000000000 0000000 headers/Android.bp0100644 0000000 0000000 00000001007 13300556574 013131 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 13300556574 013670 5ustar000000000 0000000 headers/media_plugin/media/0040755 0000000 0000000 00000000000 13300556574 014747 5ustar000000000 0000000 headers/media_plugin/media/cas/0040755 0000000 0000000 00000000000 13300556574 015515 5ustar000000000 0000000 headers/media_plugin/media/cas/CasAPI.h0100644 0000000 0000000 00000007722 13300556574 016733 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); 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, uint64_t appData, CasPluginCallback 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; // 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 00000007175 13300556574 020452 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, }; 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_DRM_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_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 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 13300556574 015531 5ustar000000000 0000000 headers/media_plugin/media/drm/DrmAPI.h0100644 0000000 0000000 00000046347 13300556574 016771 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, }; // 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 }; // Enumerate KeyStatusTypes which indicate the state of a key enum KeyStatusType { kKeyStatusType_Usable, kKeyStatusType_Expired, kKeyStatusType_OutputNotAllowed, kKeyStatusType_StatusPending, kKeyStatusType_InternalError }; // Used by sendKeysChange to report the usability status of each // key to the app. struct KeyStatus { Vector mKeyId; KeyStatusType mType; }; 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 13300556574 016235 5ustar000000000 0000000 headers/media_plugin/media/editor/II420ColorConverter.h0100644 0000000 0000000 00000012504 13300556574 022023 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 13300556574 016544 5ustar000000000 0000000 headers/media_plugin/media/hardware/CryptoAPI.h0100644 0000000 0000000 00000010141 13300556574 020521 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 13300556574 017777 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 00000063133 13300556574 021007 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.describeHDRColorInfo' 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 }; } // namespace android extern android::OMXPluginBase *createOMXPlugin(); #endif // HARDWARE_API_H_ headers/media_plugin/media/hardware/MetadataBufferType.h0100644 0000000 0000000 00000014347 13300556574 022437 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 13300556574 021324 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 13300556574 020324 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 13300556574 016416 5ustar000000000 0000000 headers/media_plugin/media/openmax/OMX_AsString.h0100644 0000000 0000000 00000154714 13300556574 021055 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_CodingAndroidOPUS: return "AndroidOPUS"; 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_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus"; case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation"; case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3"; 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"; 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 00000231163 13300556574 020356 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_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 00000013410 13300556574 021030 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_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_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; 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_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; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* OMX_AudioExt_h */ /* File EOF */ headers/media_plugin/media/openmax/OMX_Component.h0100644 0000000 0000000 00000057557 13300556574 021274 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 13300556574 021541 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 00000214357 13300556574 020213 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, 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 00000101361 13300556574 021000 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 13300556574 020373 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_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_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 13300556574 020374 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 13300556574 020427 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 00000126725 13300556574 020372 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_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_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 */ typedef struct OMX_VIDEO_PARAM_BITRATETYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; OMX_VIDEO_CONTROLRATETYPE eControlRate; OMX_U32 nTargetBitrate; } 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_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 00000042740 13300556574 021045 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_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, // Main10 profile with HDR SEI support. OMX_VIDEO_HEVCProfileMain10HDR10 = 0x1000, 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; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* OMX_VideoExt_h */ /* File EOF */ include/0040755 0000000 0000000 00000000000 13300556574 011243 5ustar000000000 0000000 include/android/0040755 0000000 0000000 00000000000 13300556574 012663 5ustar000000000 0000000 include/android/asset_manager.h0100644 0000000 0000000 00000015030 13300556574 015641 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 13300556574 016501 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 13300556574 014316 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 00000004377 13300556574 015674 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 __BEGIN_DECLS #if __ANDROID_API__ >= 24 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); /** * Get the AChoreographer instance for the current thread. This must be called * on an ALooper thread. */ AChoreographer* AChoreographer_getInstance(); /** * Post 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_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data); /** * 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_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis); #endif /* __ANDROID_API__ >= 24 */ __END_DECLS #endif // ANDROID_CHOREOGRAPHER_H /** @} */ include/android/configuration.h0100644 0000000 0000000 00000062706 13300556574 015713 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 #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); /** * Set the configuration's current screen width in dp units. */ void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value); /** * Return the current configuration screen height in dp units, or * ACONFIGURATION_SCREEN_HEIGHT_DP_ANY if not set. */ int32_t AConfiguration_getScreenHeightDp(AConfiguration* config); /** * Set the configuration's current screen width in dp units. */ void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value); /** * 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); /** * Set the configuration's smallest screen width in dp units. */ void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value); #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); /** * Set the configuration's layout direction. */ void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value); #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/hardware_buffer_jni.h0100644 0000000 0000000 00000002712 13300556574 017021 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. */ /** * @file hardware_buffer_jni.h */ #ifndef ANDROID_HARDWARE_BUFFER_JNI_H #define ANDROID_HARDWARE_BUFFER_JNI_H #include #include #include __BEGIN_DECLS /** * Return the AHardwareBuffer associated with a Java HardwareBuffer object, * for interacting with it through native code. This acquires a reference * on the AHardwareBuffer that is returned; be sure to use * AHardwareBuffer_release() when done with it so that it doesn't leak. */ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, jobject hardwareBufferObj); /** * Return a new Java HardwareBuffer object that wraps the passed native * AHardwareBuffer object. */ jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env, AHardwareBuffer* hardwareBuffer); __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_JNI_H include/android/input.h0100644 0000000 0000000 00000144265 13300556574 014204 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 #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 / modifer 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); #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); #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); #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); #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 00000070623 13300556574 014647 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 // 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 13300556574 014331 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 00000007406 13300556574 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. */ #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__ >= 24 /** * 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); /** * 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); /** * 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); #endif /* __ANDROID_API__ >= 24 */ __END_DECLS #endif // ANDROID_MULTINETWORK_H include/android/native_activity.h0100644 0000000 0000000 00000027004 13300556574 016236 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 00000004207 13300556574 016551 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__ >= 13 /** * Return the ANativeWindow associated with a Java SurfaceTexture 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_fromSurfaceTexture(JNIEnv* env, jobject surfaceTexture); #endif #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); #endif #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_WINDOW_H /** @} */ include/android/obb.h0100644 0000000 0000000 00000003155 13300556574 013577 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 00000051210 13300556574 014341 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 /** * Sensor types. * (keep in sync with hardware/sensors.h) */ 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_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_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 }; /** * 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 }; /* * 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: Must match hardware/sensors.h */ 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 { int32_t type; int32_t serial; union { int32_t data_int32[14]; float data_float[14]; }; } AAdditionalInfoEvent; /* NOTE: Must match hardware/sensors.h */ 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() */ 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() */ 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__ >= __ANDROID_API_O__ __attribute__ ((deprecated)) ASensorManager* ASensorManager_getInstance(); #else ASensorManager* ASensorManager_getInstance(); #endif #if __ANDROID_API__ >= __ANDROID_API_O__ /* * 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); #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); #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__ >= __ANDROID_API_O__ /** * 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); /** * 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); /** * 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); /** * 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: * \code{.cpp} * ASensorManager *manager = ...; * ASensor *sensor = ...; * int channelId = ...; * * ASensorManager_configureDirectReport( * manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST); * \endcode * * \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); #endif /*****************************************************************************/ /** * Enable the selected sensor with a specified sampling period and max batch report latency. * Returns a negative error code on failure. * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before. */ int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs); /** * Enable the selected sensor. Returns a negative error code on failure. */ int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor); /** * Disable the selected sensor. Returns 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. * 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(). * Returns a negative error code on failure. */ int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec); /** * Returns true if there are one or more events available in the * sensor queue. Returns 1 if the queue has events; 0 if * it does not have events; and a negative value if there is an error. */ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue); /** * Returns the next available events from the queue. Returns a negative * value if no events are available or an error has occurred, otherwise * the number of events returned. * * 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); /*****************************************************************************/ /** * 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); /** * Returns the hardware batch fifo size reserved to this sensor. */ int ASensor_getFifoReservedEventCount(ASensor const* sensor); /** * Returns this sensor's string type. */ const char* ASensor_getStringType(ASensor const* sensor); /** * Returns the reporting mode for this sensor. One of AREPORTING_MODE_* constants. */ int ASensor_getReportingMode(ASensor const* sensor); /** * Returns true if this is a wake up sensor, false otherwise. */ bool ASensor_isWakeUpSensor(ASensor const* sensor); #endif /* __ANDROID_API__ >= 21 */ #if __ANDROID_API__ >= __ANDROID_API_O__ /** * 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); /** * 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); #endif #ifdef __cplusplus }; #endif #endif // ANDROID_SENSOR_H /** @} */ include/android/sharedmem.h0100644 0000000 0000000 00000007350 13300556574 015003 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 */ #ifndef ANDROID_SHARED_MEMORY_H #define ANDROID_SHARED_MEMORY_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 for a shared memory buffer that can be shared across process. */ #ifdef __cplusplus extern "C" { #endif #if __ANDROID_API__ >= __ANDROID_API_O__ /** * 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. * * \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); /** * Get the size of the shared memory region. * * \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); /** * 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. * char *buffer = (char *) mmap(NULL, 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. * * \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); #endif #ifdef __cplusplus }; #endif #endif // ANDROID_SHARED_MEMORY_H /** @} */ include/android/sharedmem_jni.h0100644 0000000 0000000 00000004605 13300556574 015643 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 */ #ifndef ANDROID_SHARED_MEMORY_JNI_H #define ANDROID_SHARED_MEMORY_JNI_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 */ /** * Structures and functions for a shared memory buffer that can be shared across process. */ #ifdef __cplusplus extern "C" { #endif #if __ANDROID_API__ >= __ANDROID_API_O_MR1__ /** * 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. * * \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); #endif #ifdef __cplusplus }; #endif #endif // ANDROID_SHARED_MEMORY_JNI_H /** @} */ include/android/storage_manager.h0100644 0000000 0000000 00000010576 13300556574 016200 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/trace.h0100644 0000000 0000000 00000004352 13300556574 014133 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. */ /** * @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. */ #ifndef ANDROID_NATIVE_TRACE_H #define ANDROID_NATIVE_TRACE_H #include #include #ifdef __cplusplus extern "C" { #endif #if __ANDROID_API__ >= 23 /** * Returns true if tracing is enabled. Use this signal to avoid expensive computation only necessary * when tracing is enabled. */ bool ATrace_isEnabled(); /** * Writes a tracing message to indicate that the given section of code has begun. This call must be * followed by a corresponding call to 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 sectionName contains these characters they will be replaced with a * space character in the trace. */ void ATrace_beginSection(const char* sectionName); /** * Writes a tracing message to indicate that a given section of code has ended. This call must be * preceeded by a corresponding call to beginSection(char*) 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 beginSection / endSection pairs are properly nested and called from the same thread. */ void ATrace_endSection(); #endif /* __ANDROID_API__ >= 23 */ #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_TRACE_H include/android/window.h0100644 0000000 0000000 00000022526 13300556574 014347 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 13300556574 013677 5ustar000000000 0000000 include/audiomanager/AudioManager.h0100644 0000000 0000000 00000002451 13300556574 016403 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; }; // namespace android #endif // ANDROID_AUDIOMANAGER_H include/audiomanager/IAudioManager.h0100644 0000000 0000000 00000017774 13300556574 016532 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 { // transaction IDs for the unsupported methods are commented out /* ADJUSTSUGGESTEDSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION, ADJUSTSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 1, SETSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 2, ISSTREAMMUTE = IBinder::FIRST_CALL_TRANSACTION + 3, FORCEREMOTESUBMIXFULLVOLUME = IBinder::FIRST_CALL_TRANSACTION + 4, ISMASTERMUTE = IBinder::FIRST_CALL_TRANSACTION + 5, SETMASTERMUTE = IBinder::FIRST_CALL_TRANSACTION + 6, GETSTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 7, GETSTREAMMINVOLUME = IBinder::FIRST_CALL_TRANSACTION + 8, GETSTREAMMAXVOLUME = IBinder::FIRST_CALL_TRANSACTION + 9, GETLASTAUDIBLESTREAMVOLUME = IBinder::FIRST_CALL_TRANSACTION + 10, SETMICROPHONEMUTE = IBinder::FIRST_CALL_TRANSACTION + 11, SETRINGERMODEEXTERNAL = IBinder::FIRST_CALL_TRANSACTION + 12, SETRINGERMODEINTERNAL = IBinder::FIRST_CALL_TRANSACTION + 13, GETRINGERMODEEXTERNAL = IBinder::FIRST_CALL_TRANSACTION + 14, GETRINGERMODEINTERNAL = IBinder::FIRST_CALL_TRANSACTION + 15, ISVALIDRINGERMODE = IBinder::FIRST_CALL_TRANSACTION + 16, SETVIBRATESETTING = IBinder::FIRST_CALL_TRANSACTION + 17, GETVIBRATESETTING = IBinder::FIRST_CALL_TRANSACTION + 18, SHOULDVIBRATE = IBinder::FIRST_CALL_TRANSACTION + 19, SETMODE = IBinder::FIRST_CALL_TRANSACTION + 20, GETMODE = IBinder::FIRST_CALL_TRANSACTION + 21, PLAYSOUNDEFFECT = IBinder::FIRST_CALL_TRANSACTION + 22, PLAYSOUNDEFFECTVOLUME = IBinder::FIRST_CALL_TRANSACTION + 23, LOADSOUNDEFFECTS = IBinder::FIRST_CALL_TRANSACTION + 24, UNLOADSOUNDEFFECTS = IBinder::FIRST_CALL_TRANSACTION + 25, RELOADAUDIOSETTINGS = IBinder::FIRST_CALL_TRANSACTION + 26, AVRCPSUPPORTSABSOLUTEVOLUME = IBinder::FIRST_CALL_TRANSACTION + 27, SETSPEAKERPHONEON = IBinder::FIRST_CALL_TRANSACTION + 28, ISSPEAKERPHONEON = IBinder::FIRST_CALL_TRANSACTION + 29, SETBLUETOOTHSCOON = IBinder::FIRST_CALL_TRANSACTION + 30, ISBLUETOOTHSCOON = IBinder::FIRST_CALL_TRANSACTION + 31, SETBLUETOOTHA2DPON = IBinder::FIRST_CALL_TRANSACTION + 32, ISBLUETOOTHA2DPON = IBinder::FIRST_CALL_TRANSACTION + 33, REQUESTAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 34, ABANDONAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 35, UNREGISTERAUDIOFOCUSCLIENT = IBinder::FIRST_CALL_TRANSACTION + 36, GETCURRENTAUDIOFOCUS = IBinder::FIRST_CALL_TRANSACTION + 37, STARTBLUETOOTHSCO = IBinder::FIRST_CALL_TRANSACTION + 38, STARTBLUETOOTHSCOVIRTUALCALL = IBinder::FIRST_CALL_TRANSACTION + 39, STOPBLUETOOTHSCO = IBinder::FIRST_CALL_TRANSACTION + 40, FORCEVOLUMECONTROLSTREAM = IBinder::FIRST_CALL_TRANSACTION + 41, SETRINGTONEPLAYER = IBinder::FIRST_CALL_TRANSACTION + 42, GETRINGTONEPLAYER = IBinder::FIRST_CALL_TRANSACTION + 43, GETUISOUNDSSTREAMTYPE = IBinder::FIRST_CALL_TRANSACTION + 44, SETWIREDDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 45, SETBLUETOOTHA2DPDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 46, HANDLEBLUETOOTHA2DPDEVICECONFIGCHANGE = IBinder::FIRST_CALL_TRANSACTION + 47, STARTWATCHINGROUTES = IBinder::FIRST_CALL_TRANSACTION + 48, ISCAMERASOUNDFORCED = IBinder::FIRST_CALL_TRANSACTION + 49, SETVOLUMECONTROLLER = IBinder::FIRST_CALL_TRANSACTION + 50, NOTIFYVOLUMECONTROLLERVISIBLE = IBinder::FIRST_CALL_TRANSACTION + 51, ISSTREAMAFFECTEDBYRINGERMODE = IBinder::FIRST_CALL_TRANSACTION + 52, ISSTREAMAFFECTEDBYMUTE = IBinder::FIRST_CALL_TRANSACTION + 53, DISABLESAFEMEDIAVOLUME = IBinder::FIRST_CALL_TRANSACTION + 54, SETHDMISYSTEMAUDIOSUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 55, ISHDMISYSTEMAUDIOSUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 56, REGISTERAUDIOPOLICY = IBinder::FIRST_CALL_TRANSACTION + 57, UNREGISTERAUDIOPOLICYASYNC = IBinder::FIRST_CALL_TRANSACTION + 58, SETFOCUSPROPERTIESFORPOLICY = IBinder::FIRST_CALL_TRANSACTION + 59, SETVOLUMEPOLICY = IBinder::FIRST_CALL_TRANSACTION + 60, REGISTERRECORDINGCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 61, UNREGISTERRECORDINGCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 62, GETACTIVERECORDINGCONFIGURATIONS = IBinder::FIRST_CALL_TRANSACTION + 63, REGISTERPLAYBACKCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 64, UNREGISTERPLAYBACKCALLBACK = IBinder::FIRST_CALL_TRANSACTION + 65, GETACTIVEPLAYBACKCONFIGURATIONS = IBinder::FIRST_CALL_TRANSACTION + 66, */ TRACK_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 67, PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 68, PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 69, RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 70, /* DISABLE_RINGTONE_SYNC = IBinder::FIRST_CALL_TRANSACTION + 71, */ }; 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; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IAUDIOMANAGER_H include/audiomanager/IPlayer.h0100644 0000000 0000000 00000003561 13300556574 015417 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_IPLAYER_H #define ANDROID_IPLAYER_H #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class IPlayer : public IInterface { public: DECLARE_META_INTERFACE(Player); virtual void start() = 0; virtual void pause() = 0; virtual void stop() = 0; virtual void setVolume(float vol) = 0; virtual void setPan(float pan) = 0; virtual void setStartDelayMs(int delayMs) = 0; virtual void applyVolumeShaper( const sp& configuration, const sp& operation) = 0; }; // ---------------------------------------------------------------------------- class BnPlayer : public BnInterface { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IPLAYER_H include/batteryservice0100644 0000000 0000000 00000000000 13300556574 025755 2../services/batteryservice/include/batteryservice/ustar000000000 0000000 include/binder0100644 0000000 0000000 00000000000 13300556574 017713 2../libs/binder/include/binder/ustar000000000 0000000 include/diskusage/0040755 0000000 0000000 00000000000 13300556574 013222 5ustar000000000 0000000 include/diskusage/dirsize.h0100644 0000000 0000000 00000001522 13300556574 015041 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 13300556574 012027 5ustar000000000 0000000 include/gfx/.clang-format0100644 0000000 0000000 00000000440 13300556574 014375 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 13300556574 016017 2../libs/gui/include/guiustar000000000 0000000 include/input/0040755 0000000 0000000 00000000000 13300556574 012402 5ustar000000000 0000000 include/input/DisplayViewport.h0100644 0000000 0000000 00000006325 13300556574 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 namespace android { /* * 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; String8 uniqueId; 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) { } 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; } 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(); } }; /** * 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, }; } // namespace android #endif // _LIBINPUT_DISPLAY_VIEWPORT_H include/input/IInputFlinger.h0100644 0000000 0000000 00000002513 13300556574 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. */ #ifndef _LIBINPUT_IINPUT_FLINGER_H #define _LIBINPUT_IINPUT_FLINGER_H #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) }; /** * Binder implementation. */ class BnInputFlinger : public BnInterface { public: enum { DO_SOMETHING_TRANSACTION = IBinder::FIRST_CALL_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/Input.h0100644 0000000 0000000 00000050510 13300556574 013650 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 /** * Native input event structures. */ #include #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, /* 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, }; /* * 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 scale); 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; } protected: void initialize(int32_t deviceId, int32_t source); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; }; /* * 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 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 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 action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, 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 scaleFactor); // 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; 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: 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/InputDevice.h0100644 0000000 0000000 00000013547 13300556574 015001 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 namespace android { /* * Identifies a device. */ struct InputDeviceIdentifier { inline InputDeviceIdentifier() : bus(0), vendor(0), product(0), version(0) { } // Information provided by the kernel. String8 name; String8 location; String8 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. String8 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; }; /* * 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 String8& 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 String8& getAlias() const { return mAlias; } inline const String8& getDisplayName() const { return mAlias.isEmpty() ? 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 Vector& getMotionRanges() const { return mMotionRanges; } private: int32_t mId; int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; String8 mAlias; bool mIsExternal; bool mHasMic; uint32_t mSources; int32_t mKeyboardType; sp mKeyCharacterMap; bool mHasVibrator; bool mHasButtonUnderPad; 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 String8 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 String8 getInputDeviceConfigurationFilePathByName( const String8& name, InputDeviceConfigurationFileType type); } // namespace android #endif // _LIBINPUT_INPUT_DEVICE_H include/input/InputEventLabels.h0100644 0000000 0000000 00000032224 13300556574 015777 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), { NULL, 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. { NULL, 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 { NULL, 0 } }; static const InputEventLabel FLAGS[] = { DEFINE_FLAG(VIRTUAL), DEFINE_FLAG(FUNCTION), DEFINE_FLAG(GESTURE), { NULL, 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 NULL; } 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 NULL; } 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 00000041650 13300556574 015572 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 /** * 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 namespace android { /* * 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. */ 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; 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; nsecs_t downTime __attribute__((aligned(8))); inline size_t size() const { return sizeof(Key); } } key; struct Motion { uint32_t seq; 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; int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); float xOffset; float yOffset; float xPrecision; float yPrecision; uint32_t pointerCount; // 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; }; /* * 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(const String8& name, int fd); /* Creates a pair of input channels. * * Returns OK on success. */ static status_t openInputChannelPair(const String8& name, sp& outServerChannel, sp& outClientChannel); inline String8 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; private: String8 mName; int mFd; }; /* * 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 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, 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, int32_t* displayId); /* 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); } } 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, int32_t* displayId); status_t consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId); void updateTouchState(InputMessage& msg); bool rewriteMessage(const TouchState& state, 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 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/KeyCharacterMap.h0100644 0000000 0000000 00000021641 13300556574 015557 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 #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, 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 String8& filename, Format format, sp* outMap); /* Loads a key character map from its string contents. */ static status_t loadContents(const String8& 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 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 String8& 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 00000006205 13300556574 015137 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 String8& 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, 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 00000006046 13300556574 014316 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 #include namespace android { enum { /* Device id of the built in keyboard. */ DEVICE_ID_BUILT_IN_KEYBOARD = 0, /* Device id of a generic virtual keyboard with a full layout that can be used * to synthesize key events. */ DEVICE_ID_VIRTUAL_KEYBOARD = -1, }; class KeyLayoutMap; class KeyCharacterMap; /** * Loads the key layout map and key character map for a keyboard device. */ class KeyMap { public: String8 keyLayoutFile; sp keyLayoutMap; String8 keyCharacterMapFile; sp keyCharacterMap; KeyMap(); ~KeyMap(); status_t load(const InputDeviceIdentifier& deviceIdenfier, const PropertyMap* deviceConfiguration); inline bool haveKeyLayout() const { return !keyLayoutFile.isEmpty(); } inline bool haveKeyCharacterMap() const { return !keyCharacterMapFile.isEmpty(); } inline bool isComplete() const { return haveKeyLayout() && haveKeyCharacterMap(); } private: bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name); status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name); String8 getPath(const InputDeviceIdentifier& deviceIdentifier, const String8& 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/VelocityControl.h0100644 0000000 0000000 00000007232 13300556574 015713 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 00000021147 13300556574 015667 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 = NULL); ~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]; }; } // namespace android #endif // _LIBINPUT_VELOCITY_TRACKER_H include/input/VirtualKeyMap.h0100644 0000000 0000000 00000003742 13300556574 015313 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 status_t load(const String8& filename, VirtualKeyMap** outMap); inline const 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); }; Vector mVirtualKeys; VirtualKeyMap(); }; } // namespace android #endif // _LIBINPUT_KEY_CHARACTER_MAP_H include/media0100644 0000000 0000000 00000000000 13300556574 017515 2../headers/media_plugin/mediaustar000000000 0000000 include/powermanager/0040755 0000000 0000000 00000000000 13300556574 013732 5ustar000000000 0000000 include/powermanager/IPowerManager.h0100644 0000000 0000000 00000007370 13300556574 016607 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 = 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 13300556574 016467 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 13300556574 012715 5ustar000000000 0000000 include/private/binder0100644 0000000 0000000 00000000000 13300556574 023173 2../../libs/binder/include/private/binderustar000000000 0000000 include/private/gui0100644 0000000 0000000 00000000000 13300556574 021356 2../../libs/gui/include/private/guiustar000000000 0000000 include/private/ui/0040755 0000000 0000000 00000000000 13300556574 013332 5ustar000000000 0000000 include/private/ui/RegionHelper.h0100644 0000000 0000000 00000022671 13300556574 016073 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 { TYPE left, right; 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 13300556574 015332 2../libs/ui/include/uiustar000000000 0000000 include/vr/0040755 0000000 0000000 00000000000 13300556574 011672 5ustar000000000 0000000 include/vr/vr_manager/0040755 0000000 0000000 00000000000 13300556574 014013 5ustar000000000 0000000 include/vr/vr_manager/vr_manager.h0100644 0000000 0000000 00000005270 13300556574 016306 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 13300556574 012634 5ustar000000000 0000000 include_sensor/android/0040755 0000000 0000000 00000000000 13300556574 014254 5ustar000000000 0000000 include_sensor/android/looper.h0100644 0000000 0000000 00000000000 13300556574 023052 2../../include/android/looper.hustar000000000 0000000 include_sensor/android/sensor.h0100644 0000000 0000000 00000000000 13300556574 023074 2../../include/android/sensor.hustar000000000 0000000 libs/0040755 0000000 0000000 00000000000 13300556574 010551 5ustar000000000 0000000 libs/arect/0040755 0000000 0000000 00000000000 13300556574 011647 5ustar000000000 0000000 libs/arect/Android.bp0100644 0000000 0000000 00000001607 13300556574 013553 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"], } libs/arect/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 014767 0ustar000000000 0000000 libs/arect/NOTICE0100644 0000000 0000000 00000024707 13300556574 012562 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 13300556574 013272 5ustar000000000 0000000 libs/arect/include/android/0040755 0000000 0000000 00000000000 13300556574 014712 5ustar000000000 0000000 libs/arect/include/android/rect.h0100644 0000000 0000000 00000002520 13300556574 016014 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 /** * {@link ARect} is a struct that represents a rectangular window area. * * It is used with {@link * ANativeActivityCallbacks::onContentRectChanged} event callback and * ANativeWindow_lock() function. */ typedef struct ARect { #ifdef __cplusplus typedef int32_t value_type; #endif /** left position */ int32_t left; /** top position */ int32_t top; /** left position */ int32_t right; /** bottom position */ int32_t bottom; } ARect; #ifdef __cplusplus }; #endif #endif // ANDROID_RECT_H /** @} */ libs/binder/0040755 0000000 0000000 00000000000 13300556574 012014 5ustar000000000 0000000 libs/binder/Android.bp0100644 0000000 0000000 00000005112 13300556574 013713 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 { name: "libbinder", // for vndbinder vendor_available: true, vndk: { enabled: true, }, srcs: [ "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", "MemoryBase.cpp", "MemoryDealer.cpp", "MemoryHeapBase.cpp", "Parcel.cpp", "PermissionCache.cpp", "PersistableBundle.cpp", "ProcessInfoService.cpp", "ProcessState.cpp", "Static.cpp", "Status.cpp", "TextOutput.cpp", "IpPrefix.cpp", "Value.cpp", "aidl/android/content/pm/IPackageManagerNative.aidl", ], aidl: { export_aidl_headers: true, }, cflags: [ "-Wall", "-Wextra", "-Werror", ], product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, shared_libs: [ "libbase", "liblog", "libcutils", "libutils", ], header_libs: [ "libbinder_headers", ], export_header_lib_headers: [ "libbinder_headers", ], clang: true, sanitize: { misc_undefined: ["integer"], }, } subdirs = ["tests"] libs/binder/AppOpsManager.cpp0100644 0000000 0000000 00000010754 13300556574 015221 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 == NULL || 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 == NULL || !IInterface::asBinder(service)->isBinderAlive()) { sp binder = defaultServiceManager()->checkService(_appops); if (binder == NULL) { // 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 = NULL; 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 != NULL ? service->checkOperation(op, 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 != NULL ? service->noteOperation(op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } int32_t AppOpsManager::startOp(int32_t op, int32_t uid, const String16& callingPackage) { sp service = getService(); return service != NULL ? service->startOperation(getToken(service), op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { sp service = getService(); if (service != NULL) { service->finishOperation(getToken(service), op, uid, callingPackage); } } void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, const sp& callback) { sp service = getService(); if (service != NULL) { service->startWatchingMode(op, packageName, callback); } } void AppOpsManager::stopWatchingMode(const sp& callback) { sp service = getService(); if (service != NULL) { service->stopWatchingMode(callback); } } int32_t AppOpsManager::permissionToOpCode(const String16& permission) { sp service = getService(); if (service != NULL) { return service->permissionToOpCode(permission); } return -1; } }; // namespace android libs/binder/Binder.cpp0100644 0000000 0000000 00000017626 13300556574 013734 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 NULL; } BBinder* IBinder::localBinder() { return NULL; } BpBinder* IBinder::remoteBinder() { return NULL; } 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 != NULL ? IInterface::asBinder(callback) : NULL); send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL); return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply); } // --------------------------------------------------------------------------- class BBinder::Extras { public: 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; } 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 != NULL) { reply->setDataPosition(0); } return err; } status_t BBinder::linkToDeath( const sp& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/) { return INVALID_OPERATION; } 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 = 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 == 0) 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 NULL; 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; } BBinder::~BBinder() { Extras* e = mExtras.load(std::memory_order_relaxed); if (e) delete e; } 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 != NULL) { resultReceiver->send(INVALID_OPERATION); } } case SYSPROPS_TRANSACTION: { report_sysprop_change(); return NO_ERROR; } default: return UNKNOWN_TRANSACTION; } } // --------------------------------------------------------------------------- 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(NULL), 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 00000023516 13300556574 014211 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 //#undef ALOGV //#define ALOGV(...) fprintf(stderr, __VA_ARGS__) namespace android { // --------------------------------------------------------------------------- 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 NULL; 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; iincWeakHandle(handle); } 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; } 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; } 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 == NULL, "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; } 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 == NULL && obit.cookie == cookie)) && obit.flags == flags) { if (outRecipient != NULL) { *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 = NULL; } 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 != NULL) { ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); IPCThreadState* self = IPCThreadState::self(); self->clearDeathNotification(mHandle, this); self->flushCommands(); mObituaries = NULL; } mObitsSent = 1; mLock.unlock(); ALOGV("Reporting death of proxy %p for %zu recipients\n", this, obits ? obits->size() : 0U); if (obits != NULL) { 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 == NULL) 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(); mLock.lock(); Vector* obits = mObituaries; if(obits != NULL) { if (ipc) ipc->clearDeathNotification(mHandle, this); mObituaries = NULL; } mLock.unlock(); if (obits != NULL) { // 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); } 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; } // --------------------------------------------------------------------------- }; // namespace android libs/binder/BufferedTextOutput.cpp0100644 0000000 0000000 00000017154 13300556574 016335 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 // --------------------------------------------------------------------------- namespace android { struct BufferedTextOutput::BufferState : public RefBase { explicit BufferState(int32_t _seq) : seq(_seq) , buffer(NULL) , 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 mutex_t gMutex; 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; mutex_lock(&gMutex); if (gFreeBufferIndex >= 0) { res = gFreeBufferIndex; gFreeBufferIndex = gTextBuffers[res]; gTextBuffers.editItemAt(res) = -1; } else { res = gTextBuffers.size(); gTextBuffers.add(-1); } mutex_unlock(&gMutex); return res; } static void freeBufferIndex(int32_t idx) { mutex_lock(&gMutex); gTextBuffers.editItemAt(idx) = gFreeBufferIndex; gFreeBufferIndex = idx; 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+1; 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(NULL); BufferState* bs = ts->states[mIndex].get(); if (bs != NULL && bs->seq == mSeq) return bs; ts->states.editItemAt(mIndex) = new BufferState(mIndex); bs = ts->states[mIndex].get(); if (bs != NULL) return bs; } } return mGlobalState; } }; // namespace android libs/binder/Debug.cpp0100644 0000000 0000000 00000020330 13300556574 013541 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 == NULL) func = defaultPrintFunc; size_t offset; unsigned char *pos = (unsigned char *)buf; if (pos == NULL) { 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; ) { const size_t startIndex = word+(alignment-(alignment?1:0)); 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() == NULL) { return 0; } return proc->getKernelReferences(count, buf); } }; // namespace android libs/binder/IActivityManager.cpp0100644 0000000 0000000 00000004306 13300556574 015720 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 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; } }; // ------------------------------------------------------------------------------------ IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager"); }; // namespace android libs/binder/IAppOpsCallback.cpp0100644 0000000 0000000 00000004131 13300556574 015444 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"); // ---------------------------------------------------------------------- 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 = data.readString16(); 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 00000020443 13300556574 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) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); 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 NULL; 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(); } }; IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService"); // ---------------------------------------------------------------------- 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(); int32_t res = startOperation(token, code, uid, packageName); 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; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IBatteryStats.cpp0100644 0000000 0000000 00000020576 13300556574 015271 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"); // ---------------------------------------------------------------------- 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 00000003734 13300556574 014535 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 == NULL) return NULL; return const_cast(iface)->onAsBinder(); } // static sp IInterface::asBinder(const sp& iface) { if (iface == NULL) return NULL; return iface->onAsBinder(); } // --------------------------------------------------------------------------- }; // namespace android extern "C" { void _ZN7android10IInterface8asBinderEv(void *retval, void* self) { ALOGW("deprecated asBinder call, please update your code"); //ALOGI("self: %p, retval: %p", self, retval); android::sp *ret = new(retval) android::sp; *ret = android::IInterface::asBinder((android::IInterface*)self); } void _ZNK7android10IInterface8asBinderEv(void *retval, void *self) { ALOGW("deprecated asBinder call, please update your code"); //ALOGI("self: %p, retval: %p", self, retval); android::sp *ret = new(retval) android::sp; *ret = android::IInterface::asBinder((android::IInterface*)self); } } // extern "C" libs/binder/IMediaResourceMonitor.cpp0100644 0000000 0000000 00000004222 13300556574 016725 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 00000035427 13300556574 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 #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; virtual uint32_t getOffset() const; 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 uint32_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(); virtual sp getMemory(ssize_t* offset=0, size_t* size=0) 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 0; return static_cast(base) + offset; } void* IMemory::pointer() const { ssize_t offset; sp heap = getMemory(&offset); void* const base = heap!=0 ? heap->base() : MAP_FAILED; if (base == MAP_FAILED) return 0; return static_cast(base) + offset; } size_t IMemory::size() const { size_t size; getMemory(NULL, &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() { } sp BpMemory::getMemory(ssize_t* offset, size_t* size) const { if (mHeap == 0) { Parcel data, reply; data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp heap = reply.readStrongBinder(); ssize_t o = reply.readInt32(); size_t s = reply.readInt32(); if (heap != 0) { mHeap = interface_cast(heap); if (mHeap != 0) { size_t heapSize = mHeap->getSize(); if (s <= heapSize && o >= 0 && (static_cast(o) <= heapSize - s)) { mOffset = o; mSize = s; } else { // Hm. android_errorWriteWithInfoLog(0x534e4554, "26877992", -1, NULL, 0); mOffset = 0; mSize = 0; } } } } } if (offset) *offset = mOffset; if (size) *size = mSize; return (mSize > 0) ? mHeap : 0; } // --------------------------------------------------------------------------- IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); BnMemory::BnMemory() { } BnMemory::~BnMemory() { } 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->writeInt32(offset); reply->writeInt32(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); CallStack stack(LOG_TAG); } 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(); ssize_t size = reply.readInt32(); uint32_t flags = reply.readInt32(); uint32_t offset = reply.readInt32(); ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)", IInterface::asBinder(this).get(), parcel_fd, size, err, strerror(-err)); 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=%zd, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; if (!(flags & READ_ONLY)) { access |= PROT_WRITE; } mRealHeap = true; mBase = mmap(0, size, access, MAP_SHARED, fd, offset); if (mBase == MAP_FAILED) { ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, 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; } uint32_t BpMemoryHeap::getOffset() const { assertMapped(); return mOffset; } // --------------------------------------------------------------------------- IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); BnMemoryHeap::BnMemoryHeap() { } BnMemoryHeap::~BnMemoryHeap() { } 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->writeInt32(getSize()); reply->writeInt32(getFlags()); reply->writeInt32(getOffset()); 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 00000106211 13300556574 015262 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 #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" }; 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 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 NULL; } 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 NULL; } 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 NULL; } 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, NULL); } 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; } uid_t IPCThreadState::getCallingUid() const { return mCallingUid; } int64_t IPCThreadState::clearCallingIdentity() { 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; } 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); mCallingPid = (int)token; } void IPCThreadState::clearCaller() { mCallingPid = getpid(); mCallingUid = getuid(); } void IPCThreadState::flushCommands() { if (mProcess->mDriverFD <= 0) return; talkWithDriver(false); } 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()) { size_t numPending = mPendingWeakDerefs.size(); if (numPending > 0) { for (size_t i = 0; i < numPending; i++) { RefBase::weakref_type* refs = mPendingWeakDerefs[i]; refs->decWeak(mProcess.get()); } mPendingWeakDerefs.clear(); } numPending = mPendingStrongDerefs.size(); if (numPending > 0) { for (size_t i = 0; i < numPending; i++) { BBinder* obj = mPendingStrongDerefs[i]; obj->decStrong(mProcess.get()); } mPendingStrongDerefs.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 = data.errorCheck(); 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; } if (err == NO_ERROR) { 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, NULL); } if (err != NO_ERROR) { if (reply) reply->setError(err); return (mLastError = err); } if ((flags & TF_ONE_WAY) == 0) { #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(NULL, NULL); } return err; } void IPCThreadState::incStrongHandle(int32_t handle) { LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); mOut.writeInt32(BC_ACQUIRE); mOut.writeInt32(handle); } 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) { LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); mOut.writeInt32(BC_INCREFS); mOut.writeInt32(handle); } 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); } 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()), mStrictModePolicy(0), mLastTransactionBinderFlags(0) { pthread_setspecific(gTLS, this); clearCaller(); mIn.setDataCapacity(256); mOut.setDataCapacity(256); } 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(NULL, NULL); } 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(NULL, 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(NULL, 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); } 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: { binder_transaction_data tr; result = mIn.read(&tr, sizeof(tr)); ALOG_ASSERT(result == NO_ERROR, "Not enough command data for brTRANSACTION"); if (result != NO_ERROR) break; 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 uid_t origUid = mCallingUid; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; mCallingPid = tr.sender_pid; mCallingUid = tr.sender_euid; mLastTransactionBinderFlags = tr.flags; //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, 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); } //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", // mCallingPid, origPid, 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; mCallingUid = origUid; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; 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; } 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 != NULL) 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 00000010505 13300556574 017023 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 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; } }; IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); // ---------------------------------------------------------------------- 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 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; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IProcessInfoService.cpp0100644 0000000 0000000 00000006526 13300556574 016412 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 00000004005 13300556574 015570 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, NULL, IBinder::FLAG_ONEWAY); } }; IMPLEMENT_META_INTERFACE(ResultReceiver, "com.android.internal.os.IResultReceiver"); // ---------------------------------------------------------------------- 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 != NULL) { reply->writeNoException(); } return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android libs/binder/IServiceManager.cpp0100644 0000000 0000000 00000014306 13300556574 015525 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 #include #include #include #include #include #include namespace android { sp defaultServiceManager() { if (gDefaultServiceManager != NULL) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); while (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast( ProcessState::self()->getContextObject(NULL)); if (gDefaultServiceManager == NULL) sleep(1); } } return gDefaultServiceManager; } bool checkCallingPermission(const String16& permission) { return checkCallingPermission(permission, NULL, NULL); } 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 != NULL) { 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 = NULL; } gDefaultServiceManagerLock.unlock(); } // Need to retrieve the permission controller. sp binder = defaultServiceManager()->checkService(_permission); if (binder == NULL) { // 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(); } } } // ---------------------------------------------------------------------- class BpServiceManager : public BpInterface { public: explicit BpServiceManager(const sp& impl) : BpInterface(impl) { } virtual sp getService(const String16& name) const { unsigned n; for (n = 0; n < 5; n++){ if (n > 0) { if (!strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder")) { ALOGI("Waiting for vendor service %s...", String8(name).string()); CallStack stack(LOG_TAG); } else { ALOGI("Waiting for service %s...", String8(name).string()); } sleep(1); } sp svc = checkService(name); if (svc != NULL) return svc; } return NULL; } 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) { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); data.writeStrongBinder(service); data.writeInt32(allowIsolated ? 1 : 0); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); return err == NO_ERROR ? reply.readExceptionCode() : err; } virtual Vector listServices() { Vector res; int n = 0; for (;;) { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeInt32(n++); 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 00000005207 13300556574 015316 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 openOutputFile(const String16& path, const String16& seLinuxContext) { Parcel data, reply; data.writeInterfaceToken(IShellCallback::getInterfaceDescriptor()); data.writeString16(path); data.writeString16(seLinuxContext); 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"); // ---------------------------------------------------------------------- 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()); int fd = openOutputFile(path, seLinuxContext); if (reply != NULL) { 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/IpPrefix.cpp0100644 0000000 0000000 00000010762 13300556574 014251 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 13300556574 014562 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 00000032256 13300556574 015112 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(0), mLast(0) { } bool isEmpty() const { return mFirst == 0; } 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 == 0) 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 == 0) mFirst = newNode; else node->prev->next = newNode; node->prev = newNode; } void insertHead(NODE* newNode) { if (mFirst == 0) { mFirst = mLast = newNode; newNode->prev = newNode->next = 0; } else { newNode->prev = 0; 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 == 0) mFirst = node->next; else node->prev->next = node->next; if (node->next == 0) 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(0), next(0) { } 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()) { delete mList.remove(mList.head()); } } 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 = 0; 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 0; } 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 00000011246 13300556574 015362 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(NULL), 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(0), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); int fd = ashmem_create_region(name == NULL ? "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(0), 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, uint32_t offset) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(0), 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, int 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, uint32_t offset) { if (size == 0) { // try to figure out the size automatically struct stat sb; if (fstat(fd, &sb) == 0) size = sb.st_size; // if it didn't work, let mmap() fail. } if ((mFlags & DONT_MAP_LOCALLY) == 0) { void* base = (uint8_t*)mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (base == MAP_FAILED) { ALOGE("mmap(fd=%d, size=%u) failed (%s)", fd, uint32_t(size), strerror(errno)); close(fd); return -errno; } //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); mBase = base; mNeedUnmap = true; } else { mBase = 0; // 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=%lu)", fd, mBase, mSize); munmap(mBase, mSize); } mBase = 0; 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; } uint32_t MemoryHeapBase::getOffset() const { return mOffset; } // --------------------------------------------------------------------------- }; // namespace android libs/binder/Parcel.cpp0100644 0000000 0000000 00000224077 13300556574 013737 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 (0x40 << 16) // XXX This can be made public if we want to provide // support for typed data. struct small_flat_data { uint32_t type; uint32_t data; }; 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.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 != NULL) { 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 != NULL) b.get_refs()->incWeak(who); return; } case BINDER_TYPE_FD: { if ((obj.cookie != 0) && (outAshmemSize != NULL) && 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.type); } void acquire_object(const sp& proc, const flat_binder_object& obj, const void* who) { acquire_object(proc, obj, who, NULL); } static void release_object(const sp& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { switch (obj.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 != NULL) { 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 != NULL) b.get_refs()->decWeak(who); return; } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned if ((outAshmemSize != NULL) && ashmem_valid(obj.handle)) { int size = ashmem_get_size_region(obj.handle); if (size > 0) { *outAshmemSize -= size; } } close(obj.handle); } return; } } ALOGE("Invalid object type 0x%08x", obj.type); } void release_object(const sp& proc, const flat_binder_object& obj, const void* who) { release_object(proc, obj, who, NULL); } 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 != NULL) { IBinder *local = binder->localBinder(); if (!local) { BpBinder *proxy = binder->remoteBinder(); if (proxy == NULL) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; obj.type = BINDER_TYPE_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { obj.type = BINDER_TYPE_BINDER; obj.binder = reinterpret_cast(local->getWeakRefs()); obj.cookie = reinterpret_cast(local); } } else { obj.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 != NULL) { sp real = binder.promote(); if (real != NULL) { IBinder *local = real->localBinder(); if (!local) { BpBinder *proxy = real->remoteBinder(); if (proxy == NULL) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; obj.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.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.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(NULL, obj, out); } else { obj.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(NULL, 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->type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast(flat->cookie); return finish_unflatten_binder(NULL, *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->type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast(flat->cookie); return finish_unflatten_binder(NULL, *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 = NULL; } return finish_unflatten_binder(NULL, *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; } 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) { const sp proc(ProcessState::self()); 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) { // 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*)0) { 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->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; } // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { writeInt32(IPCThreadState::self()->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); // currently the interface identification token is just its name as a string return writeString16(interface); } bool Parcel::checkInterface(IBinder* binder) const { return enforceInterface(binder->getInterfaceDescriptor()); } bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { int32_t strictPolicy = readInt32(); if (threadState == NULL) { 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); } 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 NULL; } const size_t padded = pad_size(len); // sanity check for integer overflow if (mDataPos+padded < mDataPos) { return NULL; } 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 NULL; } 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::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 == NULL) 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.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::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(NULL, 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(pad_size(len)); if (buf == NULL) return BAD_VALUE; int* fds = NULL; 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.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 == NULL) 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 == NULL) { 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::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)) { 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 NULL; } if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { const void* data = mData+mDataPos; mDataPos += pad_size(len); ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos); return data; } return NULL; } 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) { 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::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 { const size_t avail = mDataSize-mDataPos; if (avail > 0) { 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 NULL; } 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 == NULL) { 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 != NULL) { return str; } } *outLen = 0; return NULL; } 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 0; err = readInt32(&numInts); if (err != NO_ERROR) return 0; native_handle* h = native_handle_create(numFds, numInts); if (!h) { return 0; } 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 0; } } err = read(h->data + numFds, sizeof(int)*numInts); if (err != NO_ERROR) { native_handle_close(h); native_handle_delete(h); h = 0; } return h; } int Parcel::readFileDescriptor() const { const flat_binder_object* flat = readObject(true); if (flat && flat->type == BINDER_TYPE_FD) { return flat->handle; } return BAD_TYPE; } int Parcel::readParcelFileDescriptor() const { int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { // skip readFileDescriptor(); } 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::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; void* ptr = ::mmap(NULL, 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 == NULL) return BAD_VALUE; int* fds = NULL; 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 NULL; } 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->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; 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->type & 0x7f7f7f00) << " = " << flat->binder; } } else { to << "NULL"; } to << ")"; } void Parcel::releaseObjects() { const sp proc(ProcessState::self()); size_t i = mObjectsSize; 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() { const sp proc(ProcessState::self()); size_t i = mObjectsSize; 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 = NULL; mObjectsSize = mObjectsCapacity = 0; mNextObjectHint = 0; 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 = NULL; 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 = NULL; 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; } 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->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); } binder_size_t* objects = (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t)); if (objects) { mObjects = objects; } mObjectsSize = objectsSize; mNextObjectHint = 0; } // 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 if (desired > mDataCapacity) { 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 == NULL && 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 = 0; 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 = NULL; mObjectsSize = 0; mObjectsCapacity = 0; mNextObjectHint = 0; mHasFds = false; mFdsKnown = true; mAllowFds = true; mOwner = NULL; mOpenAshmemSize = 0; // 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->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(NULL), 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 = NULL; mSize = 0; mMutable = false; } }; // namespace android libs/binder/PermissionCache.cpp0100644 0000000 0000000 00000006700 13300556574 015574 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, NULL, NULL); } 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/PersistableBundle.cpp0100644 0000000 0000000 00000037642 13300556574 016140 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 in sync with BUNDLE_MAGIC in frameworks/base/core/java/android/os/BaseBundle.java. BUNDLE_MAGIC = 0x4C444E42, }; 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)); 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) { 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 00000006252 13300556574 016275 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 != NULL) { 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 != NULL) { 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 != NULL) { const String16 name("processinfo"); mProcessInfoService = interface_cast(sm->checkService(name)); } } ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService); }; // namespace android libs/binder/ProcessState.cpp0100644 0000000 0000000 00000032705 13300556574 015143 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 // ------------------------------------------------------------------------- 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 != NULL) { return gProcess; } gProcess = new ProcessState("/dev/binder"); return gProcess; } sp ProcessState::initWithDriver(const char* driver) { Mutex::Autolock _l(gProcessMutex); if (gProcess != NULL) { // 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."); } 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) : NULL); mLock.unlock(); //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); if (object != NULL) 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 NULL; } 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 != NULL) 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; int dummy = 0; status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); if (result == 0) { mManagesContexts = true; } else if (result == -1) { mBinderContextCheckFunc = NULL; mBinderContextUserData = NULL; 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) { // TODO: remove these when they are defined by bionic's binder.h struct binder_node_debug_info { binder_uintptr_t ptr; binder_uintptr_t cookie; __u32 has_strong_ref; __u32 has_weak_ref; }; #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) binder_node_debug_info info = {}; uintptr_t* end = buf ? buf + buf_count : NULL; 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; } ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) { const size_t N=mHandleToObject.size(); if (N <= (size_t)handle) { handle_entry e; e.binder = NULL; e.refs = NULL; status_t err = mHandleToObject.insertAt(e, N, handle+1-N); if (err < NO_ERROR) return NULL; } return &mHandleToObject.editItemAt(handle); } sp ProcessState::getStrongProxyForHandle(int32_t handle) { sp result; AutoMutex _l(mLock); handle_entry* e = lookupHandleLocked(handle); if (e != NULL) { // 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 == NULL || !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, NULL, 0); if (status == DEAD_OBJECT) return NULL; } b = new BpBinder(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 != NULL) { // 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 == NULL || !e->refs->attemptIncWeak(this)) { b = new BpBinder(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 = NULL; } 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(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); 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 00000004674 13300556574 013757 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; sp gProcess; class LibBinderIPCtStatics { public: LibBinderIPCtStatics() { } ~LibBinderIPCtStatics() { IPCThreadState::shutdown(); } }; static LibBinderIPCtStatics gIPCStatics; // ------------ IServiceManager.cpp Mutex gDefaultServiceManagerLock; sp gDefaultServiceManager; sp gPermissionController; } // namespace android libs/binder/Status.cpp0100644 0000000 0000000 00000013331 13300556574 014001 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) { return Status(exceptionCode, OK); } Status Status::fromExceptionCode(int32_t exceptionCode, const String8& 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; } 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 int32_t header_start = parcel.dataPosition(); int32_t header_size; status = parcel.readInt32(&header_size); if (status != OK) { setFromStatusT(status); return status; } 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); if (mException == EX_SERVICE_SPECIFIC) { status = parcel.readInt32(&mErrorCode); } else if (mException == EX_PARCELABLE) { // Skip over the blob of Parcelable data const int32_t header_start = parcel.dataPosition(); int32_t header_size; status = parcel.readInt32(&header_size); if (status != OK) { setFromStatusT(status); return status; } 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)); 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 = NO_ERROR; // an exception, not a transaction failure. 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): '", mException); 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 13300556574 014671 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 00000034567 13300556574 013610 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; 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(NULL) { } Value::Value(const Value& value) : mContent(value.mContent ? value.mContent->clone() : NULL) { } 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 == NULL) || (rhs.mContent == NULL) ) { 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) { delete mContent; mContent = rhs.mContent ? rhs.mContent->clone() : NULL; return *this; } bool Value::empty() const { return mContent == NULL; } void Value::clear() { delete mContent; mContent = NULL; } int32_t Value::parcelType() const { const void* t_info(mContent ? mContent->type_ptr() : NULL); 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 != NULL ? 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():NULL); \ 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 = NULL; 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 13300556574 012725 5ustar000000000 0000000 libs/binder/aidl/android/0040755 0000000 0000000 00000000000 13300556574 014345 5ustar000000000 0000000 libs/binder/aidl/android/content/0040755 0000000 0000000 00000000000 13300556574 016017 5ustar000000000 0000000 libs/binder/aidl/android/content/pm/0040755 0000000 0000000 00000000000 13300556574 016433 5ustar000000000 0000000 libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl0100644 0000000 0000000 00000004036 13300556574 023374 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. */ int getVersionCodeForPackage(in String packageName); } libs/binder/include/0040755 0000000 0000000 00000000000 13300556574 013437 5ustar000000000 0000000 libs/binder/include/binder/0040755 0000000 0000000 00000000000 13300556574 014702 5ustar000000000 0000000 libs/binder/include/binder/AppOpsManager.h0100644 0000000 0000000 00000007316 13300556574 017554 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 #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, }; AppOpsManager(); int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t startOp(int32_t op, int32_t uid, const String16& callingPackage); 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 // --------------------------------------------------------------------------- #endif // ANDROID_APP_OPS_MANAGER_H libs/binder/include/binder/Binder.h0100644 0000000 0000000 00000007063 13300556574 016261 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); virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual status_t linkToDeath(const sp& recipient, void* cookie = NULL, uint32_t flags = 0); virtual status_t unlinkToDeath( const wp& recipient, void* cookie = NULL, uint32_t flags = 0, wp* outRecipient = NULL); 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(); protected: virtual ~BBinder(); 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; 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 00000003565 13300556574 017605 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) { sp sm(defaultServiceManager()); return sm->addService( String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated); } static void publishAndJoinThreadPool(bool allowIsolated = false) { publish(allowIsolated); 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 00000010146 13300556574 016537 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 // --------------------------------------------------------------------------- namespace android { class BpBinder : public IBinder { public: BpBinder(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); virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual status_t linkToDeath(const sp& recipient, void* cookie = NULL, uint32_t flags = 0); virtual status_t unlinkToDeath( const wp& recipient, void* cookie = NULL, uint32_t flags = 0, wp* outRecipient = NULL); 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(); 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: 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; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_BPBINDER_H libs/binder/include/binder/BufferedTextOutput.h0100644 0000000 0000000 00000003632 13300556574 020664 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 }; 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 00000002746 13300556574 016107 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 = 0, void* cookie = 0); 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 = 0, void* cookie = 0); 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 00000002340 13300556574 020247 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 #include namespace android { // ------------------------------------------------------------------------------------ class IActivityManager : public IInterface { public: DECLARE_META_INTERFACE(ActivityManager) virtual int openContentUri(const String16& /* stringUri */) = 0; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION }; }; // ------------------------------------------------------------------------------------ }; // namespace android #endif // ANDROID_IACTIVITY_MANAGER_Hlibs/binder/include/binder/IAppOpsCallback.h0100644 0000000 0000000 00000003062 13300556574 020001 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 #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: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IAPP_OPS_CALLBACK_H libs/binder/include/binder/IAppOpsService.h0100644 0000000 0000000 00000005626 13300556574 017715 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 #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) = 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; 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, }; enum { MODE_ALLOWED = 0, MODE_IGNORED = 1, MODE_ERRORED = 2 }; }; // ---------------------------------------------------------------------- class BnAppOpsService : public BnInterface { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IAPP_OPS_SERVICE_H libs/binder/include/binder/IBatteryStats.h0100644 0000000 0000000 00000005133 13300556574 017614 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 #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: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IBATTERYSTATS_H libs/binder/include/binder/IBinder.h0100644 0000000 0000000 00000015007 13300556574 016367 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 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); 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.) */ virtual status_t linkToDeath(const sp& recipient, void* cookie = NULL, 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. */ virtual status_t unlinkToDeath( const wp& recipient, void* cookie = NULL, uint32_t flags = 0, wp* outRecipient = NULL) = 0; virtual bool checkSubclass(const void* subclassID) const; typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) = 0; virtual void* findObject(const void* objectID) const = 0; 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 00000012342 13300556574 017063 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: virtual IBinder* onAsBinder(); }; // ---------------------------------------------------------------------- template class BpInterface : public INTERFACE, public BpRefBase { public: explicit BpInterface(const sp& remote); protected: virtual IBinder* onAsBinder(); }; // ---------------------------------------------------------------------- #define DECLARE_META_INTERFACE(INTERFACE) \ 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(); \ #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 != NULL) { \ intr = static_cast( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ if (intr == NULL) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { } \ #define CHECK_INTERFACE(interface, data, reply) \ if (!(data).checkInterface(this)) { return PERMISSION_DENIED; } \ // ---------------------------------------------------------------------- // No user-serviceable parts after this... template inline sp BnInterface::queryLocalInterface( const String16& _descriptor) { if (_descriptor == INTERFACE::descriptor) return this; return NULL; } 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 00000003241 13300556574 021260 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 #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 #endif // ANDROID_I_MEDIA_RESOURCE_MONITOR_H libs/binder/include/binder/IMemory.h0100644 0000000 0000000 00000005133 13300556574 016433 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 uint32_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: 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) virtual sp getMemory(ssize_t* offset=0, size_t* size=0) 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: 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 00000013265 13300556574 017623 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 IPCThreadState { public: static IPCThreadState* self(); static IPCThreadState* selfOrNull(); // self(), but won't instantiate sp process(); status_t clearLastError(); pid_t getCallingPid() const; uid_t getCallingUid() const; void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; void setLastTransactionBinderFlags(int32_t flags); int32_t getLastTransactionBinderFlags() const; int64_t clearCallingIdentity(); 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); void decStrongHandle(int32_t handle); void incWeakHandle(int32_t handle); 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(); private: IPCThreadState(); ~IPCThreadState(); status_t sendReply(const Parcel& reply, uint32_t flags); status_t waitForResponse(Parcel *reply, status_t *acquireResult=NULL); 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 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; Parcel mIn; Parcel mOut; status_t mLastError; pid_t mCallingPid; uid_t mCallingUid; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_IPC_THREAD_STATE_H libs/binder/include/binder/IPermissionController.h0100644 0000000 0000000 00000003704 13300556574 021361 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 #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 void getPackagesForUid(const uid_t uid, Vector &packages) = 0; virtual bool isRuntimePermission(const String16& permission) = 0; enum { CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, GET_PACKAGES_FOR_UID_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1, IS_RUNTIME_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 2 }; }; // ---------------------------------------------------------------------- class BnPermissionController : public BnInterface { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------- }; // namespace android #endif // ANDROID_IPERMISSION_CONTROLLER_H libs/binder/include/binder/IProcessInfoService.h0100644 0000000 0000000 00000003321 13300556574 020733 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 #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 #endif // ANDROID_I_PROCESS_INFO_SERVICE_H libs/binder/include/binder/IResultReceiver.h0100644 0000000 0000000 00000003006 13300556574 020123 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: 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 00000005070 13300556574 020056 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 #include namespace android { // ---------------------------------------------------------------------- class IServiceManager : public IInterface { public: DECLARE_META_INTERFACE(ServiceManager) /** * 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. */ virtual status_t addService( const String16& name, const sp& service, bool allowIsolated = false) = 0; /** * Return list of all existing services. */ virtual Vector listServices() = 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 != NULL) { *outService = interface_cast(sm->getService(name)); if ((*outService) != NULL) 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 00000003067 13300556574 017653 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 openOutputFile(const String16& path, const String16& seLinuxContext) = 0; enum { OP_OPEN_OUTPUT_FILE = IBinder::FIRST_CALL_TRANSACTION }; }; // ---------------------------------------------------------------------- class BnShellCallback : public BnInterface { public: 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/IpPrefix.h0100644 0000000 0000000 00000004677 13300556574 016614 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 #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; InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }; 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 #endif // ANDROID_IP_PREFIX_H libs/binder/include/binder/Map.h0100644 0000000 0000000 00000002134 13300556574 015565 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 13300556574 017120 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 00000003604 13300556574 017440 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: MemoryDealer(size_t size, const char* name = 0, 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 00000005631 13300556574 017716 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, uint32_t offset = 0); /* * maps memory from the given device */ MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); /* * maps memory from ashmem, with the given name for debugging */ MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); 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; virtual uint32_t getOffset() const; 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 == 0) mDevice = device; return mDevice ? NO_ERROR : ALREADY_EXISTS; } protected: MemoryHeapBase(); // init() takes ownership of fd status_t init(int fd, void *base, int size, int flags = 0, const char* device = NULL); private: status_t mapfd(int fd, size_t size, uint32_t offset = 0); int mFD; size_t mSize; void* mBase; uint32_t mFlags; const char* mDevice; bool mNeedUnmap; uint32_t mOffset; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_MEMORY_HEAP_BASE_H libs/binder/include/binder/Parcel.h0100644 0000000 0000000 00000103321 13300556574 016256 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 = NULL) 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 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 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 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 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(); 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; 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 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 : NULL; } }; 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 == NULL) 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 == NULL ? 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/Parcelable.h0100644 0000000 0000000 00000003574 13300556574 017113 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 00000004751 13300556574 020133 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 #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 #endif /* BINDER_PERMISSION_H */ libs/binder/include/binder/PersistableBundle.h0100644 0000000 0000000 00000013003 13300556574 020454 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 00000006025 13300556574 020626 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 #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 #endif // ANDROID_PROCESS_INFO_SERVICE_H libs/binder/include/binder/ProcessState.h0100644 0000000 0000000 00000011273 13300556574 017473 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(). /dev/binder remains the default. */ 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); private: friend class IPCThreadState; 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; }; }; // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_PROCESS_STATE_H libs/binder/include/binder/SafeInterface.h0100644 0000000 0000000 00000075422 13300556574 017561 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); }); } // 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 00000013714 13300556574 016341 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 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); 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 00000012435 13300556574 017222 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 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 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 00000014325 13300556574 016131 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); Value(const int8_t& value); Value(const int32_t& value); Value(const int64_t& value); Value(const double& value); Value(const String16& value); Value(const std::vector& value); Value(const std::vector& value); Value(const std::vector& value); Value(const std::vector& value); Value(const std::vector& value); Value(const std::vector& value); Value(const os::PersistableBundle& value); Value(const binder::Map& value); 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 // --------------------------- Value(const String8& value): Value(String16(value)) { } 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 13300556574 015111 5ustar000000000 0000000 libs/binder/include/private/binder/0040755 0000000 0000000 00000000000 13300556574 016354 5ustar000000000 0000000 libs/binder/include/private/binder/ParcelValTypes.h0100644 0000000 0000000 00000002325 13300556574 021422 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 00000002353 13300556574 017754 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 #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; extern sp gPermissionController; } // namespace android libs/binder/include/private/binder/binder_module.h0100644 0000000 0000000 00000001631 13300556574 021333 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/tests/0040755 0000000 0000000 00000000000 13300556574 013156 5ustar000000000 0000000 libs/binder/tests/Android.bp0100644 0000000 0000000 00000005273 13300556574 015065 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_test { name: "binderDriverInterfaceTest_IPC_32", srcs: ["binderDriverInterfaceTest.cpp"], compile_multilib: "32", cflags: ["-DBINDER_IPC_32BIT=1"], } cc_test { product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, name: "binderDriverInterfaceTest", srcs: ["binderDriverInterfaceTest.cpp"], } cc_test { name: "binderValueTypeTest", srcs: ["binderValueTypeTest.cpp"], shared_libs: [ "libbinder", "libutils", ], } cc_test { name: "binderLibTest_IPC_32", srcs: ["binderLibTest.cpp"], shared_libs: [ "libbinder", "libutils", ], compile_multilib: "32", cflags: ["-DBINDER_IPC_32BIT=1"], } cc_test { product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, name: "binderLibTest", srcs: ["binderLibTest.cpp"], shared_libs: [ "libbinder", "libutils", ], } cc_test { name: "binderThroughputTest", srcs: ["binderThroughputTest.cpp"], shared_libs: [ "libbinder", "libutils", ], clang: true, cflags: [ "-g", "-Wall", "-Werror", "-Wno-missing-field-initializers", "-Wno-sign-compare", "-O3", ], } cc_test { name: "binderTextOutputTest", srcs: ["binderTextOutputTest.cpp"], shared_libs: [ "libbinder", "libutils", "libbase", ], } cc_test { name: "schd-dbg", srcs: ["schd-dbg.cpp"], shared_libs: [ "libbinder", "libutils", "libbase", ], } cc_test { name: "binderSafeInterfaceTest", srcs: ["binderSafeInterfaceTest.cpp"], cppflags: [ "-Werror", "-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 00000025473 13300556574 020772 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(NULL, 64*1024, PROT_READ, MAP_SHARED, m_binderFd, 0); ASSERT_NE(m_buffer, (void *)NULL); 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: 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, WriteReadNull) { binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT); } TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNull) { binderTestIoctlErr2(BINDER_SET_IDLE_TIMEOUT, NULL, EFAULT, EINVAL); } TEST_F(BinderDriverInterfaceTest, SetMaxThreadsNull) { binderTestIoctlErr2(BINDER_SET_MAX_THREADS, NULL, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ } TEST_F(BinderDriverInterfaceTest, SetIdlePriorityNull) { binderTestIoctlErr2(BINDER_SET_IDLE_PRIORITY, NULL, EFAULT, EINVAL); } TEST_F(BinderDriverInterfaceTest, VersionNull) { binderTestIoctlErr2(BINDER_VERSION, NULL, 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"); binderTestIoctl(BINDER_WRITE_READ, &bwr); } 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 00000077221 13300556574 016422 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 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; 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_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_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, }; pid_t start_server_process(int arg2) { int ret; pid_t pid; status_t status; int pipefd[2]; char stri[16]; char strpipefd1[16]; char *childargv[] = { binderservername, binderserverarg, stri, strpipefd1, binderserversuffix, NULL }; ret = pipe(pipefd); if (ret < 0) return ret; snprintf(stri, sizeof(stri), "%d", arg2); snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]); 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(NULL); 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 != NULL); //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 != NULL) { 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(); } virtual void TearDown() { } protected: sp addServer(int32_t *idPtr = NULL) { int ret; int32_t id; Parcel data, reply; sp binder; ret = m_server->transact(BINDER_LIB_TEST_ADD_SERVER, data, &reply); EXPECT_EQ(NO_ERROR, ret); EXPECT_FALSE(binder != NULL); binder = reply.readStrongBinder(); EXPECT_TRUE(binder != NULL); ret = reply.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); if (idPtr) *idPtr = id; return binder; } 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) {} 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, NULL); pthread_cond_init(&m_waitCond, NULL); } 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; } protected: void triggerEvent(void) { pthread_mutex_lock(&m_waitMutex); pthread_cond_signal(&m_waitCond); m_eventTriggered = true; pthread_mutex_unlock(&m_waitMutex); }; private: pthread_mutex_t m_waitMutex; pthread_cond_t m_waitCond; bool m_eventTriggered; }; class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent { public: BinderLibTestCallBack() : m_result(NOT_ENOUGH_DATA) { } 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: m_result = data.readInt32(); triggerEvent(); return NO_ERROR; default: return UNKNOWN_TRANSACTION; } } status_t m_result; }; 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 != NULL); 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 != NULL); 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 != NULL); 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 != NULL); } TEST_F(BinderLibTest, DeathNotificationNoRefs) { status_t ret; sp testDeathRecipient = new TestDeathRecipient(); { sp binder = addServer(); ASSERT_TRUE(binder != NULL); 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 != NULL); 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 != NULL); 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 != NULL); for (int i = 0; i < clientcount; i++) { { Parcel data, reply; linkedclient[i] = addServer(); ASSERT_TRUE(linkedclient[i] != NULL); 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] != NULL); 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, 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, PromoteLocal) { sp strong = new BBinder(); wp weak = strong; sp strong_from_weak = weak.promote(); EXPECT_TRUE(strong != NULL); EXPECT_EQ(strong, strong_from_weak); strong = NULL; strong_from_weak = NULL; strong_from_weak = weak.promote(); EXPECT_TRUE(strong_from_weak == NULL); } TEST_F(BinderLibTest, PromoteRemote) { int ret; Parcel data, reply; sp strong = new BBinder(); sp server = addServer(); ASSERT_TRUE(server != NULL); ASSERT_TRUE(strong != NULL); 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 != NULL); EXPECT_EQ(BINDER_TYPE_HANDLE, fb->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 != NULL); __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; } } class BinderLibTestService : public BBinder { public: BinderLibTestService(int32_t id) : m_id(id) , m_nextServerId(id + 1) , m_serverStartRequested(false) { pthread_mutex_init(&m_serverWaitMutex, NULL); pthread_cond_init(&m_serverWaitCond, NULL); } ~BinderLibTestService() { exit(EXIT_SUCCESS); } 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 == NULL) { 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_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; pthread_mutex_unlock(&m_serverWaitMutex); ret = start_server_process(serverid); 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 = NULL; 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_NOP_CALL_BACK: { Parcel data2, reply2; sp binder; binder = data.readStrongBinder(); if (binder == NULL) { return BAD_VALUE; } reply2.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 == NULL) { 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 == NULL) { return BAD_VALUE; } callback = data.readStrongBinder(); if (callback == NULL) { 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 == NULL) { return BAD_VALUE; } ret = write(fd, 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 == NULL) { 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 == NULL) { 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(NULL) != -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; } 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; }; int run_server(int index, int readypipefd) { binderLibTestServiceName += String16(binderserversuffix); status_t ret; sp sm = defaultServiceManager(); { sp testService = new BinderLibTestService(index); 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__); 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 == 5 && !strcmp(argv[1], binderserverarg)) { binderserversuffix = argv[4]; return run_server(atoi(argv[2]), atoi(argv[3])); } 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 00000102613 13300556574 020405 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, 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; // 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(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(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::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, 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 00000011573 13300556574 020057 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(const CapturedStderr& cap, const char* expected, bool singleline) { std::string output; ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); android::base::ReadFdToString(cap.fd(), &output); if (singleline) output.erase(std::remove(output.begin(), output.end(), '\n')); ASSERT_STREQ(output.c_str(), 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; std::string output; ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); android::base::ReadFdToString(cap.fd(), &output); ASSERT_STREQ(output.c_str(), "foobar\n"); } TEST(TextOutput, HandlesCEndl) { CapturedStderr cap; android::aerr << "foobar" << "\n"; std::string output; ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); android::base::ReadFdToString(cap.fd(), &output); ASSERT_STREQ(output.c_str(), "foobar\n"); } TEST(TextOutput, HandlesAndroidEndl) { CapturedStderr cap; android::aerr << "foobar" << android::endl; std::string output; ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); android::base::ReadFdToString(cap.fd(), &output); ASSERT_STREQ(output.c_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 00000030766 13300556574 020070 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 **)NULL, 10) * 1000; 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 00000010020 13300556574 017612 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 "android-base/test_utils.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 00000034153 13300556574 015340 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; 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 0; } // 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/diskusage/0040755 0000000 0000000 00000000000 13300556574 012530 5ustar000000000 0000000 libs/diskusage/Android.bp0100644 0000000 0000000 00000001257 13300556574 014435 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"], } libs/diskusage/MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13300556574 015650 0ustar000000000 0000000 libs/diskusage/dirsize.c0100644 0000000 0000000 00000003477 13300556574 014355 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/graphicsenv/0040755 0000000 0000000 00000000000 13300556574 013062 5ustar000000000 0000000 libs/graphicsenv/Android.bp0100644 0000000 0000000 00000001466 13300556574 014771 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: [ "GraphicsEnv.cpp", ], shared_libs: [ "libnativeloader", "liblog", ], export_include_dirs: ["include"], } libs/graphicsenv/GraphicsEnv.cpp0100644 0000000 0000000 00000005107 13300556574 015777 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 LOG_NDEBUG 1 #define LOG_TAG "GraphicsEnv" #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*); } namespace android { /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { static GraphicsEnv env; return env; } void GraphicsEnv::setDriverPath(const std::string path) { if (!mDriverPath.empty()) { ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(), path.c_str()); return; } ALOGV("setting driver path to '%s'", path.c_str()); mDriverPath = path; } android_namespace_t* GraphicsEnv::getDriverNamespace() { static std::once_flag once; std::call_once(once, [this]() { if (mDriverPath.empty()) return; // If the sphal namespace isn't configured for a device, don't support updatable drivers. // We need a parent namespace to inherit the default search path from. auto sphalNamespace = android_get_exported_namespace("sphal"); if (!sphalNamespace) return; mDriverNamespace = android_create_namespace("gfx driver", nullptr, // ld_library_path mDriverPath.c_str(), // default_library_path ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path sphalNamespace); }); return mDriverNamespace; } } // namespace android extern "C" android_namespace_t* android_getDriverNamespace() { return android::GraphicsEnv::getInstance().getDriverNamespace(); } libs/graphicsenv/include/0040755 0000000 0000000 00000000000 13300556574 014505 5ustar000000000 0000000 libs/graphicsenv/include/graphicsenv/0040755 0000000 0000000 00000000000 13300556574 017016 5ustar000000000 0000000 libs/graphicsenv/include/graphicsenv/GraphicsEnv.h0100644 0000000 0000000 00000004131 13300556574 021374 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 struct android_namespace_t; namespace android { class GraphicsEnv { public: static GraphicsEnv& getInstance(); // 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 void setDriverPath(const std::string path); android_namespace_t* getDriverNamespace(); private: GraphicsEnv() = default; std::string mDriverPath; android_namespace_t* mDriverNamespace = nullptr; }; } // namespace android /* FIXME * Export an un-mangled function that just does * return android::GraphicsEnv::getInstance().getDriverNamespace(); * This allows libEGL to get the function pointer via dlsym, since it can't * directly link against libgui. In a future release, we'll fix this so that * libgui does not depend on graphics API libraries, and libEGL can link * against it. The current dependencies from libgui -> libEGL are: * - the GLConsumer class, which should be moved to its own library * - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and * will be removed soon. */ extern "C" android_namespace_t* android_getDriverNamespace(); #endif // ANDROID_UI_GRAPHICS_ENV_H libs/gui/0040755 0000000 0000000 00000000000 13300556574 011335 5ustar000000000 0000000 libs/gui/Android.bp0100644 0000000 0000000 00000007760 13300556574 013247 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: true, vndk: { enabled: true, }, clang: true, cppflags: [ "-Weverything", "-Werror", // 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 don't need to enumerate every case in a switch as long as a default case // is present "-Wno-switch-enum", // Allow calling variadic macros without a __VA_ARGS__ list "-Wno-gnu-zero-variadic-macro-arguments", // Don't warn about struct padding "-Wno-padded", // We are aware of the risks inherent in comparing floats for equality "-Wno-float-equal", // Pure abstract classes trigger this warning "-Wno-weak-vtables", // Allow four-character integer literals "-Wno-four-char-constants", // Allow documentation warnings "-Wno-documentation", "-DDEBUG_ONLY_CODE=0", ], product_variables: { brillo: { cflags: ["-DHAVE_NO_SURFACE_FLINGER"], }, eng: { cppflags: [ "-UDEBUG_ONLY_CODE", "-DDEBUG_ONLY_CODE=1", ], }, }, srcs: [ "BitTube.cpp", "BufferItem.cpp", "BufferItemConsumer.cpp", "BufferQueue.cpp", "BufferQueueConsumer.cpp", "BufferQueueCore.cpp", "BufferQueueProducer.cpp", "BufferSlot.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", "DisplayEventReceiver.cpp", "FrameTimestamps.cpp", "GLConsumer.cpp", "GuiConfig.cpp", "IDisplayEventConnection.cpp", "IConsumerListener.cpp", "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", "LayerState.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", "Surface.cpp", "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", "bufferqueue/1.0/H2BGraphicBufferProducer.cpp" ], shared_libs: [ "libsync", "libbinder", "libcutils", "libEGL", "libGLESv2", "libui", "libutils", "libnativewindow", "liblog", "libhidlbase", "libhidltransport", "android.hidl.token@1.0-utils", "android.hardware.graphics.bufferqueue@1.0", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", ], header_libs: [ "libnativebase_headers", "libgui_headers", ], export_shared_lib_headers: [ "libbinder", "libEGL", "libnativewindow", "libui", "android.hidl.token@1.0-utils", "android.hardware.graphics.bufferqueue@1.0", ], export_header_lib_headers: [ "libgui_headers", ], export_include_dirs: [ "include", ], } subdirs = ["tests"] libs/gui/BitTube.cpp0100644 0000000 0000000 00000012156 13300556574 013401 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/BufferItem.cpp0100644 0000000 0000000 00000017347 13300556574 014102 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(NULL), mFence(NULL), 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) { } 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); return size; } size_t BufferItem::getFlattenedSize() const { size_t size = sizeof(uint32_t); // Flags if (mGraphicBuffer != 0) { size += mGraphicBuffer->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } if (mFence != 0) { size += mFence->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } size += mSurfaceDamage.getFlattenedSize(); size = FlattenableUtils::align<8>(size); return size + getPodSize(); } size_t BufferItem::getFdCount() const { size_t count = 0; if (mGraphicBuffer != 0) { count += mGraphicBuffer->getFdCount(); } if (mFence != 0) { 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 != 0) { status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); flags |= 1; } if (mFence != 0) { 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()); // 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); 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()); // 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); 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 00000007776 13300556574 015623 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::setName(const String8& name) { Mutex::Autolock _l(mMutex); if (mAbandoned) { BI_LOGE("setName: BufferItemConsumer is abandoned!"); return; } mName = name; mConsumer->setConsumerName(name); } 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); err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != OK) { BI_LOGE("Failed to release buffer: %s (%d)", strerror(-err), err); } return err; } void BufferItemConsumer::freeBufferLocked(int slotIndex) { sp listener = mBufferFreedListener.promote(); if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) { // 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 00000006673 13300556574 014270 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 #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 != NULL) { listener->onDisconnect(); } } void BufferQueue::ProxyConsumerListener::onFrameAvailable( const BufferItem& item) { sp listener(mConsumerListener.promote()); if (listener != NULL) { listener->onFrameAvailable(item); } } void BufferQueue::ProxyConsumerListener::onFrameReplaced( const BufferItem& item) { sp listener(mConsumerListener.promote()); if (listener != NULL) { listener->onFrameReplaced(item); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { sp listener(mConsumerListener.promote()); if (listener != NULL) { listener->onBuffersReleased(); } } void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { sp listener(mConsumerListener.promote()); if (listener != NULL) { 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 == NULL, "BufferQueue: outProducer must not be NULL"); LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL"); sp core(new BufferQueueCore()); LOG_ALWAYS_FATAL_IF(core == NULL, "BufferQueue: failed to create BufferQueueCore"); sp producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer"); sp consumer(new BufferQueueConsumer(core)); LOG_ALWAYS_FATAL_IF(consumer == NULL, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; } }; // namespace android libs/gui/BufferQueueConsumer.cpp0100644 0000000 0000000 00000070777 13300556574 016012 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 #include #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; { Mutex::Autolock 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()) { const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second // 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); } 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); 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(); 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 = NULL; } 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.broadcast(); ATRACE_INT(mCore->mConsumerName.string(), static_cast(mCore->mQueue.size())); mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); VALIDATE_CONSISTENCY(); } if (listener != NULL) { 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); Mutex::Autolock 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.broadcast(); VALIDATE_CONSISTENCY(); return NO_ERROR; } status_t BufferQueueConsumer::attachBuffer(int* outSlot, const sp& buffer) { ATRACE_CALL(); if (outSlot == NULL) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == NULL) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } Mutex::Autolock 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 == NULL) { BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; } sp listener; { // Autolock scope Mutex::Autolock 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); } listener = mCore->mConnectedProducerListener; BQ_LOGV("releaseBuffer: releasing slot %d", slot); mCore->mDequeueCondition.broadcast(); VALIDATE_CONSISTENCY(); } // Autolock scope // Call back without lock held if (listener != NULL) { listener->onBufferReleased(); } return NO_ERROR; } status_t BufferQueueConsumer::connect( const sp& consumerListener, bool controlledByApp) { ATRACE_CALL(); if (consumerListener == NULL) { BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } BQ_LOGV("connect: controlledByApp=%s", controlledByApp ? "true" : "false"); Mutex::Autolock 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"); Mutex::Autolock lock(mCore->mMutex); if (mCore->mConsumerListener == NULL) { BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } mCore->mIsAbandoned = true; mCore->mConsumerListener = NULL; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mDequeueCondition.broadcast(); return NO_ERROR; } status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { ATRACE_CALL(); if (outSlotMask == NULL) { BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); return BAD_VALUE; } Mutex::Autolock 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); Mutex::Autolock 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; } Mutex::Autolock 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 Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); 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) { listener = mCore->mConsumerListener; } } // Call back without lock held if (listener != NULL) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueConsumer::setConsumerName(const String8& name) { ATRACE_CALL(); BQ_LOGV("setConsumerName: '%s'", name.string()); Mutex::Autolock lock(mCore->mMutex); mCore->mConsumerName = name; mConsumerName = name; return NO_ERROR; } status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) { ATRACE_CALL(); BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat); Mutex::Autolock lock(mCore->mMutex); mCore->mDefaultBufferFormat = defaultFormat; return NO_ERROR; } status_t BufferQueueConsumer::setDefaultBufferDataSpace( android_dataspace defaultDataSpace) { ATRACE_CALL(); BQ_LOGV("setDefaultBufferDataSpace: %u", defaultDataSpace); Mutex::Autolock lock(mCore->mMutex); mCore->mDefaultBufferDataSpace = defaultDataSpace; return NO_ERROR; } status_t BufferQueueConsumer::setConsumerUsageBits(uint64_t usage) { ATRACE_CALL(); BQ_LOGV("setConsumerUsageBits: %#" PRIx64, usage); Mutex::Autolock lock(mCore->mMutex); mCore->mConsumerUsageBits = usage; return NO_ERROR; } status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) { ATRACE_CALL(); BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false"); Mutex::Autolock lock(mCore->mMutex); mCore->mConsumerIsProtected = isProtected; return NO_ERROR; } status_t BufferQueueConsumer::setTransformHint(uint32_t hint) { ATRACE_CALL(); BQ_LOGV("setTransformHint: %#x", hint); Mutex::Autolock lock(mCore->mMutex); mCore->mTransformHint = hint; return NO_ERROR; } status_t BufferQueueConsumer::getSidebandStream(sp* outStream) const { Mutex::Autolock lock(mCore->mMutex); *outStream = mCore->mSidebandStream; return NO_ERROR; } status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush, std::vector* outHistory) { Mutex::Autolock lock(mCore->mMutex); *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush); return NO_ERROR; } status_t BufferQueueConsumer::discardFreeBuffers() { Mutex::Autolock 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; } const IPCThreadState* ipc = IPCThreadState::self(); const pid_t pid = ipc->getCallingPid(); const uid_t uid = ipc->getCallingUid(); 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); android_errorWriteWithInfoLog(0x534e4554, "27046057", static_cast(uid), NULL, 0); return PERMISSION_DENIED; } mCore->dumpState(prefix, outResult); return NO_ERROR; } } // namespace android libs/gui/BufferQueueCore.cpp0100644 0000000 0000000 00000037667 13300556574 015110 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(), mSlots(), mQueue(), mFreeSlots(), mFreeBuffers(), mUnusedSlots(), mActiveBuffers(), mDequeueCondition(), mDequeueBufferCannotBlock(false), 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 { Mutex::Autolock 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 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() { 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() const { ATRACE_CALL(); while (mIsAllocating) { mIsAllocatingCondition.wait(mMutex); } } #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 != NULL) { 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 != NULL) { 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 == NULL) { 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 == NULL && !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 00000157505 13300556574 015775 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 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) {} BufferQueueProducer::~BufferQueueProducer() {} status_t BufferQueueProducer::requestBuffer(int slot, sp* buf) { ATRACE_CALL(); BQ_LOGV("requestBuffer: slot %d", slot); Mutex::Autolock 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 Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); 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.broadcast(); } // Autolock scope // Call back without lock held if (listener != NULL) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::setAsyncMode(bool async) { ATRACE_CALL(); BQ_LOGV("setAsyncMode: async = %d", async); sp listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); 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.broadcast(); if (delta < 0) { listener = mCore->mConsumerListener; } } // Autolock scope // Call back without lock held if (listener != NULL) { 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, 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) { 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) { status_t result = mCore->mDequeueCondition.waitRelative( mCore->mMutex, mDequeueTimeout); if (result == TIMED_OUT) { return result; } } else { mCore->mDequeueCondition.wait(mCore->mMutex); } } } // 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 Mutex::Autolock 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 Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); 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, &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 == NULL) || buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = NULL; 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 == NULL)) { 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 Mutex::Autolock lock(mCore->mMutex); if (error == NO_ERROR && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.broadcast(); 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; { Mutex::Autolock 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.broadcast(); VALIDATE_CONSISTENCY(); listener = mCore->mConsumerListener; } if (listener != NULL) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::detachNextBuffer(sp* outBuffer, sp* outFence) { ATRACE_CALL(); if (outBuffer == NULL) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; } else if (outFence == NULL) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } sp listener; { Mutex::Autolock 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(); 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 != NULL) { listener->onBuffersReleased(); } return NO_ERROR; } status_t BufferQueueProducer::attachBuffer(int* outSlot, const sp& buffer) { ATRACE_CALL(); if (outSlot == NULL) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == NULL) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } Mutex::Autolock 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(); status_t returnFlags = NO_ERROR; int found; status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &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); Region surfaceDamage = input.getSurfaceDamage(); if (acquireFence == NULL) { 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 Mutex::Autolock 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" " crop=[%d,%d,%d,%d] transform=%#x scale=%s", slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace, 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.mFrameNumber = currentFrameNumber; item.mSlot = slot; item.mFence = acquireFence; item.mFenceTime = acquireFenceTime; item.mIsDroppable = mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock || (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot); item.mSurfaceDamage = surfaceDamage; item.mQueuedBuffer = true; item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh; 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; } } // 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.broadcast(); 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(); } // Don't send the slot number through the callback since the consumer shouldn't need it item.mSlot = BufferItem::INVALID_BUFFER_SLOT; // 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 Mutex::Autolock lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { mCallbackCondition.wait(mCallbackMutex); } if (frameAvailableListener != NULL) { frameAvailableListener->onFrameAvailable(item); } else if (frameReplacedListener != NULL) { frameReplacedListener->onFrameReplaced(item); } connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; ++mCurrentCallbackTicket; mCallbackCondition.broadcast(); } // 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"); } // 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); return NO_ERROR; } status_t BufferQueueProducer::cancelBuffer(int slot, const sp& fence) { ATRACE_CALL(); BQ_LOGV("cancelBuffer: slot %d", slot); Mutex::Autolock 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 == NULL) { 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.broadcast(); VALIDATE_CONSISTENCY(); return NO_ERROR; } int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); if (outValue == NULL) { 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; 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(); Mutex::Autolock 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 == NULL) { BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } if (output == NULL) { 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 != NULL) { // Set up a death notification so that we can disconnect // automatically if the remote producer dies if (IInterface::asBinder(listener)->remoteBinder() != NULL) { 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; } if (listener->needsReleaseNotify()) { mCore->mConnectedProducerListener = listener; } } break; default: BQ_LOGE("connect: unknown API %d", api); status = BAD_VALUE; break; } mCore->mConnectedPid = IPCThreadState::self()->getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; if (mDequeueTimeout < 0) { mCore->mDequeueBufferCannotBlock = mCore->mConsumerControlledByApp && producerControlledByApp; } 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 Mutex::Autolock lock(mCore->mMutex); if (mode == DisconnectMode::AllLocal) { if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) { return NO_ERROR; } api = BufferQueueCore::CURRENTLY_CONNECTED_API; } mCore->waitWhileAllocatingLocked(); 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 != NULL) { 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 = NULL; mCore->mConnectedProducerListener = NULL; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); mCore->mDequeueCondition.broadcast(); 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 != NULL) { listener->onBuffersReleased(); listener->onDisconnect(); } return status; } status_t BufferQueueProducer::setSidebandStream(const sp& stream) { sp listener; { // Autolock scope Mutex::Autolock _l(mCore->mMutex); mCore->mSidebandStream = stream; listener = mCore->mConsumerListener; } // Autolock scope if (listener != NULL) { 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; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); if (!mCore->mAllowAllocation) { BQ_LOGE("allocateBuffers: allocation is not allowed for this " "BufferQueue"); return; } newBufferCount = mCore->mFreeSlots.size(); 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; 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, {mConsumerName.string(), mConsumerName.size()}); 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); Mutex::Autolock lock(mCore->mMutex); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.broadcast(); return; } buffers.push_back(graphicBuffer); } { // Autolock scope Mutex::Autolock 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.broadcast(); 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.broadcast(); VALIDATE_CONSISTENCY(); } // Autolock scope } } status_t BufferQueueProducer::allowAllocation(bool allow) { ATRACE_CALL(); BQ_LOGV("allowAllocation: %s", allow ? "true" : "false"); Mutex::Autolock lock(mCore->mMutex); mCore->mAllowAllocation = allow; return NO_ERROR; } status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) { ATRACE_CALL(); BQ_LOGV("setGenerationNumber: %u", generationNumber); Mutex::Autolock lock(mCore->mMutex); mCore->mGenerationNumber = generationNumber; return NO_ERROR; } String8 BufferQueueProducer::getConsumerName() const { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); BQ_LOGV("getConsumerName: %s", mConsumerName.string()); return mConsumerName; } status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) { ATRACE_CALL(); BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode); Mutex::Autolock 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); Mutex::Autolock lock(mCore->mMutex); mCore->mAutoRefresh = autoRefresh; return NO_ERROR; } status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) { ATRACE_CALL(); BQ_LOGV("setDequeueTimeout: %" PRId64, timeout); Mutex::Autolock lock(mCore->mMutex); int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false, 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 = false; VALIDATE_CONSISTENCY(); return NO_ERROR; } status_t BufferQueueProducer::getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); Mutex::Autolock 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; { Mutex::Autolock lock(mCore->mMutex); listener = mCore->mConsumerListener; } if (listener != NULL) { 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"); Mutex::Autolock lock(mCore->mMutex); *outUsage = mCore->mConsumerUsageBits; return NO_ERROR; } } // namespace android libs/gui/BufferSlot.cpp0100644 0000000 0000000 00000002010 13300556574 014102 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/CleanSpec.mk0100644 0000000 0000000 00000004700 13300556574 013521 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. # # 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/*) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ $(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)/obj/SHARED_LIBRARIES/libgui_intermediates) libs/gui/ConsumerBase.cpp0100644 0000000 0000000 00000032032 13300556574 014424 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 = 0; 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 != NULL) { 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 != NULL) { 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::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::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 != NULL) { if (mSlots[item->mSlot].mGraphicBuffer != NULL) { 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 != NULL && mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); } } // namespace android libs/gui/CpuConsumer.cpp0100644 0000000 0000000 00000021273 13300556574 014306 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 #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)); } CpuConsumer::~CpuConsumer() { // ConsumerBase destructor does all the work. } void CpuConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); if (mAbandoned) { CC_LOGE("setName: CpuConsumer is abandoned!"); return; } mName = name; mConsumer->setConsumerName(name); } 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::lockNextBuffer(LockedBuffer *nativeBuffer) { status_t err; if (!nativeBuffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { CC_LOGW("Max buffers have been locked (%zd), cannot lock anymore.", mMaxLockedBuffers); return NOT_ENOUGH_DATA; } BufferItem b; Mutex::Autolock _l(mMutex); 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; } } int slot = b.mSlot; void *bufferPointer = NULL; android_ycbcr ycbcr = android_ycbcr(); PixelFormat format = mSlots[slot].mGraphicBuffer->getPixelFormat(); PixelFormat flexFormat = format; if (isPossiblyYUV(format)) { if (b.mFence.get()) { err = mSlots[slot].mGraphicBuffer->lockAsyncYCbCr( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &ycbcr, b.mFence->dup()); } else { err = mSlots[slot].mGraphicBuffer->lockYCbCr( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &ycbcr); } if (err == OK) { bufferPointer = ycbcr.y; 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 (bufferPointer == NULL) { // not flexible YUV if (b.mFence.get()) { err = mSlots[slot].mGraphicBuffer->lockAsync( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &bufferPointer, b.mFence->dup()); } else { err = mSlots[slot].mGraphicBuffer->lock( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &bufferPointer); } if (err != OK) { CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } } size_t lockedIdx = 0; for (; lockedIdx < static_cast(mMaxLockedBuffers); lockedIdx++) { if (mAcquiredBuffers[lockedIdx].mSlot == BufferQueue::INVALID_BUFFER_SLOT) { break; } } assert(lockedIdx < mMaxLockedBuffers); AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); ab.mSlot = slot; ab.mBufferPointer = bufferPointer; ab.mGraphicBuffer = mSlots[slot].mGraphicBuffer; nativeBuffer->data = reinterpret_cast(bufferPointer); nativeBuffer->width = mSlots[slot].mGraphicBuffer->getWidth(); nativeBuffer->height = mSlots[slot].mGraphicBuffer->getHeight(); nativeBuffer->format = format; nativeBuffer->flexFormat = flexFormat; nativeBuffer->stride = (ycbcr.y != NULL) ? static_cast(ycbcr.ystride) : mSlots[slot].mGraphicBuffer->getStride(); nativeBuffer->crop = b.mCrop; nativeBuffer->transform = b.mTransform; nativeBuffer->scalingMode = b.mScalingMode; nativeBuffer->timestamp = b.mTimestamp; nativeBuffer->dataSpace = b.mDataSpace; nativeBuffer->frameNumber = b.mFrameNumber; nativeBuffer->dataCb = reinterpret_cast(ycbcr.cb); nativeBuffer->dataCr = reinterpret_cast(ycbcr.cr); nativeBuffer->chromaStride = static_cast(ycbcr.cstride); nativeBuffer->chromaStep = static_cast(ycbcr.chroma_step); mCurrentLockedBuffers++; return OK; } status_t CpuConsumer::unlockBuffer(const LockedBuffer &nativeBuffer) { Mutex::Autolock _l(mMutex); size_t lockedIdx = 0; void *bufPtr = reinterpret_cast(nativeBuffer.data); for (; lockedIdx < static_cast(mMaxLockedBuffers); lockedIdx++) { if (bufPtr == mAcquiredBuffers[lockedIdx].mBufferPointer) break; } if (lockedIdx == mMaxLockedBuffers) { CC_LOGE("%s: Can't find buffer to free", __FUNCTION__); return BAD_VALUE; } return releaseAcquiredBufferLocked(lockedIdx); } status_t CpuConsumer::releaseAcquiredBufferLocked(size_t lockedIdx) { status_t err; int fd = -1; err = mAcquiredBuffers[lockedIdx].mGraphicBuffer->unlockAsync(&fd); if (err != OK) { CC_LOGE("%s: Unable to unlock graphic buffer %zd", __FUNCTION__, lockedIdx); return err; } int buf = mAcquiredBuffers[lockedIdx].mSlot; if (CC_LIKELY(fd != -1)) { sp fence(new Fence(fd)); addReleaseFenceLocked( mAcquiredBuffers[lockedIdx].mSlot, mSlots[buf].mGraphicBuffer, fence); } // release the buffer if it hasn't already been freed by the BufferQueue. // This can happen, for example, when the producer of this buffer // disconnected after this buffer was acquired. if (CC_LIKELY(mAcquiredBuffers[lockedIdx].mGraphicBuffer == mSlots[buf].mGraphicBuffer)) { releaseBufferLocked( buf, mAcquiredBuffers[lockedIdx].mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); } AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); ab.mSlot = BufferQueue::INVALID_BUFFER_SLOT; ab.mBufferPointer = NULL; ab.mGraphicBuffer.clear(); mCurrentLockedBuffers--; return OK; } void CpuConsumer::freeBufferLocked(int slotIndex) { ConsumerBase::freeBufferLocked(slotIndex); } } // namespace android libs/gui/DisplayEventReceiver.cpp0100644 0000000 0000000 00000005504 13300556574 016136 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) { sp sf(ComposerService::getComposerService()); if (sf != NULL) { mEventConnection = sf->createDisplayEventConnection(vsyncSource); if (mEventConnection != NULL) { mDataChannel = std::make_unique(); mEventConnection->stealReceiveChannel(mDataChannel.get()); } } } DisplayEventReceiver::~DisplayEventReceiver() { } status_t DisplayEventReceiver::initCheck() const { if (mDataChannel != NULL) return NO_ERROR; return NO_INIT; } int DisplayEventReceiver::getFd() const { if (mDataChannel == NULL) return NO_INIT; return mDataChannel->getFd(); } status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { if (int32_t(count) < 0) return BAD_VALUE; if (mEventConnection != NULL) { mEventConnection->setVsyncRate(count); return NO_ERROR; } return NO_INIT; } status_t DisplayEventReceiver::requestNextVsync() { if (mEventConnection != NULL) { 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 00000060371 13300556574 015146 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 // For CC_[UN]LIKELY #include #include #include #include #include #include namespace android { // ============================================================================ // 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(String8& outString, const char* name, bool pending, const FenceTime& fenceTime) { outString.appendFormat("--- %s", name); nsecs_t signalTime = fenceTime.getCachedSignalTime(); if (Fence::isValidTimestamp(signalTime)) { outString.appendFormat("%" PRId64 "\n", signalTime); } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) { outString.appendFormat("Pending\n"); } else if (&fenceTime == FenceTime::NO_FENCE.get()){ outString.appendFormat("N/A\n"); } else { outString.appendFormat("Error\n"); } } void FrameEvents::dump(String8& outString) const { if (!valid) { return; } outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber); outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime); outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime); outString.appendFormat("--- Latched \t"); if (FrameEvents::isValidTimestamp(latchTime)) { outString.appendFormat("%" PRId64 "\n", latchTime); } else { outString.appendFormat("Pending\n"); } outString.appendFormat("--- Refresh (First)\t"); if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) { outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime); } else { outString.appendFormat("Pending\n"); } outString.appendFormat("--- Refresh (Last)\t"); if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) { outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime); } else { outString.appendFormat("Pending\n"); } dumpFenceTime(outString, "Acquire \t", true, *acquireFence); dumpFenceTime(outString, "GPU Composite Done\t", !addPostCompositeCalled, *gpuCompositionDoneFence); dumpFenceTime(outString, "Display Present \t", !addPostCompositeCalled, *displayPresentFence); outString.appendFormat("--- DequeueReady \t"); if (FrameEvents::isValidTimestamp(dequeueReadyTime)) { outString.appendFormat("%" PRId64 "\n", dequeueReadyTime); } else { outString.appendFormat("Pending\n"); } dumpFenceTime(outString, "Release \t", true, *releaseFence); } // ============================================================================ // FrameEventHistory // ============================================================================ namespace { struct FrameNumberEqual { 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(String8& outString) const { auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); if (!earliestFrame->valid) { outString.appendFormat("-- 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) { mCompositorTiming = src.mCompositorTiming; if (CC_UNLIKELY(!mDeltas.empty())) { ALOGE("FrameEventHistoryDelta assign clobbering history."); } mDeltas = std::move(src.mDeltas); ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty."); 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 00000126267 13300556574 014072 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 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); #define CROP_EXT_STR "EGL_ANDROID_image_crop" #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____" "_______________" "_______________" }; // Transform matrices static float mtxIdentity[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; static float mtxFlipH[16] = { -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, }; static float mtxFlipV[16] = { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, }; static float mtxRot90[16] = { 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, }; static void mtxMul(float out[16], const float a[16], const float b[16]); Mutex GLConsumer::sStaticInitLock; sp GLConsumer::sReleasedTexImageBuffer; static bool hasEglAndroidImageCropImpl() { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); size_t cropExtLen = strlen(CROP_EXT_STR); size_t extsLen = strlen(exts); bool equal = !strcmp(CROP_EXT_STR, exts); bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); bool atEnd = (cropExtLen+1) < extsLen && !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); return equal || atStart || atEnd || inMiddle; } static bool hasEglAndroidImageCrop() { // Only compute whether the extension is present once the first time this // function is called. static bool hasIt = hasEglAndroidImageCropImpl(); return hasIt; } 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; } static bool isEglImageCroppable(const Rect& crop) { return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); } 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, 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, 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 == NULL) { 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 == NULL)) { // 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 != NULL) { 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, item.mCrop); 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 != NULL ? mCurrentTextureImage->graphicBufferHandle() : 0, 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 == NULL) { GLC_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, mCurrentCrop); 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, mCurrentCrop, 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 != NULL) { // 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, NULL); 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, NULL); if (fence == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); mEglSlots[mCurrentTexture].mEglFence = fence; } } return OK; } bool GLConsumer::isExternalFormat(PixelFormat format) { switch (format) { // supported YUV formats case HAL_PIXEL_FORMAT_YV12: // Legacy/deprecated YUV formats case HAL_PIXEL_FORMAT_YCbCr_422_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_422_I: return true; } // Any OEM format needs to be considered if (format>=0x100 && format<=0x1FF) return true; return false; } 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==NULL) { GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); } if (needsRecompute && mCurrentTextureImage != NULL) { 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, isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop, mCurrentTransform, mFilteringEnabled); } void GLConsumer::computeTransformMatrix(float outTransform[16], const sp& buf, const Rect& cropRect, uint32_t transform, bool filtering) { float xform[16]; for (int i = 0; i < 16; i++) { xform[i] = mtxIdentity[i]; } if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { float result[16]; mtxMul(result, xform, mtxFlipH); for (int i = 0; i < 16; i++) { xform[i] = result[i]; } } if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { float result[16]; mtxMul(result, xform, mtxFlipV); for (int i = 0; i < 16; i++) { xform[i] = result[i]; } } if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { float result[16]; mtxMul(result, xform, mtxRot90); for (int i = 0; i < 16; i++) { xform[i] = result[i]; } } float mtxBeforeFlipV[16]; 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; } float crop[16] = { sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1, }; mtxMul(mtxBeforeFlipV, crop, xform); } else { for (int i = 0; i < 16; i++) { mtxBeforeFlipV[i] = xform[i]; } } // SurfaceFlinger expects the top of its window textures to be at a Y // coordinate of 0, so GLConsumer must behave the same way. We don't // want to expose this to applications, however, so we must add an // additional vertical flip to the transform after all the other transforms. mtxMul(outTransform, mtxFlipV, mtxBeforeFlipV); } 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) ? NULL : mCurrentTextureImage->graphicBuffer(); } Rect GLConsumer::getCurrentCrop() const { Mutex::Autolock lock(mMutex); Rect outCrop = mCurrentCrop; if (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) { uint32_t newWidth = static_cast(mCurrentCrop.width()); uint32_t newHeight = static_cast(mCurrentCrop.height()); if (newWidth * mDefaultHeight > newHeight * mDefaultWidth) { newWidth = newHeight * mDefaultWidth / mDefaultHeight; GLC_LOGV("too wide: newWidth = %d", newWidth); } else if (newWidth * mDefaultHeight < newHeight * mDefaultWidth) { newHeight = newWidth * mDefaultHeight / mDefaultWidth; GLC_LOGV("too tall: newHeight = %d", newHeight); } uint32_t currentWidth = static_cast(mCurrentCrop.width()); uint32_t currentHeight = static_cast(mCurrentCrop.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); } GLC_LOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,outCrop.bottom); } return outCrop; } 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::doGLFenceWait() const { Mutex::Autolock lock(mMutex); return doGLFenceWaitLocked(); } 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()) { // 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(); } void GLConsumer::setName(const String8& name) { Mutex::Autolock _l(mMutex); if (mAbandoned) { GLC_LOGE("setName: GLConsumer is abandoned!"); return; } mName = name; mConsumer->setConsumerName(name); } status_t GLConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setDefaultBufferFormat: GLConsumer is abandoned!"); return NO_INIT; } return mConsumer->setDefaultBufferFormat(defaultFormat); } status_t GLConsumer::setDefaultBufferDataSpace( android_dataspace defaultDataSpace) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setDefaultBufferDataSpace: GLConsumer is abandoned!"); return NO_INIT; } return mConsumer->setDefaultBufferDataSpace(defaultDataSpace); } status_t GLConsumer::setConsumerUsageBits(uint64_t usage) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setConsumerUsageBits: GLConsumer is abandoned!"); return NO_INIT; } usage |= DEFAULT_USAGE_FLAGS; return mConsumer->setConsumerUsageBits(usage); } status_t GLConsumer::setTransformHint(uint32_t hint) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setTransformHint: GLConsumer is abandoned!"); return NO_INIT; } return mConsumer->setTransformHint(hint); } status_t GLConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { Mutex::Autolock lock(mMutex); if (mAbandoned) { GLC_LOGE("setMaxAcquiredBufferCount: GLConsumer is abandoned!"); return NO_INIT; } return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers); } 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); } static void mtxMul(float out[16], const float a[16], const float b[16]) { out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; } GLConsumer::EglImage::EglImage(sp graphicBuffer) : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY), mCropRect(Rect::EMPTY_RECT) { } GLConsumer::EglImage::~EglImage() { if (mEglImage != EGL_NO_IMAGE_KHR) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("~EglImage: eglDestroyImageKHR failed"); } eglTerminate(mEglDisplay); } } status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, const Rect& cropRect, 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; bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; if (haveImage && (displayInvalid || cropInvalid || forceCreation)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } 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; mCropRect = cropRect; mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect); } // Fail if we can't create a valid image. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = EGL_NO_DISPLAY; mCropRect.makeInvalid(); 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, const Rect& crop) { EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); const bool createProtectedImage = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, EGL_IMAGE_CROP_TOP_ANDROID, crop.top, EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, createProtectedImage ? EGL_TRUE : EGL_NONE, EGL_NONE, }; if (!crop.isValid()) { // No crop rect to set, so leave the crop out of the attrib array. Make // sure to propagate the protected content attrs if they are set. attrs[2] = attrs[10]; attrs[3] = attrs[11]; attrs[4] = EGL_NONE; } else if (!isEglImageCroppable(crop)) { // The crop rect is not at the origin, so we can't set the crop on the // EGLImage because that's not allowed by the EGL_ANDROID_image_crop // extension. In the future we can add a layered extension that // removes this restriction if there is hardware that can support it. attrs[2] = attrs[10]; attrs[3] = attrs[11]; attrs[4] = EGL_NONE; } eglInitialize(dpy, 0, 0); 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); } return image; } }; // namespace android libs/gui/GuiConfig.cpp0100644 0000000 0000000 00000001625 13300556574 013714 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 namespace android { void appendGuiConfigString(String8& configStr) { static const char* config = " [libgui" #ifdef DONT_USE_FENCE_SYNC " DONT_USE_FENCE_SYNC" #endif "]"; configStr.append(config); } }; // namespace android libs/gui/IConsumerListener.cpp0100644 0000000 0000000 00000007312 13300556574 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. */ #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.cpp0100644 0000000 0000000 00000005662 13300556574 016607 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 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.cpp0100644 0000000 0000000 00000023610 13300556574 016374 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 #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.cpp0100644 0000000 0000000 00000111242 13300556574 016363 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 #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- using ::android::hardware::graphics::bufferqueue::V1_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, }; 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 == NULL) { ALOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; } else if (outFence == NULL) { 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 != NULL) { 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); 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 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, H2BGraphicBufferProducer> { public: 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 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, HGraphicBufferProducer, "android.gui.IGraphicBufferProducer"); // ---------------------------------------------------------------------- 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 != 0); if (buffer != 0) { 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); 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 != NULL); if (buffer != NULL) { reply->write(*buffer); } reply->writeInt32(fence != NULL); if (fence != NULL) { 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; } 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; } } 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(); } 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; } return surfaceDamage.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; } return surfaceDamage.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.cpp0100644 0000000 0000000 00000005430 13300556574 015442 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 namespace android { enum { ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION, NEEDS_RELEASE_NOTIFY, }; 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; } }; // Out-of-line virtual method definition to trigger vtable emission in this // translation unit (see clang warning -Wweak-vtables) BpProducerListener::~BpProducerListener() {} IMPLEMENT_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; } return BBinder::onTransact(code, data, reply, flags); } ProducerListener::~ProducerListener() = default; DummyProducerListener::~DummyProducerListener() = default; bool BnProducerListener::needsReleaseNotify() { return true; } } // namespace android libs/gui/ISurfaceComposer.cpp0100644 0000000 0000000 00000072232 13300556574 015255 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. */ // tag as surfaceflinger #define LOG_TAG "SurfaceFlinger" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- namespace android { 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 sp createScopedConnection( const sp& parent) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(parent)); remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply); return interface_cast(reply.readStrongBinder()); } virtual void setTransactionState( const Vector& state, const Vector& displays, uint32_t flags) { 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); 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, const sp& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); data.writeStrongBinder(IInterface::asBinder(producer)); data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); data.writeInt32(minLayerZ); data.writeInt32(maxLayerZ); data.writeInt32(static_cast(useIdentityTransform)); data.writeInt32(static_cast(rotation)); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); return reply.readInt32(); } 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) { Parcel data, reply; sp result; int err = data.writeInterfaceToken( ISurfaceComposer::getInterfaceDescriptor()); if (err != NO_ERROR) { return result; } data.writeInt32(static_cast(vsyncSource)); 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 sp getBuiltInDisplay(int32_t id) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeInt32(id); remote()->transact(BnSurfaceComposer::GET_BUILT_IN_DISPLAY, 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; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); data.writeInt32(id); remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); 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 android_color_mode_t 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, android_color_mode_t 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(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; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); 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, TF_ONE_WAY); 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, TF_ONE_WAY); if (result != NO_ERROR) { ALOGE("injectVSync failed to transact: %d", result); return result; } return result; } }; // 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 CREATE_SCOPED_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp bufferProducer = interface_cast(data.readStrongBinder()); sp b = IInterface::asBinder(createScopedConnection(bufferProducer)); 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; } ComposerState s; Vector state; state.setCapacity(count); for (size_t i = 0; i < count; i++) { 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(); setTransactionState(state, displays, stateFlags); 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(); sp producer = interface_cast(data.readStrongBinder()); Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); uint32_t reqWidth = data.readUint32(); uint32_t reqHeight = data.readUint32(); int32_t minLayerZ = data.readInt32(); int32_t maxLayerZ = data.readInt32(); bool useIdentityTransform = static_cast(data.readInt32()); int32_t rotation = data.readInt32(); status_t res = captureScreen(display, producer, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform, static_cast(rotation)); reply->writeInt32(res); 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); sp connection(createDisplayEventConnection( static_cast(data.readInt32()))); 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_BUILT_IN_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); int32_t id = data.readInt32(); sp display(getBuiltInDisplay(id)); 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(colorModes[i]); } } 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; } android_color_mode_t 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); } default: { return BBinder::onTransact(code, data, reply, flags); } } } // ---------------------------------------------------------------------------- }; libs/gui/ISurfaceComposerClient.cpp0100644 0000000 0000000 00000010330 13300556574 016403 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. */ // 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, DESTROY_SURFACE, 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, uint32_t windowType, uint32_t ownerUid, sp* handle, sp* gbp) override { return callRemote(Tag::CREATE_SURFACE, name, width, height, format, flags, parent, windowType, ownerUid, handle, gbp); } status_t destroySurface(const sp& handle) override { return callRemote(Tag::DESTROY_SURFACE, handle); } 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::DESTROY_SURFACE: return callLocal(data, reply, &ISurfaceComposerClient::destroySurface); 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/LayerState.cpp0100644 0000000 0000000 00000010104 13300556574 014107 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 namespace android { status_t layer_state_t::write(Parcel& output) const { output.writeStrongBinder(surface); output.writeUint32(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); output.write(finalCrop); output.writeStrongBinder(barrierHandle); output.writeStrongBinder(reparentHandle); output.writeUint64(frameNumber); output.writeInt32(overrideScalingMode); output.writeStrongBinder(IInterface::asBinder(barrierGbp)); output.writeStrongBinder(relativeLayerHandle); output.write(transparentRegion); return NO_ERROR; } status_t layer_state_t::read(const Parcel& input) { surface = input.readStrongBinder(); what = input.readUint32(); 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); input.read(finalCrop); barrierHandle = input.readStrongBinder(); reparentHandle = input.readStrongBinder(); frameNumber = input.readUint64(); overrideScalingMode = input.readInt32(); barrierGbp = interface_cast(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); input.read(transparentRegion); 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; } }; // namespace android libs/gui/OccupancyTracker.cpp0100644 0000000 0000000 00000007303 13300556574 015301 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. */ #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.cpp0100644 0000000 0000000 00000024155 13300556574 015027 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 "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 == NULL) { ALOGE("createSplitter: inputQueue must not be NULL"); return BAD_VALUE; } if (outSplitter == NULL) { 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 == NULL) { 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.cpp0100644 0000000 0000000 00000157717 13300556574 013450 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 "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 namespace android { 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 = HAL_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 err = composerService()->getDisplayStats(NULL, &stats); *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; } using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; status_t Surface::getWideColorSupport(bool* supported) { ATRACE_CALL(); sp display( composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); Vector colorModes; status_t err = composerService()->getDisplayColorModes(display, &colorModes); if (err) return err; bool wideColorBoardConfig = getBool(false); *supported = false; for (android_color_mode_t colorMode : colorModes) { switch (colorMode) { case HAL_COLOR_MODE_DISPLAY_P3: case HAL_COLOR_MODE_ADOBE_RGB: case HAL_COLOR_MODE_DCI_P3: if (wideColorBoardConfig) { *supported = true; } break; default: break; } } return NO_ERROR; } status_t Surface::getHdrSupport(bool* supported) { ATRACE_CALL(); sp display( composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); 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; mSwapIntervalZero = (interval == 0); mGraphicBufferProducer->setAsyncMode(mSwapIntervalZero); return NO_ERROR; } 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 != NULL) { *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 == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); 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 != NULL && 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, mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, mEnableFrameTimestamps); 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(std::move(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; // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { 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(); 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; } } } 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_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) { android_dataspace dataspace = static_cast(va_arg(args, int)); return setBuffersDataSpace(dataspace); } 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); } 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, 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; // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { 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 == NULL || outFence == NULL) { return BAD_VALUE; } Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } sp buffer(NULL); sp fence(NULL); status_t result = mGraphicBufferProducer->detachNextBuffer( &buffer, &fence); if (result != NO_ERROR) { return result; } *outBuffer = buffer; if (fence != NULL && fence->isValid()) { *outFence = fence; } else { *outFence = Fence::NO_FENCE; } for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != NULL && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { mRemovedBuffers.push_back(mSlots[i].buffer); } mSlots[i].buffer = NULL; } } 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 == NULL || 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); 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(android_dataspace dataSpace) { ALOGV("Surface::setBuffersDataSpace"); Mutex::Autolock lock(mMutex); mDataSpace = dataSpace; return NO_ERROR; } void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].buffer = 0; } } 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 = NULL; 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 = NULL; 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 != 0) { 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 != 0 && 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 == 0) { 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 = 0; 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; } }; // namespace android libs/gui/SurfaceComposerClient.cpp0100644 0000000 0000000 00000106721 13300556574 016304 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 "SurfaceComposerClient" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService); ComposerService::ComposerService() : Singleton() { Mutex::Autolock _l(mLock); connectLocked(); } void ComposerService::connectLocked() { const String16 name("SurfaceFlinger"); while (getService(name, &mComposerService) != NO_ERROR) { usleep(250000); } assert(mComposerService != NULL); // Create the death listener. class DeathObserver : public IBinder::DeathRecipient { ComposerService& mComposerService; virtual void binderDied(const wp& who) { ALOGW("ComposerService remote (surfaceflinger) died [%p]", who.unsafe_get()); mComposerService.composerServiceDied(); } public: explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { } }; mDeathObserver = new DeathObserver(*const_cast(this)); IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver); } /*static*/ sp ComposerService::getComposerService() { ComposerService& instance = ComposerService::getInstance(); Mutex::Autolock _l(instance.mLock); if (instance.mComposerService == NULL) { ComposerService::getInstance().connectLocked(); assert(instance.mComposerService != NULL); ALOGD("ComposerService reconnected"); } return instance.mComposerService; } void ComposerService::composerServiceDied() { Mutex::Autolock _l(mLock); mComposerService = NULL; mDeathObserver = NULL; } // --------------------------------------------------------------------------- static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { if (lhs.client < rhs.client) return -1; if (lhs.client > rhs.client) return 1; if (lhs.state.surface < rhs.state.surface) return -1; if (lhs.state.surface > rhs.state.surface) return 1; return 0; } static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) { return compare_type(lhs.token, rhs.token); } class Composer : public Singleton { friend class Singleton; mutable Mutex mLock; SortedVector mComposerStates; SortedVector mDisplayStates; uint32_t mForceSynchronous; uint32_t mTransactionNestCount; bool mAnimation; Composer() : Singleton(), mForceSynchronous(0), mTransactionNestCount(0), mAnimation(false) { } void openGlobalTransactionImpl(); void closeGlobalTransactionImpl(bool synchronous); void setAnimationTransactionImpl(); status_t enableVSyncInjectionsImpl(bool enable); status_t injectVSyncImpl(nsecs_t when); layer_state_t* getLayerStateLocked( const sp& client, const sp& id); DisplayState& getDisplayStateLocked(const sp& token); public: sp createDisplay(const String8& displayName, bool secure); void destroyDisplay(const sp& display); sp getBuiltInDisplay(int32_t id); status_t setPosition(const sp& client, const sp& id, float x, float y); status_t setSize(const sp& client, const sp& id, uint32_t w, uint32_t h); status_t setLayer(const sp& client, const sp& id, int32_t z); status_t setRelativeLayer(const sp& client, const sp& id, const sp& relativeTo, int32_t z); status_t setFlags(const sp& client, const sp& id, uint32_t flags, uint32_t mask); status_t setTransparentRegionHint( const sp& client, const sp& id, const Region& transparentRegion); status_t setAlpha(const sp& client, const sp& id, float alpha); status_t setMatrix(const sp& client, const sp& id, float dsdx, float dtdx, float dtdy, float dsdy); status_t setOrientation(int orientation); status_t setCrop(const sp& client, const sp& id, const Rect& crop); status_t setFinalCrop(const sp& client, const sp& id, const Rect& crop); status_t setLayerStack(const sp& client, const sp& id, uint32_t layerStack); status_t deferTransactionUntil(const sp& client, const sp& id, const sp& handle, uint64_t frameNumber); status_t deferTransactionUntil(const sp& client, const sp& id, const sp& barrierSurface, uint64_t frameNumber); status_t reparentChildren(const sp& client, const sp& id, const sp& newParentHandle); status_t detachChildren(const sp& client, const sp& id); status_t setOverrideScalingMode(const sp& client, const sp& id, int32_t overrideScalingMode); status_t setGeometryAppliesWithResize(const sp& client, const sp& id); status_t setDisplaySurface(const sp& token, sp bufferProducer); void setDisplayLayerStack(const sp& token, uint32_t layerStack); void setDisplayProjection(const sp& token, uint32_t orientation, const Rect& layerStackRect, const Rect& displayRect); void setDisplaySize(const sp& token, uint32_t width, uint32_t height); static void setAnimationTransaction() { Composer::getInstance().setAnimationTransactionImpl(); } static void openGlobalTransaction() { Composer::getInstance().openGlobalTransactionImpl(); } static void closeGlobalTransaction(bool synchronous) { Composer::getInstance().closeGlobalTransactionImpl(synchronous); } static status_t enableVSyncInjections(bool enable) { return Composer::getInstance().enableVSyncInjectionsImpl(enable); } static status_t injectVSync(nsecs_t when) { return Composer::getInstance().injectVSyncImpl(when); } }; ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- sp Composer::createDisplay(const String8& displayName, bool secure) { return ComposerService::getComposerService()->createDisplay(displayName, secure); } void Composer::destroyDisplay(const sp& display) { return ComposerService::getComposerService()->destroyDisplay(display); } sp Composer::getBuiltInDisplay(int32_t id) { return ComposerService::getComposerService()->getBuiltInDisplay(id); } void Composer::openGlobalTransactionImpl() { { // scope for the lock Mutex::Autolock _l(mLock); mTransactionNestCount += 1; } } void Composer::closeGlobalTransactionImpl(bool synchronous) { sp sm(ComposerService::getComposerService()); Vector transaction; Vector displayTransaction; uint32_t flags = 0; { // scope for the lock Mutex::Autolock _l(mLock); mForceSynchronous |= synchronous; if (!mTransactionNestCount) { ALOGW("At least one call to closeGlobalTransaction() was not matched by a prior " "call to openGlobalTransaction()."); } else if (--mTransactionNestCount) { return; } transaction = mComposerStates; mComposerStates.clear(); displayTransaction = mDisplayStates; mDisplayStates.clear(); if (mForceSynchronous) { flags |= ISurfaceComposer::eSynchronous; } if (mAnimation) { flags |= ISurfaceComposer::eAnimation; } mForceSynchronous = false; mAnimation = false; } sm->setTransactionState(transaction, displayTransaction, flags); } status_t Composer::enableVSyncInjectionsImpl(bool enable) { sp sm(ComposerService::getComposerService()); return sm->enableVSyncInjections(enable); } status_t Composer::injectVSyncImpl(nsecs_t when) { sp sm(ComposerService::getComposerService()); return sm->injectVSync(when); } void Composer::setAnimationTransactionImpl() { Mutex::Autolock _l(mLock); mAnimation = true; } layer_state_t* Composer::getLayerStateLocked( const sp& client, const sp& id) { ComposerState s; s.client = client->mClient; s.state.surface = id; ssize_t index = mComposerStates.indexOf(s); if (index < 0) { // we don't have it, add an initialized layer_state to our list index = mComposerStates.add(s); } ComposerState* const out = mComposerStates.editArray(); return &(out[index].state); } status_t Composer::setPosition(const sp& client, const sp& id, float x, float y) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::ePositionChanged; s->x = x; s->y = y; return NO_ERROR; } status_t Composer::setSize(const sp& client, const sp& id, uint32_t w, uint32_t h) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eSizeChanged; s->w = w; s->h = h; // Resizing a surface makes the transaction synchronous. mForceSynchronous = true; return NO_ERROR; } status_t Composer::setLayer(const sp& client, const sp& id, int32_t z) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eLayerChanged; s->z = z; return NO_ERROR; } status_t Composer::setRelativeLayer(const sp& client, const sp& id, const sp& relativeTo, int32_t z) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eRelativeLayerChanged; s->relativeLayerHandle = relativeTo; s->z = z; return NO_ERROR; } status_t Composer::setFlags(const sp& client, const sp& id, uint32_t flags, uint32_t mask) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) || (mask & layer_state_t::eLayerSecure)) { s->what |= layer_state_t::eFlagsChanged; } s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; return NO_ERROR; } status_t Composer::setTransparentRegionHint( const sp& client, const sp& id, const Region& transparentRegion) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eTransparentRegionChanged; s->transparentRegion = transparentRegion; return NO_ERROR; } status_t Composer::setAlpha(const sp& client, const sp& id, float alpha) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eAlphaChanged; s->alpha = alpha; return NO_ERROR; } status_t Composer::setLayerStack(const sp& client, const sp& id, uint32_t layerStack) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eLayerStackChanged; s->layerStack = layerStack; return NO_ERROR; } status_t Composer::setMatrix(const sp& client, const sp& id, float dsdx, float dtdx, float dtdy, float dsdy) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eMatrixChanged; layer_state_t::matrix22_t matrix; matrix.dsdx = dsdx; matrix.dtdx = dtdx; matrix.dsdy = dsdy; matrix.dtdy = dtdy; s->matrix = matrix; return NO_ERROR; } status_t Composer::setCrop(const sp& client, const sp& id, const Rect& crop) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; s->what |= layer_state_t::eCropChanged; s->crop = crop; return NO_ERROR; } status_t Composer::setFinalCrop(const sp& client, const sp& id, const Rect& crop) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eFinalCropChanged; s->finalCrop = crop; return NO_ERROR; } status_t Composer::deferTransactionUntil( const sp& client, const sp& id, const sp& handle, uint64_t frameNumber) { Mutex::Autolock lock(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eDeferTransaction; s->barrierHandle = handle; s->frameNumber = frameNumber; return NO_ERROR; } status_t Composer::deferTransactionUntil( const sp& client, const sp& id, const sp& barrierSurface, uint64_t frameNumber) { Mutex::Autolock lock(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eDeferTransaction; s->barrierGbp = barrierSurface->getIGraphicBufferProducer(); s->frameNumber = frameNumber; return NO_ERROR; } status_t Composer::reparentChildren( const sp& client, const sp& id, const sp& newParentHandle) { Mutex::Autolock lock(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eReparentChildren; s->reparentHandle = newParentHandle; return NO_ERROR; } status_t Composer::detachChildren( const sp& client, const sp& id) { Mutex::Autolock lock(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eDetachChildren; return NO_ERROR; } status_t Composer::setOverrideScalingMode( const sp& client, const sp& id, int32_t overrideScalingMode) { Mutex::Autolock lock(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } switch (overrideScalingMode) { 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: case -1: break; default: ALOGE("unknown scaling mode: %d", overrideScalingMode); return BAD_VALUE; } s->what |= layer_state_t::eOverrideScalingModeChanged; s->overrideScalingMode = overrideScalingMode; return NO_ERROR; } status_t Composer::setGeometryAppliesWithResize( const sp& client, const sp& id) { Mutex::Autolock lock(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) { return BAD_INDEX; } s->what |= layer_state_t::eGeometryAppliesWithResize; return NO_ERROR; } // --------------------------------------------------------------------------- DisplayState& Composer::getDisplayStateLocked(const sp& token) { DisplayState s; s.token = token; ssize_t index = mDisplayStates.indexOf(s); if (index < 0) { // we don't have it, add an initialized layer_state to our list s.what = 0; index = mDisplayStates.add(s); } return mDisplayStates.editItemAt(static_cast(index)); } status_t Composer::setDisplaySurface(const sp& token, sp bufferProducer) { if (bufferProducer.get() != nullptr) { // Make sure that composition can never be stalled by a virtual display // consumer that isn't processing buffers fast enough. status_t err = bufferProducer->setAsyncMode(true); if (err != NO_ERROR) { ALOGE("Composer::setDisplaySurface Failed to enable async mode on the " "BufferQueue. This BufferQueue cannot be used for virtual " "display. (%d)", err); return err; } } Mutex::Autolock _l(mLock); DisplayState& s(getDisplayStateLocked(token)); s.surface = bufferProducer; s.what |= DisplayState::eSurfaceChanged; return NO_ERROR; } void Composer::setDisplayLayerStack(const sp& token, uint32_t layerStack) { Mutex::Autolock _l(mLock); DisplayState& s(getDisplayStateLocked(token)); s.layerStack = layerStack; s.what |= DisplayState::eLayerStackChanged; } void Composer::setDisplayProjection(const sp& token, uint32_t orientation, const Rect& layerStackRect, const Rect& displayRect) { Mutex::Autolock _l(mLock); DisplayState& s(getDisplayStateLocked(token)); s.orientation = orientation; s.viewport = layerStackRect; s.frame = displayRect; s.what |= DisplayState::eDisplayProjectionChanged; mForceSynchronous = true; // TODO: do we actually still need this? } void Composer::setDisplaySize(const sp& token, uint32_t width, uint32_t height) { Mutex::Autolock _l(mLock); DisplayState& s(getDisplayStateLocked(token)); s.width = width; s.height = height; s.what |= DisplayState::eDisplaySizeChanged; } // --------------------------------------------------------------------------- SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT), mComposer(Composer::getInstance()) { } SurfaceComposerClient::SurfaceComposerClient(const sp& root) : mStatus(NO_INIT), mComposer(Composer::getInstance()), mParent(root) { } void SurfaceComposerClient::onFirstRef() { sp sm(ComposerService::getComposerService()); if (sm != 0) { auto rootProducer = mParent.promote(); sp conn; conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) : sm->createConnection(); if (conn != 0) { mClient = conn; mStatus = NO_ERROR; } } } SurfaceComposerClient::~SurfaceComposerClient() { dispose(); } status_t SurfaceComposerClient::initCheck() const { return mStatus; } sp SurfaceComposerClient::connection() const { return IInterface::asBinder(mClient); } status_t SurfaceComposerClient::linkToComposerDeath( const sp& recipient, void* cookie, uint32_t flags) { sp sm(ComposerService::getComposerService()); return IInterface::asBinder(sm)->linkToDeath(recipient, cookie, flags); } void SurfaceComposerClient::dispose() { // this can be called more than once. sp client; Mutex::Autolock _lm(mLock); if (mClient != 0) { client = mClient; // hold ref while lock is held mClient.clear(); } mStatus = NO_INIT; } sp SurfaceComposerClient::createSurface( const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, SurfaceControl* parent, uint32_t windowType, uint32_t ownerUid) { sp sur; if (mStatus == NO_ERROR) { sp handle; sp parentHandle; sp gbp; if (parent != nullptr) { parentHandle = parent->getHandle(); } status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, &handle, &gbp); ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { sur = new SurfaceControl(this, handle, gbp); } } return sur; } sp SurfaceComposerClient::createDisplay(const String8& displayName, bool secure) { return Composer::getInstance().createDisplay(displayName, secure); } void SurfaceComposerClient::destroyDisplay(const sp& display) { Composer::getInstance().destroyDisplay(display); } sp SurfaceComposerClient::getBuiltInDisplay(int32_t id) { return Composer::getInstance().getBuiltInDisplay(id); } status_t SurfaceComposerClient::destroySurface(const sp& sid) { if (mStatus != NO_ERROR) return mStatus; status_t err = mClient->destroySurface(sid); return err; } status_t SurfaceComposerClient::clearLayerFrameStats(const sp& token) const { if (mStatus != NO_ERROR) { return mStatus; } return mClient->clearLayerFrameStats(token); } status_t SurfaceComposerClient::getLayerFrameStats(const sp& token, FrameStats* outStats) const { if (mStatus != NO_ERROR) { return mStatus; } return mClient->getLayerFrameStats(token, outStats); } inline Composer& SurfaceComposerClient::getComposer() { return mComposer; } // ---------------------------------------------------------------------------- void SurfaceComposerClient::openGlobalTransaction() { Composer::openGlobalTransaction(); } void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) { Composer::closeGlobalTransaction(synchronous); } void SurfaceComposerClient::setAnimationTransaction() { Composer::setAnimationTransaction(); } status_t SurfaceComposerClient::enableVSyncInjections(bool enable) { return Composer::enableVSyncInjections(enable); } status_t SurfaceComposerClient::injectVSync(nsecs_t when) { return Composer::injectVSync(when); } // ---------------------------------------------------------------------------- status_t SurfaceComposerClient::setCrop(const sp& id, const Rect& crop) { return getComposer().setCrop(this, id, crop); } status_t SurfaceComposerClient::setFinalCrop(const sp& id, const Rect& crop) { return getComposer().setFinalCrop(this, id, crop); } status_t SurfaceComposerClient::setPosition(const sp& id, float x, float y) { return getComposer().setPosition(this, id, x, y); } status_t SurfaceComposerClient::setSize(const sp& id, uint32_t w, uint32_t h) { return getComposer().setSize(this, id, w, h); } status_t SurfaceComposerClient::setLayer(const sp& id, int32_t z) { return getComposer().setLayer(this, id, z); } status_t SurfaceComposerClient::setRelativeLayer(const sp& id, const sp& relativeTo, int32_t z) { return getComposer().setRelativeLayer(this, id, relativeTo, z); } status_t SurfaceComposerClient::hide(const sp& id) { return getComposer().setFlags(this, id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); } status_t SurfaceComposerClient::show(const sp& id) { return getComposer().setFlags(this, id, 0, layer_state_t::eLayerHidden); } status_t SurfaceComposerClient::setFlags(const sp& id, uint32_t flags, uint32_t mask) { return getComposer().setFlags(this, id, flags, mask); } status_t SurfaceComposerClient::setTransparentRegionHint(const sp& id, const Region& transparentRegion) { return getComposer().setTransparentRegionHint(this, id, transparentRegion); } status_t SurfaceComposerClient::setAlpha(const sp& id, float alpha) { return getComposer().setAlpha(this, id, alpha); } status_t SurfaceComposerClient::setLayerStack(const sp& id, uint32_t layerStack) { return getComposer().setLayerStack(this, id, layerStack); } status_t SurfaceComposerClient::setMatrix(const sp& id, float dsdx, float dtdx, float dtdy, float dsdy) { return getComposer().setMatrix(this, id, dsdx, dtdx, dtdy, dsdy); } status_t SurfaceComposerClient::deferTransactionUntil(const sp& id, const sp& handle, uint64_t frameNumber) { return getComposer().deferTransactionUntil(this, id, handle, frameNumber); } status_t SurfaceComposerClient::deferTransactionUntil(const sp& id, const sp& barrierSurface, uint64_t frameNumber) { return getComposer().deferTransactionUntil(this, id, barrierSurface, frameNumber); } status_t SurfaceComposerClient::reparentChildren(const sp& id, const sp& newParentHandle) { return getComposer().reparentChildren(this, id, newParentHandle); } status_t SurfaceComposerClient::detachChildren(const sp& id) { return getComposer().detachChildren(this, id); } status_t SurfaceComposerClient::setOverrideScalingMode( const sp& id, int32_t overrideScalingMode) { return getComposer().setOverrideScalingMode( this, id, overrideScalingMode); } status_t SurfaceComposerClient::setGeometryAppliesWithResize( const sp& id) { return getComposer().setGeometryAppliesWithResize(this, id); } // ---------------------------------------------------------------------------- status_t SurfaceComposerClient::setDisplaySurface(const sp& token, sp bufferProducer) { return Composer::getInstance().setDisplaySurface(token, bufferProducer); } void SurfaceComposerClient::setDisplayLayerStack(const sp& token, uint32_t layerStack) { Composer::getInstance().setDisplayLayerStack(token, layerStack); } void SurfaceComposerClient::setDisplayProjection(const sp& token, uint32_t orientation, const Rect& layerStackRect, const Rect& displayRect) { Composer::getInstance().setDisplayProjection(token, orientation, layerStackRect, displayRect); } void SurfaceComposerClient::setDisplaySize(const sp& token, uint32_t width, uint32_t height) { Composer::getInstance().setDisplaySize(token, width, height); } // ---------------------------------------------------------------------------- status_t SurfaceComposerClient::getDisplayConfigs( const sp& display, Vector* configs) { return ComposerService::getComposerService()->getDisplayConfigs(display, configs); } status_t SurfaceComposerClient::getDisplayInfo(const sp& display, DisplayInfo* info) { Vector configs; status_t result = getDisplayConfigs(display, &configs); if (result != NO_ERROR) { return result; } int activeId = getActiveConfig(display); if (activeId < 0) { ALOGE("No active configuration found"); return NAME_NOT_FOUND; } *info = configs[static_cast(activeId)]; return NO_ERROR; } int SurfaceComposerClient::getActiveConfig(const sp& display) { return ComposerService::getComposerService()->getActiveConfig(display); } status_t SurfaceComposerClient::setActiveConfig(const sp& display, int id) { return ComposerService::getComposerService()->setActiveConfig(display, id); } status_t SurfaceComposerClient::getDisplayColorModes(const sp& display, Vector* outColorModes) { return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes); } android_color_mode_t SurfaceComposerClient::getActiveColorMode(const sp& display) { return ComposerService::getComposerService()->getActiveColorMode(display); } status_t SurfaceComposerClient::setActiveColorMode(const sp& display, android_color_mode_t colorMode) { return ComposerService::getComposerService()->setActiveColorMode(display, colorMode); } void SurfaceComposerClient::setDisplayPowerMode(const sp& token, int mode) { ComposerService::getComposerService()->setPowerMode(token, mode); } status_t SurfaceComposerClient::clearAnimationFrameStats() { return ComposerService::getComposerService()->clearAnimationFrameStats(); } status_t SurfaceComposerClient::getAnimationFrameStats(FrameStats* outStats) { return ComposerService::getComposerService()->getAnimationFrameStats(outStats); } status_t SurfaceComposerClient::getHdrCapabilities(const sp& display, HdrCapabilities* outCapabilities) { return ComposerService::getComposerService()->getHdrCapabilities(display, outCapabilities); } // ---------------------------------------------------------------------------- status_t ScreenshotClient::capture( const sp& display, const sp& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) { sp s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; return s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform); } status_t ScreenshotClient::captureToBuffer(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, uint32_t rotation, sp* outBuffer) { sp s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; sp gbpConsumer; sp producer; BufferQueue::createBufferQueue(&producer, &gbpConsumer); sp consumer(new BufferItemConsumer(gbpConsumer, GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER, 1, true)); status_t ret = s->captureScreen(display, producer, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform, static_cast(rotation)); if (ret != NO_ERROR) { return ret; } BufferItem b; consumer->acquireBuffer(&b, 0, true); *outBuffer = b.mGraphicBuffer; return ret; } ScreenshotClient::ScreenshotClient() : mHaveBuffer(false) { memset(&mBuffer, 0, sizeof(mBuffer)); } ScreenshotClient::~ScreenshotClient() { ScreenshotClient::release(); } sp ScreenshotClient::getCpuConsumer() const { if (mCpuConsumer == NULL) { sp consumer; BufferQueue::createBufferQueue(&mProducer, &consumer); mCpuConsumer = new CpuConsumer(consumer, 1); mCpuConsumer->setName(String8("ScreenshotClient")); } return mCpuConsumer; } status_t ScreenshotClient::update(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, uint32_t rotation) { sp s(ComposerService::getComposerService()); if (s == NULL) return NO_INIT; sp cpuConsumer = getCpuConsumer(); if (mHaveBuffer) { mCpuConsumer->unlockBuffer(mBuffer); memset(&mBuffer, 0, sizeof(mBuffer)); mHaveBuffer = false; } status_t err = s->captureScreen(display, mProducer, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform, static_cast(rotation)); if (err == NO_ERROR) { err = mCpuConsumer->lockNextBuffer(&mBuffer); if (err == NO_ERROR) { mHaveBuffer = true; } } return err; } status_t ScreenshotClient::update(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform) { return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform, ISurfaceComposer::eRotateNone); } status_t ScreenshotClient::update(const sp& display, Rect sourceCrop, bool useIdentityTransform) { return ScreenshotClient::update(display, sourceCrop, 0, 0, INT32_MIN, INT32_MAX, useIdentityTransform, ISurfaceComposer::eRotateNone); } status_t ScreenshotClient::update(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform) { return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight, INT32_MIN, INT32_MAX, useIdentityTransform, ISurfaceComposer::eRotateNone); } void ScreenshotClient::release() { if (mHaveBuffer) { mCpuConsumer->unlockBuffer(mBuffer); memset(&mBuffer, 0, sizeof(mBuffer)); mHaveBuffer = false; } mCpuConsumer.clear(); } void const* ScreenshotClient::getPixels() const { return mBuffer.data; } uint32_t ScreenshotClient::getWidth() const { return mBuffer.width; } uint32_t ScreenshotClient::getHeight() const { return mBuffer.height; } PixelFormat ScreenshotClient::getFormat() const { return mBuffer.format; } uint32_t ScreenshotClient::getStride() const { return mBuffer.stride; } size_t ScreenshotClient::getSize() const { return mBuffer.stride * mBuffer.height * bytesPerPixel(mBuffer.format); } android_dataspace ScreenshotClient::getDataSpace() const { return mBuffer.dataSpace; } // ---------------------------------------------------------------------------- }; // namespace android libs/gui/SurfaceControl.cpp0100644 0000000 0000000 00000017654 13300556574 015004 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 "SurfaceControl" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // ============================================================================ // SurfaceControl // ============================================================================ SurfaceControl::SurfaceControl( const sp& client, const sp& handle, const sp& gbp) : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp) { } SurfaceControl::~SurfaceControl() { destroy(); } void SurfaceControl::destroy() { if (isValid()) { mClient->destroySurface(mHandle); } // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. mClient.clear(); mHandle.clear(); mGraphicBufferProducer.clear(); IPCThreadState::self()->flushCommands(); } void SurfaceControl::clear() { // here, the window manager tells us explicitly that we should destroy // the surface's resource. Soon after this call, it will also release // its last reference (which will call the dtor); however, it is possible // that a client living in the same process still holds references which // would delay the call to the dtor -- that is why we need this explicit // "clear()" call. destroy(); } void SurfaceControl::disconnect() { if (mGraphicBufferProducer != NULL) { mGraphicBufferProducer->disconnect( BufferQueueCore::CURRENTLY_CONNECTED_API); } } bool SurfaceControl::isSameSurface( const sp& lhs, const sp& rhs) { if (lhs == 0 || rhs == 0) return false; return lhs->mHandle == rhs->mHandle; } status_t SurfaceControl::setLayerStack(uint32_t layerStack) { status_t err = validate(); if (err < 0) return err; return mClient->setLayerStack(mHandle, layerStack); } status_t SurfaceControl::setLayer(int32_t layer) { status_t err = validate(); if (err < 0) return err; return mClient->setLayer(mHandle, layer); } status_t SurfaceControl::setRelativeLayer(const sp& relativeTo, int32_t layer) { status_t err = validate(); if (err < 0) return err; return mClient->setRelativeLayer(mHandle, relativeTo, layer); } status_t SurfaceControl::setPosition(float x, float y) { status_t err = validate(); if (err < 0) return err; return mClient->setPosition(mHandle, x, y); } status_t SurfaceControl::setGeometryAppliesWithResize() { status_t err = validate(); if (err < 0) return err; return mClient->setGeometryAppliesWithResize(mHandle); } status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { status_t err = validate(); if (err < 0) return err; return mClient->setSize(mHandle, w, h); } status_t SurfaceControl::hide() { status_t err = validate(); if (err < 0) return err; return mClient->hide(mHandle); } status_t SurfaceControl::show() { status_t err = validate(); if (err < 0) return err; return mClient->show(mHandle); } status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { status_t err = validate(); if (err < 0) return err; return mClient->setFlags(mHandle, flags, mask); } status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { status_t err = validate(); if (err < 0) return err; return mClient->setTransparentRegionHint(mHandle, transparent); } status_t SurfaceControl::setAlpha(float alpha) { status_t err = validate(); if (err < 0) return err; return mClient->setAlpha(mHandle, alpha); } status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { status_t err = validate(); if (err < 0) return err; return mClient->setMatrix(mHandle, dsdx, dtdx, dtdy, dsdy); } status_t SurfaceControl::setCrop(const Rect& crop) { status_t err = validate(); if (err < 0) return err; return mClient->setCrop(mHandle, crop); } status_t SurfaceControl::setFinalCrop(const Rect& crop) { status_t err = validate(); if (err < 0) return err; return mClient->setFinalCrop(mHandle, crop); } status_t SurfaceControl::deferTransactionUntil(const sp& handle, uint64_t frameNumber) { status_t err = validate(); if (err < 0) return err; return mClient->deferTransactionUntil(mHandle, handle, frameNumber); } status_t SurfaceControl::deferTransactionUntil(const sp& handle, uint64_t frameNumber) { status_t err = validate(); if (err < 0) return err; return mClient->deferTransactionUntil(mHandle, handle, frameNumber); } status_t SurfaceControl::reparentChildren(const sp& newParentHandle) { status_t err = validate(); if (err < 0) return err; return mClient->reparentChildren(mHandle, newParentHandle); } status_t SurfaceControl::detachChildren() { status_t err = validate(); if (err < 0) return err; return mClient->detachChildren(mHandle); } status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) { status_t err = validate(); if (err < 0) return err; return mClient->setOverrideScalingMode(mHandle, overrideScalingMode); } status_t SurfaceControl::clearLayerFrameStats() const { status_t err = validate(); if (err < 0) return err; const sp& client(mClient); return client->clearLayerFrameStats(mHandle); } status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const { status_t err = validate(); if (err < 0) return err; const sp& client(mClient); return client->getLayerFrameStats(mHandle, outStats); } status_t SurfaceControl::validate() const { if (mHandle==0 || mClient==0) { ALOGE("invalid handle (%p) or client (%p)", mHandle.get(), mClient.get()); return NO_INIT; } return NO_ERROR; } status_t SurfaceControl::writeSurfaceToParcel( const sp& control, Parcel* parcel) { sp bp; if (control != NULL) { bp = control->mGraphicBufferProducer; } return parcel->writeStrongBinder(IInterface::asBinder(bp)); } sp SurfaceControl::generateSurfaceLocked() const { // This surface is always consumed by SurfaceFlinger, so the // producerControlledByApp value doesn't matter; using false. mSurfaceData = new Surface(mGraphicBufferProducer, false); return mSurfaceData; } sp SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); if (mSurfaceData == 0) { return generateSurfaceLocked(); } return mSurfaceData; } sp SurfaceControl::createSurface() const { Mutex::Autolock _l(mLock); return generateSurfaceLocked(); } sp SurfaceControl::getHandle() const { Mutex::Autolock lock(mLock); return mHandle; } // ---------------------------------------------------------------------------- }; // namespace android libs/gui/SyncFeatures.cpp0100644 0000000 0000000 00000005751 13300556574 014461 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. */ #define LOG_TAG "GLConsumer" #define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); namespace android { ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures); SyncFeatures::SyncFeatures() : Singleton(), mHasNativeFenceSync(false), mHasFenceSync(false), mHasWaitSync(false) { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); // This can only be called after EGL has been initialized; otherwise the // check below will abort. const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all // GLES reads for a given buffer have completed. mHasNativeFenceSync = true; } if (strstr(exts, "EGL_KHR_fence_sync")) { mHasFenceSync = true; } if (strstr(exts, "EGL_KHR_wait_sync")) { mHasWaitSync = true; } mString.append("[using:"); if (useNativeFenceSync()) { mString.append(" EGL_ANDROID_native_fence_sync"); } if (useFenceSync()) { mString.append(" EGL_KHR_fence_sync"); } if (useWaitSync()) { mString.append(" EGL_KHR_wait_sync"); } mString.append("]"); } bool SyncFeatures::useNativeFenceSync() const { // EGL_ANDROID_native_fence_sync is not compatible with using the // EGL_KHR_fence_sync extension for the same purpose. return mHasNativeFenceSync; } bool SyncFeatures::useFenceSync() const { #ifdef DONT_USE_FENCE_SYNC // on some devices it's better to not use EGL_KHR_fence_sync // even if they have it return false; #else // currently we shall only attempt to use EGL_KHR_fence_sync if // USE_FENCE_SYNC is set in our makefile return !mHasNativeFenceSync && mHasFenceSync; #endif } bool SyncFeatures::useWaitSync() const { return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync; } String8 SyncFeatures::toString() const { return mString; } } // namespace android libs/gui/bufferqueue/0040755 0000000 0000000 00000000000 13300556574 013653 5ustar000000000 0000000 libs/gui/bufferqueue/1.0/0040755 0000000 0000000 00000000000 13300556574 014151 5ustar000000000 0000000 libs/gui/bufferqueue/1.0/B2HProducerListener.cpp0100644 0000000 0000000 00000002375 13300556574 020446 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 namespace android { namespace hardware { namespace graphics { namespace bufferqueue { namespace V1_0 { namespace utils { // B2HProducerListener B2HProducerListener::B2HProducerListener( sp const& base): mBase(base) { } Return B2HProducerListener::onBufferReleased() { mBase->onBufferReleased(); return Void(); } Return B2HProducerListener::needsReleaseNotify() { return mBase->needsReleaseNotify(); } } // namespace utils } // namespace V1_0 } // namespace bufferqueue } // namespace graphics } // namespace hardware } // namespace android libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp0100644 0000000 0000000 00000130302 13300556574 021360 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 LOG_TAG "H2BGraphicBufferProducer" #include #include #include #include namespace android { namespace hardware { namespace graphics { namespace bufferqueue { namespace V1_0 { namespace utils { using Status = HGraphicBufferProducer::Status; using ::android::hardware::graphics::common::V1_0::Dataspace; typedef ::android::hardware::media::V1_0::Rect HRect; typedef ::android::hardware::media::V1_0::Region HRegion; // Conversion functions // native_handle_t helper functions. /** * \brief Take an fd and create a native handle containing only the given fd. * The created handle will need to be deleted manually with * `native_handle_delete()`. * * \param[in] fd The source file descriptor (of type `int`). * \return The create `native_handle_t*` that contains the given \p fd. If the * supplied \p fd is negative, the created native handle will contain no file * descriptors. * * If the native handle cannot be created, the return value will be * `nullptr`. * * This function does not duplicate the file descriptor. */ inline native_handle_t* native_handle_create_from_fd(int fd) { if (fd < 0) { return native_handle_create(0, 0); } native_handle_t* nh = native_handle_create(1, 0); if (nh == nullptr) { return nullptr; } nh->data[0] = fd; return nh; } /** * \brief Extract a file descriptor from a native handle. * * \param[in] nh The source `native_handle_t*`. * \param[in] index The index of the file descriptor in \p nh to read from. This * input has the default value of `0`. * \return The `index`-th file descriptor in \p nh. If \p nh does not have * enough file descriptors, the returned value will be `-1`. * * This function does not duplicate the file descriptor. */ inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) { return ((nh == nullptr) || (nh->numFds == 0) || (nh->numFds <= index) || (index < 0)) ? -1 : nh->data[index]; } /** * \brief Convert `Return` to `status_t`. This is for legacy binder * calls. * * \param[in] t The source `Return`. * \return The corresponding `status_t`. * * This function first check if \p t has a transport error. If it does, then the * return value is the transport error code. Otherwise, the return value is * converted from `Status` contained inside \p t. * * Note: * - This `Status` is omx-specific. It is defined in `types.hal`. * - The name of this function is not `convert`. */ // convert: Return -> status_t inline status_t toStatusT(Return const& t) { return t.isOk() ? static_cast(static_cast(t)) : UNKNOWN_ERROR; } /** * \brief Convert `Return` to `status_t`. This is for legacy binder calls. * * \param[in] t The source `Return`. * \return The corresponding `status_t`. */ // convert: Return -> status_t inline status_t toStatusT(Return const& t) { return t.isOk() ? OK : UNKNOWN_ERROR; } /** * \brief Wrap `GraphicBuffer` in `AnwBuffer`. * * \param[out] t The wrapper of type `AnwBuffer`. * \param[in] l The source `GraphicBuffer`. */ // wrap: GraphicBuffer -> AnwBuffer inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) { t->attr.width = l.getWidth(); t->attr.height = l.getHeight(); t->attr.stride = l.getStride(); t->attr.format = static_cast(l.getPixelFormat()); t->attr.layerCount = l.getLayerCount(); t->attr.usage = uint32_t(l.getUsage()); // FIXME: need 64-bits usage version t->attr.id = l.getId(); t->attr.generationNumber = l.getGenerationNumber(); t->nativeHandle = hidl_handle(l.handle); } /** * \brief Convert `AnwBuffer` to `GraphicBuffer`. * * \param[out] l The destination `GraphicBuffer`. * \param[in] t The source `AnwBuffer`. * * This function will duplicate all file descriptors in \p t. */ // convert: AnwBuffer -> GraphicBuffer // Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) { native_handle_t* handle = t.nativeHandle == nullptr ? nullptr : native_handle_clone(t.nativeHandle); size_t const numInts = 12 + static_cast(handle ? handle->numInts : 0); int32_t* ints = new int32_t[numInts]; size_t numFds = static_cast(handle ? handle->numFds : 0); int* fds = new int[numFds]; ints[0] = 'GBFR'; ints[1] = static_cast(t.attr.width); ints[2] = static_cast(t.attr.height); ints[3] = static_cast(t.attr.stride); ints[4] = static_cast(t.attr.format); ints[5] = static_cast(t.attr.layerCount); ints[6] = static_cast(t.attr.usage); ints[7] = static_cast(t.attr.id >> 32); ints[8] = static_cast(t.attr.id & 0xFFFFFFFF); ints[9] = static_cast(t.attr.generationNumber); ints[10] = 0; ints[11] = 0; if (handle) { ints[10] = static_cast(handle->numFds); ints[11] = static_cast(handle->numInts); int* intsStart = handle->data + handle->numFds; std::copy(handle->data, intsStart, fds); std::copy(intsStart, intsStart + handle->numInts, &ints[12]); } void const* constBuffer = static_cast(ints); size_t size = numInts * sizeof(int32_t); int const* constFds = static_cast(fds); status_t status = l->unflatten(constBuffer, size, constFds, numFds); delete [] fds; delete [] ints; native_handle_delete(handle); return status == NO_ERROR; } // Ref: frameworks/native/libs/ui/Fence.cpp /** * \brief Return the size of the non-fd buffer required to flatten a fence. * * \param[in] fence The input fence of type `hidl_handle`. * \return The required size of the flat buffer. * * The current version of this function always returns 4, which is the number of * bytes required to store the number of file descriptors contained in the fd * part of the flat buffer. */ inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) { return 4; }; /** * \brief Return the number of file descriptors contained in a fence. * * \param[in] fence The input fence of type `hidl_handle`. * \return `0` if \p fence does not contain a valid file descriptor, or `1` * otherwise. */ inline size_t getFenceFdCount(hidl_handle const& fence) { return native_handle_read_fd(fence) == -1 ? 0 : 1; } /** * \brief Unflatten `Fence` to `hidl_handle`. * * \param[out] fence The destination `hidl_handle`. * \param[out] nh The underlying native handle. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR`, \p nh will point to a newly created * native handle, which needs to be deleted with `native_handle_delete()` * afterwards. */ inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < 4) { return NO_MEMORY; } uint32_t numFdsInHandle; FlattenableUtils::read(buffer, size, numFdsInHandle); if (numFdsInHandle > 1) { return BAD_VALUE; } if (numFds < numFdsInHandle) { return NO_MEMORY; } if (numFdsInHandle) { *nh = native_handle_create_from_fd(*fds); if (*nh == nullptr) { return NO_MEMORY; } *fence = *nh; ++fds; --numFds; } else { *nh = nullptr; *fence = hidl_handle(); } return NO_ERROR; } /** * \brief Flatten `hidl_handle` as `Fence`. * * \param[in] fence The source `hidl_handle`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. */ inline status_t flattenFence(hidl_handle const& fence, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (size < getFenceFlattenedSize(fence) || numFds < getFenceFdCount(fence)) { 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(getFenceFdCount(fence))); int fd = native_handle_read_fd(fence); if (fd != -1) { *fds = fd; ++fds; --numFds; } return NO_ERROR; } /** * \brief Wrap `Fence` in `hidl_handle`. * * \param[out] t The wrapper of type `hidl_handle`. * \param[out] nh The native handle pointed to by \p t. * \param[in] l The source `Fence`. * * On success, \p nh will hold a newly created native handle, which must be * deleted manually with `native_handle_delete()` afterwards. */ // wrap: Fence -> hidl_handle inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) { size_t const baseSize = l.getFlattenedSize(); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = l.getFdCount(); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = static_cast(baseFds.get()); size_t numFds = baseNumFds; if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (unflattenFence(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } /** * \brief Convert `hidl_handle` to `Fence`. * * \param[out] l The destination `Fence`. `l` must not have been used * (`l->isValid()` must return `false`) before this function is called. * \param[in] t The source `hidl_handle`. * * If \p t contains a valid file descriptor, it will be duplicated. */ // convert: hidl_handle -> Fence inline bool convertTo(Fence* l, hidl_handle const& t) { int fd = native_handle_read_fd(t); if (fd != -1) { fd = dup(fd); if (fd == -1) { return false; } } native_handle_t* nh = native_handle_create_from_fd(fd); if (nh == nullptr) { if (fd != -1) { close(fd); } return false; } size_t const baseSize = getFenceFlattenedSize(t); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { native_handle_delete(nh); return false; } size_t const baseNumFds = getFenceFdCount(t); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { native_handle_delete(nh); return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = static_cast(baseFds.get()); size_t numFds = baseNumFds; if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) { native_handle_delete(nh); return false; } native_handle_delete(nh); void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } // Ref: frameworks/native/libs/ui/Region.cpp /** * \brief Unflatten `HRegion`. * * \param[out] t The destination `HRegion`. * \param[in,out] buffer The pointer to the flat buffer. * \param[in,out] size The size of the flat buffer. * \return `NO_ERROR` on success; other value on failure. */ inline status_t unflatten(HRegion* t, 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(HRect)) { return NO_MEMORY; } if (numRects > (UINT32_MAX / sizeof(HRect))) { return NO_MEMORY; } t->resize(numRects); for (size_t r = 0; r < numRects; ++r) { ::android::Rect rect(::android::Rect::EMPTY_RECT); status_t status = rect.unflatten(buffer, size); if (status != NO_ERROR) { return status; } FlattenableUtils::advance(buffer, size, sizeof(rect)); (*t)[r] = HRect{ static_cast(rect.left), static_cast(rect.top), static_cast(rect.right), static_cast(rect.bottom)}; } return NO_ERROR; } // Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp: // IGraphicBufferProducer::QueueBufferInput /** * \brief Return a lower bound on the size of the buffer required to flatten * `HGraphicBufferProducer::QueueBufferInput`. * * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. * \return A lower bound on the size of the flat buffer. */ constexpr size_t minFlattenedSize( HGraphicBufferProducer::QueueBufferInput const& /* t */) { return sizeof(int64_t) + // timestamp sizeof(int) + // isAutoTimestamp sizeof(android_dataspace) + // dataSpace sizeof(::android::Rect) + // crop sizeof(int) + // scalingMode sizeof(uint32_t) + // transform sizeof(uint32_t) + // stickyTransform sizeof(bool); // getFrameTimestamps } /** * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. * * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. * \param[out] nh The underlying native handle for `t->fence`. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * If the return value is `NO_ERROR` and `t->fence` contains a valid file * descriptor, \p nh will be a newly created native handle holding that file * descriptor. \p nh needs to be deleted with `native_handle_delete()` * afterwards. */ inline status_t unflatten( HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { if (size < minFlattenedSize(*t)) { return NO_MEMORY; } FlattenableUtils::read(buffer, size, t->timestamp); int lIsAutoTimestamp; FlattenableUtils::read(buffer, size, lIsAutoTimestamp); t->isAutoTimestamp = static_cast(lIsAutoTimestamp); android_dataspace_t lDataSpace; FlattenableUtils::read(buffer, size, lDataSpace); t->dataSpace = static_cast(lDataSpace); ::android::Rect lCrop; FlattenableUtils::read(buffer, size, lCrop); t->crop = HRect{ static_cast(lCrop.left), static_cast(lCrop.top), static_cast(lCrop.right), static_cast(lCrop.bottom)}; int lScalingMode; FlattenableUtils::read(buffer, size, lScalingMode); t->scalingMode = static_cast(lScalingMode); FlattenableUtils::read(buffer, size, t->transform); FlattenableUtils::read(buffer, size, t->stickyTransform); FlattenableUtils::read(buffer, size, t->getFrameTimestamps); status_t status = unflattenFence(&(t->fence), nh, buffer, size, fds, numFds); if (status != NO_ERROR) { return status; } return unflatten(&(t->surfaceDamage), buffer, size); } /** * \brief Wrap `IGraphicBufferProducer::QueueBufferInput` in * `HGraphicBufferProducer::QueueBufferInput`. * * \param[out] t The wrapper of type * `HGraphicBufferProducer::QueueBufferInput`. * \param[out] nh The underlying native handle for `t->fence`. * \param[in] l The source `IGraphicBufferProducer::QueueBufferInput`. * * If the return value is `true` and `t->fence` contains a valid file * descriptor, \p nh will be a newly created native handle holding that file * descriptor. \p nh needs to be deleted with `native_handle_delete()` * afterwards. */ inline bool wrapAs( HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, BGraphicBufferProducer::QueueBufferInput const& l) { size_t const baseSize = l.getFlattenedSize(); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = l.getFdCount(); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = baseFds.get(); size_t numFds = baseNumFds; if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { return false; } return true; } // Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot /** * \brief Return the size of the non-fd buffer required to flatten * `FenceTimeSnapshot`. * * \param[in] t The input `FenceTimeSnapshot`. * \return The required size of the flat buffer. */ inline size_t getFlattenedSize( HGraphicBufferProducer::FenceTimeSnapshot const& t) { constexpr size_t min = sizeof(t.state); switch (t.state) { case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: return min; case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: return min + getFenceFlattenedSize(t.fence); case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: return min + sizeof( ::android::FenceTime::Snapshot::signalTime); } return 0; } /** * \brief Return the number of file descriptors contained in * `FenceTimeSnapshot`. * * \param[in] t The input `FenceTimeSnapshot`. * \return The number of file descriptors contained in \p snapshot. */ inline size_t getFdCount( HGraphicBufferProducer::FenceTimeSnapshot const& t) { return t.state == HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ? getFenceFdCount(t.fence) : 0; } /** * \brief Flatten `FenceTimeSnapshot`. * * \param[in] t The source `FenceTimeSnapshot`. * \param[out] nh The cloned native handle, if necessary. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * This function will duplicate the file descriptor in `t.fence` if `t.state == * FENCE`, in which case \p nh will be returned. */ inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, native_handle_t** nh, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (size < getFlattenedSize(t)) { return NO_MEMORY; } *nh = nullptr; switch (t.state) { case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: FlattenableUtils::write(buffer, size, ::android::FenceTime::Snapshot::State::EMPTY); return NO_ERROR; case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: FlattenableUtils::write(buffer, size, ::android::FenceTime::Snapshot::State::FENCE); *nh = t.fence.getNativeHandle() == nullptr ? nullptr : native_handle_clone(t.fence); return flattenFence(hidl_handle(*nh), buffer, size, fds, numFds); case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: FlattenableUtils::write(buffer, size, ::android::FenceTime::Snapshot::State::SIGNAL_TIME); FlattenableUtils::write(buffer, size, t.signalTimeNs); return NO_ERROR; } return NO_ERROR; } // Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta /** * \brief Return a lower bound on the size of the non-fd buffer required to * flatten `FrameEventsDelta`. * * \param[in] t The input `FrameEventsDelta`. * \return A lower bound on the size of the flat buffer. */ constexpr size_t minFlattenedSize( HGraphicBufferProducer::FrameEventsDelta const& /* t */) { return sizeof(uint64_t) + // mFrameNumber sizeof(uint8_t) + // mIndex sizeof(uint8_t) + // mAddPostCompositeCalled sizeof(uint8_t) + // mAddRetireCalled sizeof(uint8_t) + // mAddReleaseCalled sizeof(nsecs_t) + // mPostedTime sizeof(nsecs_t) + // mRequestedPresentTime sizeof(nsecs_t) + // mLatchTime sizeof(nsecs_t) + // mFirstRefreshStartTime sizeof(nsecs_t); // mLastRefreshStartTime } /** * \brief Return the size of the non-fd buffer required to flatten * `FrameEventsDelta`. * * \param[in] t The input `FrameEventsDelta`. * \return The required size of the flat buffer. */ inline size_t getFlattenedSize( HGraphicBufferProducer::FrameEventsDelta const& t) { return minFlattenedSize(t) + getFlattenedSize(t.gpuCompositionDoneFence) + getFlattenedSize(t.displayPresentFence) + getFlattenedSize(t.displayRetireFence) + getFlattenedSize(t.releaseFence); }; /** * \brief Return the number of file descriptors contained in * `FrameEventsDelta`. * * \param[in] t The input `FrameEventsDelta`. * \return The number of file descriptors contained in \p t. */ inline size_t getFdCount( HGraphicBufferProducer::FrameEventsDelta const& t) { return getFdCount(t.gpuCompositionDoneFence) + getFdCount(t.displayPresentFence) + getFdCount(t.displayRetireFence) + getFdCount(t.releaseFence); }; /** * \brief Flatten `FrameEventsDelta`. * * \param[in] t The source `FrameEventsDelta`. * \param[out] nh The array of native handles that are cloned. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * On success, this function will duplicate file descriptors contained in \p t. * The cloned native handles will be stored in \p nh. These native handles will * need to be closed by the caller. */ // Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: // FrameEventsDelta::flatten inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, std::vector* nh, void*& buffer, size_t& size, int*& fds, size_t numFds) { // Check that t.index is within a valid range. if (t.index >= static_cast(FrameEventHistory::MAX_FRAME_HISTORY) || t.index > std::numeric_limits::max()) { return BAD_VALUE; } FlattenableUtils::write(buffer, size, t.frameNumber); // These are static_cast to uint8_t for alignment. FlattenableUtils::write(buffer, size, static_cast(t.index)); FlattenableUtils::write( buffer, size, static_cast(t.addPostCompositeCalled)); FlattenableUtils::write( buffer, size, static_cast(t.addRetireCalled)); FlattenableUtils::write( buffer, size, static_cast(t.addReleaseCalled)); FlattenableUtils::write(buffer, size, t.postedTimeNs); FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs); FlattenableUtils::write(buffer, size, t.latchTimeNs); FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs); FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs); FlattenableUtils::write(buffer, size, t.dequeueReadyTime); // Fences HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4]; tSnapshot[0] = &t.gpuCompositionDoneFence; tSnapshot[1] = &t.displayPresentFence; tSnapshot[2] = &t.displayRetireFence; tSnapshot[3] = &t.releaseFence; nh->resize(4); for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { status_t status = flatten( *(tSnapshot[snapshotIndex]), &((*nh)[snapshotIndex]), buffer, size, fds, numFds); if (status != NO_ERROR) { while (snapshotIndex > 0) { --snapshotIndex; native_handle_close((*nh)[snapshotIndex]); native_handle_delete((*nh)[snapshotIndex]); (*nh)[snapshotIndex] = nullptr; } return status; } } return NO_ERROR; } // Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta /** * \brief Return the size of the non-fd buffer required to flatten * `HGraphicBufferProducer::FrameEventHistoryDelta`. * * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. * \return The required size of the flat buffer. */ inline size_t getFlattenedSize( HGraphicBufferProducer::FrameEventHistoryDelta const& t) { size_t size = 4 + // mDeltas.size() sizeof(t.compositorTiming); for (size_t i = 0; i < t.deltas.size(); ++i) { size += getFlattenedSize(t.deltas[i]); } return size; } /** * \brief Return the number of file descriptors contained in * `HGraphicBufferProducer::FrameEventHistoryDelta`. * * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. * \return The number of file descriptors contained in \p t. */ inline size_t getFdCount( HGraphicBufferProducer::FrameEventHistoryDelta const& t) { size_t numFds = 0; for (size_t i = 0; i < t.deltas.size(); ++i) { numFds += getFdCount(t.deltas[i]); } return numFds; } /** * \brief Flatten `FrameEventHistoryDelta`. * * \param[in] t The source `FrameEventHistoryDelta`. * \param[out] nh The array of arrays of cloned native handles. * \param[in,out] buffer The pointer to the flat non-fd buffer. * \param[in,out] size The size of the flat non-fd buffer. * \param[in,out] fds The pointer to the flat fd buffer. * \param[in,out] numFds The size of the flat fd buffer. * \return `NO_ERROR` on success; other value on failure. * * On success, this function will duplicate file descriptors contained in \p t. * The cloned native handles will be stored in \p nh. Before making the call, \p * nh should have enough space to store `n` pointers to arrays of native * handles, where `n` is the length of `t.deltas`, and each `nh[i]` should have * enough space to store `4` native handles. */ inline status_t flatten( HGraphicBufferProducer::FrameEventHistoryDelta const& t, std::vector >* nh, void*& buffer, size_t& size, int*& fds, size_t& numFds) { if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { return BAD_VALUE; } if (size < getFlattenedSize(t)) { return NO_MEMORY; } FlattenableUtils::write(buffer, size, t.compositorTiming); FlattenableUtils::write(buffer, size, static_cast(t.deltas.size())); nh->resize(t.deltas.size()); for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) { status_t status = flatten( t.deltas[deltaIndex], &((*nh)[deltaIndex]), buffer, size, fds, numFds); if (status != NO_ERROR) { while (deltaIndex > 0) { --deltaIndex; for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { native_handle_close((*nh)[deltaIndex][snapshotIndex]); native_handle_delete((*nh)[deltaIndex][snapshotIndex]); (*nh)[deltaIndex][snapshotIndex] = nullptr; } } return status; } } return NO_ERROR; } /** * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to * `::android::FrameEventHistoryDelta`. * * \param[out] l The destination `::android::FrameEventHistoryDelta`. * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. * * This function will duplicate all file descriptors contained in \p t. */ inline bool convertTo( ::android::FrameEventHistoryDelta* l, HGraphicBufferProducer::FrameEventHistoryDelta const& t) { size_t const baseSize = getFlattenedSize(t); std::unique_ptr baseBuffer( new (std::nothrow) uint8_t[baseSize]); if (!baseBuffer) { return false; } size_t const baseNumFds = getFdCount(t); std::unique_ptr baseFds( new (std::nothrow) int[baseNumFds]); if (!baseFds) { return false; } void* buffer = static_cast(baseBuffer.get()); size_t size = baseSize; int* fds = static_cast(baseFds.get()); size_t numFds = baseNumFds; std::vector > nhAA; if (flatten(t, &nhAA, buffer, size, fds, numFds) != NO_ERROR) { return false; } void const* constBuffer = static_cast(baseBuffer.get()); size = baseSize; int const* constFds = static_cast(baseFds.get()); numFds = baseNumFds; if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { for (auto nhA : nhAA) { for (auto nh : nhA) { if (nh != nullptr) { native_handle_close(nh); native_handle_delete(nh); } } } return false; } for (auto nhA : nhAA) { for (auto nh : nhA) { if (nh != nullptr) { native_handle_delete(nh); } } } return true; } // Ref: frameworks/native/libs/gui/IGraphicBufferProducer.cpp: // IGraphicBufferProducer::QueueBufferOutput /** * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to * `IGraphicBufferProducer::QueueBufferOutput`. * * \param[out] l The destination `IGraphicBufferProducer::QueueBufferOutput`. * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. * * This function will duplicate all file descriptors contained in \p t. */ // convert: HGraphicBufferProducer::QueueBufferOutput -> // IGraphicBufferProducer::QueueBufferOutput inline bool convertTo( BGraphicBufferProducer::QueueBufferOutput* l, HGraphicBufferProducer::QueueBufferOutput const& t) { if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) { return false; } l->width = t.width; l->height = t.height; l->transformHint = t.transformHint; l->numPendingBuffers = t.numPendingBuffers; l->nextFrameNumber = t.nextFrameNumber; l->bufferReplaced = t.bufferReplaced; return true; } /** * \brief Convert `IGraphicBufferProducer::DisconnectMode` to * `HGraphicBufferProducer::DisconnectMode`. * * \param[in] l The source `IGraphicBufferProducer::DisconnectMode`. * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. */ inline HGraphicBufferProducer::DisconnectMode toHDisconnectMode( BGraphicBufferProducer::DisconnectMode l) { switch (l) { case BGraphicBufferProducer::DisconnectMode::Api: return HGraphicBufferProducer::DisconnectMode::API; case BGraphicBufferProducer::DisconnectMode::AllLocal: return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL; } return HGraphicBufferProducer::DisconnectMode::API; } // H2BGraphicBufferProducer status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp* buf) { *buf = new GraphicBuffer(); status_t fnStatus; status_t transStatus = toStatusT(mBase->requestBuffer( static_cast(slot), [&fnStatus, &buf] (Status status, AnwBuffer const& buffer) { fnStatus = toStatusT(status); if (!convertTo(buf->get(), buffer)) { fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } })); return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { return toStatusT(mBase->setMaxDequeuedBufferCount( static_cast(maxDequeuedBuffers))); } status_t H2BGraphicBufferProducer::setAsyncMode(bool async) { return toStatusT(mBase->setAsyncMode(async)); } // FIXME: usage bits truncated -- needs a 64-bits usage version status_t H2BGraphicBufferProducer::dequeueBuffer(int* slot, sp* fence, uint32_t w, uint32_t h, ::android::PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { *fence = new Fence(); status_t fnStatus; status_t transStatus = toStatusT(mBase->dequeueBuffer( w, h, static_cast(format), uint32_t(usage), outTimestamps != nullptr, [&fnStatus, slot, fence, outTimestamps] ( Status status, int32_t tSlot, hidl_handle const& tFence, HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) { fnStatus = toStatusT(status); *slot = tSlot; if (!convertTo(fence->get(), tFence)) { ALOGE("H2BGraphicBufferProducer::dequeueBuffer - " "Invalid output fence"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } if (outTimestamps && !convertTo(outTimestamps, tTs)) { ALOGE("H2BGraphicBufferProducer::dequeueBuffer - " "Invalid output timestamps"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } })); if (outBufferAge) { // Since the HAL version doesn't return the buffer age, set it to 0: *outBufferAge = 0; } return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::detachBuffer(int slot) { return toStatusT(mBase->detachBuffer(static_cast(slot))); } status_t H2BGraphicBufferProducer::detachNextBuffer( sp* outBuffer, sp* outFence) { *outBuffer = new GraphicBuffer(); *outFence = new Fence(); status_t fnStatus; status_t transStatus = toStatusT(mBase->detachNextBuffer( [&fnStatus, outBuffer, outFence] ( Status status, AnwBuffer const& tBuffer, hidl_handle const& tFence) { fnStatus = toStatusT(status); if (!convertTo(outFence->get(), tFence)) { ALOGE("H2BGraphicBufferProducer::detachNextBuffer - " "Invalid output fence"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } if (!convertTo(outBuffer->get(), tBuffer)) { ALOGE("H2BGraphicBufferProducer::detachNextBuffer - " "Invalid output buffer"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } })); return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::attachBuffer( int* outSlot, const sp& buffer) { AnwBuffer tBuffer; wrapAs(&tBuffer, *buffer); status_t fnStatus; status_t transStatus = toStatusT(mBase->attachBuffer(tBuffer, [&fnStatus, outSlot] (Status status, int32_t slot) { fnStatus = toStatusT(status); *outSlot = slot; })); return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::queueBuffer( int slot, const QueueBufferInput& input, QueueBufferOutput* output) { HGraphicBufferProducer::QueueBufferInput tInput; native_handle_t* nh; if (!wrapAs(&tInput, &nh, input)) { ALOGE("H2BGraphicBufferProducer::queueBuffer - " "Invalid input"); return BAD_VALUE; } status_t fnStatus; status_t transStatus = toStatusT(mBase->queueBuffer(slot, tInput, [&fnStatus, output] ( Status status, HGraphicBufferProducer::QueueBufferOutput const& tOutput) { fnStatus = toStatusT(status); if (!convertTo(output, tOutput)) { ALOGE("H2BGraphicBufferProducer::queueBuffer - " "Invalid output"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } })); native_handle_delete(nh); return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::cancelBuffer(int slot, const sp& fence) { hidl_handle tFence; native_handle_t* nh = nullptr; if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) { ALOGE("H2BGraphicBufferProducer::cancelBuffer - " "Invalid input fence"); return BAD_VALUE; } status_t status = toStatusT(mBase->cancelBuffer( static_cast(slot), tFence)); native_handle_delete(nh); return status; } int H2BGraphicBufferProducer::query(int what, int* value) { int result; status_t transStatus = toStatusT(mBase->query( static_cast(what), [&result, value] (int32_t tResult, int32_t tValue) { result = static_cast(tResult); *value = static_cast(tValue); })); return transStatus == NO_ERROR ? result : static_cast(transStatus); } status_t H2BGraphicBufferProducer::connect( const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { sp tListener = listener == nullptr ? nullptr : new B2HProducerListener(listener); status_t fnStatus; status_t transStatus = toStatusT(mBase->connect( tListener, static_cast(api), producerControlledByApp, [&fnStatus, output] ( Status status, HGraphicBufferProducer::QueueBufferOutput const& tOutput) { fnStatus = toStatusT(status); if (!convertTo(output, tOutput)) { ALOGE("H2BGraphicBufferProducer::connect - " "Invalid output"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } })); return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) { return toStatusT(mBase->disconnect( static_cast(api), toHDisconnectMode(mode))); } status_t H2BGraphicBufferProducer::setSidebandStream( const sp& stream) { return toStatusT(mBase->setSidebandStream(stream == nullptr ? nullptr : stream->handle())); } // FIXME: usage bits truncated -- needs a 64-bits usage version void H2BGraphicBufferProducer::allocateBuffers(uint32_t width, uint32_t height, ::android::PixelFormat format, uint64_t usage) { mBase->allocateBuffers( width, height, static_cast(format), uint32_t(usage)); } status_t H2BGraphicBufferProducer::allowAllocation(bool allow) { return toStatusT(mBase->allowAllocation(allow)); } status_t H2BGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) { return toStatusT(mBase->setGenerationNumber(generationNumber)); } String8 H2BGraphicBufferProducer::getConsumerName() const { String8 lName; mBase->getConsumerName([&lName] (hidl_string const& name) { lName = name.c_str(); }); return lName; } status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) { return toStatusT(mBase->setSharedBufferMode(sharedBufferMode)); } status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) { return toStatusT(mBase->setAutoRefresh(autoRefresh)); } status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) { return toStatusT(mBase->setDequeueTimeout(static_cast(timeout))); } status_t H2BGraphicBufferProducer::getLastQueuedBuffer( sp* outBuffer, sp* outFence, float outTransformMatrix[16]) { status_t fnStatus; status_t transStatus = toStatusT(mBase->getLastQueuedBuffer( [&fnStatus, outBuffer, outFence, &outTransformMatrix] ( Status status, AnwBuffer const& buffer, hidl_handle const& fence, hidl_array const& transformMatrix) { fnStatus = toStatusT(status); *outBuffer = new GraphicBuffer(); if (!convertTo(outBuffer->get(), buffer)) { ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " "Invalid output buffer"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } *outFence = new Fence(); if (!convertTo(outFence->get(), fence)) { ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - " "Invalid output fence"); fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus; } std::copy(transformMatrix.data(), transformMatrix.data() + 16, outTransformMatrix); })); return transStatus == NO_ERROR ? fnStatus : transStatus; } void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { mBase->getFrameTimestamps([outDelta] ( HGraphicBufferProducer::FrameEventHistoryDelta const& tDelta) { convertTo(outDelta, tDelta); }); } status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const { status_t fnStatus; status_t transStatus = toStatusT(mBase->getUniqueId( [&fnStatus, outId] (Status status, uint64_t id) { fnStatus = toStatusT(status); *outId = id; })); return transStatus == NO_ERROR ? fnStatus : transStatus; } status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t* outUsage) const { ALOGW("getConsumerUsage is not fully supported"); int result; status_t transStatus = toStatusT(mBase->query( NATIVE_WINDOW_CONSUMER_USAGE_BITS, [&result, outUsage] (int32_t tResult, int32_t tValue) { result = static_cast(tResult); *outUsage = static_cast(tValue); })); return transStatus == NO_ERROR ? result : static_cast(transStatus); } } // namespace utils } // namespace V1_0 } // namespace bufferqueue } // namespace graphics } // namespace hardware } // namespace android libs/gui/include/0040755 0000000 0000000 00000000000 13300556574 012760 5ustar000000000 0000000 libs/gui/include/gui/0040755 0000000 0000000 00000000000 13300556574 013544 5ustar000000000 0000000 libs/gui/include/gui/BufferItem.h0100644 0000000 0000000 00000010706 13300556574 015746 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. */ #ifndef ANDROID_GUI_BUFFERITEM_H #define ANDROID_GUI_BUFFERITEM_H #include #include #include #include #include #include namespace android { class Fence; class GraphicBuffer; class BufferItem : public Flattenable { friend class Flattenable; size_t getPodSize() const; 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); public: // The default value of mBuf, used to indicate this doesn't correspond to a slot. enum { INVALID_BUFFER_SLOT = -1 }; BufferItem(); ~BufferItem(); BufferItem(const BufferItem&) = default; BufferItem& operator=(const BufferItem&) = default; static const char* scalingModeName(uint32_t scalingMode); // mGraphicBuffer points to the buffer allocated for this slot, or is NULL // if the buffer in this slot has been acquired in the past (see // BufferSlot.mAcquireCalled). sp mGraphicBuffer; // mFence is a fence that will signal when the buffer is idle. sp mFence; // The std::shared_ptr wrapper around mFence. std::shared_ptr mFenceTime{FenceTime::NO_FENCE}; // mCrop is the current crop rectangle for this buffer slot. Rect mCrop; // mTransform is the current transform flags for this buffer slot. // refer to NATIVE_WINDOW_TRANSFORM_* in uint32_t mTransform; // mScalingMode is the current scaling mode for this buffer slot. // refer to NATIVE_WINDOW_SCALING_* in uint32_t mScalingMode; // mTimestamp is the current timestamp for this buffer slot. This gets // to set by queueBuffer each time this slot is queued. This value // is guaranteed to be monotonically increasing for each newly // acquired buffer. int64_t mTimestamp; // mIsAutoTimestamp indicates whether mTimestamp was generated // automatically when the buffer was queued. bool mIsAutoTimestamp; // mDataSpace is the current dataSpace value for this buffer slot. This gets // set by queueBuffer each time this slot is queued. The meaning of the // dataSpace is format-dependent. android_dataspace mDataSpace; // mFrameNumber is the number of the queued frame for this slot. uint64_t mFrameNumber; // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT). int mSlot; // mIsDroppable whether this buffer was queued with the // property that it can be replaced by a new buffer for the purpose of // making sure dequeueBuffer() won't block. // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer // was queued. bool mIsDroppable; // Indicates whether this buffer has been seen by a consumer yet bool mAcquireCalled; // Indicates this buffer must be transformed by the inverse transform of the screen // it is displayed onto. This is applied after mTransform. bool mTransformToDisplayInverse; // Describes the portion of the surface that has been modified since the // previous frame Region mSurfaceDamage; // Indicates that the consumer should acquire the next frame as soon as it // can and not wait for a frame to become available. This is only relevant // in shared buffer mode. bool mAutoRefresh; // Indicates that this buffer was queued by the producer. When in shared // buffer mode acquire() can return a BufferItem that wasn't in the queue. bool mQueuedBuffer; // Indicates that this BufferItem contains a stale buffer which has already // been released by the BufferQueue. bool mIsStale; }; } // namespace android #endif libs/gui/include/gui/BufferItemConsumer.h0100644 0000000 0000000 00000010336 13300556574 017461 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 ANDROID_GUI_BUFFERITEMCONSUMER_H #define ANDROID_GUI_BUFFERITEMCONSUMER_H #include #include #define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer" namespace android { class String8; /** * BufferItemConsumer is a BufferQueue consumer endpoint that allows clients * access to the whole BufferItem entry from BufferQueue. Multiple buffers may * be acquired at once, to be used concurrently by the client. This consumer can * operate either in synchronous or asynchronous mode. */ class BufferItemConsumer: public ConsumerBase { public: typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; struct BufferFreedListener : public virtual RefBase { virtual void onBufferFreed(const wp& graphicBuffer) = 0; }; enum { DEFAULT_MAX_BUFFERS = -1 }; enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; // Create a new buffer item consumer. The consumerUsage parameter determines // the consumer usage flags passed to the graphics allocator. The // bufferCount parameter specifies how many buffers can be locked for user // access at the same time. // controlledByApp tells whether this consumer is controlled by the // application. BufferItemConsumer(const sp& consumer, uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false); ~BufferItemConsumer() override; // set the name of the BufferItemConsumer that will be used to identify it in // log messages. void setName(const String8& name); // setBufferFreedListener sets the listener object that will be notified // when an old buffer is being freed. void setBufferFreedListener(const wp& listener); // Gets the next graphics buffer from the producer, filling out the // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue // of buffers is empty, and INVALID_OPERATION if the maximum number of // buffers is already acquired. // // Only a fixed number of buffers can be acquired at a time, determined by // the construction-time bufferCount parameter. If INVALID_OPERATION is // returned by acquireBuffer, then old buffers must be returned to the // queue by calling releaseBuffer before more buffers can be acquired. // // If waitForFence is true, and the acquired BufferItem has a valid fence object, // acquireBuffer will wait on the fence with no timeout before returning. status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen, bool waitForFence = true); // Returns an acquired buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be acquired at a time, old buffers // must be released by calling releaseBuffer to ensure new buffers can be // acquired by acquireBuffer. Once a BufferItem is released, the caller must // not access any members of the BufferItem, and should immediately remove // all of its references to the BufferItem itself. status_t releaseBuffer(const BufferItem &item, const sp& releaseFence = Fence::NO_FENCE); private: void freeBufferLocked(int slotIndex) override; // mBufferFreedListener is the listener object that will be called when // an old buffer is being freed. If it is not NULL it will be called from // freeBufferLocked. wp mBufferFreedListener; }; } // namespace android #endif // ANDROID_GUI_CPUCONSUMER_H libs/gui/include/gui/BufferQueue.h0100644 0000000 0000000 00000007301 13300556574 016131 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 ANDROID_GUI_BUFFERQUEUE_H #define ANDROID_GUI_BUFFERQUEUE_H #include #include #include #include #include namespace android { class BufferQueue { public: // BufferQueue will keep track of at most this value of buffers. // Attempts at runtime to increase the number of buffers past this will fail. enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; // Used as a placeholder slot# when the value isn't pointing to an existing buffer. enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT }; // Alias to -- please scope from there in future code! enum { NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, PRESENT_LATER = IGraphicBufferConsumer::PRESENT_LATER, }; // When in async mode we reserve two slots in order to guarantee that the // producer and consumer can run asynchronously. enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 }; // for backward source compatibility typedef ::android::ConsumerListener ConsumerListener; // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak // reference to the actual consumer object. It forwards all calls to that // consumer object so long as it exists. // // This class exists to avoid having a circular reference between the // BufferQueue object and the consumer object. The reason this can't be a weak // reference in the BufferQueue class is because we're planning to expose the // consumer side of a BufferQueue as a binder interface, which doesn't support // weak references. class ProxyConsumerListener : public BnConsumerListener { public: explicit ProxyConsumerListener(const wp& consumerListener); ~ProxyConsumerListener() override; void onDisconnect() override; void onFrameAvailable(const BufferItem& item) override; void onFrameReplaced(const BufferItem& item) override; void onBuffersReleased() override; void onSidebandStreamChanged() override; void addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override; private: // mConsumerListener is a weak reference to the IConsumerListener. This is // the raison d'etre of ProxyConsumerListener. wp mConsumerListener; }; // BufferQueue manages a pool of gralloc memory slots to be used by // producers and consumers. allocator is used to allocate all the // needed gralloc buffers. static void createBufferQueue(sp* outProducer, sp* outConsumer, bool consumerIsSurfaceFlinger = false); BufferQueue() = delete; // Create through createBufferQueue }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_BUFFERQUEUE_H libs/gui/include/gui/BufferQueueConsumer.h0100644 0000000 0000000 00000017707 13300556574 017660 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. */ #ifndef ANDROID_GUI_BUFFERQUEUECONSUMER_H #define ANDROID_GUI_BUFFERQUEUECONSUMER_H #include #include #include #include #include namespace android { class BufferQueueCore; class BufferQueueConsumer : public BnGraphicBufferConsumer { public: BufferQueueConsumer(const sp& core); ~BufferQueueConsumer() override; // acquireBuffer attempts to acquire ownership of the next pending buffer in // the BufferQueue. If no buffer is pending then it returns // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the // information about the buffer is returned in BufferItem. If the buffer // returned had previously been acquired then the BufferItem::mGraphicBuffer // field of buffer is set to NULL and it is assumed that the consumer still // holds a reference to the buffer. // // If expectedPresent is nonzero, it indicates the time when the buffer // will be displayed on screen. If the buffer's timestamp is farther in the // future, the buffer won't be acquired, and PRESENT_LATER will be // returned. The presentation time is in nanoseconds, and the time base // is CLOCK_MONOTONIC. virtual status_t acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber = 0) override; // See IGraphicBufferConsumer::detachBuffer virtual status_t detachBuffer(int slot); // See IGraphicBufferConsumer::attachBuffer virtual status_t attachBuffer(int* slot, const sp& buffer); // releaseBuffer releases a buffer slot from the consumer back to the // BufferQueue. This may be done while the buffer's contents are still // being accessed. The fence will signal when the buffer is no longer // in use. frameNumber is used to indentify the exact buffer returned. // // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free // any references to the just-released buffer that it might have, as if it // had received a onBuffersReleased() call with a mask set for the released // buffer. // // Note that the dependencies on EGL will be removed once we switch to using // the Android HW Sync HAL. virtual status_t releaseBuffer(int slot, uint64_t frameNumber, const sp& releaseFence, EGLDisplay display, EGLSyncKHR fence); // connect connects a consumer to the BufferQueue. Only one // consumer may be connected, and when that consumer disconnects the // BufferQueue is placed into the "abandoned" state, causing most // interactions with the BufferQueue by the producer to fail. // controlledByApp indicates whether the consumer is controlled by // the application. // // consumerListener may not be NULL. virtual status_t connect(const sp& consumerListener, bool controlledByApp); // disconnect disconnects a consumer from the BufferQueue. All // buffers will be freed and the BufferQueue is placed in the "abandoned" // state, causing most interactions with the BufferQueue by the producer to // fail. virtual status_t disconnect(); // getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask // indicating which buffer slots have been released by the BufferQueue // but have not yet been released by the consumer. // // This should be called from the onBuffersReleased() callback. virtual status_t getReleasedBuffers(uint64_t* outSlotMask); // setDefaultBufferSize is used to set the size of buffers returned by // dequeueBuffer when a width and height of zero is requested. Default // is 1x1. virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height); // see IGraphicBufferConsumer::setMaxBufferCount virtual status_t setMaxBufferCount(int bufferCount); // setMaxAcquiredBufferCount sets the maximum number of buffers that can // be acquired by the consumer at one time (default 1). This call will // fail if a producer is connected to the BufferQueue. virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); // setConsumerName sets the name used in logging status_t setConsumerName(const String8& name) override; // setDefaultBufferFormat allows the BufferQueue to create // GraphicBuffers of a defaultFormat if no format is specified // in dequeueBuffer. The initial default is HAL_PIXEL_FORMAT_RGBA_8888. virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat); // setDefaultBufferDataSpace allows the BufferQueue to create // GraphicBuffers of a defaultDataSpace if no data space is specified // in queueBuffer. // The initial default is HAL_DATASPACE_UNKNOWN virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace); // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. // These are merged with the bits passed to dequeueBuffer. The values are // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. virtual status_t setConsumerUsageBits(uint64_t usage) override; // setConsumerIsProtected will turn on an internal bit that indicates whether // the consumer can handle protected gralloc buffers (i.e. with // GRALLOC_USAGE_PROTECTED set). IGraphicBufferProducer can query this // capability using NATIVE_WINDOW_CONSUMER_IS_PROTECTED. virtual status_t setConsumerIsProtected(bool isProtected); // setTransformHint bakes in rotation to buffers so overlays can be used. // The values are enumerated in window.h, e.g. // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). virtual status_t setTransformHint(uint32_t hint); // Retrieve the sideband buffer stream, if any. status_t getSidebandStream(sp* outStream) const override; // See IGraphicBufferConsumer::getOccupancyHistory virtual status_t getOccupancyHistory(bool forceFlush, std::vector* outHistory) override; // See IGraphicBufferConsumer::discardFreeBuffers virtual status_t discardFreeBuffers() override; // dump our state in a String status_t dumpState(const String8& prefix, String8* outResult) const override; // Functions required for backwards compatibility. // These will be modified/renamed in IGraphicBufferConsumer and will be // removed from this class at that time. See b/13306289. virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp& releaseFence) { return releaseBuffer(buf, frameNumber, releaseFence, display, fence); } virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) { return connect(consumer, controlledByApp); } virtual status_t consumerDisconnect() { return disconnect(); } // End functions required for backwards compatibility private: sp mCore; // This references mCore->mSlots. Lock mCore->mMutex while accessing. BufferQueueDefs::SlotsType& mSlots; // This is a cached copy of the name stored in the BufferQueueCore. // It's updated during setConsumerName. String8 mConsumerName; }; // class BufferQueueConsumer } // namespace android #endif libs/gui/include/gui/BufferQueueCore.h0100644 0000000 0000000 00000032227 13300556574 016747 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. */ #ifndef ANDROID_GUI_BUFFERQUEUECORE_H #define ANDROID_GUI_BUFFERQUEUECORE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define ATRACE_BUFFER_INDEX(index) \ if (ATRACE_ENABLED()) { \ char ___traceBuf[1024]; \ snprintf(___traceBuf, 1024, "%s: %d", \ mCore->mConsumerName.string(), (index)); \ android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ } namespace android { class IConsumerListener; class IProducerListener; class BufferQueueCore : public virtual RefBase { friend class BufferQueueProducer; friend class BufferQueueConsumer; public: // Used as a placeholder slot number when the value isn't pointing to an // existing buffer. enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT }; // We reserve two slots in order to guarantee that the producer and // consumer can run asynchronously. enum { MAX_MAX_ACQUIRED_BUFFERS = BufferQueueDefs::NUM_BUFFER_SLOTS - 2 }; enum { // The API number used to indicate the currently connected producer CURRENTLY_CONNECTED_API = -1, // The API number used to indicate that no producer is connected NO_CONNECTED_API = 0, }; typedef Vector Fifo; // BufferQueueCore manages a pool of gralloc memory slots to be used by // producers and consumers. BufferQueueCore(); virtual ~BufferQueueCore(); private: // Dump our state in a string void dumpState(const String8& prefix, String8* outResult) const; // getMinUndequeuedBufferCountLocked returns the minimum number of buffers // that must remain in a state other than DEQUEUED. The async parameter // tells whether we're in asynchronous mode. int getMinUndequeuedBufferCountLocked() const; // getMinMaxBufferCountLocked returns the minimum number of buffers allowed // given the current BufferQueue state. The async parameter tells whether // we're in asynchonous mode. int getMinMaxBufferCountLocked() const; // getMaxBufferCountLocked returns the maximum number of buffers that can be // allocated at once. This value depends on the following member variables: // // mMaxDequeuedBufferCount // mMaxAcquiredBufferCount // mMaxBufferCount // mAsyncMode // mDequeueBufferCannotBlock // // Any time one of these member variables is changed while a producer is // connected, mDequeueCondition must be broadcast. int getMaxBufferCountLocked() const; // This performs the same computation but uses the given arguments instead // of the member variables for mMaxBufferCount, mAsyncMode, and // mDequeueBufferCannotBlock. int getMaxBufferCountLocked(bool asyncMode, bool dequeueBufferCannotBlock, int maxBufferCount) const; // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the // given slot. void clearBufferSlotLocked(int slot); // freeAllBuffersLocked frees the GraphicBuffer and sync resources for // all slots, even if they're currently dequeued, queued, or acquired. void freeAllBuffersLocked(); // discardFreeBuffersLocked releases all currently-free buffers held by the // queue, in order to reduce the memory consumption of the queue to the // minimum possible without discarding data. void discardFreeBuffersLocked(); // If delta is positive, makes more slots available. If negative, takes // away slots. Returns false if the request can't be met. bool adjustAvailableSlotsLocked(int delta); // waitWhileAllocatingLocked blocks until mIsAllocating is false. void waitWhileAllocatingLocked() const; #if DEBUG_ONLY_CODE // validateConsistencyLocked ensures that the free lists are in sync with // the information stored in mSlots void validateConsistencyLocked() const; #endif // mMutex is the mutex used to prevent concurrent access to the member // variables of BufferQueueCore objects. It must be locked whenever any // member variable is accessed. mutable Mutex mMutex; // mIsAbandoned indicates that the BufferQueue will no longer be used to // consume image buffers pushed to it using the IGraphicBufferProducer // interface. It is initialized to false, and set to true in the // consumerDisconnect method. A BufferQueue that is abandoned will return // the NO_INIT error from all IGraphicBufferProducer methods capable of // returning an error. bool mIsAbandoned; // mConsumerControlledByApp indicates whether the connected consumer is // controlled by the application. bool mConsumerControlledByApp; // mConsumerName is a string used to identify the BufferQueue in log // messages. It is set by the IGraphicBufferConsumer::setConsumerName // method. String8 mConsumerName; // mConsumerListener is used to notify the connected consumer of // asynchronous events that it may wish to react to. It is initially // set to NULL and is written by consumerConnect and consumerDisconnect. sp mConsumerListener; // mConsumerUsageBits contains flags that the consumer wants for // GraphicBuffers. uint64_t mConsumerUsageBits; // mConsumerIsProtected indicates the consumer is ready to handle protected // buffer. bool mConsumerIsProtected; // mConnectedApi indicates the producer API that is currently connected // to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated // by the connect and disconnect methods. int mConnectedApi; // PID of the process which last successfully called connect(...) pid_t mConnectedPid; // mLinkedToDeath is used to set a binder death notification on // the producer. sp mLinkedToDeath; // mConnectedProducerListener is used to handle the onBufferReleased // notification. sp mConnectedProducerListener; // mSlots is an array of buffer slots that must be mirrored on the producer // side. This allows buffer ownership to be transferred between the producer // and consumer without sending a GraphicBuffer over Binder. The entire // array is initialized to NULL at construction time, and buffers are // allocated for a slot when requestBuffer is called with that slot's index. BufferQueueDefs::SlotsType mSlots; // mQueue is a FIFO of queued buffers used in synchronous mode. Fifo mQueue; // mFreeSlots contains all of the slots which are FREE and do not currently // have a buffer attached. std::set mFreeSlots; // mFreeBuffers contains all of the slots which are FREE and currently have // a buffer attached. std::list mFreeBuffers; // mUnusedSlots contains all slots that are currently unused. They should be // free and not have a buffer attached. std::list mUnusedSlots; // mActiveBuffers contains all slots which have a non-FREE buffer attached. std::set mActiveBuffers; // mDequeueCondition is a condition variable used for dequeueBuffer in // synchronous mode. mutable Condition mDequeueCondition; // mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to // block. This flag is set during connect when both the producer and // consumer are controlled by the application. bool mDequeueBufferCannotBlock; // mDefaultBufferFormat can be set so it will override the buffer format // when it isn't specified in dequeueBuffer. PixelFormat mDefaultBufferFormat; // mDefaultWidth holds the default width of allocated buffers. It is used // in dequeueBuffer if a width and height of 0 are specified. uint32_t mDefaultWidth; // mDefaultHeight holds the default height of allocated buffers. It is used // in dequeueBuffer if a width and height of 0 are specified. uint32_t mDefaultHeight; // mDefaultBufferDataSpace holds the default dataSpace of queued buffers. // It is used in queueBuffer if a dataspace of 0 (HAL_DATASPACE_UNKNOWN) // is specified. android_dataspace mDefaultBufferDataSpace; // mMaxBufferCount is the limit on the number of buffers that will be // allocated at one time. This limit can be set by the consumer. int mMaxBufferCount; // mMaxAcquiredBufferCount is the number of buffers that the consumer may // acquire at one time. It defaults to 1, and can be changed by the consumer // via setMaxAcquiredBufferCount, but this may only be done while no // producer is connected to the BufferQueue. This value is used to derive // the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer. int mMaxAcquiredBufferCount; // mMaxDequeuedBufferCount is the number of buffers that the producer may // dequeue at one time. It defaults to 1, and can be changed by the producer // via setMaxDequeuedBufferCount. int mMaxDequeuedBufferCount; // mBufferHasBeenQueued is true once a buffer has been queued. It is reset // when something causes all buffers to be freed (e.g., changing the buffer // count). bool mBufferHasBeenQueued; // mFrameCounter is the free running counter, incremented on every // successful queueBuffer call and buffer allocation. uint64_t mFrameCounter; // mTransformHint is used to optimize for screen rotations. uint32_t mTransformHint; // mSidebandStream is a handle to the sideband buffer stream, if any sp mSidebandStream; // mIsAllocating indicates whether a producer is currently trying to allocate buffers (which // releases mMutex while doing the allocation proper). Producers should not modify any of the // FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to // false. bool mIsAllocating; // mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating // becomes false. mutable Condition mIsAllocatingCondition; // mAllowAllocation determines whether dequeueBuffer is allowed to allocate // new buffers bool mAllowAllocation; // mBufferAge tracks the age of the contents of the most recently dequeued // buffer as the number of frames that have elapsed since it was last queued uint64_t mBufferAge; // mGenerationNumber stores the current generation number of the attached // producer. Any attempt to attach a buffer with a different generation // number will fail. uint32_t mGenerationNumber; // mAsyncMode indicates whether or not async mode is enabled. // In async mode an extra buffer will be allocated to allow the producer to // enqueue buffers without blocking. bool mAsyncMode; // mSharedBufferMode indicates whether or not shared buffer mode is enabled. bool mSharedBufferMode; // When shared buffer mode is enabled, this indicates whether the consumer // should acquire buffers even if BufferQueue doesn't indicate that they are // available. bool mAutoRefresh; // When shared buffer mode is enabled, this tracks which slot contains the // shared buffer. int mSharedBufferSlot; // Cached data about the shared buffer in shared buffer mode struct SharedBufferCache { SharedBufferCache(Rect _crop, uint32_t _transform, uint32_t _scalingMode, android_dataspace _dataspace) : crop(_crop), transform(_transform), scalingMode(_scalingMode), dataspace(_dataspace) { } Rect crop; uint32_t transform; uint32_t scalingMode; android_dataspace dataspace; } mSharedBufferCache; // The slot of the last queued buffer int mLastQueuedSlot; OccupancyTracker mOccupancyTracker; const uint64_t mUniqueId; }; // class BufferQueueCore } // namespace android #endif libs/gui/include/gui/BufferQueueDefs.h0100644 0000000 0000000 00000001705 13300556574 016735 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. */ #ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H #include #include namespace android { class BufferQueueCore; namespace BufferQueueDefs { typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS]; } // namespace BufferQueueDefs } // namespace android #endif libs/gui/include/gui/BufferQueueProducer.h0100644 0000000 0000000 00000026067 13300556574 017647 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. */ #ifndef ANDROID_GUI_BUFFERQUEUEPRODUCER_H #define ANDROID_GUI_BUFFERQUEUEPRODUCER_H #include #include namespace android { struct BufferSlot; class BufferQueueProducer : public BnGraphicBufferProducer, private IBinder::DeathRecipient { public: friend class BufferQueue; // Needed to access binderDied BufferQueueProducer(const sp& core, bool consumerIsSurfaceFlinger = false); ~BufferQueueProducer() override; // requestBuffer returns the GraphicBuffer for slot N. // // In normal operation, this is called the first time slot N is returned // by dequeueBuffer. It must be called again if dequeueBuffer returns // flags indicating that previously-returned buffers are no longer valid. virtual status_t requestBuffer(int slot, sp* buf); // see IGraphicsBufferProducer::setMaxDequeuedBufferCount virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); // see IGraphicsBufferProducer::setAsyncMode virtual status_t setAsyncMode(bool async); // dequeueBuffer gets the next buffer slot index for the producer to use. // If a buffer slot is available then that slot index is written to the // location pointed to by the buf argument and a status of OK is returned. // If no slot is available then a status of -EBUSY is returned and buf is // unmodified. // // The outFence parameter will be updated to hold the fence associated with // the buffer. The contents of the buffer must not be overwritten until the // fence signals. If the fence is Fence::NO_FENCE, the buffer may be // written immediately. // // The width and height parameters must be no greater than the minimum of // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). // An error due to invalid dimensions might not be reported until // updateTexImage() is called. If width and height are both zero, the // default values specified by setDefaultBufferSize() are used instead. // // If the format is 0, the default format will be used. // // The usage argument specifies gralloc buffer usage flags. The values // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These // will be merged with the usage flags specified by setConsumerUsageBits. // // The return value may be a negative error value or a non-negative // collection of flags. If the flags are set, the return values are // valid, but additional actions must be performed. // // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the // producer must discard cached GraphicBuffer references for the slot // returned in buf. // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer // must discard cached GraphicBuffer references for all slots. // // In both cases, the producer will need to call requestBuffer to get a // GraphicBuffer handle for the returned slot. virtual status_t dequeueBuffer(int* outSlot, sp* outFence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override; // See IGraphicBufferProducer::detachBuffer virtual status_t detachBuffer(int slot); // See IGraphicBufferProducer::detachNextBuffer virtual status_t detachNextBuffer(sp* outBuffer, sp* outFence); // See IGraphicBufferProducer::attachBuffer virtual status_t attachBuffer(int* outSlot, const sp& buffer); // queueBuffer returns a filled buffer to the BufferQueue. // // Additional data is provided in the QueueBufferInput struct. Notably, // a timestamp must be provided for the buffer. The timestamp is in // nanoseconds, and must be monotonically increasing. Its other semantics // (zero point, etc) are producer-specific and should be documented by the // producer. // // The caller may provide a fence that signals when all rendering // operations have completed. Alternatively, NO_FENCE may be used, // indicating that the buffer is ready immediately. // // Some values are returned in the output struct: the current settings // for default width and height, the current transform hint, and the // number of queued buffers. virtual status_t queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output); // cancelBuffer returns a dequeued buffer to the BufferQueue, but doesn't // queue it for use by the consumer. // // The buffer will not be overwritten until the fence signals. The fence // will usually be the one obtained from dequeueBuffer. virtual status_t cancelBuffer(int slot, const sp& fence); // Query native window attributes. The "what" values are enumerated in // window.h (e.g. NATIVE_WINDOW_FORMAT). virtual int query(int what, int* outValue); // connect attempts to connect a producer API to the BufferQueue. This // must be called before any other IGraphicBufferProducer methods are // called except for getAllocator. A consumer must already be connected. // // This method will fail if connect was previously called on the // BufferQueue and no corresponding disconnect call was made (i.e. if // it's still connected to a producer). // // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU). virtual status_t connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output); // See IGraphicBufferProducer::disconnect virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api); // Attaches a sideband buffer stream to the IGraphicBufferProducer. // // A sideband stream is a device-specific mechanism for passing buffers // from the producer to the consumer without using dequeueBuffer/ // queueBuffer. If a sideband stream is present, the consumer can choose // whether to acquire buffers from the sideband stream or from the queued // buffers. // // Passing NULL or a different stream handle will detach the previous // handle if any. virtual status_t setSidebandStream(const sp& stream); // See IGraphicBufferProducer::allocateBuffers virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) override; // See IGraphicBufferProducer::allowAllocation virtual status_t allowAllocation(bool allow); // See IGraphicBufferProducer::setGenerationNumber virtual status_t setGenerationNumber(uint32_t generationNumber); // See IGraphicBufferProducer::getConsumerName virtual String8 getConsumerName() const override; // See IGraphicBufferProducer::setSharedBufferMode virtual status_t setSharedBufferMode(bool sharedBufferMode) override; // See IGraphicBufferProducer::setAutoRefresh virtual status_t setAutoRefresh(bool autoRefresh) override; // See IGraphicBufferProducer::setDequeueTimeout virtual status_t setDequeueTimeout(nsecs_t timeout) override; // See IGraphicBufferProducer::getLastQueuedBuffer virtual status_t getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) override; // See IGraphicBufferProducer::getFrameTimestamps virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override; // See IGraphicBufferProducer::getUniqueId virtual status_t getUniqueId(uint64_t* outId) const override; // See IGraphicBufferProducer::getConsumerUsage virtual status_t getConsumerUsage(uint64_t* outUsage) const override; private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp& who); // Returns the slot of the next free buffer if one is available or // BufferQueueCore::INVALID_BUFFER_SLOT otherwise int getFreeBufferLocked() const; // Returns the next free slot if one is available or // BufferQueueCore::INVALID_BUFFER_SLOT otherwise int getFreeSlotLocked() const; void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta); // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may // block if there are no available slots and we are not in non-blocking // mode (producer and consumer controlled by the application). If it blocks, // it will release mCore->mMutex while blocked so that other operations on // the BufferQueue may succeed. enum class FreeSlotCaller { Dequeue, Attach, }; status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const; sp mCore; // This references mCore->mSlots. Lock mCore->mMutex while accessing. BufferQueueDefs::SlotsType& mSlots; // This is a cached copy of the name stored in the BufferQueueCore. // It's updated during connect and dequeueBuffer (which should catch // most updates). String8 mConsumerName; uint32_t mStickyTransform; // This controls whether the GraphicBuffer pointer in the BufferItem is // cleared after being queued bool mConsumerIsSurfaceFlinger; // This saves the fence from the last queueBuffer, such that the // next queueBuffer call can throttle buffer production. The prior // queueBuffer's fence is not nessessarily available elsewhere, // since the previous buffer might have already been acquired. sp mLastQueueBufferFence; Rect mLastQueuedCrop; uint32_t mLastQueuedTransform; // Take-a-ticket system for ensuring that onFrame* callbacks are called in // the order that frames are queued. While the BufferQueue lock // (mCore->mMutex) is held, a ticket is retained by the producer. After // dropping the BufferQueue lock, the producer must wait on the condition // variable until the current callback ticket matches its retained ticket. Mutex mCallbackMutex; int mNextCallbackTicket; // Protected by mCore->mMutex int mCurrentCallbackTicket; // Protected by mCallbackMutex Condition mCallbackCondition; // Sets how long dequeueBuffer or attachBuffer will block if a buffer or // slot is not yet available. nsecs_t mDequeueTimeout; }; // class BufferQueueProducer } // namespace android #endif libs/gui/include/gui/BufferSlot.h0100644 0000000 0000000 00000016753 13300556574 016001 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. */ #ifndef ANDROID_GUI_BUFFERSLOT_H #define ANDROID_GUI_BUFFERSLOT_H #include #include #include #include #include namespace android { class Fence; // BufferState tracks the states in which a buffer slot can be. struct BufferState { // All slots are initially FREE (not dequeued, queued, acquired, or shared). BufferState() : mDequeueCount(0), mQueueCount(0), mAcquireCount(0), mShared(false) { } uint32_t mDequeueCount; uint32_t mQueueCount; uint32_t mAcquireCount; bool mShared; // A buffer can be in one of five states, represented as below: // // | mShared | mDequeueCount | mQueueCount | mAcquireCount | // --------|---------|---------------|-------------|---------------| // FREE | false | 0 | 0 | 0 | // DEQUEUED| false | 1 | 0 | 0 | // QUEUED | false | 0 | 1 | 0 | // ACQUIRED| false | 0 | 0 | 1 | // SHARED | true | any | any | any | // // FREE indicates that the buffer is available to be dequeued by the // producer. The slot is "owned" by BufferQueue. It transitions to DEQUEUED // when dequeueBuffer is called. // // DEQUEUED indicates that the buffer has been dequeued by the producer, but // has not yet been queued or canceled. The producer may modify the // buffer's contents as soon as the associated release fence is signaled. // The slot is "owned" by the producer. It can transition to QUEUED (via // queueBuffer or attachBuffer) or back to FREE (via cancelBuffer or // detachBuffer). // // QUEUED indicates that the buffer has been filled by the producer and // queued for use by the consumer. The buffer contents may continue to be // modified for a finite time, so the contents must not be accessed until // the associated fence is signaled. The slot is "owned" by BufferQueue. It // can transition to ACQUIRED (via acquireBuffer) or to FREE (if another // buffer is queued in asynchronous mode). // // ACQUIRED indicates that the buffer has been acquired by the consumer. As // with QUEUED, the contents must not be accessed by the consumer until the // acquire fence is signaled. The slot is "owned" by the consumer. It // transitions to FREE when releaseBuffer (or detachBuffer) is called. A // detached buffer can also enter the ACQUIRED state via attachBuffer. // // SHARED indicates that this buffer is being used in shared buffer // mode. It can be in any combination of the other states at the same time, // except for FREE (since that excludes being in any other state). It can // also be dequeued, queued, or acquired multiple times. inline bool isFree() const { return !isAcquired() && !isDequeued() && !isQueued(); } inline bool isDequeued() const { return mDequeueCount > 0; } inline bool isQueued() const { return mQueueCount > 0; } inline bool isAcquired() const { return mAcquireCount > 0; } inline bool isShared() const { return mShared; } inline void reset() { *this = BufferState(); } const char* string() const; inline void dequeue() { mDequeueCount++; } inline void detachProducer() { if (mDequeueCount > 0) { mDequeueCount--; } } inline void attachProducer() { mDequeueCount++; } inline void queue() { if (mDequeueCount > 0) { mDequeueCount--; } mQueueCount++; } inline void cancel() { if (mDequeueCount > 0) { mDequeueCount--; } } inline void freeQueued() { if (mQueueCount > 0) { mQueueCount--; } } inline void acquire() { if (mQueueCount > 0) { mQueueCount--; } mAcquireCount++; } inline void acquireNotInQueue() { mAcquireCount++; } inline void release() { if (mAcquireCount > 0) { mAcquireCount--; } } inline void detachConsumer() { if (mAcquireCount > 0) { mAcquireCount--; } } inline void attachConsumer() { mAcquireCount++; } }; struct BufferSlot { BufferSlot() : mGraphicBuffer(nullptr), mEglDisplay(EGL_NO_DISPLAY), mBufferState(), mRequestBufferCalled(false), mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), mFence(Fence::NO_FENCE), mAcquireCalled(false), mNeedsReallocation(false) { } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. sp mGraphicBuffer; // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects. EGLDisplay mEglDisplay; // mBufferState is the current state of this buffer slot. BufferState mBufferState; // mRequestBufferCalled is used for validating that the producer did // call requestBuffer() when told to do so. Technically this is not // needed but useful for debugging and catching producer bugs. bool mRequestBufferCalled; // mFrameNumber is the number of the queued frame for this slot. This // is used to dequeue buffers in LRU order (useful because buffers // may be released before their release fence is signaled). uint64_t mFrameNumber; // mEglFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and may be set to a // new sync object in releaseBuffer. (This is deprecated in favor of // mFence, below.) EGLSyncKHR mEglFence; // mFence is a fence which will signal when work initiated by the // previous owner of the buffer is finished. When the buffer is FREE, // the fence indicates when the consumer has finished reading // from the buffer, or when the producer has finished writing if it // called cancelBuffer after queueing some writes. When the buffer is // QUEUED, it indicates when the producer has finished filling the // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been // passed to the consumer or producer along with ownership of the // buffer, and mFence is set to NO_FENCE. sp mFence; // Indicates whether this buffer has been seen by a consumer yet bool mAcquireCalled; // Indicates whether the buffer was re-allocated without notifying the // producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when // dequeued to prevent the producer from using a stale cached buffer. bool mNeedsReallocation; }; } // namespace android #endif libs/gui/include/gui/ConsumerBase.h0100644 0000000 0000000 00000027631 13300556574 016311 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_GUI_CONSUMERBASE_H #define ANDROID_GUI_CONSUMERBASE_H #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class String8; class GraphicBuffer; // ConsumerBase is a base class for BufferQueue consumer end-points. It // handles common tasks like management of the connection to the BufferQueue // and the buffer pool. class ConsumerBase : public virtual RefBase, protected ConsumerListener { public: struct FrameAvailableListener : public virtual RefBase { // See IConsumerListener::onFrame{Available,Replaced} virtual void onFrameAvailable(const BufferItem& item) = 0; virtual void onFrameReplaced(const BufferItem& /* item */) {} }; ~ConsumerBase() override; // abandon frees all the buffers and puts the ConsumerBase into the // 'abandoned' state. Once put in this state the ConsumerBase can never // leave it. When in the 'abandoned' state, all methods of the // IGraphicBufferProducer interface will fail with the NO_INIT error. // // Note that while calling this method causes all the buffers to be freed // from the perspective of the the ConsumerBase, if there are additional // references on the buffers (e.g. if a buffer is referenced by a client // or by OpenGL ES as a texture) then those buffer will remain allocated. void abandon(); // Returns true if the ConsumerBase is in the 'abandoned' state bool isAbandoned(); // set the name of the ConsumerBase that will be used to identify it in // log messages. void setName(const String8& name); // dumpState writes the current state to a string. Child classes should add // their state to the dump by overriding the dumpLocked method, which is // called by these methods after locking the mutex. void dumpState(String8& result) const; void dumpState(String8& result, const char* prefix) const; // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. void setFrameAvailableListener(const wp& listener); // See IGraphicBufferConsumer::detachBuffer status_t detachBuffer(int slot); // See IGraphicBufferConsumer::setDefaultBufferSize status_t setDefaultBufferSize(uint32_t width, uint32_t height); // See IGraphicBufferConsumer::setDefaultBufferFormat status_t setDefaultBufferFormat(PixelFormat defaultFormat); // See IGraphicBufferConsumer::setDefaultBufferDataSpace status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace); // See IGraphicBufferConsumer::getOccupancyHistory status_t getOccupancyHistory(bool forceFlush, std::vector* outHistory); // See IGraphicBufferConsumer::discardFreeBuffers status_t discardFreeBuffers(); private: ConsumerBase(const ConsumerBase&); void operator=(const ConsumerBase&); protected: // ConsumerBase constructs a new ConsumerBase object to consume image // buffers from the given IGraphicBufferConsumer. // The controlledByApp flag indicates that this consumer is under the application's // control. explicit ConsumerBase(const sp& consumer, bool controlledByApp = false); // onLastStrongRef gets called by RefBase just before the dtor of the most // derived class. It is used to clean up the buffers so that ConsumerBase // can coordinate the clean-up by calling into virtual methods implemented // by the derived classes. This would not be possible from the // ConsuemrBase dtor because by the time that gets called the derived // classes have already been destructed. // // This methods should not need to be overridden by derived classes, but // if they are overridden the ConsumerBase implementation must be called // from the derived class. virtual void onLastStrongRef(const void* id); // Implementation of the IConsumerListener interface. These // calls are used to notify the ConsumerBase of asynchronous events in the // BufferQueue. The onFrameAvailable, onFrameReplaced, and // onBuffersReleased methods should not need to be overridden by derived // classes, but if they are overridden the ConsumerBase implementation must // be called from the derived class. The ConsumerBase version of // onSidebandStreamChanged does nothing and can be overriden by derived // classes if they want the notification. virtual void onFrameAvailable(const BufferItem& item) override; virtual void onFrameReplaced(const BufferItem& item) override; virtual void onBuffersReleased() override; virtual void onSidebandStreamChanged() override; // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in that // slot. Otherwise it has no effect. // // Derived classes should override this method to clean up any state they // keep per slot. If it is overridden, the derived class's implementation // must call ConsumerBase::freeBufferLocked. // // This method must be called with mMutex locked. virtual void freeBufferLocked(int slotIndex); // abandonLocked puts the BufferQueue into the abandoned state, causing // all future operations on it to fail. This method rather than the public // abandon method should be overridden by child classes to add abandon- // time behavior. // // Derived classes should override this method to clean up any object // state they keep (as opposed to per-slot state). If it is overridden, // the derived class's implementation must call ConsumerBase::abandonLocked. // // This method must be called with mMutex locked. virtual void abandonLocked(); // dumpLocked dumps the current state of the ConsumerBase object to the // result string. Each line is prefixed with the string pointed to by the // prefix argument. The buffer argument points to a buffer that may be // used for intermediate formatting data, and the size of that buffer is // indicated by the size argument. // // Derived classes should override this method to dump their internal // state. If this method is overridden the derived class's implementation // should call ConsumerBase::dumpLocked. // // This method must be called with mMutex locked. virtual void dumpLocked(String8& result, const char* prefix) const; // acquireBufferLocked fetches the next buffer from the BufferQueue and // updates the buffer slot for the buffer returned. // // Derived classes should override this method to perform any // initialization that must take place the first time a buffer is assigned // to a slot. If it is overridden the derived class's implementation must // call ConsumerBase::acquireBufferLocked. virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber = 0); // releaseBufferLocked relinquishes control over a buffer, returning that // control to the BufferQueue. // // Derived classes should override this method to perform any cleanup that // must take place when a buffer is released back to the BufferQueue. If // it is overridden the derived class's implementation must call // ConsumerBase::releaseBufferLocked. virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence); // returns true iff the slot still has the graphicBuffer in it. bool stillTracking(int slot, const sp graphicBuffer); // addReleaseFence* adds the sync points associated with a fence to the set // of sync points that must be reached before the buffer in the given slot // may be used after the slot has been released. This should be called by // derived classes each time some asynchronous work is kicked off that // references the buffer. status_t addReleaseFence(int slot, const sp graphicBuffer, const sp& fence); status_t addReleaseFenceLocked(int slot, const sp graphicBuffer, const sp& fence); // Slot contains the information and object references that // ConsumerBase maintains about a BufferQueue buffer slot. struct Slot { // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if // no Gralloc buffer is in the slot. sp mGraphicBuffer; // mFence is a fence which will signal when the buffer associated with // this buffer slot is no longer being used by the consumer and can be // overwritten. The buffer can be dequeued before the fence signals; // the producer is responsible for delaying writes until it signals. sp mFence; // the frame number of the last acquired frame for this slot uint64_t mFrameNumber; }; // mSlots stores the buffers that have been allocated by the BufferQueue // for each buffer slot. It is initialized to null pointers, and gets // filled in with the result of BufferQueue::acquire when the // client dequeues a buffer from a // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; // mAbandoned indicates that the BufferQueue will no longer be used to // consume images buffers pushed to it using the IGraphicBufferProducer // interface. It is initialized to false, and set to true in the abandon // method. A BufferQueue that has been abandoned will return the NO_INIT // error from all IConsumerBase methods capable of returning an error. bool mAbandoned; // mName is a string used to identify the ConsumerBase in log messages. // It can be set by the setName method. String8 mName; // mFrameAvailableListener is the listener object that will be called when a // new frame becomes available. If it is not NULL it will be called from // queueBuffer. The listener object is protected by mFrameAvailableMutex // (not mMutex). Mutex mFrameAvailableMutex; wp mFrameAvailableListener; // The ConsumerBase has-a BufferQueue and is responsible for creating this object // if none is supplied sp mConsumer; // The final release fence of the most recent buffer released by // releaseBufferLocked. sp mPrevFinalReleaseFence; // mMutex is the mutex used to prevent concurrent access to the member // variables of ConsumerBase objects. It must be locked whenever the // member variables are accessed or when any of the *Locked methods are // called. // // This mutex is intended to be locked by derived classes. mutable Mutex mMutex; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_CONSUMERBASE_H libs/gui/include/gui/CpuConsumer.h0100644 0000000 0000000 00000012157 13300556574 016163 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 ANDROID_GUI_CPUCONSUMER_H #define ANDROID_GUI_CPUCONSUMER_H #include #include #include #include namespace android { class BufferQueue; class GraphicBuffer; class String8; /** * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU * access to the underlying gralloc buffers provided by BufferQueue. Multiple * buffers may be acquired by it at once, to be used concurrently by the * CpuConsumer owner. Sets gralloc usage flags to be software-read-only. * This queue is synchronous by default. */ class CpuConsumer : public ConsumerBase { public: typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; struct LockedBuffer { uint8_t *data; uint32_t width; uint32_t height; PixelFormat format; uint32_t stride; Rect crop; uint32_t transform; uint32_t scalingMode; int64_t timestamp; android_dataspace dataSpace; uint64_t frameNumber; // this is the same as format, except for formats that are compatible with // a flexible format (e.g. HAL_PIXEL_FORMAT_YCbCr_420_888). In the latter // case this contains that flexible format PixelFormat flexFormat; // Values below are only valid when using HAL_PIXEL_FORMAT_YCbCr_420_888 // or compatible format, in which case LockedBuffer::data // contains the Y channel, and stride is the Y channel stride. For other // formats, these will all be 0. uint8_t *dataCb; uint8_t *dataCr; uint32_t chromaStride; uint32_t chromaStep; LockedBuffer() : data(NULL), width(0), height(0), format(PIXEL_FORMAT_NONE), stride(0), crop(Rect::EMPTY_RECT), transform(0), scalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), timestamp(0), dataSpace(HAL_DATASPACE_UNKNOWN), frameNumber(0), flexFormat(PIXEL_FORMAT_NONE), dataCb(NULL), dataCr(NULL), chromaStride(0), chromaStep(0) {} }; // Create a new CPU consumer. The maxLockedBuffers parameter specifies // how many buffers can be locked for user access at the same time. CpuConsumer(const sp& bq, size_t maxLockedBuffers, bool controlledByApp = false); virtual ~CpuConsumer(); // set the name of the CpuConsumer that will be used to identify it in // log messages. void setName(const String8& name); // Gets the next graphics buffer from the producer and locks it for CPU use, // filling out the passed-in locked buffer structure with the native pointer // and metadata. Returns BAD_VALUE if no new buffer is available, and // NOT_ENOUGH_DATA if the maximum number of buffers is already locked. // // Only a fixed number of buffers can be locked at a time, determined by the // construction-time maxLockedBuffers parameter. If INVALID_OPERATION is // returned by lockNextBuffer, then old buffers must be returned to the queue // by calling unlockBuffer before more buffers can be acquired. status_t lockNextBuffer(LockedBuffer *nativeBuffer); // Returns a locked buffer to the queue, allowing it to be reused. Since // only a fixed number of buffers may be locked at a time, old buffers must // be released by calling unlockBuffer to ensure new buffers can be acquired by // lockNextBuffer. status_t unlockBuffer(const LockedBuffer &nativeBuffer); private: // Maximum number of buffers that can be locked at a time size_t mMaxLockedBuffers; status_t releaseAcquiredBufferLocked(size_t lockedIdx); virtual void freeBufferLocked(int slotIndex); // Tracking for buffers acquired by the user struct AcquiredBuffer { // Need to track the original mSlot index and the buffer itself because // the mSlot entry may be freed/reused before the acquired buffer is // released. int mSlot; sp mGraphicBuffer; void *mBufferPointer; AcquiredBuffer() : mSlot(BufferQueue::INVALID_BUFFER_SLOT), mBufferPointer(NULL) { } }; Vector mAcquiredBuffers; // Count of currently locked buffers size_t mCurrentLockedBuffers; }; } // namespace android #endif // ANDROID_GUI_CPUCONSUMER_H libs/gui/include/gui/DisplayEventReceiver.h0100644 0000000 0000000 00000010467 13300556574 020016 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_GUI_DISPLAY_EVENT_H #define ANDROID_GUI_DISPLAY_EVENT_H #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- class IDisplayEventConnection; namespace gui { class BitTube; } // namespace gui static inline constexpr uint32_t fourcc(char c1, char c2, char c3, char c4) { return static_cast(c1) << 24 | static_cast(c2) << 16 | static_cast(c3) << 8 | static_cast(c4); } // ---------------------------------------------------------------------------- class DisplayEventReceiver { public: enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), }; struct Event { struct Header { uint32_t type; uint32_t id; nsecs_t timestamp __attribute__((aligned(8))); }; struct VSync { uint32_t count; }; struct Hotplug { bool connected; }; Header header; union { VSync vsync; Hotplug hotplug; }; }; public: /* * DisplayEventReceiver creates and registers an event connection with * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate * or requestNextVsync to receive them. * Other events start being delivered immediately. */ DisplayEventReceiver( ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp); /* * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events * stop being delivered immediately. Note that the queue could have * some events pending. These will be delivered. */ ~DisplayEventReceiver(); /* * initCheck returns the state of DisplayEventReceiver after construction. */ status_t initCheck() const; /* * getFd returns the file descriptor to use to receive events. * OWNERSHIP IS RETAINED by DisplayEventReceiver. DO NOT CLOSE this * file-descriptor. */ int getFd() const; /* * getEvents reads events from the queue and returns how many events were * read. Returns 0 if there are no more events or a negative error code. * If NOT_ENOUGH_DATA is returned, the object has become invalid forever, it * should be destroyed and getEvents() shouldn't be called again. */ ssize_t getEvents(Event* events, size_t count); static ssize_t getEvents(gui::BitTube* dataChannel, Event* events, size_t count); /* * sendEvents write events to the queue and returns how many events were * written. */ static ssize_t sendEvents(gui::BitTube* dataChannel, Event const* events, size_t count); /* * setVsyncRate() sets the Event::VSync delivery rate. A value of * 1 returns every Event::VSync. A value of 2 returns every other event, * etc... a value of 0 returns no event unless requestNextVsync() has * been called. */ status_t setVsyncRate(uint32_t count); /* * requestNextVsync() schedules the next Event::VSync. It has no effect * if the vsync rate is > 0. */ status_t requestNextVsync(); private: sp mEventConnection; std::unique_ptr mDataChannel; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_DISPLAY_EVENT_H libs/gui/include/gui/FrameTimestamps.h0100644 0000000 0000000 00000025204 13300556574 017016 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_GUI_FRAMETIMESTAMPS_H #define ANDROID_GUI_FRAMETIMESTAMPS_H #include #include #include #include #include #include #include namespace android { struct FrameEvents; class FrameEventHistoryDelta; class String8; // Identifiers for all the events that may be recorded or reported. enum class FrameEvent { POSTED, REQUESTED_PRESENT, LATCH, ACQUIRE, FIRST_REFRESH_START, LAST_REFRESH_START, GPU_COMPOSITION_DONE, DISPLAY_PRESENT, DEQUEUE_READY, RELEASE, EVENT_COUNT, // Not an actual event. }; // A collection of timestamps corresponding to a single frame. struct FrameEvents { static constexpr auto EVENT_COUNT = static_cast(FrameEvent::EVENT_COUNT); static_assert(EVENT_COUNT <= 32, "Event count sanity check failed."); static constexpr nsecs_t TIMESTAMP_PENDING = -2; static inline bool isValidTimestamp(nsecs_t time) { return time != TIMESTAMP_PENDING; } bool hasPostedInfo() const; bool hasRequestedPresentInfo() const; bool hasLatchInfo() const; bool hasFirstRefreshStartInfo() const; bool hasLastRefreshStartInfo() const; bool hasAcquireInfo() const; bool hasGpuCompositionDoneInfo() const; bool hasDisplayPresentInfo() const; bool hasReleaseInfo() const; bool hasDequeueReadyInfo() const; void checkFencesForCompletion(); void dump(String8& outString) const; bool valid{false}; int connectId{0}; uint64_t frameNumber{0}; // Whether or not certain points in the frame's life cycle have been // encountered help us determine if timestamps aren't available because // a) we'll just never get them or b) they're not ready yet. bool addPostCompositeCalled{false}; bool addReleaseCalled{false}; nsecs_t postedTime{TIMESTAMP_PENDING}; nsecs_t requestedPresentTime{TIMESTAMP_PENDING}; nsecs_t latchTime{TIMESTAMP_PENDING}; nsecs_t firstRefreshStartTime{TIMESTAMP_PENDING}; nsecs_t lastRefreshStartTime{TIMESTAMP_PENDING}; nsecs_t dequeueReadyTime{TIMESTAMP_PENDING}; std::shared_ptr acquireFence{FenceTime::NO_FENCE}; std::shared_ptr gpuCompositionDoneFence{FenceTime::NO_FENCE}; std::shared_ptr displayPresentFence{FenceTime::NO_FENCE}; std::shared_ptr releaseFence{FenceTime::NO_FENCE}; }; struct CompositorTiming { nsecs_t deadline{0}; nsecs_t interval{16666667}; nsecs_t presentLatency{16666667}; }; // A short history of frames that are synchronized between the consumer and // producer via deltas. class FrameEventHistory { public: virtual ~FrameEventHistory(); FrameEvents* getFrame(uint64_t frameNumber); FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint); void checkFencesForCompletion(); void dump(String8& outString) const; static constexpr size_t MAX_FRAME_HISTORY = 8; protected: std::array mFrames; CompositorTiming mCompositorTiming; }; // The producer's interface to FrameEventHistory class ProducerFrameEventHistory : public FrameEventHistory { public: ~ProducerFrameEventHistory() override; // Public for testing. static nsecs_t snapToNextTick( nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval); nsecs_t getNextCompositeDeadline(const nsecs_t now) const; nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; } nsecs_t getCompositeToPresentLatency() const { return mCompositorTiming.presentLatency; } // virtual for testing. virtual void updateAcquireFence( uint64_t frameNumber, std::shared_ptr&& acquire); void applyDelta(const FrameEventHistoryDelta& delta); void updateSignalTimes(); protected: void applyFenceDelta(FenceTimeline* timeline, std::shared_ptr* dst, const FenceTime::Snapshot& src) const; // virtual for testing. virtual std::shared_ptr createFenceTime( const sp& fence) const; size_t mAcquireOffset{0}; // The consumer updates it's timelines in Layer and SurfaceFlinger since // they can coordinate shared timelines better. The producer doesn't have // shared timelines though, so just let it own and update all of them. FenceTimeline mAcquireTimeline; FenceTimeline mGpuCompositionDoneTimeline; FenceTimeline mPresentTimeline; FenceTimeline mReleaseTimeline; }; // Used by the consumer to create a new frame event record that is // partially complete. struct NewFrameEventsEntry { uint64_t frameNumber{0}; nsecs_t postedTime{0}; nsecs_t requestedPresentTime{0}; std::shared_ptr acquireFence{FenceTime::NO_FENCE}; }; // Used by the consumer to keep track of which fields it already sent to // the producer. class FrameEventDirtyFields { public: inline void reset() { mBitset.reset(); } inline bool anyDirty() const { return mBitset.any(); } template inline void setDirty() { constexpr size_t eventIndex = static_cast(event); static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index."); mBitset.set(eventIndex); } template inline bool isDirty() const { constexpr size_t eventIndex = static_cast(event); static_assert(eventIndex < FrameEvents::EVENT_COUNT, "Bad index."); return mBitset[eventIndex]; } private: std::bitset mBitset; }; // The consumer's interface to FrameEventHistory class ConsumerFrameEventHistory : public FrameEventHistory { public: ~ConsumerFrameEventHistory() override; void onDisconnect(); void initializeCompositorTiming(const CompositorTiming& compositorTiming); void addQueue(const NewFrameEventsEntry& newEntry); void addLatch(uint64_t frameNumber, nsecs_t latchTime); void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime); void addPostComposition(uint64_t frameNumber, const std::shared_ptr& gpuCompositionDone, const std::shared_ptr& displayPresent, const CompositorTiming& compositorTiming); void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime, std::shared_ptr&& release); void getAndResetDelta(FrameEventHistoryDelta* delta); private: void getFrameDelta(FrameEventHistoryDelta* delta, const std::array::iterator& frame); std::array mFramesDirty; size_t mQueueOffset{0}; size_t mCompositionOffset{0}; size_t mReleaseOffset{0}; int mCurrentConnectId{0}; bool mProducerWantsEvents{false}; }; // A single frame update from the consumer to producer that can be sent // through Binder. // Although this may be sent multiple times for the same frame as new // timestamps are set, Fences only need to be sent once. class FrameEventsDelta : public Flattenable { friend class ProducerFrameEventHistory; public: FrameEventsDelta() = default; FrameEventsDelta(size_t index, const FrameEvents& frameTimestamps, const FrameEventDirtyFields& dirtyFields); // Movable. FrameEventsDelta(FrameEventsDelta&& src) = default; FrameEventsDelta& operator=(FrameEventsDelta&& src) = default; // Not copyable. FrameEventsDelta(const FrameEventsDelta& src) = delete; FrameEventsDelta& operator=(const FrameEventsDelta& 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); private: static constexpr size_t minFlattenedSize(); size_t mIndex{0}; uint64_t mFrameNumber{0}; bool mAddPostCompositeCalled{0}; bool mAddReleaseCalled{0}; nsecs_t mPostedTime{FrameEvents::TIMESTAMP_PENDING}; nsecs_t mRequestedPresentTime{FrameEvents::TIMESTAMP_PENDING}; nsecs_t mLatchTime{FrameEvents::TIMESTAMP_PENDING}; nsecs_t mFirstRefreshStartTime{FrameEvents::TIMESTAMP_PENDING}; nsecs_t mLastRefreshStartTime{FrameEvents::TIMESTAMP_PENDING}; nsecs_t mDequeueReadyTime{FrameEvents::TIMESTAMP_PENDING}; FenceTime::Snapshot mGpuCompositionDoneFence; FenceTime::Snapshot mDisplayPresentFence; FenceTime::Snapshot mReleaseFence; // This is a static method with an auto return value so we can call // it without needing const and non-const versions. template static inline auto allFences(ThisT fed) -> std::arraymReleaseFence), 3> { return {{ &fed->mGpuCompositionDoneFence, &fed->mDisplayPresentFence, &fed->mReleaseFence }}; } }; // A collection of updates from consumer to producer that can be sent // through Binder. class FrameEventHistoryDelta : public Flattenable { friend class ConsumerFrameEventHistory; friend class ProducerFrameEventHistory; public: FrameEventHistoryDelta() = default; // Movable. FrameEventHistoryDelta(FrameEventHistoryDelta&& src) = default; FrameEventHistoryDelta& operator=(FrameEventHistoryDelta&& src); // Not copyable. FrameEventHistoryDelta(const FrameEventHistoryDelta& src) = delete; FrameEventHistoryDelta& operator=( const FrameEventHistoryDelta& 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); private: static constexpr size_t minFlattenedSize(); std::vector mDeltas; CompositorTiming mCompositorTiming; }; } // namespace android #endif libs/gui/include/gui/GLConsumer.h0100644 0000000 0000000 00000056630 13300556574 015742 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_GUI_CONSUMER_H #define ANDROID_GUI_CONSUMER_H #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class String8; /* * GLConsumer consumes buffers of graphics data from a BufferQueue, * and makes them available to OpenGL as a texture. * * A typical usage pattern is to set up the GLConsumer with the * desired options, and call updateTexImage() when a new frame is desired. * If a new frame is available, the texture will be updated. If not, * the previous contents are retained. * * By default, the texture is attached to the GL_TEXTURE_EXTERNAL_OES * texture target, in the EGL context of the first thread that calls * updateTexImage(). * * This class was previously called SurfaceTexture. */ class GLConsumer : public ConsumerBase { public: enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; // GLConsumer constructs a new GLConsumer object. If the constructor with // the tex parameter is used, tex indicates the name of the OpenGL ES // texture to which images are to be streamed. texTarget specifies the // OpenGL ES texture target to which the texture will be bound in // updateTexImage. useFenceSync specifies whether fences should be used to // synchronize access to buffers if that behavior is enabled at // compile-time. // // A GLConsumer may be detached from one OpenGL ES context and then // attached to a different context using the detachFromContext and // attachToContext methods, respectively. The intention of these methods is // purely to allow a GLConsumer to be transferred from one consumer // context to another. If such a transfer is not needed there is no // requirement that either of these methods be called. // // If the constructor with the tex parameter is used, the GLConsumer is // created in a state where it is considered attached to an OpenGL ES // context for the purposes of the attachToContext and detachFromContext // methods. However, despite being considered "attached" to a context, the // specific OpenGL ES context doesn't get latched until the first call to // updateTexImage. After that point, all calls to updateTexImage must be // made with the same OpenGL ES context current. // // If the constructor without the tex parameter is used, the GLConsumer is // created in a detached state, and attachToContext must be called before // calls to updateTexImage. GLConsumer(const sp& bq, uint32_t tex, uint32_t texureTarget, bool useFenceSync, bool isControlledByApp); GLConsumer(const sp& bq, uint32_t texureTarget, bool useFenceSync, bool isControlledByApp); // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. // // This call may only be made while the OpenGL ES context to which the // target texture belongs is bound to the calling thread. // // This calls doGLFenceWait to ensure proper synchronization. status_t updateTexImage(); // releaseTexImage releases the texture acquired in updateTexImage(). // This is intended to be used in single buffer mode. // // This call may only be made while the OpenGL ES context to which the // target texture belongs is bound to the calling thread. status_t releaseTexImage(); // setReleaseFence stores a fence that will signal when the current buffer // is no longer being read. This fence will be returned to the producer // when the current buffer is released by updateTexImage(). Multiple // fences can be set for a given buffer; they will be merged into a single // union fence. virtual void setReleaseFence(const sp& fence); // getTransformMatrix retrieves 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. // // This transform is necessary to compensate for transforms that the stream // content producer may implicitly apply to the content. By forcing users of // a GLConsumer to apply this transform we avoid performing an extra // copy of the data that would be needed to hide the transform from the // user. // // The matrix is stored in column-major order so that it may be passed // directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv // functions. void getTransformMatrix(float mtx[16]); // Computes the transform matrix documented by getTransformMatrix // from the BufferItem sub parts. static void computeTransformMatrix(float outTransform[16], const sp& buf, const Rect& cropRect, uint32_t transform, bool filtering); // getTimestamp retrieves the timestamp associated with the texture image // set by the most recent call to updateTexImage. // // The timestamp is in nanoseconds, and is monotonically increasing. Its // other semantics (zero point, etc) are source-dependent and should be // documented by the source. int64_t getTimestamp(); // getDataSpace retrieves the DataSpace associated with the texture image // set by the most recent call to updateTexImage. android_dataspace getCurrentDataSpace(); // getFrameNumber retrieves the frame number associated with the texture // image set by the most recent call to updateTexImage. // // The frame number is an incrementing counter set to 0 at the creation of // the BufferQueue associated with this consumer. uint64_t getFrameNumber(); // setDefaultBufferSize is used to set the size of buffers returned by // requestBuffers when a with and height of zero is requested. // A call to setDefaultBufferSize() may trigger requestBuffers() to // be called from the client. // The width and height parameters must be no greater than the minimum of // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). // An error due to invalid dimensions might not be reported until // updateTexImage() is called. status_t setDefaultBufferSize(uint32_t width, uint32_t height); // setFilteringEnabled sets whether the transform matrix should be computed // for use with bilinear filtering. void setFilteringEnabled(bool enabled); // getCurrentBuffer returns the buffer associated with the current image. // When outSlot is not nullptr, the current buffer slot index is also // returned. sp getCurrentBuffer(int* outSlot = nullptr) const; // getCurrentTextureTarget returns the texture target of the current // texture as returned by updateTexImage(). uint32_t getCurrentTextureTarget() const; // getCurrentCrop returns the cropping rectangle of the current buffer. Rect getCurrentCrop() const; // getCurrentTransform returns the transform of the current buffer. uint32_t getCurrentTransform() const; // getCurrentScalingMode returns the scaling mode of the current buffer. uint32_t getCurrentScalingMode() const; // getCurrentFence returns the fence indicating when the current buffer is // ready to be read from. sp getCurrentFence() const; // getCurrentFence returns the FenceTime indicating when the current // buffer is ready to be read from. std::shared_ptr getCurrentFenceTime() const; // doGLFenceWait inserts a wait command into the OpenGL ES command stream // to ensure that it is safe for future OpenGL ES commands to access the // current texture buffer. status_t doGLFenceWait() const; // set the name of the GLConsumer that will be used to identify it in // log messages. void setName(const String8& name); // These functions call the corresponding BufferQueue implementation // so the refactoring can proceed smoothly status_t setDefaultBufferFormat(PixelFormat defaultFormat); status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace); status_t setConsumerUsageBits(uint64_t usage); status_t setTransformHint(uint32_t hint); status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); // detachFromContext detaches the GLConsumer from the calling thread's // current OpenGL ES context. This context must be the same as the context // that was current for previous calls to updateTexImage. // // Detaching a GLConsumer from an OpenGL ES context will result in the // deletion of the OpenGL ES texture object into which the images were being // streamed. After a GLConsumer has been detached from the OpenGL ES // context calls to updateTexImage will fail returning INVALID_OPERATION // until the GLConsumer is attached to a new OpenGL ES context using the // attachToContext method. status_t detachFromContext(); // attachToContext attaches a GLConsumer that is currently in the // 'detached' state to the current OpenGL ES context. A GLConsumer is // in the 'detached' state iff detachFromContext has successfully been // called and no calls to attachToContext have succeeded since the last // detachFromContext call. Calls to attachToContext made on a // GLConsumer that is not in the 'detached' state will result in an // INVALID_OPERATION error. // // The tex argument specifies the OpenGL ES texture object name in the // new context into which the image contents will be streamed. A successful // call to attachToContext will result in this texture object being bound to // the texture target and populated with the image contents that were // current at the time of the last call to detachFromContext. status_t attachToContext(uint32_t tex); protected: // abandonLocked overrides the ConsumerBase method to clear // mCurrentTextureImage in addition to the ConsumerBase behavior. virtual void abandonLocked(); // dumpLocked overrides the ConsumerBase method to dump GLConsumer- // specific info in addition to the ConsumerBase behavior. virtual void dumpLocked(String8& result, const char* prefix) const; // acquireBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase behavior. virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber = 0) override; // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) override; status_t releaseBufferLocked(int slot, const sp graphicBuffer, EGLSyncKHR eglFence) { return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence); } static bool isExternalFormat(PixelFormat format); struct PendingRelease { PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer(), display(nullptr), fence(nullptr) {} bool isPending; int currentTexture; sp graphicBuffer; EGLDisplay display; EGLSyncKHR fence; }; // This releases the buffer in the slot referenced by mCurrentTexture, // then updates state to refer to the BufferItem, which must be a // newly-acquired buffer. If pendingRelease is not null, the parameters // which would have been passed to releaseBufferLocked upon the successful // completion of the method will instead be returned to the caller, so that // it may call releaseBufferLocked itself later. status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease = nullptr); // Binds mTexName and the current buffer to mTexTarget. Uses // mCurrentTexture if it's set, mCurrentTextureImage if not. If the // bind succeeds, this calls doGLFenceWait. status_t bindTextureImageLocked(); // Gets the current EGLDisplay and EGLContext values, and compares them // to mEglDisplay and mEglContext. If the fields have been previously // set, the values must match; if not, the fields are set to the current // values. // The contextCheck argument is used to ensure that a GL context is // properly set; when set to false, the check is not performed. status_t checkAndUpdateEglStateLocked(bool contextCheck = false); private: // EglImage is a utility class for tracking and creating EGLImageKHRs. There // is primarily just one image per slot, but there is also special cases: // - For releaseTexImage, we use a debug image (mReleasedTexImage) // - After freeBuffer, we must still keep the current image/buffer // Reference counting EGLImages lets us handle all these cases easily while // also only creating new EGLImages from buffers when required. class EglImage : public LightRefBase { public: EglImage(sp graphicBuffer); // createIfNeeded creates an EGLImage if required (we haven't created // one yet, or the EGLDisplay or crop-rect has changed). status_t createIfNeeded(EGLDisplay display, const Rect& cropRect, bool forceCreate = false); // This calls glEGLImageTargetTexture2DOES to bind the image to the // texture in the specified texture target. void bindToTextureTarget(uint32_t texTarget); const sp& graphicBuffer() { return mGraphicBuffer; } const native_handle* graphicBufferHandle() { return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle; } private: // Only allow instantiation using ref counting. friend class LightRefBase; virtual ~EglImage(); // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer, const Rect& crop); // Disallow copying EglImage(const EglImage& rhs); void operator = (const EglImage& rhs); // mGraphicBuffer is the buffer that was used to create this image. sp mGraphicBuffer; // mEglImage is the EGLImage created from mGraphicBuffer. EGLImageKHR mEglImage; // mEGLDisplay is the EGLDisplay that was used to create mEglImage. EGLDisplay mEglDisplay; // mCropRect is the crop rectangle passed to EGL when mEglImage // was created. Rect mCropRect; }; // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in that // slot and destroy the EGLImage in that slot. Otherwise it has no effect. // // This method must be called with mMutex locked. virtual void freeBufferLocked(int slotIndex); // computeCurrentTransformMatrixLocked computes the transform matrix for the // current texture. It uses mCurrentTransform and the current GraphicBuffer // to compute this matrix and stores it in mCurrentTransformMatrix. // mCurrentTextureImage must not be NULL. void computeCurrentTransformMatrixLocked(); // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command // stream to ensure that it is safe for future OpenGL ES commands to // access the current texture buffer. status_t doGLFenceWaitLocked() const; // syncForReleaseLocked performs the synchronization needed to release the // current slot from an OpenGL ES context. If needed it will set the // current slot's fence to guard against a producer accessing the buffer // before the outstanding accesses have completed. status_t syncForReleaseLocked(EGLDisplay dpy); // returns a graphic buffer used when the texture image has been released static sp getDebugTexImageBuffer(); // The default consumer usage flags that GLConsumer always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed // from the GLConsumer user. In particular, GLConsumer will always // consume buffers as hardware textures. static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; // mCurrentTextureImage is the EglImage/buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. sp mCurrentTextureImage; // mCurrentCrop is the crop rectangle that applies to the current texture. // It gets set each time updateTexImage is called. Rect mCurrentCrop; // mCurrentTransform is the transform identifier for the current texture. It // gets set each time updateTexImage is called. uint32_t mCurrentTransform; // mCurrentScalingMode is the scaling mode for the current texture. It gets // set each time updateTexImage is called. uint32_t mCurrentScalingMode; // mCurrentFence is the fence received from BufferQueue in updateTexImage. sp mCurrentFence; // The FenceTime wrapper around mCurrentFence. std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; // mCurrentTransformMatrix is the transform matrix for the current texture. // It gets computed by computeTransformMatrix each time updateTexImage is // called. float mCurrentTransformMatrix[16]; // mCurrentTimestamp is the timestamp for the current texture. It // gets set each time updateTexImage is called. int64_t mCurrentTimestamp; // mCurrentDataSpace is the dataspace for the current texture. It // gets set each time updateTexImage is called. android_dataspace mCurrentDataSpace; // mCurrentFrameNumber is the frame counter for the current texture. // It gets set each time updateTexImage is called. uint64_t mCurrentFrameNumber; uint32_t mDefaultWidth, mDefaultHeight; // mFilteringEnabled indicates whether the transform matrix is computed for // use with bilinear filtering. It defaults to true and is changed by // setFilteringEnabled(). bool mFilteringEnabled; // mTexName is the name of the OpenGL texture to which streamed images will // be bound when updateTexImage is called. It is set at construction time // and can be changed with a call to attachToContext. uint32_t mTexName; // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync // extension should be used to prevent buffers from being dequeued before // it's safe for them to be written. It gets set at construction time and // never changes. const bool mUseFenceSync; // mTexTarget is the GL texture target with which the GL texture object is // associated. It is set in the constructor and never changed. It is // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android // Browser. In that case it is set to GL_TEXTURE_2D to allow // glCopyTexSubImage to read from the texture. This is a hack to work // around a GL driver limitation on the number of FBO attachments, which the // browser's tile cache exceeds. const uint32_t mTexTarget; // EGLSlot contains the information and object references that // GLConsumer maintains about a BufferQueue buffer slot. struct EglSlot { EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} // mEglImage is the EGLImage created from mGraphicBuffer. sp mEglImage; // mFence is the EGL sync object that must signal before the buffer // associated with this buffer slot may be dequeued. It is initialized // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based // on a compile-time option) set to a new sync object in updateTexImage. EGLSyncKHR mEglFence; }; // mEglDisplay is the EGLDisplay with which this GLConsumer is currently // associated. It is intialized to EGL_NO_DISPLAY and gets set to the // current display when updateTexImage is called for the first time and when // attachToContext is called. EGLDisplay mEglDisplay; // mEglContext is the OpenGL ES context with which this GLConsumer is // currently associated. It is initialized to EGL_NO_CONTEXT and gets set // to the current GL context when updateTexImage is called for the first // time and when attachToContext is called. EGLContext mEglContext; // mEGLSlots stores the buffers that have been allocated by the BufferQueue // for each buffer slot. It is initialized to null pointers, and gets // filled in with the result of BufferQueue::acquire when the // client dequeues a buffer from a // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, // indicating that no buffer slot is currently bound to the texture. Note, // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean // that no buffer is bound to the texture. A call to setBufferCount will // reset mCurrentTexture to INVALID_BUFFER_SLOT. int mCurrentTexture; // mAttached indicates whether the ConsumerBase is currently attached to // an OpenGL ES context. For legacy reasons, this is initialized to true, // indicating that the ConsumerBase is considered to be attached to // whatever context is current at the time of the first updateTexImage call. // It is set to false by detachFromContext, and then set to true again by // attachToContext. bool mAttached; // protects static initialization static Mutex sStaticInitLock; // mReleasedTexImageBuffer is a dummy buffer used when in single buffer // mode and releaseTexImage() has been called static sp sReleasedTexImageBuffer; sp mReleasedTexImage; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_CONSUMER_H libs/gui/include/gui/GuiConfig.h0100644 0000000 0000000 00000001574 13300556574 015573 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 ANDROID_GUI_CONFIG_H #define ANDROID_GUI_CONFIG_H #include namespace android { // Append the libgui configuration details to configStr. void appendGuiConfigString(String8& configStr); }; // namespace android #endif /*ANDROID_GUI_CONFIG_H*/ libs/gui/include/gui/IConsumerListener.h0100644 0000000 0000000 00000010560 13300556574 017326 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. */ #pragma once #include #include #include #include #include namespace android { class BufferItem; class FrameEventHistoryDelta; struct NewFrameEventsEntry; // ConsumerListener is the interface through which the BufferQueue notifies the consumer of events // that the consumer may wish to react to. Because the consumer will generally have a mutex that is // locked during calls from the consumer to the BufferQueue, these calls from the BufferQueue to the // consumer *MUST* be called only when the BufferQueue mutex is NOT locked. class ConsumerListener : public virtual RefBase { public: ConsumerListener() {} virtual ~ConsumerListener(); // onDisconnect is called when a producer disconnects from the BufferQueue. virtual void onDisconnect() {} /* Asynchronous */ // onFrameAvailable is called from queueBuffer each time an additional frame becomes available // for consumption. This means that frames that are queued while in asynchronous mode only // trigger the callback if no previous frames are pending. Frames queued while in synchronous // mode always trigger the callback. The item passed to the callback will contain all of the // information about the queued frame except for its GraphicBuffer pointer, which will always be // null (except if the consumer is SurfaceFlinger). // // This is called without any lock held and can be called concurrently by multiple threads. virtual void onFrameAvailable(const BufferItem& item) = 0; /* Asynchronous */ // onFrameReplaced is called from queueBuffer if the frame being queued is replacing an existing // slot in the queue. Any call to queueBuffer that doesn't call onFrameAvailable will call this // callback instead. The item passed to the callback will contain all of the information about // the queued frame except for its GraphicBuffer pointer, which will always be null. // // This is called without any lock held and can be called concurrently by multiple threads. virtual void onFrameReplaced(const BufferItem& /* item */) {} /* Asynchronous */ // onBuffersReleased is called to notify the buffer consumer that the BufferQueue has released // its references to one or more GraphicBuffers contained in its slots. The buffer consumer // should then call BufferQueue::getReleasedBuffers to retrieve the list of buffers. // // This is called without any lock held and can be called concurrently by multiple threads. virtual void onBuffersReleased() = 0; /* Asynchronous */ // onSidebandStreamChanged is called to notify the buffer consumer that the BufferQueue's // sideband buffer stream has changed. This is called when a stream is first attached and when // it is either detached or replaced by a different stream. virtual void onSidebandStreamChanged() = 0; /* Asynchronous */ // Notifies the consumer of any new producer-side timestamps and returns the combined frame // history that hasn't already been retrieved. // // WARNING: This method can only be called when the BufferQueue is in the consumer's process. virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/, FrameEventHistoryDelta* /*outDelta*/) {} }; class IConsumerListener : public ConsumerListener, public IInterface { public: DECLARE_META_INTERFACE(ConsumerListener) }; class BnConsumerListener : public SafeBnInterface { public: BnConsumerListener() : SafeBnInterface("BnConsumerListener") {} status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; }; } // namespace android libs/gui/include/gui/IDisplayEventConnection.h0100644 0000000 0000000 00000004135 13300556574 020455 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. */ #pragma once #include #include #include #include namespace android { namespace gui { class BitTube; } // namespace gui class IDisplayEventConnection : public IInterface { public: DECLARE_META_INTERFACE(DisplayEventConnection) /* * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file * descriptor of outChannel will be initialized, and this effectively "steals" the receive * channel from the remote end (such that the remote end can only use its send channel). */ virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0; /* * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event. * A value of 2 returns every other event, etc. A value of 0 returns no event unless * requestNextVsync() has been called. */ virtual status_t setVsyncRate(uint32_t count) = 0; /* * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0. */ virtual void requestNextVsync() = 0; // Asynchronous }; class BnDisplayEventConnection : public SafeBnInterface { public: BnDisplayEventConnection() : SafeBnInterface("BnDisplayEventConnection") {} status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; }; } // namespace android libs/gui/include/gui/IGraphicBufferConsumer.h0100644 0000000 0000000 00000035551 13300556574 020257 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. */ #pragma once #include #include #include #include #include #include #include namespace android { class BufferItem; class Fence; class GraphicBuffer; class IConsumerListener; class NativeHandle; class IGraphicBufferConsumer : public IInterface { public: DECLARE_META_INTERFACE(GraphicBufferConsumer) enum { // Returned by releaseBuffer, after which the consumer must free any references to the // just-released buffer that it might have. STALE_BUFFER_SLOT = 1, // Returned by dequeueBuffer if there are no pending buffers available. NO_BUFFER_AVAILABLE, // Returned by dequeueBuffer if it's too early for the buffer to be acquired. PRESENT_LATER, }; // acquireBuffer attempts to acquire ownership of the next pending buffer in the BufferQueue. // If no buffer is pending then it returns NO_BUFFER_AVAILABLE. If a buffer is successfully // acquired, the information about the buffer is returned in BufferItem. // // If the buffer returned had previously been acquired then the BufferItem::mGraphicBuffer field // of buffer is set to NULL and it is assumed that the consumer still holds a reference to the // buffer. // // If presentWhen is non-zero, it indicates the time when the buffer will be displayed on // screen. If the buffer's timestamp is farther in the future, the buffer won't be acquired, and // PRESENT_LATER will be returned. The presentation time is in nanoseconds, and the time base // is CLOCK_MONOTONIC. // // If maxFrameNumber is non-zero, it indicates that acquireBuffer should only return a buffer // with a frame number less than or equal to maxFrameNumber. If no such frame is available // (such as when a buffer has been replaced but the consumer has not received the // onFrameReplaced callback), then PRESENT_LATER will be returned. // // Return of NO_ERROR means the operation completed as normal. // // Return of a positive value means the operation could not be completed at this time, but the // user should try again later: // * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer) // * PRESENT_LATER - the buffer's timestamp is farther in the future // // Return of a negative value means an error has occurred: // * INVALID_OPERATION - too many buffers have been acquired virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen, uint64_t maxFrameNumber = 0) = 0; // detachBuffer attempts to remove all ownership of the buffer in the given slot from the buffer // queue. If this call succeeds, the slot will be freed, and there will be no way to obtain the // buffer from this interface. The freed slot will remain unallocated until either it is // selected to hold a freshly allocated buffer in dequeueBuffer or a buffer is attached to the // slot. The buffer must have already been acquired. // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - the given slot number is invalid, either because it is out of the range // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not // currently acquired. virtual status_t detachBuffer(int slot) = 0; // attachBuffer attempts to transfer ownership of a buffer to the BufferQueue. If this call // succeeds, it will be as if this buffer was acquired from the returned slot number. As such, // this call will fail if attaching this buffer would cause too many buffers to be // simultaneously acquired. // // If the buffer is successfully attached, its frameNumber is initialized to 0. This must be // passed into the releaseBuffer call or else the buffer will be deallocated as stale. // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - outSlot or buffer were NULL, or the generation number of the buffer did not // match the BufferQueue. // * INVALID_OPERATION - cannot attach the buffer because it would cause too many buffers // to be acquired. // * NO_MEMORY - no free slots available virtual status_t attachBuffer(int* outSlot, const sp& buffer) = 0; // releaseBuffer releases a buffer slot from the consumer back to the BufferQueue. This may be // done while the buffer's contents are still being accessed. The fence will signal when the // buffer is no longer in use. frameNumber is used to identify the exact buffer returned. // // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free any references to the // just-released buffer that it might have, as if it had received a onBuffersReleased() call // with a mask set for the released buffer. // // Note that the dependencies on EGL will be removed once we switch to using the Android HW // Sync HAL. // // Return of NO_ERROR means the operation completed as normal. // // Return of a positive value means the operation could not be completed at this time, but the // user should try again later: // * STALE_BUFFER_SLOT - see above (second paragraph) // // Return of a negative value means an error has occurred: // * BAD_VALUE - one of the following could've happened: // * the buffer slot was invalid // * the fence was NULL // * the buffer slot specified is not in the acquired state virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp& releaseFence) = 0; status_t releaseHelper(int buf, uint64_t frameNumber, const sp& releaseFence) { return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); } // This is explicitly *not* the actual signature of IGBC::releaseBuffer, but: // 1) We have no easy way to send the EGL objects across Binder // 2) This has always been broken, probably because // 3) IGBC is rarely remoted // For now, we will choose to bury our heads in the sand and ignore this problem until such time // as we can finally finish converting away from EGL sync to native Android sync using ReleaseBuffer = decltype(&IGraphicBufferConsumer::releaseHelper); // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected, // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state, // causing most interactions with the BufferQueue by the producer to fail. controlledByApp // indicates whether the consumer is controlled by the application. // // consumer may not be NULL. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the BufferQueue has been abandoned // * BAD_VALUE - a NULL consumer was provided virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) = 0; // consumerDisconnect disconnects a consumer from the BufferQueue. All buffers will be freed and // the BufferQueue is placed in the "abandoned" state, causing most interactions with the // BufferQueue by the producer to fail. // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - no consumer is currently connected virtual status_t consumerDisconnect() = 0; // getReleasedBuffers sets the value pointed to by slotMask to a bit set. Each bit index with a // 1 corresponds to a released buffer slot with that index value. In particular, a released // buffer is one that has been released by the BufferQueue but has not yet been released by // the consumer. // // This should be called from the onBuffersReleased() callback. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the BufferQueue has been abandoned. virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0; // setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a // width and height of zero is requested. Default is 1x1. // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - either w or h was zero virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; // setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue // (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the // consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would // cause this value to be exceeded then that call will fail. This call will fail if a producer // is connected to the BufferQueue. // // The count must be between 1 and NUM_BUFFER_SLOTS, inclusive. The count cannot be less than // maxAcquiredBufferCount. // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - one of the below conditions occurred: // * bufferCount was out of range (see above). // * failure to adjust the number of available slots. // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxBufferCount(int bufferCount) = 0; // setMaxAcquiredBufferCount sets the maximum number of buffers that can be acquired by the // consumer at one time (default 1). If this method succeeds, any new buffer slots will be both // unallocated and owned by the BufferQueue object (i.e. they are not owned by the producer or // consumer). Calling this may also cause some buffer slots to be emptied. // // This function should not be called with a value of maxAcquiredBuffers that is less than the // number of currently acquired buffer slots. Doing so will result in a BAD_VALUE error. // // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot // cause the maxBufferCount value to be exceeded. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the BufferQueue has been abandoned // * BAD_VALUE - one of the below conditions occurred: // * maxAcquiredBuffers was out of range (see above). // * failure to adjust the number of available slots. // * client would have more than the requested number of acquired buffers after // this call // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; // setConsumerName sets the name used in logging virtual status_t setConsumerName(const String8& name) = 0; // setDefaultBufferFormat allows the BufferQueue to create GraphicBuffers of a defaultFormat if // no format is specified in dequeueBuffer. The initial default is PIXEL_FORMAT_RGBA_8888. // // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) = 0; // setDefaultBufferDataSpace is a request to the producer to provide buffers of the indicated // dataSpace. The producer may ignore this request. The initial default is // HAL_DATASPACE_UNKNOWN. // // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) = 0; // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. These are merged // with the bits passed to dequeueBuffer. The values are enumerated in gralloc.h, // e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. // // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setConsumerUsageBits(uint64_t usage) = 0; // setConsumerIsProtected will turn on an internal bit that indicates whether // the consumer can handle protected gralloc buffers (i.e. with // GRALLOC_USAGE_PROTECTED set). IGraphicBufferProducer can query this // capability using NATIVE_WINDOW_CONSUMER_IS_PROTECTED. virtual status_t setConsumerIsProtected(bool isProtected) = 0; // setTransformHint bakes in rotation to buffers so overlays can be used. The values are // enumerated in window.h, e.g. NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 // (no transform). // // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setTransformHint(uint32_t hint) = 0; // Retrieve the sideband buffer stream, if any. virtual status_t getSidebandStream(sp* outStream) const = 0; // Retrieves any stored segments of the occupancy history of this BufferQueue and clears them. // Optionally closes out the pending segment if forceFlush is true. virtual status_t getOccupancyHistory(bool forceFlush, std::vector* outHistory) = 0; // discardFreeBuffers releases all currently-free buffers held by the BufferQueue, in order to // reduce the memory consumption of the BufferQueue to the minimum possible without // discarding data. // The consumer invoking this method is responsible for calling getReleasedBuffers() after this // call to free up any of its locally cached buffers. virtual status_t discardFreeBuffers() = 0; // dump state into a string virtual status_t dumpState(const String8& prefix, String8* outResult) const = 0; // Provide backwards source compatibility void dumpState(String8& result, const char* prefix) { String8 returned; dumpState(String8(prefix), &returned); result.append(returned); } }; class BnGraphicBufferConsumer : public SafeBnInterface { public: BnGraphicBufferConsumer() : SafeBnInterface("BnGraphicBufferConsumer") {} status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override; }; } // namespace android libs/gui/include/gui/IGraphicBufferProducer.h0100644 0000000 0000000 00000074016 13300556574 020246 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_GUI_IGRAPHICBUFFERPRODUCER_H #define ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H #include #include #include #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- class IProducerListener; class NativeHandle; class Surface; typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer HGraphicBufferProducer; /* * This class defines the Binder IPC interface for the producer side of * a queue of graphics buffers. It's used to send graphics data from one * component to another. For example, a class that decodes video for * playback might use this to provide frames. This is typically done * indirectly, through Surface. * * The underlying mechanism is a BufferQueue, which implements * BnGraphicBufferProducer. In normal operation, the producer calls * dequeueBuffer() to get an empty buffer, fills it with data, then * calls queueBuffer() to make it available to the consumer. * * This class was previously called ISurfaceTexture. */ class IGraphicBufferProducer : public IInterface { public: DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer) 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, }; // requestBuffer requests a new buffer for the given index. The server (i.e. // the IGraphicBufferProducer implementation) assigns the newly created // buffer to the given slot index, and the client is expected to mirror the // slot->buffer mapping so that it's not necessary to transfer a // GraphicBuffer for every dequeue operation. // // The slot must be in the range of [0, NUM_BUFFER_SLOTS). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - one of the two conditions occurred: // * slot was out of range (see above) // * buffer specified by the slot is not dequeued virtual status_t requestBuffer(int slot, sp* buf) = 0; // setMaxDequeuedBufferCount sets the maximum number of buffers that can be // dequeued by the producer at one time. If this method succeeds, any new // buffer slots will be both unallocated and owned by the BufferQueue object // (i.e. they are not owned by the producer or consumer). Calling this may // also cause some buffer slots to be emptied. If the caller is caching the // contents of the buffer slots, it should empty that cache after calling // this method. // // This function should not be called with a value of maxDequeuedBuffers // that is less than the number of currently dequeued buffer slots. Doing so // will result in a BAD_VALUE error. // // The buffer count should be at least 1 (inclusive), but at most // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The // minimum undequeued buffer count can be obtained by calling // query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned. // * BAD_VALUE - one of the below conditions occurred: // * bufferCount was out of range (see above). // * client would have more than the requested number of dequeued // buffers after this call. // * this call would cause the maxBufferCount value to be exceeded. // * failure to adjust the number of available slots. virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) = 0; // Set the async flag if the producer intends to asynchronously queue // buffers without blocking. Typically this is used for triple-buffering // and/or when the swap interval is set to zero. // // Enabling async mode will internally allocate an additional buffer to // allow for the asynchronous behavior. If it is not enabled queue/dequeue // calls may block. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned. // * BAD_VALUE - one of the following has occurred: // * this call would cause the maxBufferCount value to be // exceeded // * failure to adjust the number of available slots. virtual status_t setAsyncMode(bool async) = 0; // dequeueBuffer requests a new buffer slot for the client to use. Ownership // of the slot is transfered to the client, meaning that the server will not // use the contents of the buffer associated with that slot. // // The slot index returned may or may not contain a buffer (client-side). // If the slot is empty the client should call requestBuffer to assign a new // buffer to that slot. // // Once the client is done filling this buffer, it is expected to transfer // buffer ownership back to the server with either cancelBuffer on // the dequeued slot or to fill in the contents of its associated buffer // contents and call queueBuffer. // // If dequeueBuffer returns the BUFFER_NEEDS_REALLOCATION flag, the client is // expected to call requestBuffer immediately. // // If dequeueBuffer returns the RELEASE_ALL_BUFFERS flag, the client is // expected to release all of the mirrored slot->buffer mappings. // // The fence parameter will be updated to hold the fence associated with // the buffer. The contents of the buffer must not be overwritten until the // fence signals. If the fence is Fence::NO_FENCE, the buffer may be written // immediately. // // The width and height parameters must be no greater than the minimum of // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). // An error due to invalid dimensions might not be reported until // updateTexImage() is called. If width and height are both zero, the // default values specified by setDefaultBufferSize() are used instead. // // If the format is 0, the default format will be used. // // The usage argument specifies gralloc buffer usage flags. The values // are enumerated in , e.g. GRALLOC_USAGE_HW_RENDER. These // will be merged with the usage flags specified by // IGraphicBufferConsumer::setConsumerUsageBits. // // This call will block until a buffer is available to be dequeued. If // both the producer and consumer are controlled by the app, then this call // can never block and will return WOULD_BLOCK if no buffer is available. // // A non-negative value with flags set (see above) will be returned upon // success. // // Return of a negative means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - both in async mode and buffer count was less than the // max numbers of buffers that can be allocated at once. // * INVALID_OPERATION - cannot attach the buffer because it would cause // too many buffers to be dequeued, either because // the producer already has a single buffer dequeued // and did not set a buffer count, or because a // buffer count was set and this call would cause // it to be exceeded. // * WOULD_BLOCK - no buffer is currently available, and blocking is disabled // since both the producer/consumer are controlled by app // * NO_MEMORY - out of memory, cannot allocate the graphics buffer. // * TIMED_OUT - the timeout set by setDequeueTimeout was exceeded while // waiting for a buffer to become available. // // All other negative values are an unknown error returned downstream // from the graphics allocator (typically errno). virtual status_t dequeueBuffer(int* slot, sp* fence, uint32_t w, uint32_t h, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) = 0; // detachBuffer attempts to remove all ownership of the buffer in the given // slot from the buffer queue. If this call succeeds, the slot will be // freed, and there will be no way to obtain the buffer from this interface. // The freed slot will remain unallocated until either it is selected to // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached // to the slot. The buffer must have already been dequeued, and the caller // must already possesses the sp (i.e., must have called // requestBuffer). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - the given slot number is invalid, either because it is // out of the range [0, NUM_BUFFER_SLOTS), or because the slot // it refers to is not currently dequeued and requested. virtual status_t detachBuffer(int slot) = 0; // 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. // 2) It will not block, since if it cannot find an appropriate buffer to // return, it will return an error instead. // // Only slots that are free but still contain a GraphicBuffer will be // considered, and the oldest of those will be returned. outBuffer is // equivalent to outBuffer from the requestBuffer call, and outFence is // equivalent to fence from the dequeueBuffer call. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - either outBuffer or outFence were NULL. // * NO_MEMORY - no slots were found that were both free and contained a // GraphicBuffer. virtual status_t detachNextBuffer(sp* outBuffer, sp* outFence) = 0; // attachBuffer attempts to transfer ownership of a buffer to the buffer // queue. If this call succeeds, it will be as if this buffer was dequeued // from the returned slot number. As such, this call will fail if attaching // this buffer would cause too many buffers to be simultaneously dequeued. // // If attachBuffer returns the RELEASE_ALL_BUFFERS flag, the caller is // expected to release all of the mirrored slot->buffer mappings. // // A non-negative value with flags set (see above) will be returned upon // success. // // Return of a negative value means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - outSlot or buffer were NULL, invalid combination of // async mode and buffer count override, or the generation // number of the buffer did not match the buffer queue. // * INVALID_OPERATION - cannot attach the buffer because it would cause // too many buffers to be dequeued, either because // the producer already has a single buffer dequeued // and did not set a buffer count, or because a // buffer count was set and this call would cause // it to be exceeded. // * WOULD_BLOCK - no buffer slot is currently available, and blocking is // disabled since both the producer/consumer are // controlled by the app. // * TIMED_OUT - the timeout set by setDequeueTimeout was exceeded while // waiting for a slot to become available. virtual status_t attachBuffer(int* outSlot, const sp& buffer) = 0; // queueBuffer indicates that the client has finished filling in the // contents of the buffer associated with slot and transfers ownership of // that slot back to the server. // // It is not valid to call queueBuffer on a slot that is not owned // by the client or one for which a buffer associated via requestBuffer // (an attempt to do so will fail with a return value of BAD_VALUE). // // In addition, the input must be described by the client (as documented // below). Any other properties (zero point, etc) // are client-dependent, and should be documented by the client. // // The slot must be in the range of [0, NUM_BUFFER_SLOTS). // // Upon success, the output will be filled with meaningful values // (refer to the documentation below). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - one of the below conditions occurred: // * fence was NULL // * scaling mode was unknown // * both in async mode and buffer count was less than the // max numbers of buffers that can be allocated at once // * slot index was out of range (see above). // * the slot was not in the dequeued state // * the slot was enqueued without requesting a buffer // * crop rect is out of bounds of the buffer dimensions struct QueueBufferInput : public Flattenable { friend class Flattenable; explicit inline QueueBufferInput(const Parcel& parcel); // timestamp - a monotonically increasing value in nanoseconds // isAutoTimestamp - if the timestamp was synthesized at queue time // dataSpace - description of the contents, interpretation depends on format // crop - a crop rectangle that's used as a hint to the consumer // scalingMode - a set of flags from NATIVE_WINDOW_SCALING_* in // transform - a set of flags from NATIVE_WINDOW_TRANSFORM_* in // fence - a fence that the consumer must wait on before reading the buffer, // set this to Fence::NO_FENCE if the buffer is ready immediately // sticky - the sticky transform set in Surface (only used by the LEGACY // camera mode). // getFrameTimestamps - whether or not the latest frame timestamps // should be retrieved from the consumer. inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp, android_dataspace _dataSpace, const Rect& _crop, int _scalingMode, uint32_t _transform, const sp& _fence, uint32_t _sticky = 0, bool _getFrameTimestamps = false) : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp), dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode), transform(_transform), stickyTransform(_sticky), fence(_fence), surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { } inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp, android_dataspace* outDataSpace, Rect* outCrop, int* outScalingMode, uint32_t* outTransform, sp* outFence, uint32_t* outStickyTransform = nullptr, bool* outGetFrameTimestamps = nullptr) const { *outTimestamp = timestamp; *outIsAutoTimestamp = bool(isAutoTimestamp); *outDataSpace = dataSpace; *outCrop = crop; *outScalingMode = scalingMode; *outTransform = transform; *outFence = fence; if (outStickyTransform != NULL) { *outStickyTransform = stickyTransform; } if (outGetFrameTimestamps) { *outGetFrameTimestamps = getFrameTimestamps; } } // Flattenable protocol static constexpr size_t minFlattenedSize(); 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); const Region& getSurfaceDamage() const { return surfaceDamage; } void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; } private: int64_t timestamp{0}; int isAutoTimestamp{0}; android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN}; Rect crop; int scalingMode{0}; uint32_t transform{0}; uint32_t stickyTransform{0}; sp fence; Region surfaceDamage; bool getFrameTimestamps{false}; }; struct QueueBufferOutput : public Flattenable { QueueBufferOutput() = default; // Moveable. QueueBufferOutput(QueueBufferOutput&& src) = default; QueueBufferOutput& operator=(QueueBufferOutput&& src) = default; // Not copyable. QueueBufferOutput(const QueueBufferOutput& src) = delete; QueueBufferOutput& operator=(const QueueBufferOutput& src) = delete; // Flattenable protocol static constexpr size_t minFlattenedSize(); 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); uint32_t width{0}; uint32_t height{0}; uint32_t transformHint{0}; uint32_t numPendingBuffers{0}; uint64_t nextFrameNumber{0}; FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; }; virtual status_t queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) = 0; // cancelBuffer indicates that the client does not wish to fill in the // buffer associated with slot and transfers ownership of the slot back to // the server. // // The buffer is not queued for use by the consumer. // // The slot must be in the range of [0, NUM_BUFFER_SLOTS). // // The buffer will not be overwritten until the fence signals. The fence // will usually be the one obtained from dequeueBuffer. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - one of the below conditions occurred: // * fence was NULL // * slot index was out of range (see above). // * the slot was not in the dequeued state virtual status_t cancelBuffer(int slot, const sp& fence) = 0; // query retrieves some information for this surface // 'what' tokens allowed are that of NATIVE_WINDOW_* in // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned. // * BAD_VALUE - what was out of range virtual int query(int what, int* value) = 0; // connect attempts to connect a client API to the IGraphicBufferProducer. // This must be called before any other IGraphicBufferProducer methods are // called except for getAllocator. A consumer must be already connected. // // This method will fail if the connect was previously called on the // IGraphicBufferProducer and no corresponding disconnect call was made. // // The listener is an optional binder callback object that can be used if // the producer wants to be notified when the consumer releases a buffer // back to the BufferQueue. It is also used to detect the death of the // producer. If only the latter functionality is desired, there is a // DummyProducerListener class in IProducerListener.h that can be used. // // The api should be one of the NATIVE_WINDOW_API_* values in // // The producerControlledByApp should be set to true if the producer is hosted // by an untrusted process (typically app_process-forked processes). If both // the producer and the consumer are app-controlled then all buffer queues // will operate in async mode regardless of the async flag. // // Upon success, the output will be filled with meaningful data // (refer to QueueBufferOutput documentation above). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - one of the following occurred: // * the buffer queue was abandoned // * no consumer has yet connected // * BAD_VALUE - one of the following has occurred: // * the producer is already connected // * api was out of range (see above). // * output was NULL. // * Failure to adjust the number of available slots. This can // happen because of trying to allocate/deallocate the async // buffer in response to the value of producerControlledByApp. // * DEAD_OBJECT - the token is hosted by an already-dead process // // Additional negative errors may be returned by the internals, they // should be treated as opaque fatal unrecoverable errors. virtual status_t connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) = 0; enum class DisconnectMode { // Disconnect only the specified API. Api, // Disconnect any API originally connected from the process calling disconnect. AllLocal }; // disconnect attempts to disconnect a client API from the // IGraphicBufferProducer. Calling this method will cause any subsequent // calls to other IGraphicBufferProducer methods to fail except for // getAllocator and connect. Successfully calling connect after this will // allow the other methods to succeed again. // // The api should be one of the NATIVE_WINDOW_API_* values in // // Alternatively if mode is AllLocal, then the API value is ignored, and any API // connected from the same PID calling disconnect will be disconnected. // // Disconnecting from an abandoned IGraphicBufferProducer is legal and // is considered a no-op. // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the producer is not connected // * BAD_VALUE - one of the following has occurred: // * the api specified does not match the one that was connected // * api was out of range (see above). // * DEAD_OBJECT - the token is hosted by an already-dead process virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) = 0; // Attaches a sideband buffer stream to the IGraphicBufferProducer. // // A sideband stream is a device-specific mechanism for passing buffers // from the producer to the consumer without using dequeueBuffer/ // queueBuffer. If a sideband stream is present, the consumer can choose // whether to acquire buffers from the sideband stream or from the queued // buffers. // // Passing NULL or a different stream handle will detach the previous // handle if any. virtual status_t setSidebandStream(const sp& stream) = 0; // Allocates buffers based on the given dimensions/format. // // This function will allocate up to the maximum number of buffers // permitted by the current BufferQueue configuration. It will use the // given format, dimensions, and usage bits, which are interpreted in the // same way as for dequeueBuffer, and the async flag must be set the same // way as for dequeueBuffer to ensure that the correct number of buffers are // allocated. This is most useful to avoid an allocation delay during // dequeueBuffer. If there are already the maximum number of buffers // allocated, this function has no effect. virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) = 0; // Sets whether dequeueBuffer is allowed to allocate new buffers. // // Normally dequeueBuffer does not discriminate between free slots which // already have an allocated buffer and those which do not, and will // allocate a new buffer if the slot doesn't have a buffer or if the slot's // buffer doesn't match the requested size, format, or usage. This method // allows the producer to restrict the eligible slots to those which already // have an allocated buffer of the correct size, format, and usage. If no // eligible slot is available, dequeueBuffer will block or return an error // as usual. virtual status_t allowAllocation(bool allow) = 0; // Sets the current generation number of the BufferQueue. // // This generation number will be inserted into any buffers allocated by the // BufferQueue, and any attempts to attach a buffer with a different // generation number will fail. Buffers already in the queue are not // affected and will retain their current generation number. The generation // number defaults to 0. virtual status_t setGenerationNumber(uint32_t generationNumber) = 0; // Returns the name of the connected consumer. virtual String8 getConsumerName() const = 0; // Used to enable/disable shared buffer mode. // // When shared buffer mode is enabled the first buffer that is queued or // dequeued will be cached and returned to all subsequent calls to // dequeueBuffer and acquireBuffer. This allows the producer and consumer to // simultaneously access the same buffer. virtual status_t setSharedBufferMode(bool sharedBufferMode) = 0; // Used to enable/disable auto-refresh. // // Auto refresh has no effect outside of shared buffer mode. In shared // buffer mode, when enabled, it indicates to the consumer that it should // attempt to acquire buffers even if it is not aware of any being // available. virtual status_t setAutoRefresh(bool autoRefresh) = 0; // Sets how long dequeueBuffer will wait for a buffer to become available // before returning an error (TIMED_OUT). // // This timeout also affects the attachBuffer call, which will block if // there is not a free slot available into which the attached buffer can be // placed. // // By default, the BufferQueue will wait forever, which is indicated by a // timeout of -1. If set (to a value other than -1), this will disable // non-blocking mode and its corresponding spare buffer (which is used to // ensure a buffer is always available). // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - Failure to adjust the number of available slots. This can // happen because of trying to allocate/deallocate the async // buffer. virtual status_t setDequeueTimeout(nsecs_t timeout) = 0; // Returns the last queued buffer along with a fence which must signal // before the contents of the buffer are read. If there are no buffers in // the queue, outBuffer will be populated with nullptr and outFence will be // populated with Fence::NO_FENCE // // outTransformMatrix is not modified if outBuffer is null. // // Returns NO_ERROR or the status of the Binder transaction virtual status_t getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) = 0; // Gets the frame events that haven't already been retrieved. virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {} // Returns a unique id for this BufferQueue virtual status_t getUniqueId(uint64_t* outId) const = 0; // Returns the consumer usage flags for this BufferQueue. This returns the // full 64-bit usage flags, rather than the truncated 32-bit usage flags // returned by querying the now deprecated // NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute. virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0; }; // ---------------------------------------------------------------------------- class BnGraphicBufferProducer : public BnInterface { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H libs/gui/include/gui/IProducerListener.h0100644 0000000 0000000 00000004305 13300556574 017316 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. */ #ifndef ANDROID_GUI_IPRODUCERLISTENER_H #define ANDROID_GUI_IPRODUCERLISTENER_H #include #include namespace android { // ProducerListener is the interface through which the BufferQueue notifies the // producer of events that the producer may wish to react to. Because the // producer will generally have a mutex that is locked during calls from the // producer to the BufferQueue, these calls from the BufferQueue to the // producer *MUST* be called only when the BufferQueue mutex is NOT locked. class ProducerListener : public virtual RefBase { public: ProducerListener() {} virtual ~ProducerListener(); // onBufferReleased is called from IGraphicBufferConsumer::releaseBuffer to // notify the producer that a new buffer is free and ready to be dequeued. // // This is called without any lock held and can be called concurrently by // multiple threads. virtual void onBufferReleased() = 0; // Asynchronous virtual bool needsReleaseNotify() = 0; }; class IProducerListener : public ProducerListener, public IInterface { public: DECLARE_META_INTERFACE(ProducerListener) }; class BnProducerListener : public BnInterface { public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual bool needsReleaseNotify(); }; class DummyProducerListener : public BnProducerListener { public: virtual ~DummyProducerListener(); virtual void onBufferReleased() {} virtual bool needsReleaseNotify() { return false; } }; } // namespace android #endif libs/gui/include/gui/ISurfaceComposer.h0100644 0000000 0000000 00000020000 13300556574 017113 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_GUI_ISURFACE_COMPOSER_H #define ANDROID_GUI_ISURFACE_COMPOSER_H #include #include #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- struct ComposerState; struct DisplayState; struct DisplayInfo; struct DisplayStatInfo; class HdrCapabilities; class IDisplayEventConnection; class IGraphicBufferProducer; class ISurfaceComposerClient; class Rect; enum class FrameEvent; /* * This class defines the Binder IPC interface for accessing various * SurfaceFlinger features. */ class ISurfaceComposer: public IInterface { public: DECLARE_META_INTERFACE(SurfaceComposer) // flags for setTransactionState() enum { eSynchronous = 0x01, eAnimation = 0x02, }; enum { eDisplayIdMain = 0, eDisplayIdHdmi = 1 }; enum Rotation { eRotateNone = 0, eRotate90 = 1, eRotate180 = 2, eRotate270 = 3 }; enum VsyncSource { eVsyncSourceApp = 0, eVsyncSourceSurfaceFlinger = 1 }; /* create connection with surface flinger, requires * ACCESS_SURFACE_FLINGER permission */ virtual sp createConnection() = 0; /** create a scoped connection with surface flinger. * Surfaces produced with this connection will act * as children of the passed in GBP. That is to say * SurfaceFlinger will draw them relative and confined to * drawing of buffers from the layer associated with parent. * As this is graphically equivalent in reach to just drawing * pixels into the parent buffers, it requires no special permission. */ virtual sp createScopedConnection( const sp& parent) = 0; /* return an IDisplayEventConnection */ virtual sp createDisplayEventConnection( VsyncSource vsyncSource = eVsyncSourceApp) = 0; /* create a virtual display * requires ACCESS_SURFACE_FLINGER permission. */ virtual sp createDisplay(const String8& displayName, bool secure) = 0; /* destroy a virtual display * requires ACCESS_SURFACE_FLINGER permission. */ virtual void destroyDisplay(const sp& display) = 0; /* get the token for the existing default displays. possible values * for id are eDisplayIdMain and eDisplayIdHdmi. */ virtual sp getBuiltInDisplay(int32_t id) = 0; /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ virtual void setTransactionState(const Vector& state, const Vector& displays, uint32_t flags) = 0; /* signal that we're done booting. * Requires ACCESS_SURFACE_FLINGER permission */ virtual void bootFinished() = 0; /* verify that an IGraphicBufferProducer was created by SurfaceFlinger. */ virtual bool authenticateSurfaceTexture( const sp& surface) const = 0; /* Returns the frame timestamps supported by SurfaceFlinger. */ virtual status_t getSupportedFrameTimestamps( std::vector* outSupported) const = 0; /* set display power mode. depending on the mode, it can either trigger * screen on, off or low power mode and wait for it to complete. * requires ACCESS_SURFACE_FLINGER permission. */ virtual void setPowerMode(const sp& display, int mode) = 0; /* returns information for each configuration of the given display * intended to be used to get information about built-in displays */ virtual status_t getDisplayConfigs(const sp& display, Vector* configs) = 0; /* returns display statistics for a given display * intended to be used by the media framework to properly schedule * video frames */ virtual status_t getDisplayStats(const sp& display, DisplayStatInfo* stats) = 0; /* indicates which of the configurations returned by getDisplayInfo is * currently active */ virtual int getActiveConfig(const sp& display) = 0; /* specifies which configuration (of those returned by getDisplayInfo) * should be used */ virtual status_t setActiveConfig(const sp& display, int id) = 0; virtual status_t getDisplayColorModes(const sp& display, Vector* outColorModes) = 0; virtual android_color_mode_t getActiveColorMode(const sp& display) = 0; virtual status_t setActiveColorMode(const sp& display, android_color_mode_t colorMode) = 0; /* Capture the specified screen. requires READ_FRAME_BUFFER permission * This function will fail if there is a secure window on screen. */ virtual status_t captureScreen(const sp& display, const sp& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, Rotation rotation = eRotateNone) = 0; /* Clears the frame statistics for animations. * * Requires the ACCESS_SURFACE_FLINGER permission. */ virtual status_t clearAnimationFrameStats() = 0; /* Gets the frame statistics for animations. * * Requires the ACCESS_SURFACE_FLINGER permission. */ virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0; /* Gets the supported HDR capabilities of the given display. * * Requires the ACCESS_SURFACE_FLINGER permission. */ virtual status_t getHdrCapabilities(const sp& display, HdrCapabilities* outCapabilities) const = 0; virtual status_t enableVSyncInjections(bool enable) = 0; virtual status_t injectVSync(nsecs_t when) = 0; }; // ---------------------------------------------------------------------------- class BnSurfaceComposer: public BnInterface { public: enum { // Note: BOOT_FINISHED must remain this value, it is called from // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC CREATE_DISPLAY_EVENT_CONNECTION, CREATE_DISPLAY, DESTROY_DISPLAY, GET_BUILT_IN_DISPLAY, SET_TRANSACTION_STATE, AUTHENTICATE_SURFACE, GET_SUPPORTED_FRAME_TIMESTAMPS, GET_DISPLAY_CONFIGS, GET_ACTIVE_CONFIG, SET_ACTIVE_CONFIG, CONNECT_DISPLAY, CAPTURE_SCREEN, CLEAR_ANIMATION_FRAME_STATS, GET_ANIMATION_FRAME_STATS, SET_POWER_MODE, GET_DISPLAY_STATS, GET_HDR_CAPABILITIES, GET_DISPLAY_COLOR_MODES, GET_ACTIVE_COLOR_MODE, SET_ACTIVE_COLOR_MODE, ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, CREATE_SCOPED_CONNECTION }; virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_ISURFACE_COMPOSER_H libs/gui/include/gui/ISurfaceComposerClient.h0100644 0000000 0000000 00000005030 13300556574 020260 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. */ #pragma once #include #include #include namespace android { class FrameStats; class IGraphicBufferProducer; class ISurfaceComposerClient : public IInterface { public: DECLARE_META_INTERFACE(SurfaceComposerClient) // flags for createSurface() enum { // (keep in sync with Surface.java) eHidden = 0x00000004, eDestroyBackbuffer = 0x00000020, eSecure = 0x00000080, eNonPremultiplied = 0x00000100, eOpaque = 0x00000400, eProtectedByApp = 0x00000800, eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, eFXSurfaceNormal = 0x00000000, eFXSurfaceDim = 0x00020000, eFXSurfaceMask = 0x000F0000, }; /* * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp& parent, uint32_t windowType, uint32_t ownerUid, sp* handle, sp* gbp) = 0; /* * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t destroySurface(const sp& handle) = 0; /* * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t clearLayerFrameStats(const sp& handle) const = 0; /* * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t getLayerFrameStats(const sp& handle, FrameStats* outStats) const = 0; }; class BnSurfaceComposerClient : public SafeBnInterface { public: BnSurfaceComposerClient() : SafeBnInterface("BnSurfaceComposerClient") {} status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; }; } // namespace android libs/gui/include/gui/OccupancyTracker.h0100644 0000000 0000000 00000005622 13300556574 017157 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_GUI_OCCUPANCYTRACKER_H #define ANDROID_GUI_OCCUPANCYTRACKER_H #include #include #include #include namespace android { class String8; class OccupancyTracker { public: OccupancyTracker() : mPendingSegment(), mSegmentHistory(), mLastOccupancy(0), mLastOccupancyChangeTime(0) {} struct Segment : public Parcelable { Segment() : totalTime(0), numFrames(0), occupancyAverage(0.0f), usedThirdBuffer(false) {} Segment(nsecs_t _totalTime, size_t _numFrames, float _occupancyAverage, bool _usedThirdBuffer) : totalTime(_totalTime), numFrames(_numFrames), occupancyAverage(_occupancyAverage), usedThirdBuffer(_usedThirdBuffer) {} // Parcelable interface virtual status_t writeToParcel(Parcel* parcel) const override; virtual status_t readFromParcel(const Parcel* parcel) override; nsecs_t totalTime; size_t numFrames; // Average occupancy of the queue over this segment. (0.0, 1.0) implies // double-buffered, (1.0, 2.0) implies triple-buffered. float occupancyAverage; // Whether a third buffer was used at all during this segment (since a // segment could read as double-buffered on average, but still require a // third buffer to avoid jank for some smaller portion) bool usedThirdBuffer; }; void registerOccupancyChange(size_t occupancy); std::vector getSegmentHistory(bool forceFlush); private: static constexpr size_t MAX_HISTORY_SIZE = 10; static constexpr nsecs_t NEW_SEGMENT_DELAY = ms2ns(100); static constexpr size_t LONG_SEGMENT_THRESHOLD = 3; struct PendingSegment { void clear() { totalTime = 0; numFrames = 0; mOccupancyTimes.clear(); } nsecs_t totalTime; size_t numFrames; std::unordered_map mOccupancyTimes; }; void recordPendingSegment(); PendingSegment mPendingSegment; std::deque mSegmentHistory; size_t mLastOccupancy; nsecs_t mLastOccupancyChangeTime; }; // class OccupancyTracker } // namespace android #endif libs/gui/include/gui/StreamSplitter.h0100644 0000000 0000000 00000016654 13300556574 016710 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. */ #ifndef ANDROID_GUI_STREAMSPLITTER_H #define ANDROID_GUI_STREAMSPLITTER_H #include #include #include #include #include #include namespace android { class GraphicBuffer; class IGraphicBufferConsumer; class IGraphicBufferProducer; // StreamSplitter is an autonomous class that manages one input BufferQueue // and multiple output BufferQueues. By using the buffer attach and detach logic // in BufferQueue, it is able to present the illusion of a single split // BufferQueue, where each buffer queued to the input is available to be // acquired by each of the outputs, and is able to be dequeued by the input // again only once all of the outputs have released it. class StreamSplitter : public BnConsumerListener { public: // createSplitter creates a new splitter, outSplitter, using inputQueue as // the input BufferQueue. Output BufferQueues must be added using addOutput // before queueing any buffers to the input. // // A return value other than NO_ERROR means that an error has occurred and // outSplitter has not been modified. BAD_VALUE is returned if inputQueue or // outSplitter is NULL. See IGraphicBufferConsumer::consumerConnect for // explanations of other error codes. static status_t createSplitter(const sp& inputQueue, sp* outSplitter); // addOutput adds an output BufferQueue to the splitter. The splitter // connects to outputQueue as a CPU producer, and any buffers queued // to the input will be queued to each output. It is assumed that all of the // outputs are added before any buffers are queued on the input. If any // output is abandoned by its consumer, the splitter will abandon its input // queue (see onAbandoned). // // A return value other than NO_ERROR means that an error has occurred and // outputQueue has not been added to the splitter. BAD_VALUE is returned if // outputQueue is NULL. See IGraphicBufferProducer::connect for explanations // of other error codes. status_t addOutput(const sp& outputQueue); // setName sets the consumer name of the input queue void setName(const String8& name); private: // From IConsumerListener // // During this callback, we store some tracking information, detach the // buffer from the input, and attach it to each of the outputs. This call // can block if there are too many outstanding buffers. If it blocks, it // will resume when onBufferReleasedByOutput releases a buffer back to the // input. virtual void onFrameAvailable(const BufferItem& item); // From IConsumerListener // We don't care about released buffers because we detach each buffer as // soon as we acquire it. See the comment for onBufferReleased below for // some clarifying notes about the name. virtual void onBuffersReleased() {} // From IConsumerListener // We don't care about sideband streams, since we won't be splitting them virtual void onSidebandStreamChanged() {} // This is the implementation of the onBufferReleased callback from // IProducerListener. It gets called from an OutputListener (see below), and // 'from' is which producer interface from which the callback was received. // // During this callback, we detach the buffer from the output queue that // generated the callback, update our state tracking to see if this is the // last output releasing the buffer, and if so, release it to the input. // If we release the buffer to the input, we allow a blocked // onFrameAvailable call to proceed. void onBufferReleasedByOutput(const sp& from); // When this is called, the splitter disconnects from (i.e., abandons) its // input queue and signals any waiting onFrameAvailable calls to wake up. // It still processes callbacks from other outputs, but only detaches their // buffers so they can continue operating until they run out of buffers to // acquire. This must be called with mMutex locked. void onAbandonedLocked(); // This is a thin wrapper class that lets us determine which BufferQueue // the IProducerListener::onBufferReleased callback is associated with. We // create one of these per output BufferQueue, and then pass the producer // into onBufferReleasedByOutput above. class OutputListener : public BnProducerListener, public IBinder::DeathRecipient { public: OutputListener(const sp& splitter, const sp& output); virtual ~OutputListener(); // From IProducerListener virtual void onBufferReleased(); // From IBinder::DeathRecipient virtual void binderDied(const wp& who); private: sp mSplitter; sp mOutput; }; class BufferTracker : public LightRefBase { public: BufferTracker(const sp& buffer); const sp& getBuffer() const { return mBuffer; } const sp& getMergedFence() const { return mMergedFence; } void mergeFence(const sp& with); // Returns the new value // Only called while mMutex is held size_t incrementReleaseCountLocked() { return ++mReleaseCount; } private: // Only destroy through LightRefBase friend LightRefBase; ~BufferTracker(); // Disallow copying BufferTracker(const BufferTracker& other); BufferTracker& operator=(const BufferTracker& other); sp mBuffer; // One instance that holds this native handle sp mMergedFence; size_t mReleaseCount; }; // Only called from createSplitter StreamSplitter(const sp& inputQueue); // Must be accessed through RefBase virtual ~StreamSplitter(); static const int MAX_OUTSTANDING_BUFFERS = 2; // mIsAbandoned is set to true when an output dies. Once the StreamSplitter // has been abandoned, it will continue to detach buffers from other // outputs, but it will disconnect from the input and not attempt to // communicate with it further. bool mIsAbandoned; Mutex mMutex; Condition mReleaseCondition; int mOutstandingBuffers; sp mInput; Vector > mOutputs; // Map of GraphicBuffer IDs (GraphicBuffer::getId()) to buffer tracking // objects (which are mostly for counting how many outputs have released the // buffer, but also contain merged release fences). KeyedVector > mBuffers; }; } // namespace android #endif libs/gui/include/gui/Surface.h0100644 0000000 0000000 00000045315 13300556574 015312 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_GUI_SURFACE_H #define ANDROID_GUI_SURFACE_H #include #include #include #include #include #include #include #include namespace android { class ISurfaceComposer; /* * An implementation of ANativeWindow that feeds graphics buffers into a * BufferQueue. * * This is typically used by programs that want to render frames through * some means (maybe OpenGL, a software renderer, or a hardware decoder) * and have the frames they create forwarded to SurfaceFlinger for * compositing. For example, a video decoder could render a frame and call * eglSwapBuffers(), which invokes ANativeWindow callbacks defined by * Surface. Surface then forwards the buffers through Binder IPC * to the BufferQueue's producer interface, providing the new frame to a * consumer such as GLConsumer. */ class Surface : public ANativeObjectBase { public: /* * creates a Surface from the given IGraphicBufferProducer (which concrete * implementation is a BufferQueue). * * Surface is mainly state-less while it's disconnected, it can be * viewed as a glorified IGraphicBufferProducer holder. It's therefore * safe to create other Surfaces from the same IGraphicBufferProducer. * * However, once a Surface is connected, it'll prevent other Surfaces * referring to the same IGraphicBufferProducer to become connected and * therefore prevent them to be used as actual producers of buffers. * * the controlledByApp flag indicates that this Surface (producer) is * controlled by the application. This flag is used at connect time. */ explicit Surface(const sp& bufferProducer, bool controlledByApp = false); /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this * Surface was created with. Usually it's an error to use the * IGraphicBufferProducer while the Surface is connected. */ sp getIGraphicBufferProducer() const; /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ static bool isValid(const sp& surface) { return surface != NULL && surface->getIGraphicBufferProducer() != NULL; } /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. * * A sideband stream is a device-specific mechanism for passing buffers * from the producer to the consumer without using dequeueBuffer/ * queueBuffer. If a sideband stream is present, the consumer can choose * whether to acquire buffers from the sideband stream or from the queued * buffers. * * Passing NULL or a different stream handle will detach the previous * handle if any. */ void setSidebandStream(const sp& stream); /* Allocates buffers based on the current dimensions/format. * * This function will allocate up to the maximum number of buffers * permitted by the current BufferQueue configuration. It will use the * default format and dimensions. This is most useful to avoid an allocation * delay during dequeueBuffer. If there are already the maximum number of * buffers allocated, this function has no effect. */ void allocateBuffers(); /* Sets the generation number on the IGraphicBufferProducer and updates the * generation number on any buffers attached to the Surface after this call. * See IGBP::setGenerationNumber for more information. */ status_t setGenerationNumber(uint32_t generationNumber); // See IGraphicBufferProducer::getConsumerName String8 getConsumerName() const; // See IGraphicBufferProducer::getNextFrameNumber uint64_t getNextFrameNumber() const; /* Set the scaling mode to be used with a Surface. * See NATIVE_WINDOW_SET_SCALING_MODE and its parameters * in . */ int setScalingMode(int mode); // See IGraphicBufferProducer::setDequeueTimeout status_t setDequeueTimeout(nsecs_t timeout); /* * Wait for frame number to increase past lastFrame for at most * timeoutNs. Useful for one thread to wait for another unknown * thread to queue a buffer. */ bool waitForNextFrame(uint64_t lastFrame, nsecs_t timeout); // See IGraphicBufferProducer::getLastQueuedBuffer // See GLConsumer::getTransformMatrix for outTransformMatrix format status_t getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]); status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration); /* Enables or disables frame timestamp tracking. It is disabled by default * to avoid overhead during queue and dequeue for applications that don't * need the feature. If disabled, calls to getFrameTimestamps will fail. */ void enableFrameTimestamps(bool enable); status_t getCompositorTiming( nsecs_t* compositeDeadline, nsecs_t* compositeInterval, nsecs_t* compositeToPresentLatency); // See IGraphicBufferProducer::getFrameTimestamps status_t getFrameTimestamps(uint64_t frameNumber, nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime, nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime); status_t getWideColorSupport(bool* supported); status_t getHdrSupport(bool* supported); status_t getUniqueId(uint64_t* outId) const; status_t getConsumerUsage(uint64_t* outUsage) const; // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call nsecs_t getLastDequeueStartTime() const; protected: virtual ~Surface(); // Virtual for testing. virtual sp composerService() const; virtual nsecs_t now() const; private: // can't be copied Surface& operator = (const Surface& rhs); Surface(const Surface& rhs); // ANativeWindow hooks static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd); static int hook_perform(ANativeWindow* window, int operation, ...); static int hook_query(const ANativeWindow* window, int what, int* value); static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd); static int hook_setSwapInterval(ANativeWindow* window, int interval); static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer); static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer); int dispatchConnect(va_list args); int dispatchDisconnect(va_list args); int dispatchSetBufferCount(va_list args); int dispatchSetBuffersGeometry(va_list args); int dispatchSetBuffersDimensions(va_list args); int dispatchSetBuffersUserDimensions(va_list args); int dispatchSetBuffersFormat(va_list args); int dispatchSetScalingMode(va_list args); int dispatchSetBuffersTransform(va_list args); int dispatchSetBuffersStickyTransform(va_list args); int dispatchSetBuffersTimestamp(va_list args); int dispatchSetCrop(va_list args); int dispatchSetUsage(va_list args); int dispatchSetUsage64(va_list args); int dispatchLock(va_list args); int dispatchUnlockAndPost(va_list args); int dispatchSetSidebandStream(va_list args); int dispatchSetBuffersDataSpace(va_list args); int dispatchSetSurfaceDamage(va_list args); int dispatchSetSharedBufferMode(va_list args); int dispatchSetAutoRefresh(va_list args); int dispatchGetDisplayRefreshCycleDuration(va_list args); int dispatchGetNextFrameId(va_list args); int dispatchEnableFrameTimestamps(va_list args); int dispatchGetCompositorTiming(va_list args); int dispatchGetFrameTimestamps(va_list args); int dispatchGetWideColorSupport(va_list args); int dispatchGetHdrSupport(va_list args); int dispatchGetConsumerUsage64(va_list args); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); virtual int perform(int operation, va_list args); virtual int setSwapInterval(int interval); virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer); virtual int connect(int api); virtual int setBufferCount(int bufferCount); virtual int setBuffersUserDimensions(uint32_t width, uint32_t height); virtual int setBuffersFormat(PixelFormat format); virtual int setBuffersTransform(uint32_t transform); virtual int setBuffersStickyTransform(uint32_t transform); virtual int setBuffersTimestamp(int64_t timestamp); virtual int setBuffersDataSpace(android_dataspace dataSpace); virtual int setCrop(Rect const* rect); virtual int setUsage(uint64_t reqUsage); virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects); public: virtual int disconnect(int api, IGraphicBufferProducer::DisconnectMode mode = IGraphicBufferProducer::DisconnectMode::Api); virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers); virtual int setAsyncMode(bool async); virtual int setSharedBufferMode(bool sharedBufferMode); virtual int setAutoRefresh(bool autoRefresh); virtual int setBuffersDimensions(uint32_t width, uint32_t height); virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); virtual int unlockAndPost(); virtual int query(int what, int* value) const; virtual int connect(int api, const sp& listener); // When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch // GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or // attachBuffer call. This allows clients with their own buffer caches to free up buffers no // longer in use by this surface. virtual int connect( int api, const sp& listener, bool reportBufferRemoval); virtual int detachNextBuffer(sp* outBuffer, sp* outFence); virtual int attachBuffer(ANativeWindowBuffer*); // When client connects to Surface with reportBufferRemoval set to true, any buffers removed // from this Surface will be collected and returned here. Once this method returns, these // buffers will no longer be referenced by this Surface unless they are attached to this // Surface later. The list of removed buffers will only be stored until the next dequeueBuffer, // detachNextBuffer, or attachBuffer call. status_t getAndFlushRemovedBuffers(std::vector>* out); protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; void querySupportedTimestampsLocked() const; void freeAllBuffers(); int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; struct BufferSlot { sp buffer; Region dirtyRegion; }; // mSurfaceTexture is the interface to the surface texture server. All // operations on the surface texture client ultimately translate into // interactions with the server using this interface. // TODO: rename to mBufferProducer sp mGraphicBufferProducer; // mSlots stores the buffers that have been allocated for each buffer slot. // It is initialized to null pointers, and gets filled in with the result of // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. BufferSlot mSlots[NUM_BUFFER_SLOTS]; // mReqWidth is the buffer width that will be requested at the next dequeue // operation. It is initialized to 1. uint32_t mReqWidth; // mReqHeight is the buffer height that will be requested at the next // dequeue operation. It is initialized to 1. uint32_t mReqHeight; // mReqFormat is the buffer pixel format that will be requested at the next // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888. PixelFormat mReqFormat; // mReqUsage is the set of buffer usage flags that will be requested // at the next deuque operation. It is initialized to 0. uint64_t mReqUsage; // mTimestamp is the timestamp that will be used for the next buffer queue // operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that // a timestamp is auto-generated when queueBuffer is called. int64_t mTimestamp; // mDataSpace is the buffer dataSpace that will be used for the next buffer // queue operation. It defaults to HAL_DATASPACE_UNKNOWN, which // means that the buffer contains some type of color data. android_dataspace mDataSpace; // mCrop is the crop rectangle that will be used for the next buffer // that gets queued. It is set by calling setCrop. Rect mCrop; // mScalingMode is the scaling mode that will be used for the next // buffers that get queued. It is set by calling setScalingMode. int mScalingMode; // mTransform is the transform identifier that will be used for the next // buffer that gets queued. It is set by calling setTransform. uint32_t mTransform; // mStickyTransform is a transform that is applied on top of mTransform // in each buffer that is queued. This is typically used to force the // compositor to apply a transform, and will prevent the transform hint // from being set by the compositor. uint32_t mStickyTransform; // mDefaultWidth is default width of the buffers, regardless of the // native_window_set_buffers_dimensions call. uint32_t mDefaultWidth; // mDefaultHeight is default height of the buffers, regardless of the // native_window_set_buffers_dimensions call. uint32_t mDefaultHeight; // mUserWidth, if non-zero, is an application-specified override // of mDefaultWidth. This is lower priority than the width set by // native_window_set_buffers_dimensions. uint32_t mUserWidth; // mUserHeight, if non-zero, is an application-specified override // of mDefaultHeight. This is lower priority than the height set // by native_window_set_buffers_dimensions. uint32_t mUserHeight; // mTransformHint is the transform probably applied to buffers of this // window. this is only a hint, actual transform may differ. uint32_t mTransformHint; // mProducerControlledByApp whether this buffer producer is controlled // by the application bool mProducerControlledByApp; // mSwapIntervalZero set if we should drop buffers at queue() time to // achieve an asynchronous swap interval bool mSwapIntervalZero; // mConsumerRunningBehind whether the consumer is running more than // one buffer behind the producer. mutable bool mConsumerRunningBehind; // mMutex is the mutex used to prevent concurrent access to the member // variables of Surface objects. It must be locked whenever the // member variables are accessed. mutable Mutex mMutex; // must be used from the lock/unlock thread sp mLockedBuffer; sp mPostedBuffer; bool mConnectedToCpu; // When a CPU producer is attached, this reflects the region that the // producer wished to update as well as whether the Surface was able to copy // the previous buffer back to allow a partial update. // // When a non-CPU producer is attached, this reflects the surface damage // (the change since the previous frame) passed in by the producer. Region mDirtyRegion; // mBufferAge tracks the age of the contents of the most recently dequeued // buffer as the number of frames that have elapsed since it was last queued uint64_t mBufferAge; // Stores the current generation number. See setGenerationNumber and // IGraphicBufferProducer::setGenerationNumber for more information. uint32_t mGenerationNumber; // Caches the values that have been passed to the producer. bool mSharedBufferMode; bool mAutoRefresh; // If in shared buffer mode and auto refresh is enabled, store the shared // buffer slot and return it for all calls to queue/dequeue without going // over Binder. int mSharedBufferSlot; // This is true if the shared buffer has already been queued/canceled. It's // used to prevent a mismatch between the number of queue/dequeue calls. bool mSharedBufferHasBeenQueued; // These are used to satisfy the NATIVE_WINDOW_LAST_*_DURATION queries nsecs_t mLastDequeueDuration = 0; nsecs_t mLastQueueDuration = 0; // Stores the time right before we call IGBP::dequeueBuffer nsecs_t mLastDequeueStartTime = 0; Condition mQueueBufferCondition; uint64_t mNextFrameNumber = 1; uint64_t mLastFrameNumber = 0; // Mutable because ANativeWindow::query needs this class const. mutable bool mQueriedSupportedTimestamps; mutable bool mFrameTimestampsSupportsPresent; // A cached copy of the FrameEventHistory maintained by the consumer. bool mEnableFrameTimestamps = false; std::unique_ptr mFrameEventHistory; bool mReportRemovedBuffers = false; std::vector> mRemovedBuffers; }; } // namespace android #endif // ANDROID_GUI_SURFACE_H libs/gui/include/gui/SurfaceComposerClient.h0100644 0000000 0000000 00000025560 13300556574 020161 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_GUI_SURFACE_COMPOSER_CLIENT_H #define ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H #include #include #include #include #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- struct DisplayInfo; class Composer; class HdrCapabilities; class ISurfaceComposerClient; class IGraphicBufferProducer; class Region; // --------------------------------------------------------------------------- class SurfaceComposerClient : public RefBase { friend class Composer; public: SurfaceComposerClient(); SurfaceComposerClient(const sp& parent); virtual ~SurfaceComposerClient(); // Always make sure we could initialize status_t initCheck() const; // Return the connection of this client sp connection() const; // Forcibly remove connection before all references have gone away. void dispose(); // callback when the composer is dies status_t linkToComposerDeath(const sp& recipient, void* cookie = NULL, uint32_t flags = 0); // Get a list of supported configurations for a given display static status_t getDisplayConfigs(const sp& display, Vector* configs); // Get the DisplayInfo for the currently-active configuration static status_t getDisplayInfo(const sp& display, DisplayInfo* info); // Get the index of the current active configuration (relative to the list // returned by getDisplayInfo) static int getActiveConfig(const sp& display); // Set a new active configuration using an index relative to the list // returned by getDisplayInfo static status_t setActiveConfig(const sp& display, int id); // Gets the list of supported color modes for the given display static status_t getDisplayColorModes(const sp& display, Vector* outColorModes); // Gets the active color mode for the given display static android_color_mode_t getActiveColorMode(const sp& display); // Sets the active color mode for the given display static status_t setActiveColorMode(const sp& display, android_color_mode_t colorMode); /* Triggers screen on/off or low power mode and waits for it to complete */ static void setDisplayPowerMode(const sp& display, int mode); // ------------------------------------------------------------------------ // surface creation / destruction //! Create a surface sp createSurface( const String8& name,// name of the surface uint32_t w, // width in pixel uint32_t h, // height in pixel PixelFormat format, // pixel-format desired uint32_t flags = 0, // usage flags SurfaceControl* parent = nullptr, // parent uint32_t windowType = 0, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.) uint32_t ownerUid = 0 // UID of the task ); //! Create a virtual display static sp createDisplay(const String8& displayName, bool secure); //! Destroy a virtual display static void destroyDisplay(const sp& display); //! Get the token for the existing default displays. //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi. static sp getBuiltInDisplay(int32_t id); // ------------------------------------------------------------------------ // Composer parameters // All composer parameters must be changed within a transaction // several surfaces can be updated in one transaction, all changes are // committed at once when the transaction is closed. // closeGlobalTransaction() requires an IPC with the server. //! Open a composer transaction on all active SurfaceComposerClients. static void openGlobalTransaction(); //! Close a composer transaction on all active SurfaceComposerClients. static void closeGlobalTransaction(bool synchronous = false); static status_t enableVSyncInjections(bool enable); static status_t injectVSync(nsecs_t when); //! Flag the currently open transaction as an animation transaction. static void setAnimationTransaction(); status_t hide(const sp& id); status_t show(const sp& id); status_t setFlags(const sp& id, uint32_t flags, uint32_t mask); status_t setTransparentRegionHint(const sp& id, const Region& transparent); status_t setLayer(const sp& id, int32_t layer); status_t setRelativeLayer(const sp& id, const sp& relativeTo, int32_t layer); status_t setAlpha(const sp& id, float alpha=1.0f); status_t setMatrix(const sp& id, float dsdx, float dtdx, float dtdy, float dsdy); status_t setPosition(const sp& id, float x, float y); status_t setSize(const sp& id, uint32_t w, uint32_t h); status_t setCrop(const sp& id, const Rect& crop); status_t setFinalCrop(const sp& id, const Rect& crop); status_t setLayerStack(const sp& id, uint32_t layerStack); status_t deferTransactionUntil(const sp& id, const sp& handle, uint64_t frameNumber); status_t deferTransactionUntil(const sp& id, const sp& handle, uint64_t frameNumber); status_t reparentChildren(const sp& id, const sp& newParentHandle); status_t detachChildren(const sp& id); status_t setOverrideScalingMode(const sp& id, int32_t overrideScalingMode); status_t setGeometryAppliesWithResize(const sp& id); status_t destroySurface(const sp& id); status_t clearLayerFrameStats(const sp& token) const; status_t getLayerFrameStats(const sp& token, FrameStats* outStats) const; static status_t clearAnimationFrameStats(); static status_t getAnimationFrameStats(FrameStats* outStats); static status_t getHdrCapabilities(const sp& display, HdrCapabilities* outCapabilities); static status_t setDisplaySurface(const sp& token, sp bufferProducer); static void setDisplayLayerStack(const sp& token, uint32_t layerStack); static void setDisplaySize(const sp& token, uint32_t width, uint32_t height); /* setDisplayProjection() defines the projection of layer stacks * to a given display. * * - orientation defines the display's orientation. * - layerStackRect defines which area of the window manager coordinate * space will be used. * - displayRect defines where on the display will layerStackRect be * mapped to. displayRect is specified post-orientation, that is * it uses the orientation seen by the end-user. */ static void setDisplayProjection(const sp& token, uint32_t orientation, const Rect& layerStackRect, const Rect& displayRect); private: virtual void onFirstRef(); Composer& getComposer(); mutable Mutex mLock; status_t mStatus; sp mClient; Composer& mComposer; wp mParent; }; // --------------------------------------------------------------------------- class ScreenshotClient { public: // if cropping isn't required, callers may pass in a default Rect, e.g.: // capture(display, producer, Rect(), reqWidth, ...); static status_t capture( const sp& display, const sp& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform); static status_t captureToBuffer( const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, uint32_t rotation, sp* outbuffer); private: mutable sp mCpuConsumer; mutable sp mProducer; CpuConsumer::LockedBuffer mBuffer; bool mHaveBuffer; public: ScreenshotClient(); ~ScreenshotClient(); // frees the previous screenshot and captures a new one // if cropping isn't required, callers may pass in a default Rect, e.g.: // update(display, Rect(), useIdentityTransform); status_t update(const sp& display, Rect sourceCrop, bool useIdentityTransform); status_t update(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform); status_t update(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform); status_t update(const sp& display, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, uint32_t rotation); sp getCpuConsumer() const; // release memory occupied by the screenshot void release(); // pixels are valid until this object is freed or // release() or update() is called void const* getPixels() const; uint32_t getWidth() const; uint32_t getHeight() const; PixelFormat getFormat() const; uint32_t getStride() const; // size of allocated memory in bytes size_t getSize() const; android_dataspace getDataSpace() const; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H libs/gui/include/gui/SurfaceControl.h0100644 0000000 0000000 00000015113 13300556574 016644 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_GUI_SURFACE_CONTROL_H #define ANDROID_GUI_SURFACE_CONTROL_H #include #include #include #include #include #include #include #include #include namespace android { // --------------------------------------------------------------------------- class IGraphicBufferProducer; class Surface; class SurfaceComposerClient; // --------------------------------------------------------------------------- class SurfaceControl : public RefBase { public: static bool isValid(const sp& surface) { return (surface != 0) && surface->isValid(); } bool isValid() { return mHandle!=0 && mClient!=0; } static bool isSameSurface( const sp& lhs, const sp& rhs); // release surface data from java void clear(); // disconnect any api that's connected void disconnect(); status_t setLayerStack(uint32_t layerStack); status_t setLayer(int32_t layer); // Sets a Z order relative to the Surface specified by "relativeTo" but // without becoming a full child of the relative. Z-ordering works exactly // as if it were a child however. // // As a nod to sanity, only non-child surfaces may have a relative Z-order. // // This overrides any previous and is overriden by any future calls // to setLayer. // // If the relative dissapears, the Surface will have no layer and be // invisible, until the next time set(Relative)Layer is called. // // TODO: This is probably a hack. Currently it exists only to work around // some framework usage of the hidden APPLICATION_MEDIA_OVERLAY window type // which allows inserting a window between a SurfaceView and it's main application // window. However, since we are using child windows for the SurfaceView, but not using // child windows elsewhere in O, the WindowManager can't set the layer appropriately. // This is only used by the "TvInputService" and following the port of ViewRootImpl // to child surfaces, we can then port this and remove this method. status_t setRelativeLayer(const sp& relativeTo, int32_t layer); status_t setPosition(float x, float y); status_t setSize(uint32_t w, uint32_t h); status_t hide(); status_t show(); status_t setFlags(uint32_t flags, uint32_t mask); status_t setTransparentRegionHint(const Region& transparent); status_t setAlpha(float alpha=1.0f); status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy); status_t setCrop(const Rect& crop); status_t setFinalCrop(const Rect& crop); // If the size changes in this transaction, all geometry updates specified // in this transaction will not complete until a buffer of the new size // arrives. As some elements normally apply immediately, this enables // freezing the total geometry of a surface until a resize is completed. status_t setGeometryAppliesWithResize(); // Defers applying any changes made in this transaction until the Layer // identified by handle reaches the given frameNumber. If the Layer identified // by handle is removed, then we will apply this transaction regardless of // what frame number has been reached. status_t deferTransactionUntil(const sp& handle, uint64_t frameNumber); // A variant of deferTransactionUntil which identifies the Layer we wait for by // Surface instead of Handle. Useful for clients which may not have the // SurfaceControl for some of their Surfaces. Otherwise behaves identically. status_t deferTransactionUntil(const sp& barrier, uint64_t frameNumber); // Reparents all children of this layer to the new parent handle. status_t reparentChildren(const sp& newParentHandle); // Detaches all child surfaces (and their children recursively) // from their SurfaceControl. // The child SurfaceControl's will not throw exceptions or return errors, // but transactions will have no effect. // The child surfaces will continue to follow their parent surfaces, // and remain eligible for rendering, but their relative state will be // frozen. We use this in the WindowManager, in app shutdown/relaunch // scenarios, where the app would otherwise clean up its child Surfaces. // Sometimes the WindowManager needs to extend their lifetime slightly // in order to perform an exit animation or prevent flicker. status_t detachChildren(); // Set an override scaling mode as documented in // the override scaling mode will take precedence over any client // specified scaling mode. -1 will clear the override scaling mode. status_t setOverrideScalingMode(int32_t overrideScalingMode); static status_t writeSurfaceToParcel( const sp& control, Parcel* parcel); sp getSurface() const; sp createSurface() const; sp getHandle() const; status_t clearLayerFrameStats() const; status_t getLayerFrameStats(FrameStats* outStats) const; private: // can't be copied SurfaceControl& operator = (SurfaceControl& rhs); SurfaceControl(const SurfaceControl& rhs); friend class SurfaceComposerClient; friend class Surface; SurfaceControl( const sp& client, const sp& handle, const sp& gbp); ~SurfaceControl(); sp generateSurfaceLocked() const; status_t validate() const; void destroy(); sp mClient; sp mHandle; sp mGraphicBufferProducer; mutable Mutex mLock; mutable sp mSurfaceData; }; }; // namespace android #endif // ANDROID_GUI_SURFACE_CONTROL_H libs/gui/include/gui/bufferqueue/0040755 0000000 0000000 00000000000 13300556574 016062 5ustar000000000 0000000 libs/gui/include/gui/bufferqueue/1.0/0040755 0000000 0000000 00000000000 13300556574 016360 5ustar000000000 0000000 libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h0100644 0000000 0000000 00000003767 13300556574 022330 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H #define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H #include #include #include #include #include #include namespace android { namespace hardware { namespace graphics { namespace bufferqueue { namespace V1_0 { namespace utils { using ::android::hidl::base::V1_0::IBase; using ::android::hardware::hidl_array; using ::android::hardware::hidl_memory; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::sp; typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener HProducerListener; typedef ::android::IProducerListener BProducerListener; struct B2HProducerListener : public HProducerListener { sp mBase; B2HProducerListener(sp const& base); Return onBufferReleased() override; Return needsReleaseNotify() override; }; } // namespace utils } // namespace V1_0 } // namespace omx } // namespace media } // namespace hardware } // namespace android #endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_B2HPRODUCERLISTENER_H libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h0100644 0000000 0000000 00000010573 13300556574 023243 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_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H #define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H #include #include #include #include #include #include #include namespace android { namespace hardware { namespace graphics { namespace bufferqueue { namespace V1_0 { namespace utils { using ::android::hidl::base::V1_0::IBase; using ::android::hardware::hidl_array; using ::android::hardware::hidl_memory; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::sp; using ::android::hardware::graphics::common::V1_0::PixelFormat; using ::android::hardware::media::V1_0::AnwBuffer; typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer HGraphicBufferProducer; typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener HProducerListener; typedef ::android::IGraphicBufferProducer BGraphicBufferProducer; using ::android::BnGraphicBufferProducer; using ::android::IProducerListener; struct H2BGraphicBufferProducer : public ::android::H2BConverter< HGraphicBufferProducer, BGraphicBufferProducer, BnGraphicBufferProducer> { H2BGraphicBufferProducer(sp const& base) : CBase(base) {} status_t requestBuffer(int slot, sp* buf) override; status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override; status_t setAsyncMode(bool async) override; status_t dequeueBuffer(int* slot, sp* fence, uint32_t w, uint32_t h, ::android::PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override; status_t detachBuffer(int slot) override; status_t detachNextBuffer(sp* outBuffer, sp* outFence) override; status_t attachBuffer(int* outSlot, const sp& buffer) override; status_t queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) override; status_t cancelBuffer(int slot, const sp& fence) override; int query(int what, int* value) override; status_t connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override; status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override; status_t setSidebandStream(const sp& stream) override; void allocateBuffers(uint32_t width, uint32_t height, ::android::PixelFormat format, uint64_t usage) override; status_t allowAllocation(bool allow) override; status_t setGenerationNumber(uint32_t generationNumber) override; String8 getConsumerName() const override; status_t setSharedBufferMode(bool sharedBufferMode) override; status_t setAutoRefresh(bool autoRefresh) override; status_t setDequeueTimeout(nsecs_t timeout) override; status_t getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) override; void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override; status_t getUniqueId(uint64_t* outId) const override; status_t getConsumerUsage(uint64_t* outUsage) const override; }; } // namespace utils } // namespace V1_0 } // namespace bufferqueue } // namespace graphics } // namespace hardware } // namespace android #endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BGRAPHICBUFFERPRODUCER_H libs/gui/include/gui/view/0040755 0000000 0000000 00000000000 13300556574 014516 5ustar000000000 0000000 libs/gui/include/gui/view/Surface.h0100644 0000000 0000000 00000004166 13300556574 016263 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_GUI_VIEW_SURFACE_H #define ANDROID_GUI_VIEW_SURFACE_H #include #include #include #include namespace android { class IGraphicBufferProducer; namespace view { /** * A simple holder for an IGraphicBufferProducer, to match the managed-side * android.view.Surface parcelable behavior. * * This implements android/view/Surface.aidl * * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly * used in managed Binder calls. */ class Surface : public Parcelable { public: String16 name; sp graphicBufferProducer; virtual status_t writeToParcel(Parcel* parcel) const override; virtual status_t readFromParcel(const Parcel* parcel) override; // nameAlreadyWritten set to true by Surface.java, because it splits // Parceling itself between managed and native code, so it only wants a part // of the full parceling to happen on its native side. status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const; // nameAlreadyRead set to true by Surface.java, because it splits // Parceling itself between managed and native code, so it only wants a part // of the full parceling to happen on its native side. status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead); private: static String16 readMaybeEmptyString16(const Parcel* parcel); }; } // namespace view } // namespace android #endif // ANDROID_GUI_VIEW_SURFACE_H libs/gui/include/private/0040755 0000000 0000000 00000000000 13300556574 014432 5ustar000000000 0000000 libs/gui/include/private/gui/0040755 0000000 0000000 00000000000 13300556574 015216 5ustar000000000 0000000 libs/gui/include/private/gui/BitTube.h0100644 0000000 0000000 00000006100 13300556574 016717 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. */ #pragma once #include #include #include namespace android { class Parcel; namespace gui { class BitTube : public Parcelable { public: // creates an uninitialized BitTube (to unparcel into) BitTube() = default; // creates a BitTube with a a specified send and receive buffer size explicit BitTube(size_t bufsize); // creates a BitTube with a default (4KB) send buffer struct DefaultSizeType {}; static constexpr DefaultSizeType DefaultSize{}; explicit BitTube(DefaultSizeType); explicit BitTube(const Parcel& data); virtual ~BitTube() = default; // check state after construction status_t initCheck() const; // get receive file-descriptor int getFd() const; // get the send file-descriptor. int getSendFd() const; // moves the receive file descriptor out of this BitTube base::unique_fd moveReceiveFd(); // resets this BitTube's receive file descriptor to receiveFd void setReceiveFd(base::unique_fd&& receiveFd); // send objects (sized blobs). All objects are guaranteed to be written or the call fails. template static ssize_t sendObjects(BitTube* 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(BitTube* tube, T* events, size_t count) { return recvObjects(tube, events, count, sizeof(T)); } // implement the Parcelable protocol. Only parcels the receive file descriptor status_t writeToParcel(Parcel* reply) const; status_t readFromParcel(const Parcel* parcel); 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); base::unique_fd mSendFd; mutable base::unique_fd mReceiveFd; static ssize_t sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize); static ssize_t recvObjects(BitTube* tube, void* events, size_t count, size_t objSize); }; } // namespace gui } // namespace android libs/gui/include/private/gui/ComposerService.h0100644 0000000 0000000 00000003663 13300556574 020504 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_PRIVATE_GUI_COMPOSER_SERVICE_H #define ANDROID_PRIVATE_GUI_COMPOSER_SERVICE_H #include #include #include #include namespace android { // --------------------------------------------------------------------------- class ISurfaceComposer; // --------------------------------------------------------------------------- // This holds our connection to the composer service (i.e. SurfaceFlinger). // If the remote side goes away, we will re-establish the connection. // Users of this class should not retain the value from // getComposerService() for an extended period. // // (It's not clear that using Singleton is useful here anymore.) class ComposerService : public Singleton { sp mComposerService; sp mDeathObserver; Mutex mLock; ComposerService(); void connectLocked(); void composerServiceDied(); friend class Singleton; public: // Get a connection to the Composer Service. This will block until // a connection is established. static sp getComposerService(); }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_PRIVATE_GUI_COMPOSER_SERVICE_H libs/gui/include/private/gui/LayerState.h0100644 0000000 0000000 00000010772 13300556574 017450 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_SF_LAYER_STATE_H #define ANDROID_SF_LAYER_STATE_H #include #include #include #include #include #include namespace android { class Parcel; class ISurfaceComposerClient; /* * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { enum { eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java eLayerOpaque = 0x02, // SURFACE_OPAQUE eLayerSecure = 0x80, // SECURE }; enum { ePositionChanged = 0x00000001, eLayerChanged = 0x00000002, eSizeChanged = 0x00000004, eAlphaChanged = 0x00000008, eMatrixChanged = 0x00000010, eTransparentRegionChanged = 0x00000020, eFlagsChanged = 0x00000040, eLayerStackChanged = 0x00000080, eCropChanged = 0x00000100, eDeferTransaction = 0x00000200, eFinalCropChanged = 0x00000400, eOverrideScalingModeChanged = 0x00000800, eGeometryAppliesWithResize = 0x00001000, eReparentChildren = 0x00002000, eDetachChildren = 0x00004000, eRelativeLayerChanged = 0x00008000 }; layer_state_t() : what(0), x(0), y(0), z(0), w(0), h(0), layerStack(0), alpha(0), flags(0), mask(0), reserved(0), crop(Rect::INVALID_RECT), finalCrop(Rect::INVALID_RECT), frameNumber(0), overrideScalingMode(-1) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; } status_t write(Parcel& output) const; status_t read(const Parcel& input); struct matrix22_t { float dsdx{0}; float dtdx{0}; float dtdy{0}; float dsdy{0}; }; sp surface; uint32_t what; float x; float y; int32_t z; uint32_t w; uint32_t h; uint32_t layerStack; float alpha; uint8_t flags; uint8_t mask; uint8_t reserved; matrix22_t matrix; Rect crop; Rect finalCrop; sp barrierHandle; sp reparentHandle; uint64_t frameNumber; int32_t overrideScalingMode; sp barrierGbp; sp relativeLayerHandle; // non POD must be last. see write/read Region transparentRegion; }; struct ComposerState { sp client; layer_state_t state; status_t write(Parcel& output) const; status_t read(const Parcel& input); }; struct DisplayState { enum { eOrientationDefault = 0, eOrientation90 = 1, eOrientation180 = 2, eOrientation270 = 3, eOrientationUnchanged = 4, eOrientationSwapMask = 0x01 }; enum { eSurfaceChanged = 0x01, eLayerStackChanged = 0x02, eDisplayProjectionChanged = 0x04, eDisplaySizeChanged = 0x08 }; DisplayState(); uint32_t what; sp token; sp surface; uint32_t layerStack; uint32_t orientation; Rect viewport; Rect frame; uint32_t width, height; status_t write(Parcel& output) const; status_t read(const Parcel& input); }; }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H libs/gui/include/private/gui/SyncFeatures.h0100644 0000000 0000000 00000002507 13300556574 020003 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_GUI_SYNC_FEATURES_H #define ANDROID_GUI_SYNC_FEATURES_H #include #include namespace android { // ---------------------------------------------------------------------------- class SyncFeatures : public Singleton { friend class Singleton; bool mHasNativeFenceSync; bool mHasFenceSync; bool mHasWaitSync; String8 mString; SyncFeatures(); public: bool useNativeFenceSync() const; bool useFenceSync() const; bool useWaitSync() const; String8 toString() const; }; // ---------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_GUI_SYNC_FEATURES_H libs/gui/tests/0040755 0000000 0000000 00000000000 13300556574 012477 5ustar000000000 0000000 libs/gui/tests/Android.bp0100644 0000000 0000000 00000002314 13300556574 014377 0ustar000000000 0000000 // Build the unit tests, // Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) // to integrate with auto-test framework. cc_test { name: "libgui_test", test_suites: ["device-tests"], clang: true, srcs: [ "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "FillBuffer.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", "Malicious.cpp", "MultiTextureConsumer_test.cpp", "StreamSplitter_test.cpp", "SurfaceTextureClient_test.cpp", "SurfaceTextureFBO_test.cpp", "SurfaceTextureGLThreadToGL_test.cpp", "SurfaceTextureGLToGL_test.cpp", "SurfaceTextureGL_test.cpp", "SurfaceTextureMultiContextGL_test.cpp", "Surface_test.cpp", "TextureRenderer.cpp", ], shared_libs: [ "android.hardware.configstore@1.0", "android.hardware.configstore-utils", "liblog", "libEGL", "libGLESv1_CM", "libGLESv2", "libbinder", "libcutils", "libgui", "libhidlbase", "libhidltransport", "libui", "libutils", "libnativewindow" ], } libs/gui/tests/AndroidTest.xml0100644 0000000 0000000 00000002302 13300556574 015433 0ustar000000000 0000000 libs/gui/tests/BufferItemConsumer_test.cpp0100644 0000000 0000000 00000015160 13300556574 020006 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 "BufferItemConsumer_test" //#define LOG_NDEBUG 0 #include #include #include #include namespace android { static constexpr int kWidth = 100; static constexpr int kHeight = 100; static constexpr int kMaxLockedBuffers = 3; static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; static constexpr int kFrameSleepUs = 30 * 1000; class BufferItemConsumerTest : public ::testing::Test { protected: struct BufferFreedListener : public BufferItemConsumer::BufferFreedListener { explicit BufferFreedListener(BufferItemConsumerTest* test) : mTest(test) {} void onBufferFreed(const wp& /* gBuffer */) override { mTest->HandleBufferFreed(); } BufferItemConsumerTest* mTest; }; void SetUp() override { BufferQueue::createBufferQueue(&mProducer, &mConsumer); mBIC = new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true); String8 name("BufferItemConsumer_Under_Test"); mBIC->setName(name); mBFL = new BufferFreedListener(this); mBIC->setBufferFreedListener(mBFL); sp producerListener = new DummyProducerListener(); IGraphicBufferProducer::QueueBufferOutput bufferOutput; ASSERT_EQ(NO_ERROR, mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU, true, &bufferOutput)); ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(kMaxLockedBuffers)); } int GetFreedBufferCount() { std::lock_guard lock(mMutex); return mFreedBufferCount; } void HandleBufferFreed() { std::lock_guard lock(mMutex); mFreedBufferCount++; ALOGV("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount); } void DequeueBuffer(int* outSlot) { ASSERT_NE(outSlot, nullptr); int slot; sp outFence; status_t ret = mProducer->dequeueBuffer(&slot, &outFence, kWidth, kHeight, 0, 0, nullptr, nullptr); ASSERT_GE(ret, 0); ALOGV("dequeueBuffer: slot=%d", slot); if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { ret = mProducer->requestBuffer(slot, &mBuffers[slot]); ASSERT_EQ(NO_ERROR, ret); } *outSlot = slot; } void QueueBuffer(int slot) { ALOGV("enqueueBuffer: slot=%d", slot); IGraphicBufferProducer::QueueBufferInput bufferInput( 0ULL, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput bufferOutput; status_t ret = mProducer->queueBuffer(slot, bufferInput, &bufferOutput); ASSERT_EQ(NO_ERROR, ret); } void AcquireBuffer(int* outSlot) { ASSERT_NE(outSlot, nullptr); BufferItem buffer; status_t ret = mBIC->acquireBuffer(&buffer, 0, false); ASSERT_EQ(NO_ERROR, ret); ALOGV("acquireBuffer: slot=%d", buffer.mSlot); *outSlot = buffer.mSlot; } void ReleaseBuffer(int slot) { ALOGV("releaseBuffer: slot=%d", slot); BufferItem buffer; buffer.mSlot = slot; buffer.mGraphicBuffer = mBuffers[slot]; status_t ret = mBIC->releaseBuffer(buffer, Fence::NO_FENCE); ASSERT_EQ(NO_ERROR, ret); } std::mutex mMutex; int mFreedBufferCount{0}; sp mBIC; sp mBFL; sp mProducer; sp mConsumer; sp mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; }; // Test that detaching buffer from consumer side triggers onBufferFreed. TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) { int slot; // Producer: generate a dummy buffer. DequeueBuffer(&slot); QueueBuffer(slot); ASSERT_EQ(0, GetFreedBufferCount()); // Consumer: acquire the buffer and then detach it. AcquireBuffer(&slot); status_t ret = mBIC->detachBuffer(slot); ASSERT_EQ(NO_ERROR, ret); // Sleep to give some time for callbacks to happen. usleep(kFrameSleepUs); ASSERT_EQ(1, GetFreedBufferCount()); } // Test that detaching buffer from producer side triggers onBufferFreed. TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromProducer) { int slot; // Let buffer go through the cycle at least once. DequeueBuffer(&slot); QueueBuffer(slot); AcquireBuffer(&slot); ReleaseBuffer(slot); ASSERT_EQ(0, GetFreedBufferCount()); // Producer: generate the buffer again. DequeueBuffer(&slot); // Producer: detach the buffer. status_t ret = mProducer->detachBuffer(slot); ASSERT_EQ(NO_ERROR, ret); // Sleep to give some time for callbacks to happen. usleep(kFrameSleepUs); ASSERT_EQ(1, GetFreedBufferCount()); } // Test that abandoning BufferItemConsumer triggers onBufferFreed. TEST_F(BufferItemConsumerTest, TriggerBufferFreed_AbandonBufferItemConsumer) { int slot; // Let buffer go through the cycle at least once. DequeueBuffer(&slot); QueueBuffer(slot); AcquireBuffer(&slot); ReleaseBuffer(slot); // Abandon the BufferItemConsumer. mBIC->abandon(); // Sleep to give some time for callbacks to happen. usleep(kFrameSleepUs); ASSERT_EQ(1, GetFreedBufferCount()); } // Test that delete BufferItemConsumer triggers onBufferFreed. TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DeleteBufferItemConsumer) { int slot; // Let buffer go through the cycle at least once. DequeueBuffer(&slot); QueueBuffer(slot); AcquireBuffer(&slot); ReleaseBuffer(slot); // Delete the BufferItemConsumer. mBIC.clear(); // Sleep to give some time for callbacks to happen. usleep(kFrameSleepUs); ASSERT_EQ(1, GetFreedBufferCount()); } } // namespace android libs/gui/tests/BufferQueue_test.cpp0100644 0000000 0000000 00000142757 13300556574 016475 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_test" //#define LOG_NDEBUG 0 #include "DummyConsumer.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; namespace android { class BufferQueueTest : public ::testing::Test { public: protected: BufferQueueTest() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); } ~BufferQueueTest() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); } void GetMinUndequeuedBufferCount(int* bufferCount) { ASSERT_TRUE(bufferCount != NULL); ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount)); ASSERT_GE(*bufferCount, 0); } void createBufferQueue() { BufferQueue::createBufferQueue(&mProducer, &mConsumer); } void testBufferItem(const IGraphicBufferProducer::QueueBufferInput& input, const BufferItem& item) { int64_t timestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop; int scalingMode; uint32_t transform; sp fence; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &fence, NULL); ASSERT_EQ(timestamp, item.mTimestamp); ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp); ASSERT_EQ(dataSpace, item.mDataSpace); ASSERT_EQ(crop, item.mCrop); ASSERT_EQ(static_cast(scalingMode), item.mScalingMode); ASSERT_EQ(transform, item.mTransform); ASSERT_EQ(fence, item.mFence); } sp mProducer; sp mConsumer; }; static const uint32_t TEST_DATA = 0x12345678u; // XXX: Tests that fork a process to hold the BufferQueue must run before tests // that use a local BufferQueue, or else Binder will get unhappy // // In one instance this was a crash in the createBufferQueue where the // binder call to create a buffer allocator apparently got garbage back. // See b/36592665. TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { const String16 PRODUCER_NAME = String16("BQTestProducer"); const String16 CONSUMER_NAME = String16("BQTestConsumer"); pid_t forkPid = fork(); ASSERT_NE(forkPid, -1); if (forkPid == 0) { // Child process sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp serviceManager = defaultServiceManager(); serviceManager->addService(PRODUCER_NAME, IInterface::asBinder(producer)); serviceManager->addService(CONSUMER_NAME, IInterface::asBinder(consumer)); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); LOG_ALWAYS_FATAL("Shouldn't be here"); } sp serviceManager = defaultServiceManager(); sp binderProducer = serviceManager->getService(PRODUCER_NAME); mProducer = interface_cast(binderProducer); EXPECT_TRUE(mProducer != NULL); sp binderConsumer = serviceManager->getService(CONSUMER_NAME); mConsumer = interface_cast(binderConsumer); EXPECT_TRUE(mConsumer != NULL); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast(&dataIn))); *dataIn = TEST_DATA; ASSERT_EQ(OK, buffer->unlock()); IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); uint32_t* dataOut; ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast(&dataOut))); ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); } TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { createBufferQueue(); sp dc(new DummyConsumer); mConsumer->consumerConnect(dc, false); IGraphicBufferProducer::QueueBufferOutput qbo; mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); mProducer->setMaxDequeuedBufferCount(3); int slot; sp fence; sp buf; IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); BufferItem item; for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); } ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); // Acquire the third buffer, which should fail. ASSERT_EQ(INVALID_OPERATION, mConsumer->acquireBuffer(&item, 0)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) { createBufferQueue(); sp dc(new DummyConsumer); mConsumer->consumerConnect(dc, false); EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(10)); IGraphicBufferProducer::QueueBufferOutput qbo; mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); mProducer->setMaxDequeuedBufferCount(3); int minBufferCount; ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount( minBufferCount - 1)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(0)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(-3)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount( BufferQueue::MAX_MAX_ACQUIRED_BUFFERS+1)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(100)); int slot; sp fence; sp buf; IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); BufferItem item; EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(3)); for (int i = 0; i < 3; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); } EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(2)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { createBufferQueue(); sp dc(new DummyConsumer); mConsumer->consumerConnect(dc, false); IGraphicBufferProducer::QueueBufferOutput qbo; mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); mProducer->setMaxDequeuedBufferCount(2); int minBufferCount; ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount)); EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(2)); EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(minBufferCount)); int slot; sp fence; sp buf; IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); BufferItem item; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(3)); for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); } EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount( BufferQueue::MAX_MAX_ACQUIRED_BUFFERS)); } TEST_F(BufferQueueTest, SetMaxBufferCountWithLegalValues_Succeeds) { createBufferQueue(); sp dc(new DummyConsumer); mConsumer->consumerConnect(dc, false); // Test shared buffer mode EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); } TEST_F(BufferQueueTest, SetMaxBufferCountWithIllegalValues_ReturnsError) { createBufferQueue(); sp dc(new DummyConsumer); mConsumer->consumerConnect(dc, false); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(0)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount( BufferQueue::NUM_BUFFER_SLOTS + 1)); EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(5)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(3)); } TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer( BufferQueueDefs::NUM_BUFFER_SLOTS)); // Index too high ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(0)); // Not dequeued int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not dequeued sp safeToClobberBuffer; // Can no longer request buffer from this slot ASSERT_EQ(BAD_VALUE, mProducer->requestBuffer(slot, &safeToClobberBuffer)); uint32_t* dataIn; ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast(&dataIn))); *dataIn = TEST_DATA; ASSERT_EQ(OK, buffer->unlock()); int newSlot; ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer)); ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL)); ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(newSlot, input, &output)); BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); uint32_t* dataOut; ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast(&dataOut))); ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); } TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(-1)); // Index too low ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer( BufferQueueDefs::NUM_BUFFER_SLOTS)); // Index too high ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(0)); // Not acquired BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); ASSERT_EQ(OK, mConsumer->detachBuffer(item.mSlot)); ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(item.mSlot)); // Not acquired uint32_t* dataIn; ASSERT_EQ(OK, item.mGraphicBuffer->lock( GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast(&dataIn))); *dataIn = TEST_DATA; ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); int newSlot; sp safeToClobberBuffer; ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer)); ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL)); ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataOut; ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast(&dataOut))); ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, buffer->unlock()); } TEST_F(BufferQueueTest, MoveFromConsumerToProducer) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast(&dataIn))); *dataIn = TEST_DATA; ASSERT_EQ(OK, buffer->unlock()); IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); ASSERT_EQ(OK, mConsumer->detachBuffer(item.mSlot)); int newSlot; ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, item.mGraphicBuffer)); ASSERT_EQ(OK, mProducer->queueBuffer(newSlot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); uint32_t* dataOut; ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast(&dataOut))); ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); } TEST_F(BufferQueueTest, TestDisallowingAllocation) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); static const uint32_t WIDTH = 320; static const uint32_t HEIGHT = 240; ASSERT_EQ(OK, mConsumer->setDefaultBufferSize(WIDTH, HEIGHT)); int slot; sp fence; sp buffer; // This should return an error since it would require an allocation ASSERT_EQ(OK, mProducer->allowAllocation(false)); ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); // This should succeed, now that we've lifted the prohibition ASSERT_EQ(OK, mProducer->allowAllocation(true)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); // Release the previous buffer back to the BufferQueue mProducer->cancelBuffer(slot, fence); // This should fail since we're requesting a different size ASSERT_EQ(OK, mProducer->allowAllocation(false)); ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); } TEST_F(BufferQueueTest, TestGenerationNumbers) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mProducer->setGenerationNumber(1)); // Get one buffer to play with int slot; sp fence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // Ensure that the generation number we set propagates to allocated buffers ASSERT_EQ(1U, buffer->getGenerationNumber()); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); ASSERT_EQ(OK, mProducer->setGenerationNumber(2)); // These should fail, since we've changed the generation number on the queue int outSlot; ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&outSlot, buffer)); ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&outSlot, buffer)); buffer->setGenerationNumber(2); // This should succeed now that we've changed the buffer's generation number ASSERT_EQ(OK, mProducer->attachBuffer(&outSlot, buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(outSlot)); // This should also succeed with the new generation number ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer)); } TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mProducer->setSharedBufferMode(true)); // Get a buffer int sharedSlot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Queue the buffer IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); // Repeatedly queue and dequeue a buffer from the producer side, it should // always return the same one. And we won't run out of buffers because it's // always the same one and because async mode gets enabled. int slot; for (int i = 0; i < 5; i++) { ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } // acquire the buffer BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(sharedSlot, item.mSlot); testBufferItem(input, item); ASSERT_EQ(true, item.mQueuedBuffer); ASSERT_EQ(false, item.mAutoRefresh); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // attempt to acquire a second time should return no buffer available ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, mConsumer->acquireBuffer(&item, 0)); } TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mProducer->setSharedBufferMode(true)); ASSERT_EQ(OK, mProducer->setAutoRefresh(true)); // Get a buffer int sharedSlot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Queue the buffer IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); // Repeatedly acquire and release a buffer from the consumer side, it should // always return the same one. BufferItem item; for (int i = 0; i < 5; i++) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(sharedSlot, item.mSlot); testBufferItem(input, item); ASSERT_EQ(i == 0, item.mQueuedBuffer); ASSERT_EQ(true, item.mAutoRefresh); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); } // Repeatedly queue and dequeue a buffer from the producer side, it should // always return the same one. int slot; for (int i = 0; i < 5; i++) { ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } // Repeatedly acquire and release a buffer from the consumer side, it should // always return the same one. First grabbing them from the queue and then // when the queue is empty, returning the shared buffer. for (int i = 0; i < 10; i++) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(sharedSlot, item.mSlot); ASSERT_EQ(0, item.mTimestamp); ASSERT_EQ(false, item.mIsAutoTimestamp); ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace); ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop); ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode); ASSERT_EQ(0u, item.mTransform); ASSERT_EQ(Fence::NO_FENCE, item.mFence); ASSERT_EQ(i == 0, item.mQueuedBuffer); ASSERT_EQ(true, item.mAutoRefresh); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); } } TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); // Dequeue a buffer int sharedSlot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&sharedSlot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(sharedSlot, &buffer)); // Enable shared buffer mode ASSERT_EQ(OK, mProducer->setSharedBufferMode(true)); // Queue the buffer IGraphicBufferProducer::QueueBufferInput input(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); // Repeatedly queue and dequeue a buffer from the producer side, it should // always return the same one. And we won't run out of buffers because it's // always the same one and because async mode gets enabled. int slot; for (int i = 0; i < 5; i++) { ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(sharedSlot, slot); ASSERT_EQ(OK, mProducer->queueBuffer(sharedSlot, input, &output)); } // acquire the buffer BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(sharedSlot, item.mSlot); testBufferItem(input, item); ASSERT_EQ(true, item.mQueuedBuffer); ASSERT_EQ(false, item.mAutoRefresh); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // attempt to acquire a second time should return no buffer available ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, mConsumer->acquireBuffer(&item, 0)); } TEST_F(BufferQueueTest, TestTimeouts) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); // Fill up the queue. Since the controlledByApp flags are set to true, this // queue should be in non-blocking mode, and we should be recycling the same // two buffers for (int i = 0; i < 5; ++i) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; auto result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr); if (i < 2) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); } else { ASSERT_EQ(OK, result); } sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput output{}; ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); } const auto TIMEOUT = ms2ns(250); mProducer->setDequeueTimeout(TIMEOUT); // Setting a timeout will change the BufferQueue into blocking mode (with // one droppable buffer in the queue and one free from the previous // dequeue/queues), so dequeue and queue two more buffers: one to replace // the current droppable buffer, and a second to max out the buffer count sp buffer; // Save a buffer to attach later for (int i = 0; i < 2; ++i) { int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); } int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; auto startTime = systemTime(); ASSERT_EQ(TIMED_OUT, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_GE(systemTime() - startTime, TIMEOUT); // We're technically attaching the same buffer multiple times (since we // queued it previously), but that doesn't matter for this test startTime = systemTime(); ASSERT_EQ(TIMED_OUT, mProducer->attachBuffer(&slot, buffer)); ASSERT_GE(systemTime() - startTime, TIMEOUT); } TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp sourceFence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &sourceFence, 0, 0, 0, 0, nullptr, nullptr)); sp buffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->detachBuffer(slot)); ASSERT_EQ(OK, mProducer->allowAllocation(false)); slot = BufferQueue::INVALID_BUFFER_SLOT; ASSERT_EQ(OK, mProducer->attachBuffer(&slot, buffer)); } TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); // Dequeue and queue the first buffer, storing the handle int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); sp firstBuffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer)); IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Dequeue a second buffer slot = BufferQueue::INVALID_BUFFER_SLOT; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); sp secondBuffer; ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer)); // Ensure it's a new buffer ASSERT_NE(firstBuffer->getNativeBuffer()->handle, secondBuffer->getNativeBuffer()->handle); // Queue the second buffer ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Acquire and release both buffers for (size_t i = 0; i < 2; ++i) { BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); } // Make sure we got the second buffer back sp returnedBuffer; sp returnedFence; float transform[16]; ASSERT_EQ(OK, mProducer->getLastQueuedBuffer(&returnedBuffer, &returnedFence, transform)); ASSERT_EQ(secondBuffer->getNativeBuffer()->handle, returnedBuffer->getNativeBuffer()->handle); } TEST_F(BufferQueueTest, TestOccupancyHistory) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; sp buffer = nullptr; IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); BufferItem item{}; // Preallocate, dequeue, request, and cancel 3 buffers so we don't get // BUFFER_NEEDS_REALLOCATION below int slots[3] = {}; mProducer->setMaxDequeuedBufferCount(3); for (size_t i = 0; i < 3; ++i) { status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } for (size_t i = 0; i < 3; ++i) { ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE)); } // Create 3 segments // The first segment is a two-buffer segment, so we only put one buffer into // the queue at a time for (size_t i = 0; i < 5; ++i) { ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } // Sleep between segments std::this_thread::sleep_for(500ms); // The second segment is a double-buffer segment. It starts the same as the // two-buffer segment, but then at the end, we put two buffers in the queue // at the same time before draining it. for (size_t i = 0; i < 5; ++i) { ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Sleep between segments std::this_thread::sleep_for(500ms); // The third segment is a triple-buffer segment, so the queue is switching // between one buffer and two buffers deep. ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); for (size_t i = 0; i < 5; ++i) { ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Now we read the segments std::vector history; ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history)); // Since we didn't force a flush, we should only get the first two segments // (since the third segment hasn't been closed out by the appearance of a // new segment yet) ASSERT_EQ(2u, history.size()); // The first segment (which will be history[1], since the newest segment // should be at the front of the vector) should be a two-buffer segment, // which implies that the occupancy average should be between 0 and 1, and // usedThirdBuffer should be false const auto& firstSegment = history[1]; ASSERT_EQ(5u, firstSegment.numFrames); ASSERT_LT(0, firstSegment.occupancyAverage); ASSERT_GT(1, firstSegment.occupancyAverage); ASSERT_EQ(false, firstSegment.usedThirdBuffer); // The second segment should be a double-buffered segment, which implies that // the occupancy average should be between 0 and 1, but usedThirdBuffer // should be true const auto& secondSegment = history[0]; ASSERT_EQ(7u, secondSegment.numFrames); ASSERT_LT(0, secondSegment.occupancyAverage); ASSERT_GT(1, secondSegment.occupancyAverage); ASSERT_EQ(true, secondSegment.usedThirdBuffer); // If we read the segments again without flushing, we shouldn't get any new // segments ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history)); ASSERT_EQ(0u, history.size()); // Read the segments again, this time forcing a flush so we get the third // segment ASSERT_EQ(OK, mConsumer->getOccupancyHistory(true, &history)); ASSERT_EQ(1u, history.size()); // This segment should be a triple-buffered segment, which implies that the // occupancy average should be between 1 and 2, and usedThirdBuffer should // be true const auto& thirdSegment = history[0]; ASSERT_EQ(6u, thirdSegment.numFrames); ASSERT_LT(1, thirdSegment.occupancyAverage); ASSERT_GT(2, thirdSegment.occupancyAverage); ASSERT_EQ(true, thirdSegment.usedThirdBuffer); } TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; sp buffer = nullptr; IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); BufferItem item{}; // Preallocate, dequeue, request, and cancel 4 buffers so we don't get // BUFFER_NEEDS_REALLOCATION below int slots[4] = {}; mProducer->setMaxDequeuedBufferCount(4); for (size_t i = 0; i < 4; ++i) { status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } for (size_t i = 0; i < 4; ++i) { ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE)); } // Get buffers in all states: dequeued, filled, acquired, free // Fill 3 buffers ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Dequeue 1 buffer ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); // Acquire and free 1 buffer ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Acquire 1 buffer, leaving 1 filled buffer in queue ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); // Now discard the free buffers ASSERT_EQ(OK, mConsumer->discardFreeBuffers()); // Check no free buffers in dump String8 dumpString; mConsumer->dumpState(String8{}, &dumpString); // Parse the dump to ensure that all buffer slots that are FREE also // have a null GraphicBuffer // Fragile - assumes the following format for the dump for a buffer entry: // ":%p\][^:]*state=FREE" where %p is the buffer pointer in hex. ssize_t idx = dumpString.find("state=FREE"); while (idx != -1) { ssize_t bufferPtrIdx = idx - 1; while (bufferPtrIdx > 0) { if (dumpString[bufferPtrIdx] == ':') { bufferPtrIdx++; break; } bufferPtrIdx--; } ASSERT_GT(bufferPtrIdx, 0) << "Can't parse queue dump to validate"; ssize_t nullPtrIdx = dumpString.find("0x0]", bufferPtrIdx); ASSERT_EQ(bufferPtrIdx, nullPtrIdx) << "Free buffer not discarded"; idx = dumpString.find("FREE", idx + 1); } } TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; sp buffer = nullptr; IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); BufferItem item{}; // Preallocate, dequeue, request, and cancel 2 buffers so we don't get // BUFFER_NEEDS_REALLOCATION below int slots[2] = {}; ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2)); for (size_t i = 0; i < 2; ++i) { status_t result = mProducer->dequeueBuffer(&slots[i], &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer)); } for (size_t i = 0; i < 2; ++i) { ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE)); } // Fill 2 buffers without consumer consuming them. Verify that all // queued buffer returns proper bufferReplaced flag ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(false, output.bufferReplaced); ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(true, output.bufferReplaced); } TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; sp dummyListener(new DummyProducerListener); ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp fence = Fence::NO_FENCE; sp buffer = nullptr; IGraphicBufferProducer::QueueBufferInput input(0ull, true, HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT, NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); // Dequeue, request, and queue one buffer status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result); ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Acquire and release the buffer. Upon acquiring, the buffer handle should // be non-null since this is the first time we've acquired this slot. BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_NE(nullptr, item.mGraphicBuffer.get()); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Dequeue and queue the buffer again ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Acquire and release the buffer again. Upon acquiring, the buffer handle // should be null since this is not the first time we've acquired this slot. ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_EQ(nullptr, item.mGraphicBuffer.get()); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // Dequeue and queue the buffer again ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); // Disconnect the producer end. This should clear all of the slots and mark // the buffer in the queue as stale. ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); // Acquire the buffer again. Upon acquiring, the buffer handle should not be // null since the queued buffer should have been marked as stale, which // should trigger the BufferQueue to resend the buffer handle. ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_NE(nullptr, item.mGraphicBuffer.get()); } TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { createBufferQueue(); sp dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); IGraphicBufferProducer::QueueBufferOutput output; sp dummyListener(new DummyProducerListener); ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, mProducer->connect( dummyListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(BAD_VALUE, mProducer->connect( dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output)); ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA)); ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); } } // namespace android libs/gui/tests/CpuConsumer_test.cpp0100644 0000000 0000000 00000061105 13300556574 016505 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 "CpuConsumer_test" //#define LOG_NDEBUG 0 //#define LOG_NNDEBUG 0 #ifdef LOG_NNDEBUG #define ALOGVV(...) ALOGV(__VA_ARGS__) #else #define ALOGVV(...) ((void)0) #endif #include #include #include #include #include #include #include #include #include #define CPU_CONSUMER_TEST_FORMAT_RAW 0 #define CPU_CONSUMER_TEST_FORMAT_Y8 0 #define CPU_CONSUMER_TEST_FORMAT_Y16 0 #define CPU_CONSUMER_TEST_FORMAT_RGBA_8888 1 namespace android { struct CpuConsumerTestParams { uint32_t width; uint32_t height; int maxLockedBuffers; PixelFormat format; }; ::std::ostream& operator<<(::std::ostream& os, const CpuConsumerTestParams& p) { return os << "[ (" << p.width << ", " << p.height << "), B:" << p.maxLockedBuffers << ", F:0x" << ::std::hex << p.format << "]"; } class CpuConsumerTest : public ::testing::TestWithParam { protected: virtual void SetUp() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); CpuConsumerTestParams params = GetParam(); ALOGV("** Starting test %s (%d x %d, %d, 0x%x)", test_info->name(), params.width, params.height, params.maxLockedBuffers, params.format); sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); mCC = new CpuConsumer(consumer, params.maxLockedBuffers); String8 name("CpuConsumer_Under_Test"); mCC->setName(name); mSTC = new Surface(producer); mANW = mSTC; } virtual void TearDown() { mANW.clear(); mSTC.clear(); mCC.clear(); } class FrameWaiter : public CpuConsumer::FrameAvailableListener { public: FrameWaiter(): mPendingFrames(0) { } void waitForFrame() { Mutex::Autolock lock(mMutex); while (mPendingFrames == 0) { mCondition.wait(mMutex); } mPendingFrames--; } virtual void onFrameAvailable(const BufferItem&) { Mutex::Autolock lock(mMutex); mPendingFrames++; mCondition.signal(); } int mPendingFrames; Mutex mMutex; Condition mCondition; }; // Note that SurfaceTexture will lose the notifications // onBuffersReleased and onFrameAvailable as there is currently // no way to forward the events. This DisconnectWaiter will not let the // disconnect finish until finishDisconnect() is called. It will // also block until a disconnect is called class DisconnectWaiter : public BufferQueue::ConsumerListener { public: DisconnectWaiter () : mWaitForDisconnect(false), mPendingFrames(0) { } void waitForFrame() { Mutex::Autolock lock(mMutex); while (mPendingFrames == 0) { mFrameCondition.wait(mMutex); } mPendingFrames--; } virtual void onFrameAvailable(const BufferItem&) { Mutex::Autolock lock(mMutex); mPendingFrames++; mFrameCondition.signal(); } virtual void onBuffersReleased() { Mutex::Autolock lock(mMutex); while (!mWaitForDisconnect) { mDisconnectCondition.wait(mMutex); } } void finishDisconnect() { Mutex::Autolock lock(mMutex); mWaitForDisconnect = true; mDisconnectCondition.signal(); } private: Mutex mMutex; bool mWaitForDisconnect; Condition mDisconnectCondition; int mPendingFrames; Condition mFrameCondition; }; sp mCC; sp mSTC; sp mANW; }; #define ASSERT_NO_ERROR(err, msg) \ ASSERT_EQ(NO_ERROR, err) << (msg) << strerror(-(err)) void checkPixel(const CpuConsumer::LockedBuffer &buf, uint32_t x, uint32_t y, uint32_t r, uint32_t g=0, uint32_t b=0) { // Ignores components that don't exist for given pixel switch(buf.format) { case HAL_PIXEL_FORMAT_RAW16: { String8 msg; uint16_t *bPtr = (uint16_t*)buf.data; bPtr += y * buf.stride + x; // GRBG Bayer mosaic; only check the matching channel switch( ((y & 1) << 1) | (x & 1) ) { case 0: // G case 3: // G EXPECT_EQ(g, *bPtr); break; case 1: // R EXPECT_EQ(r, *bPtr); break; case 2: // B EXPECT_EQ(b, *bPtr); break; } break; } // ignores g,b case HAL_PIXEL_FORMAT_Y8: { uint8_t *bPtr = (uint8_t*)buf.data; bPtr += y * buf.stride + x; EXPECT_EQ(r, *bPtr) << "at x = " << x << " y = " << y; break; } // ignores g,b case HAL_PIXEL_FORMAT_Y16: { // stride is in pixels, not in bytes uint16_t *bPtr = ((uint16_t*)buf.data) + y * buf.stride + x; EXPECT_EQ(r, *bPtr) << "at x = " << x << " y = " << y; break; } case HAL_PIXEL_FORMAT_RGBA_8888: { const int bytesPerPixel = 4; uint8_t *bPtr = (uint8_t*)buf.data; bPtr += (y * buf.stride + x) * bytesPerPixel; EXPECT_EQ(r, bPtr[0]) << "at x = " << x << " y = " << y; EXPECT_EQ(g, bPtr[1]) << "at x = " << x << " y = " << y; EXPECT_EQ(b, bPtr[2]) << "at x = " << x << " y = " << y; break; } default: { ADD_FAILURE() << "Unknown format for check:" << buf.format; break; } } } // Fill a YV12 buffer with a multi-colored checkerboard pattern void fillYV12Buffer(uint8_t* buf, int w, int h, int stride); // Fill a Y8/Y16 buffer with a multi-colored checkerboard pattern template // T == uint8_t or uint16_t void fillGreyscaleBuffer(T* buf, int w, int h, int stride, int bpp) { const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; const int yuvTexOffsetY = 0; ASSERT_TRUE(bpp == 8 || bpp == 16); ASSERT_TRUE(sizeof(T)*8 == bpp); // stride is in pixels, not in bytes int yuvTexStrideY = stride; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int parityX = (x / blockWidth) & 1; int parityY = (y / blockHeight) & 1; T intensity = (parityX ^ parityY) ? 63 : 191; buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; } } } inline uint8_t chooseColorRgba8888(int blockX, int blockY, uint8_t channel) { const int colorVariations = 3; uint8_t color = ((blockX % colorVariations) + (blockY % colorVariations)) % (colorVariations) == channel ? 191: 63; return color; } // Fill a RGBA8888 buffer with a multi-colored checkerboard pattern void fillRgba8888Buffer(uint8_t* buf, int w, int h, int stride) { const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; const int bytesPerPixel = 4; // stride is in pixels, not in bytes for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { int blockX = (x / blockWidth); int blockY = (y / blockHeight); uint8_t r = chooseColorRgba8888(blockX, blockY, 0); uint8_t g = chooseColorRgba8888(blockX, blockY, 1); uint8_t b = chooseColorRgba8888(blockX, blockY, 2); buf[(y*stride + x)*bytesPerPixel + 0] = r; buf[(y*stride + x)*bytesPerPixel + 1] = g; buf[(y*stride + x)*bytesPerPixel + 2] = b; buf[(y*stride + x)*bytesPerPixel + 3] = 255; } } } // Fill a RAW sensor buffer with a multi-colored checkerboard pattern. // Assumes GRBG mosaic ordering. Result should be a grid in a 2x2 pattern // of [ R, B; G, W] void fillBayerRawBuffer(uint8_t* buf, int w, int h, int stride) { ALOGVV("fillBayerRawBuffer: %p with %d x %d, stride %d", buf, w, h ,stride); // Blocks need to be even-width/height, aim for 8-wide otherwise const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; for (int y = 0; y < h; y+=2) { uint16_t *bPtr1 = ((uint16_t*)buf) + stride*y; uint16_t *bPtr2 = bPtr1 + stride; for (int x = 0; x < w; x+=2) { int blockX = (x / blockWidth ) & 1; int blockY = (y / blockHeight) & 1; unsigned short r = (blockX == blockY) ? 1000 : 200; unsigned short g = blockY ? 1000: 200; unsigned short b = blockX ? 1000: 200; // GR row *bPtr1++ = g; *bPtr1++ = r; // BG row *bPtr2++ = b; *bPtr2++ = g; } } } template // uint8_t or uint16_t void checkGreyscaleBuffer(const CpuConsumer::LockedBuffer &buf) { uint32_t w = buf.width; uint32_t h = buf.height; const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; const int blockRows = h / blockHeight; const int blockCols = w / blockWidth; // Top-left square is bright checkPixel(buf, 0, 0, 191); checkPixel(buf, 1, 0, 191); checkPixel(buf, 0, 1, 191); checkPixel(buf, 1, 1, 191); // One-right square is dark checkPixel(buf, blockWidth, 0, 63); checkPixel(buf, blockWidth + 1, 0, 63); checkPixel(buf, blockWidth, 1, 63); checkPixel(buf, blockWidth + 1, 1, 63); // One-down square is dark checkPixel(buf, 0, blockHeight, 63); checkPixel(buf, 1, blockHeight, 63); checkPixel(buf, 0, blockHeight + 1, 63); checkPixel(buf, 1, blockHeight + 1, 63); // One-diag square is bright checkPixel(buf, blockWidth, blockHeight, 191); checkPixel(buf, blockWidth + 1, blockHeight, 191); checkPixel(buf, blockWidth, blockHeight + 1, 191); checkPixel(buf, blockWidth + 1, blockHeight + 1, 191); // Test bottom-right pixel const int maxBlockX = ((w-1 + (blockWidth-1)) / blockWidth) & 0x1; const int maxBlockY = ((h-1 + (blockHeight-1)) / blockHeight) & 0x1; uint32_t pixelValue = ((maxBlockX % 2) == (maxBlockY % 2)) ? 191 : 63; checkPixel(buf, w-1, h-1, pixelValue); } void checkRgba8888Buffer(const CpuConsumer::LockedBuffer &buf) { uint32_t w = buf.width; uint32_t h = buf.height; const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; const int blockRows = h / blockHeight; const int blockCols = w / blockWidth; // Top-left square is bright red checkPixel(buf, 0, 0, 191, 63, 63); checkPixel(buf, 1, 0, 191, 63, 63); checkPixel(buf, 0, 1, 191, 63, 63); checkPixel(buf, 1, 1, 191, 63, 63); // One-right square is bright green checkPixel(buf, blockWidth, 0, 63, 191, 63); checkPixel(buf, blockWidth + 1, 0, 63, 191, 63); checkPixel(buf, blockWidth, 1, 63, 191, 63); checkPixel(buf, blockWidth + 1, 1, 63, 191, 63); // One-down square is bright green checkPixel(buf, 0, blockHeight, 63, 191, 63); checkPixel(buf, 1, blockHeight, 63, 191, 63); checkPixel(buf, 0, blockHeight + 1, 63, 191, 63); checkPixel(buf, 1, blockHeight + 1, 63, 191, 63); // One-diag square is bright blue checkPixel(buf, blockWidth, blockHeight, 63, 63, 191); checkPixel(buf, blockWidth + 1, blockHeight, 63, 63, 191); checkPixel(buf, blockWidth, blockHeight + 1, 63, 63, 191); checkPixel(buf, blockWidth + 1, blockHeight + 1, 63, 63, 191); // Test bottom-right pixel { const int maxBlockX = ((w-1) / blockWidth); const int maxBlockY = ((h-1) / blockHeight); uint8_t r = chooseColorRgba8888(maxBlockX, maxBlockY, 0); uint8_t g = chooseColorRgba8888(maxBlockX, maxBlockY, 1); uint8_t b = chooseColorRgba8888(maxBlockX, maxBlockY, 2); checkPixel(buf, w-1, h-1, r, g, b); } } void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) { uint32_t w = buf.width; uint32_t h = buf.height; const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; const int blockRows = h / blockHeight; const int blockCols = w / blockWidth; // Top-left square is red checkPixel(buf, 0, 0, 1000, 200, 200); checkPixel(buf, 1, 0, 1000, 200, 200); checkPixel(buf, 0, 1, 1000, 200, 200); checkPixel(buf, 1, 1, 1000, 200, 200); // One-right square is blue checkPixel(buf, blockWidth, 0, 200, 200, 1000); checkPixel(buf, blockWidth + 1, 0, 200, 200, 1000); checkPixel(buf, blockWidth, 1, 200, 200, 1000); checkPixel(buf, blockWidth + 1, 1, 200, 200, 1000); // One-down square is green checkPixel(buf, 0, blockHeight, 200, 1000, 200); checkPixel(buf, 1, blockHeight, 200, 1000, 200); checkPixel(buf, 0, blockHeight + 1, 200, 1000, 200); checkPixel(buf, 1, blockHeight + 1, 200, 1000, 200); // One-diag square is white checkPixel(buf, blockWidth, blockHeight, 1000, 1000, 1000); checkPixel(buf, blockWidth + 1, blockHeight, 1000, 1000, 1000); checkPixel(buf, blockWidth, blockHeight + 1, 1000, 1000, 1000); checkPixel(buf, blockWidth + 1, blockHeight + 1, 1000, 1000, 1000); // Test bottom-right pixel const int maxBlockX = ((w-1) / blockWidth) & 0x1; const int maxBlockY = ((w-1) / blockHeight) & 0x1; unsigned short maxR = (maxBlockX == maxBlockY) ? 1000 : 200; unsigned short maxG = maxBlockY ? 1000: 200; unsigned short maxB = maxBlockX ? 1000: 200; checkPixel(buf, w-1, h-1, maxR, maxG, maxB); } void checkAnyBuffer(const CpuConsumer::LockedBuffer &buf, int format) { switch (format) { case HAL_PIXEL_FORMAT_RAW16: checkBayerRawBuffer(buf); break; case HAL_PIXEL_FORMAT_Y8: checkGreyscaleBuffer(buf); break; case HAL_PIXEL_FORMAT_Y16: checkGreyscaleBuffer(buf); break; case HAL_PIXEL_FORMAT_RGBA_8888: checkRgba8888Buffer(buf); break; } } // Configures the ANativeWindow producer-side interface based on test parameters void configureANW(const sp& anw, const CpuConsumerTestParams& params, int maxBufferSlack) { status_t err; err = native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU); ASSERT_NO_ERROR(err, "connect error: "); err = native_window_set_buffers_dimensions(anw.get(), params.width, params.height); ASSERT_NO_ERROR(err, "set_buffers_dimensions error: "); err = native_window_set_buffers_format(anw.get(), params.format); ASSERT_NO_ERROR(err, "set_buffers_format error: "); err = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN); ASSERT_NO_ERROR(err, "set_usage error: "); int minUndequeuedBuffers; err = anw.get()->query(anw.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers); ASSERT_NO_ERROR(err, "query error: "); ALOGVV("Setting buffer count to %d", maxBufferSlack + 1 + minUndequeuedBuffers); err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers); ASSERT_NO_ERROR(err, "set_buffer_count error: "); } // Produce one frame of image data; assumes format and resolution configuration // is already done. void produceOneFrame(const sp& anw, const CpuConsumerTestParams& params, int64_t timestamp, uint32_t *stride) { status_t err; ANativeWindowBuffer* anb; ALOGVV("Dequeue buffer from %p", anw.get()); err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); ASSERT_NO_ERROR(err, "dequeueBuffer error: "); ASSERT_TRUE(anb != NULL); sp buf(GraphicBuffer::from(anb)); *stride = buf->getStride(); uint8_t* img = NULL; ALOGVV("Lock buffer from %p for write", anw.get()); err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); ASSERT_NO_ERROR(err, "lock error: "); switch (params.format) { case HAL_PIXEL_FORMAT_YV12: fillYV12Buffer(img, params.width, params.height, *stride); break; case HAL_PIXEL_FORMAT_RAW16: fillBayerRawBuffer(img, params.width, params.height, buf->getStride()); break; case HAL_PIXEL_FORMAT_Y8: fillGreyscaleBuffer(img, params.width, params.height, buf->getStride(), /*bpp*/8); break; case HAL_PIXEL_FORMAT_Y16: fillGreyscaleBuffer((uint16_t*)img, params.width, params.height, buf->getStride(), /*bpp*/16); break; case HAL_PIXEL_FORMAT_RGBA_8888: fillRgba8888Buffer(img, params.width, params.height, buf->getStride()); break; default: FAIL() << "Unknown pixel format under test!"; break; } ALOGVV("Unlock buffer from %p", anw.get()); err = buf->unlock(); ASSERT_NO_ERROR(err, "unlock error: "); ALOGVV("Set timestamp to %p", anw.get()); err = native_window_set_buffers_timestamp(anw.get(), timestamp); ASSERT_NO_ERROR(err, "set_buffers_timestamp error: "); ALOGVV("Queue buffer to %p", anw.get()); err = anw->queueBuffer(anw.get(), buf->getNativeBuffer(), -1); ASSERT_NO_ERROR(err, "queueBuffer error:"); }; // This test is disabled because the HAL_PIXEL_FORMAT_RAW16 format is not // supported on all devices. TEST_P(CpuConsumerTest, FromCpuSingle) { status_t err; CpuConsumerTestParams params = GetParam(); // Set up ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, 1)); // Produce const int64_t time = 12345678L; uint32_t stride; ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time, &stride)); // Consume CpuConsumer::LockedBuffer b; err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); ASSERT_TRUE(b.data != NULL); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); EXPECT_EQ(stride, b.stride); EXPECT_EQ(time, b.timestamp); checkAnyBuffer(b, GetParam().format); mCC->unlockBuffer(b); } // This test is disabled because the HAL_PIXEL_FORMAT_RAW16 format is not // supported on all devices. TEST_P(CpuConsumerTest, FromCpuManyInQueue) { status_t err; CpuConsumerTestParams params = GetParam(); const int numInQueue = 5; // Set up ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, numInQueue)); // Produce const int64_t time[numInQueue] = { 1L, 2L, 3L, 4L, 5L}; uint32_t stride[numInQueue]; for (int i = 0; i < numInQueue; i++) { ALOGV("Producing frame %d", i); ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time[i], &stride[i])); } // Consume for (int i = 0; i < numInQueue; i++) { ALOGV("Consuming frame %d", i); CpuConsumer::LockedBuffer b; err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); ASSERT_TRUE(b.data != NULL); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); EXPECT_EQ(stride[i], b.stride); EXPECT_EQ(time[i], b.timestamp); checkAnyBuffer(b, GetParam().format); mCC->unlockBuffer(b); } } // This test is disabled because the HAL_PIXEL_FORMAT_RAW16 format is not // supported on all devices. TEST_P(CpuConsumerTest, FromCpuLockMax) { status_t err; CpuConsumerTestParams params = GetParam(); // Set up ASSERT_NO_FATAL_FAILURE(configureANW(mANW, params, params.maxLockedBuffers + 1)); // Produce const int64_t time = 1234L; uint32_t stride; for (int i = 0; i < params.maxLockedBuffers + 1; i++) { ALOGV("Producing frame %d", i); ASSERT_NO_FATAL_FAILURE(produceOneFrame(mANW, params, time, &stride)); } // Consume std::vector b(params.maxLockedBuffers); for (int i = 0; i < params.maxLockedBuffers; i++) { ALOGV("Locking frame %d", i); err = mCC->lockNextBuffer(&b[i]); ASSERT_NO_ERROR(err, "getNextBuffer error: "); ASSERT_TRUE(b[i].data != NULL); EXPECT_EQ(params.width, b[i].width); EXPECT_EQ(params.height, b[i].height); EXPECT_EQ(params.format, b[i].format); EXPECT_EQ(stride, b[i].stride); EXPECT_EQ(time, b[i].timestamp); checkAnyBuffer(b[i], GetParam().format); } ALOGV("Locking frame %d (too many)", params.maxLockedBuffers); CpuConsumer::LockedBuffer bTooMuch; err = mCC->lockNextBuffer(&bTooMuch); ASSERT_TRUE(err == NOT_ENOUGH_DATA) << "Allowing too many locks"; ALOGV("Unlocking frame 0"); err = mCC->unlockBuffer(b[0]); ASSERT_NO_ERROR(err, "Could not unlock buffer 0: "); ALOGV("Locking frame %d (should work now)", params.maxLockedBuffers); err = mCC->lockNextBuffer(&bTooMuch); ASSERT_NO_ERROR(err, "Did not allow new lock after unlock"); ASSERT_TRUE(bTooMuch.data != NULL); EXPECT_EQ(params.width, bTooMuch.width); EXPECT_EQ(params.height, bTooMuch.height); EXPECT_EQ(params.format, bTooMuch.format); EXPECT_EQ(stride, bTooMuch.stride); EXPECT_EQ(time, bTooMuch.timestamp); checkAnyBuffer(bTooMuch, GetParam().format); ALOGV("Unlocking extra buffer"); err = mCC->unlockBuffer(bTooMuch); ASSERT_NO_ERROR(err, "Could not unlock extra buffer: "); ALOGV("Locking frame %d (no more available)", params.maxLockedBuffers + 1); err = mCC->lockNextBuffer(&b[0]); ASSERT_EQ(BAD_VALUE, err) << "Not out of buffers somehow"; for (int i = 1; i < params.maxLockedBuffers; i++) { mCC->unlockBuffer(b[i]); } } CpuConsumerTestParams y8TestSets[] = { { 512, 512, 1, HAL_PIXEL_FORMAT_Y8}, { 512, 512, 3, HAL_PIXEL_FORMAT_Y8}, { 2608, 1960, 1, HAL_PIXEL_FORMAT_Y8}, { 2608, 1960, 3, HAL_PIXEL_FORMAT_Y8}, { 100, 100, 1, HAL_PIXEL_FORMAT_Y8}, { 100, 100, 3, HAL_PIXEL_FORMAT_Y8}, }; CpuConsumerTestParams y16TestSets[] = { { 512, 512, 1, HAL_PIXEL_FORMAT_Y16}, { 512, 512, 3, HAL_PIXEL_FORMAT_Y16}, { 2608, 1960, 1, HAL_PIXEL_FORMAT_Y16}, { 2608, 1960, 3, HAL_PIXEL_FORMAT_Y16}, { 100, 100, 1, HAL_PIXEL_FORMAT_Y16}, { 100, 100, 3, HAL_PIXEL_FORMAT_Y16}, }; CpuConsumerTestParams rawTestSets[] = { { 512, 512, 1, HAL_PIXEL_FORMAT_RAW16}, { 512, 512, 3, HAL_PIXEL_FORMAT_RAW16}, { 2608, 1960, 1, HAL_PIXEL_FORMAT_RAW16}, { 2608, 1960, 3, HAL_PIXEL_FORMAT_RAW16}, { 100, 100, 1, HAL_PIXEL_FORMAT_RAW16}, { 100, 100, 3, HAL_PIXEL_FORMAT_RAW16}, }; CpuConsumerTestParams rgba8888TestSets[] = { { 512, 512, 1, HAL_PIXEL_FORMAT_RGBA_8888}, { 512, 512, 3, HAL_PIXEL_FORMAT_RGBA_8888}, { 2608, 1960, 1, HAL_PIXEL_FORMAT_RGBA_8888}, { 2608, 1960, 3, HAL_PIXEL_FORMAT_RGBA_8888}, { 100, 100, 1, HAL_PIXEL_FORMAT_RGBA_8888}, { 100, 100, 3, HAL_PIXEL_FORMAT_RGBA_8888}, }; #if CPU_CONSUMER_TEST_FORMAT_Y8 INSTANTIATE_TEST_CASE_P(Y8Tests, CpuConsumerTest, ::testing::ValuesIn(y8TestSets)); #endif #if CPU_CONSUMER_TEST_FORMAT_Y16 INSTANTIATE_TEST_CASE_P(Y16Tests, CpuConsumerTest, ::testing::ValuesIn(y16TestSets)); #endif #if CPU_CONSUMER_TEST_FORMAT_RAW INSTANTIATE_TEST_CASE_P(RawTests, CpuConsumerTest, ::testing::ValuesIn(rawTestSets)); #endif #if CPU_CONSUMER_TEST_FORMAT_RGBA_8888 INSTANTIATE_TEST_CASE_P(Rgba8888Tests, CpuConsumerTest, ::testing::ValuesIn(rgba8888TestSets)); #endif } // namespace android libs/gui/tests/DisconnectWaiter.h0100644 0000000 0000000 00000004176 13300556574 016122 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. */ #ifndef ANDROID_DISCONNECT_WAITER_H #define ANDROID_DISCONNECT_WAITER_H #include #include #include namespace android { // Note that GLConsumer will lose the notifications // onBuffersReleased and onFrameAvailable as there is currently // no way to forward the events. This DisconnectWaiter will not let the // disconnect finish until finishDisconnect() is called. It will // also block until a disconnect is called class DisconnectWaiter : public BnConsumerListener { public: DisconnectWaiter () : mWaitForDisconnect(false), mPendingFrames(0) { } void waitForFrame() { Mutex::Autolock lock(mMutex); while (mPendingFrames == 0) { mFrameCondition.wait(mMutex); } mPendingFrames--; } virtual void onFrameAvailable(const BufferItem& /* item */) { Mutex::Autolock lock(mMutex); mPendingFrames++; mFrameCondition.signal(); } virtual void onBuffersReleased() { Mutex::Autolock lock(mMutex); while (!mWaitForDisconnect) { mDisconnectCondition.wait(mMutex); } } virtual void onSidebandStreamChanged() {} void finishDisconnect() { Mutex::Autolock lock(mMutex); mWaitForDisconnect = true; mDisconnectCondition.signal(); } private: Mutex mMutex; bool mWaitForDisconnect; Condition mDisconnectCondition; int mPendingFrames; Condition mFrameCondition; }; } // namespace android #endif libs/gui/tests/DummyConsumer.h0100644 0000000 0000000 00000001614 13300556574 015456 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 { struct DummyConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} }; } // namespace android libs/gui/tests/FillBuffer.cpp0100644 0000000 0000000 00000010051 13300556574 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. */ #include "FillBuffer.h" #include #include namespace android { void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; const int yuvTexOffsetY = 0; int yuvTexStrideY = stride; int yuvTexOffsetV = yuvTexStrideY * h; int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; int yuvTexStrideU = yuvTexStrideV; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { int parityX = (x / blockWidth) & 1; int parityY = (y / blockHeight) & 1; unsigned char intensity = (parityX ^ parityY) ? 63 : 191; buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; if (x < w / 2 && y < h / 2) { buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; if (x * 2 < w / 2 && y * 2 < h / 2) { buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = intensity; } } } } } void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, const android_native_rect_t& rect) { const int yuvTexOffsetY = 0; int yuvTexStrideY = stride; int yuvTexOffsetV = yuvTexStrideY * h; int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; int yuvTexStrideU = yuvTexStrideV; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { bool inside = rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom; buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; if (x < w / 2 && y < h / 2) { bool inside = rect.left <= 2*x && 2*x < rect.right && rect.top <= 2*y && 2*y < rect.bottom; buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = inside ? 16 : 255; } } } } void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { const size_t PIXEL_SIZE = 4; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { off_t offset = (y * stride + x) * PIXEL_SIZE; for (int c = 0; c < 4; c++) { int parityX = (x / (1 << (c+2))) & 1; int parityY = (y / (1 << (c+2))) & 1; buf[offset + c] = (parityX ^ parityY) ? 231 : 35; } } } } void produceOneRGBA8Frame(const sp& anw) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &anb)); ASSERT_TRUE(anb != NULL); sp buf(GraphicBuffer::from(anb)); uint8_t* img = NULL; ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img))); fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride()); ASSERT_EQ(NO_ERROR, buf->unlock()); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf->getNativeBuffer(), -1)); } } // namespace android libs/gui/tests/FillBuffer.h0100644 0000000 0000000 00000002765 13300556574 014677 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. */ #ifndef ANDROID_FILL_BUFFER_H #define ANDROID_FILL_BUFFER_H #include #include namespace android { // Fill a YV12 buffer with a multi-colored checkerboard pattern void fillYV12Buffer(uint8_t* buf, int w, int h, int stride); // Fill a YV12 buffer with red outside a given rectangle and green inside it. void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, const android_native_rect_t& rect); void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride); // Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern // using the CPU. This assumes that the ANativeWindow is already configured to // allow this to be done (e.g. the format is set to RGBA8). // // Calls to this function should be wrapped in an ASSERT_NO_FATAL_FAILURE(). void produceOneRGBA8Frame(const sp& anw); } // namespace android #endif libs/gui/tests/FrameWaiter.h0100644 0000000 0000000 00000002450 13300556574 015054 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. */ #ifndef ANDROID_FRAME_WAITER_H #define ANDROID_FRAME_WAITER_H #include namespace android { class FrameWaiter : public GLConsumer::FrameAvailableListener { public: FrameWaiter(): mPendingFrames(0) { } void waitForFrame() { Mutex::Autolock lock(mMutex); while (mPendingFrames == 0) { mCondition.wait(mMutex); } mPendingFrames--; } virtual void onFrameAvailable(const BufferItem& /* item */) { Mutex::Autolock lock(mMutex); mPendingFrames++; mCondition.signal(); } private: int mPendingFrames; Mutex mMutex; Condition mCondition; }; } // namespace android #endif libs/gui/tests/GLTest.cpp0100644 0000000 0000000 00000026245 13300556574 014353 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 "GLTest.h" #include #include namespace android { static int abs(int value) { return value > 0 ? value : -value; } void GLTest::SetUp() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); EGLint majorVersion; EGLint minorVersion; EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); RecordProperty("EglVersionMajor", majorVersion); RecordProperty("EglVersionMinor", minorVersion); EGLint numConfigs = 0; EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 1, &numConfigs)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); if (displaySecsEnv != NULL) { mDisplaySecs = atoi(displaySecsEnv); if (mDisplaySecs < 0) { mDisplaySecs = 0; } } else { mDisplaySecs = 0; } if (mDisplaySecs > 0) { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); SurfaceComposerClient::closeGlobalTransaction(); sp window = mSurfaceControl->getSurface(); mEglSurface = createWindowSurface(mEglDisplay, mGlConfig, window); } else { EGLint pbufferAttribs[] = { EGL_WIDTH, getSurfaceWidth(), EGL_HEIGHT, getSurfaceHeight(), EGL_NONE }; mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, pbufferAttribs); } ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurface); mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLint w, h; EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); RecordProperty("EglSurfaceWidth", w); RecordProperty("EglSurfaceHeight", h); glViewport(0, 0, w, h); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); } void GLTest::TearDown() { // Display the result if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { eglSwapBuffers(mEglDisplay, mEglSurface); sleep(mDisplaySecs); } if (mComposerClient != NULL) { mComposerClient->dispose(); } if (mEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mEglContext); } if (mEglSurface != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, mEglSurface); } if (mEglDisplay != EGL_NO_DISPLAY) { eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEglDisplay); } ASSERT_EQ(EGL_SUCCESS, eglGetError()); const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); } EGLint const* GLTest::getConfigAttribs() { static const EGLint sDefaultConfigAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_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_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_NONE }; return sDefaultConfigAttribs; } EGLint const* GLTest::getContextAttribs() { static const EGLint sDefaultContextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; return sDefaultContextAttribs; } EGLint GLTest::getSurfaceWidth() { return 512; } EGLint GLTest::getSurfaceHeight() { return 512; } EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, sp& window) const { return eglCreateWindowSurface(display, config, window.get(), NULL); } ::testing::AssertionResult GLTest::checkPixel(int x, int y, int r, int g, int b, int a, int tolerance) { GLubyte pixel[4]; String8 msg; glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); GLenum err = glGetError(); if (err != GL_NO_ERROR) { msg += String8::format("error reading pixel: %#x", err); while ((err = glGetError()) != GL_NO_ERROR) { msg += String8::format(", %#x", err); } return ::testing::AssertionFailure(::testing::Message(msg.string())); } if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { msg += String8::format("r(%d isn't %d)", pixel[0], r); } if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("g(%d isn't %d)", pixel[1], g); } if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("b(%d isn't %d)", pixel[2], b); } if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("a(%d isn't %d)", pixel[3], a); } if (!msg.isEmpty()) { return ::testing::AssertionFailure(::testing::Message(msg.string())); } else { return ::testing::AssertionSuccess(); } } ::testing::AssertionResult GLTest::assertRectEq(const Rect &r1, const Rect &r2, int tolerance) { String8 msg; if (abs(r1.left - r2.left) > tolerance) { msg += String8::format("left(%d isn't %d)", r1.left, r2.left); } if (abs(r1.top - r2.top) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("top(%d isn't %d)", r1.top, r2.top); } if (abs(r1.right - r2.right) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("right(%d isn't %d)", r1.right, r2.right); } if (abs(r1.bottom - r2.bottom) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("bottom(%d isn't %d)", r1.bottom, r2.bottom); } if (!msg.isEmpty()) { msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]", r1.left, r1.top, r1.right, r1.bottom, r2.left, r2.top, r2.right, r2.bottom); fprintf(stderr, "assertRectEq: %s\n", msg.string()); return ::testing::AssertionFailure(::testing::Message(msg.string())); } else { return ::testing::AssertionSuccess(); } } void GLTest::loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) { GLuint shader = glCreateShader(shaderType); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (shader) { glShaderSource(shader, 1, &pSource, NULL); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glCompileShader(shader); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); } } else { char* buf = (char*) malloc(0x1000); if (buf) { glGetShaderInfoLog(shader, 0x1000, NULL, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); } } glDeleteShader(shader); shader = 0; } } ASSERT_TRUE(shader != 0); *outShader = shader; } void GLTest::createProgram(const char* pVertexSource, const char* pFragmentSource, GLuint* outPgm) { GLuint vertexShader, fragmentShader; { SCOPED_TRACE("compiling vertex shader"); ASSERT_NO_FATAL_FAILURE(loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader)); } { SCOPED_TRACE("compiling fragment shader"); ASSERT_NO_FATAL_FAILURE(loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader)); } GLuint program = glCreateProgram(); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (program) { glAttachShader(program, vertexShader); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glAttachShader(program, fragmentShader); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); 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 = (char*) malloc(bufLength); if (buf) { glGetProgramInfoLog(program, bufLength, NULL, buf); printf("Program link log:\n%s\n", buf); free(buf); FAIL(); } } glDeleteProgram(program); program = 0; } } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); ASSERT_TRUE(program != 0); *outPgm = program; } } // namespace android libs/gui/tests/GLTest.h0100644 0000000 0000000 00000004137 13300556574 014014 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. */ #ifndef ANDROID_GL_TEST_H #define ANDROID_GL_TEST_H #include #include #include #include namespace android { class GLTest : public ::testing::Test { public: static void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader); static void createProgram(const char* pVertexSource, const char* pFragmentSource, GLuint* outPgm); protected: GLTest() : mDisplaySecs(0), mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), mGlConfig(NULL) { } virtual void SetUp(); virtual void TearDown(); virtual EGLint const* getConfigAttribs(); virtual EGLint const* getContextAttribs(); virtual EGLint getSurfaceWidth(); virtual EGLint getSurfaceHeight(); virtual EGLSurface createWindowSurface(EGLDisplay display, EGLConfig config, sp& window) const; ::testing::AssertionResult checkPixel(int x, int y, int r, int g, int b, int a, int tolerance = 2); ::testing::AssertionResult assertRectEq(const Rect &r1, const Rect &r2, int tolerance = 1); int mDisplaySecs; sp mComposerClient; sp mSurfaceControl; EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLContext mEglContext; EGLConfig mGlConfig; }; } // namespace android #endif libs/gui/tests/IGraphicBufferProducer_test.cpp0100644 0000000 0000000 00000064635 13300556574 020601 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 "IGraphicBufferProducer_test" //#define LOG_NDEBUG 0 #include "DummyConsumer.h" #include #include #include #include #include #include #include #include #define ASSERT_OK(x) ASSERT_EQ(OK, (x)) #define EXPECT_OK(x) EXPECT_EQ(OK, (x)) #define TEST_TOKEN ((IProducerListener*)(NULL)) #define TEST_API NATIVE_WINDOW_API_CPU #define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API #define TEST_CONTROLLED_BY_APP false #define TEST_PRODUCER_USAGE_BITS (0) namespace android { namespace { // Default dimensions before setDefaultBufferSize is called const uint32_t DEFAULT_WIDTH = 1; const uint32_t DEFAULT_HEIGHT = 1; // Default format before setDefaultBufferFormat is called const PixelFormat DEFAULT_FORMAT = HAL_PIXEL_FORMAT_RGBA_8888; // Default transform hint before setTransformHint is called const uint32_t DEFAULT_TRANSFORM_HINT = 0; // TODO: Make these constants in header const int DEFAULT_CONSUMER_USAGE_BITS = 0; // Parameters for a generic "valid" input for queueBuffer. const int64_t QUEUE_BUFFER_INPUT_TIMESTAMP = 1384888611; const bool QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP = false; const android_dataspace QUEUE_BUFFER_INPUT_DATASPACE = HAL_DATASPACE_UNKNOWN; const Rect QUEUE_BUFFER_INPUT_RECT = Rect(DEFAULT_WIDTH, DEFAULT_HEIGHT); const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0; const int QUEUE_BUFFER_INPUT_TRANSFORM = 0; const sp QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE; }; // namespace anonymous class IGraphicBufferProducerTest : public ::testing::Test { protected: IGraphicBufferProducerTest() {} virtual void SetUp() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); mDC = new DummyConsumer; BufferQueue::createBufferQueue(&mProducer, &mConsumer); // Test check: Can't connect producer if no consumer yet ASSERT_EQ(NO_INIT, TryConnectProducer()); // Must connect consumer before producer connects will succeed. ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false)); } virtual void TearDown() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); } status_t TryConnectProducer() { IGraphicBufferProducer::QueueBufferOutput output; return mProducer->connect(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, &output); // TODO: use params to vary token, api, producercontrolledbyapp, etc } // Connect to a producer in a 'correct' fashion. // Precondition: Consumer is connected. void ConnectProducer() { ASSERT_OK(TryConnectProducer()); } // Create a generic "valid" input for queueBuffer // -- uses the default buffer format, width, etc. static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() { return QueueBufferInputBuilder().build(); } // Builder pattern to slightly vary *almost* correct input // -- avoids copying and pasting struct QueueBufferInputBuilder { QueueBufferInputBuilder() { timestamp = QUEUE_BUFFER_INPUT_TIMESTAMP; isAutoTimestamp = QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP; dataSpace = QUEUE_BUFFER_INPUT_DATASPACE; crop = QUEUE_BUFFER_INPUT_RECT; scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE; transform = QUEUE_BUFFER_INPUT_TRANSFORM; fence = QUEUE_BUFFER_INPUT_FENCE; } IGraphicBufferProducer::QueueBufferInput build() { return IGraphicBufferProducer::QueueBufferInput( timestamp, isAutoTimestamp, dataSpace, crop, scalingMode, transform, fence); } QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { this->timestamp = timestamp; return *this; } QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) { this->isAutoTimestamp = isAutoTimestamp; return *this; } QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) { this->dataSpace = dataSpace; return *this; } QueueBufferInputBuilder& setCrop(Rect crop) { this->crop = crop; return *this; } QueueBufferInputBuilder& setScalingMode(int scalingMode) { this->scalingMode = scalingMode; return *this; } QueueBufferInputBuilder& setTransform(uint32_t transform) { this->transform = transform; return *this; } QueueBufferInputBuilder& setFence(sp fence) { this->fence = fence; return *this; } private: int64_t timestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop; int scalingMode; uint32_t transform; sp fence; }; // struct QueueBufferInputBuilder // To easily store dequeueBuffer results into containers struct DequeueBufferResult { int slot; sp fence; }; status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) { return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr, nullptr); } void setupDequeueRequestBuffer(int *slot, sp *fence, sp *buffer) { ASSERT_TRUE(slot != NULL); ASSERT_TRUE(fence != NULL); ASSERT_TRUE(buffer != NULL); ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); EXPECT_LE(0, *slot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot); // Request the buffer (pre-requisite for queueing) ASSERT_OK(mProducer->requestBuffer(*slot, buffer)); } private: // hide from test body sp mDC; protected: // accessible from test body sp mProducer; sp mConsumer; }; TEST_F(IGraphicBufferProducerTest, ConnectFirst_ReturnsError) { IGraphicBufferProducer::QueueBufferOutput output; // NULL output returns BAD_VALUE EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, /*output*/NULL)); // Invalid API returns bad value EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, /*api*/0xDEADBEEF, TEST_CONTROLLED_BY_APP, &output)); // TODO: get a token from a dead process somehow } TEST_F(IGraphicBufferProducerTest, 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(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, &output)); ASSERT_OK(mConsumer->consumerDisconnect()); // Can't connect when IGBP is abandoned EXPECT_EQ(NO_INIT, mProducer->connect(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, &output)); } TEST_F(IGraphicBufferProducerTest, Disconnect_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_OK(mProducer->disconnect(TEST_API)); } TEST_F(IGraphicBufferProducerTest, Disconnect_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // Must disconnect with same API number ASSERT_EQ(BAD_VALUE, mProducer->disconnect(TEST_API_OTHER)); // API must not be out of range ASSERT_EQ(BAD_VALUE, mProducer->disconnect(/*api*/0xDEADBEEF)); // TODO: somehow kill mProducer so that this returns DEAD_OBJECT } TEST_F(IGraphicBufferProducerTest, Query_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int32_t value = -1; EXPECT_OK(mProducer->query(NATIVE_WINDOW_WIDTH, &value)); EXPECT_EQ(DEFAULT_WIDTH, static_cast(value)); EXPECT_OK(mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); EXPECT_EQ(DEFAULT_HEIGHT, static_cast(value)); EXPECT_OK(mProducer->query(NATIVE_WINDOW_FORMAT, &value)); EXPECT_EQ(DEFAULT_FORMAT, value); EXPECT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); EXPECT_LE(0, value); EXPECT_GE(BufferQueue::NUM_BUFFER_SLOTS, value); EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value); } TEST_F(IGraphicBufferProducerTest, 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)); // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP // Value was NULL EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL)); ASSERT_OK(mConsumer->consumerDisconnect()); // BQ was abandoned EXPECT_EQ(NO_INIT, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); // TODO: other things in window.h that are supported by Surface::query // but not by BufferQueue::query } // TODO: queue under more complicated situations not involving just a single buffer TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int dequeuedSlot = -1; sp dequeuedFence; ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); EXPECT_LE(0, dequeuedSlot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot); // Request the buffer (pre-requisite for queueing) sp dequeuedBuffer; ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; // Queue the buffer back into the BQ ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output)); { EXPECT_EQ(DEFAULT_WIDTH, output.width); EXPECT_EQ(DEFAULT_HEIGHT, output.height); EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint); // Since queueBuffer was called exactly once EXPECT_EQ(1u, output.numPendingBuffers); EXPECT_EQ(2u, output.nextFrameNumber); } // Buffer was not in the dequeued state EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); } TEST_F(IGraphicBufferProducerTest, Queue_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // Invalid slot number { // 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(BufferQueue::NUM_BUFFER_SLOTS, input, &output)); } // Slot was not in the dequeued state (all slots start out in Free state) { IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output)); } // Put the slot into the "dequeued" state for the rest of the test int dequeuedSlot = -1; sp dequeuedFence; ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); // Slot was enqueued without requesting a buffer { IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); } // Request the buffer so that the rest of the tests don't fail on earlier checks. sp dequeuedBuffer; ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); // Fence was NULL { sp nullFence = NULL; IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); } // Scaling mode was unknown { IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setScalingMode(-1).build(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); } // Crop rect is out of bounds of the buffer dimensions { IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setCrop(Rect(DEFAULT_WIDTH + 1, DEFAULT_HEIGHT + 1)) .build(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); } // Abandon the buffer queue so that the last test fails ASSERT_OK(mConsumer->consumerDisconnect()); // The buffer queue has been abandoned. { IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output)); } } TEST_F(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int dequeuedSlot = -1; sp dequeuedFence; ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); // No return code, but at least test that it doesn't blow up... // TODO: add a return code mProducer->cancelBuffer(dequeuedSlot, dequeuedFence); } TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueue::NUM_BUFFER_SLOTS - minUndequeuedBuffers; ASSERT_OK(mProducer->setAsyncMode(false)) << "async mode: " << false; ASSERT_OK(mProducer->setMaxDequeuedBufferCount(minBuffers)) << "bufferCount: " << minBuffers; // Should now be able to dequeue up to minBuffers times DequeueBufferResult result; for (int i = 0; i < minBuffers; ++i) { EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, &result))) << "iteration: " << i << ", slot: " << result.slot; } ASSERT_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_OK(mProducer->requestBuffer(result.slot, &buffer)); ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output)); // Should now be able to dequeue up to maxBuffers times int dequeuedSlot = -1; sp dequeuedFence; for (int i = 0; i < maxBuffers; ++i) { EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "iteration: " << i << ", slot: " << dequeuedSlot; } // Cancel a buffer, so we can decrease the buffer count ASSERT_OK(mProducer->cancelBuffer(dequeuedSlot, dequeuedFence)); // Should now be able to decrease the max dequeued count by 1 ASSERT_OK(mProducer->setMaxDequeuedBufferCount(maxBuffers-1)); } TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueue::NUM_BUFFER_SLOTS - minUndequeuedBuffers; ASSERT_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_OK(mProducer->setMaxDequeuedBufferCount(2)); // Dequeue 2 buffers int dequeuedSlot = -1; sp dequeuedFence; for (int i = 0; i < 2; i++) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "slot: " << dequeuedSlot; } // Client has too many buffers dequeued EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1)) << "bufferCount: " << minBuffers; // Abandon buffer queue ASSERT_OK(mConsumer->consumerDisconnect()); // Fail because the buffer queue was abandoned EXPECT_EQ(NO_INIT, mProducer->setMaxDequeuedBufferCount(minBuffers)) << "bufferCount: " << minBuffers; } TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) { ASSERT_OK(mConsumer->setMaxAcquiredBufferCount(1)) << "maxAcquire: " << 1; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_OK(mProducer->setAsyncMode(true)) << "async mode: " << true; ASSERT_OK(mProducer->setMaxDequeuedBufferCount(1)) << "maxDequeue: " << 1; int dequeuedSlot = -1; sp dequeuedFence; IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; sp dequeuedBuffer; // Should now be able to queue/dequeue as many buffers as we want without // blocking for (int i = 0; i < 5; ++i) { ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "slot : " << dequeuedSlot; ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output)); } } TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Fails) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); // Prerequisite to fail out a valid setBufferCount call { int dequeuedSlot = -1; sp dequeuedFence; ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))) << "slot: " << dequeuedSlot; } // Abandon buffer queue ASSERT_OK(mConsumer->consumerDisconnect()); // Fail because the buffer queue was abandoned EXPECT_EQ(NO_INIT, mProducer->setAsyncMode(false)) << "asyncMode: " << false; } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_dequeueBuffer) { int slot = -1; sp fence; ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr)); } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_detachNextBuffer) { sp fence; sp buffer; ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_requestBuffer) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int slot = -1; sp fence; ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr, nullptr))); EXPECT_LE(0, slot); EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot); ASSERT_OK(mProducer->disconnect(TEST_API)); sp buffer; ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer)); } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_detachBuffer) { int slot = -1; sp fence; sp buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); ASSERT_OK(mProducer->disconnect(TEST_API)); ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_queueBuffer) { int slot = -1; sp fence; sp buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); ASSERT_OK(mProducer->disconnect(TEST_API)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output)); } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_cancelBuffer) { int slot = -1; sp fence; sp buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); ASSERT_OK(mProducer->disconnect(TEST_API)); ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, fence)); } TEST_F(IGraphicBufferProducerTest, DisconnectedProducerReturnsError_attachBuffer) { int slot = -1; sp fence; sp buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); ASSERT_OK(mProducer->detachBuffer(slot)); ASSERT_OK(mProducer->disconnect(TEST_API)); ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); } } // namespace android libs/gui/tests/Malicious.cpp0100644 0000000 0000000 00000020306 13300556574 015126 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. */ #include #include #include #include #include namespace android { namespace test { class ProxyBQP : public BnGraphicBufferProducer { public: ProxyBQP(const sp& producer) : mProducer(producer) {} // Pass through calls to mProducer status_t requestBuffer(int slot, sp* buf) override { return mProducer->requestBuffer(slot, buf); } status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override { return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); } status_t setAsyncMode(bool async) override { return mProducer->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 mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); } status_t detachBuffer(int slot) override { return mProducer->detachBuffer(slot); } status_t detachNextBuffer(sp* outBuffer, sp* outFence) override { return mProducer->detachNextBuffer(outBuffer, outFence); } status_t attachBuffer(int* outSlot, const sp& buffer) override { return mProducer->attachBuffer(outSlot, buffer); } status_t queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) override { return mProducer->queueBuffer(slot, input, output); } status_t cancelBuffer(int slot, const sp& fence) override { return mProducer->cancelBuffer(slot, fence); } int query(int what, int* value) override { return mProducer->query(what, value); } status_t connect(const sp& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override { return mProducer->connect(listener, api, producerControlledByApp, output); } status_t disconnect(int api, DisconnectMode mode) override { return mProducer->disconnect(api, mode); } status_t setSidebandStream(const sp& stream) override { return mProducer->setSidebandStream(stream); } void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) override { mProducer->allocateBuffers(width, height, format, usage); } status_t allowAllocation(bool allow) override { return mProducer->allowAllocation(allow); } status_t setGenerationNumber(uint32_t generationNumber) override { return mProducer->setGenerationNumber(generationNumber); } String8 getConsumerName() const override { return mProducer->getConsumerName(); } status_t setSharedBufferMode(bool sharedBufferMode) override { return mProducer->setSharedBufferMode(sharedBufferMode); } status_t setAutoRefresh(bool autoRefresh) override { return mProducer->setAutoRefresh(autoRefresh); } status_t setDequeueTimeout(nsecs_t timeout) override { return mProducer->setDequeueTimeout(timeout); } status_t getLastQueuedBuffer(sp* outBuffer, sp* outFence, float outTransformMatrix[16]) override { return mProducer->getLastQueuedBuffer(outBuffer, outFence, outTransformMatrix); } void getFrameTimestamps(FrameEventHistoryDelta*) override {} status_t getUniqueId(uint64_t* outId) const override { return mProducer->getUniqueId(outId); } status_t getConsumerUsage(uint64_t* outUsage) const override { return mProducer->getConsumerUsage(outUsage); } protected: sp mProducer; }; class MaliciousBQP : public ProxyBQP { public: MaliciousBQP(const sp& producer) : ProxyBQP(producer) {} void beMalicious(int32_t value) { mMaliciousValue = value; } void setExpectedSlot(int32_t slot) { mExpectedSlot = slot; } // Override dequeueBuffer, optionally corrupting the returned slot number status_t dequeueBuffer(int* buf, sp* fence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override { EXPECT_EQ(BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(buf, fence, width, height, format, usage, outBufferAge, outTimestamps)); EXPECT_EQ(mExpectedSlot, *buf); if (mMaliciousValue != 0) { *buf = mMaliciousValue; return NO_ERROR; } else { return BUFFER_NEEDS_REALLOCATION; } } private: int32_t mMaliciousValue = 0; int32_t mExpectedSlot = 0; }; class DummyListener : public BnConsumerListener { public: void onFrameAvailable(const BufferItem&) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} }; sp getMaliciousBQP() { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp listener = new DummyListener; consumer->consumerConnect(listener, false); sp malicious = new MaliciousBQP(producer); return malicious; } TEST(Malicious, Bug36991414Max) { sp malicious = getMaliciousBQP(); sp surface = new Surface(malicious); ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); ANativeWindow_Buffer buffer; ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); malicious->setExpectedSlot(1); malicious->beMalicious(std::numeric_limits::max()); ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); } TEST(Malicious, Bug36991414Min) { sp malicious = getMaliciousBQP(); sp surface = new Surface(malicious); ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); ANativeWindow_Buffer buffer; ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); malicious->setExpectedSlot(1); malicious->beMalicious(std::numeric_limits::min()); ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); } TEST(Malicious, Bug36991414NegativeOne) { sp malicious = getMaliciousBQP(); sp surface = new Surface(malicious); ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); ANativeWindow_Buffer buffer; ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); malicious->setExpectedSlot(1); malicious->beMalicious(-1); ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); } TEST(Malicious, Bug36991414NumSlots) { sp malicious = getMaliciousBQP(); sp surface = new Surface(malicious); ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false)); ANativeWindow_Buffer buffer; ASSERT_EQ(NO_ERROR, surface->lock(&buffer, nullptr)); ASSERT_EQ(NO_ERROR, surface->unlockAndPost()); malicious->setExpectedSlot(1); malicious->beMalicious(BufferQueueDefs::NUM_BUFFER_SLOTS); ASSERT_EQ(FAILED_TRANSACTION, surface->lock(&buffer, nullptr)); } } // namespace test } // namespace android libs/gui/tests/MultiTextureConsumer_test.cpp0100644 0000000 0000000 00000010276 13300556574 020434 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. */ #define LOG_TAG "MultiTextureConsumer_test" //#define LOG_NDEBUG 0 #include "GLTest.h" #include #include #include #include namespace android { class MultiTextureConsumerTest : public GLTest { protected: enum { TEX_ID = 123 }; virtual void SetUp() { GLTest::SetUp(); sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); mGlConsumer = new GLConsumer(consumer, TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); mSurface = new Surface(producer); mANW = mSurface.get(); } virtual void TearDown() { GLTest::TearDown(); } virtual EGLint const* getContextAttribs() { return NULL; } virtual EGLint const* getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; return sDefaultConfigAttribs; } sp mGlConsumer; sp mSurface; ANativeWindow* mANW; }; TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) { ANativeWindow_Buffer buffer; ASSERT_EQ(native_window_set_usage(mANW, GRALLOC_USAGE_SW_WRITE_OFTEN), NO_ERROR); ASSERT_EQ(native_window_set_buffers_format(mANW, HAL_PIXEL_FORMAT_RGBA_8888), NO_ERROR); glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_CULL_FACE); glViewport(0, 0, getSurfaceWidth(), getSurfaceHeight()); glOrthof(0, getSurfaceWidth(), 0, getSurfaceHeight(), 0, 1); glEnableClientState(GL_VERTEX_ARRAY); glColor4f(1, 1, 1, 1); glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); uint32_t texel = 0x80808080; glBindTexture(GL_TEXTURE_2D, TEX_ID+1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texel); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, TEX_ID+1); glEnable(GL_TEXTURE_2D); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); glEnable(GL_TEXTURE_EXTERNAL_OES); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glClear(GL_COLOR_BUFFER_BIT); for (int i=0 ; i<8 ; i++) { mSurface->lock(&buffer, NULL); memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4); mSurface->unlockAndPost(); mGlConsumer->updateTexImage(); GLfloat vertices[][2] = { {i*16.0f, 0}, {(i+1)*16.0f, 0}, {(i+1)*16.0f, 16.0f}, {i*16.0f, 16.0f} }; glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); } for (int i=0 ; i<8 ; i++) { EXPECT_TRUE(checkPixel(i*16 + 8, 8, i*16, i*16, i*16, i*16, 0)); } } } // namespace android libs/gui/tests/StreamSplitter_test.cpp0100644 0000000 0000000 00000022426 13300556574 017227 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 "StreamSplitter_test" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include namespace android { class StreamSplitterTest : public ::testing::Test { protected: StreamSplitterTest() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); } ~StreamSplitterTest() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); } }; struct DummyListener : public BnConsumerListener { virtual void onFrameAvailable(const BufferItem& /* item */) {} virtual void onBuffersReleased() {} virtual void onSidebandStreamChanged() {} }; static const uint32_t TEST_DATA = 0x12345678u; TEST_F(StreamSplitterTest, OneInputOneOutput) { sp inputProducer; sp inputConsumer; BufferQueue::createBufferQueue(&inputProducer, &inputConsumer); sp outputProducer; sp outputConsumer; BufferQueue::createBufferQueue(&outputProducer, &outputConsumer); ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false)); sp splitter; status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter); ASSERT_EQ(OK, status); ASSERT_EQ(OK, splitter->addOutput(outputProducer)); // Never allow the output BufferQueue to allocate a buffer ASSERT_EQ(OK, outputProducer->allowAllocation(false)); IGraphicBufferProducer::QueueBufferOutput qbOutput; ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast(&dataIn))); *dataIn = TEST_DATA; ASSERT_EQ(OK, buffer->unlock()); IGraphicBufferProducer::QueueBufferInput qbInput(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput)); // Now that we have dequeued/allocated one buffer, prevent any further // allocations ASSERT_EQ(OK, inputProducer->allowAllocation(false)); BufferItem item; ASSERT_EQ(OK, outputConsumer->acquireBuffer(&item, 0)); uint32_t* dataOut; ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast(&dataOut))); ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); // This should succeed even with allocation disabled since it will have // received the buffer back from the output BufferQueue ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); } TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { const int NUM_OUTPUTS = 4; sp inputProducer; sp inputConsumer; BufferQueue::createBufferQueue(&inputProducer, &inputConsumer); sp outputProducers[NUM_OUTPUTS] = {}; sp outputConsumers[NUM_OUTPUTS] = {}; for (int output = 0; output < NUM_OUTPUTS; ++output) { BufferQueue::createBufferQueue(&outputProducers[output], &outputConsumers[output]); ASSERT_EQ(OK, outputConsumers[output]->consumerConnect( new DummyListener, false)); } sp splitter; status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter); ASSERT_EQ(OK, status); for (int output = 0; output < NUM_OUTPUTS; ++output) { ASSERT_EQ(OK, splitter->addOutput(outputProducers[output])); // Never allow the output BufferQueues to allocate a buffer ASSERT_EQ(OK, outputProducers[output]->allowAllocation(false)); } IGraphicBufferProducer::QueueBufferOutput qbOutput; ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); uint32_t* dataIn; ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, reinterpret_cast(&dataIn))); *dataIn = TEST_DATA; ASSERT_EQ(OK, buffer->unlock()); IGraphicBufferProducer::QueueBufferInput qbInput(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput)); // Now that we have dequeued/allocated one buffer, prevent any further // allocations ASSERT_EQ(OK, inputProducer->allowAllocation(false)); for (int output = 0; output < NUM_OUTPUTS; ++output) { BufferItem item; ASSERT_EQ(OK, outputConsumers[output]->acquireBuffer(&item, 0)); uint32_t* dataOut; ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, reinterpret_cast(&dataOut))); ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); ASSERT_EQ(OK, outputConsumers[output]->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); } // This should succeed even with allocation disabled since it will have // received the buffer back from the output BufferQueues ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); } TEST_F(StreamSplitterTest, OutputAbandonment) { sp inputProducer; sp inputConsumer; BufferQueue::createBufferQueue(&inputProducer, &inputConsumer); sp outputProducer; sp outputConsumer; BufferQueue::createBufferQueue(&outputProducer, &outputConsumer); ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false)); sp splitter; status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter); ASSERT_EQ(OK, status); ASSERT_EQ(OK, splitter->addOutput(outputProducer)); IGraphicBufferProducer::QueueBufferOutput qbOutput; ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); int slot; sp fence; sp buffer; ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer)); // Abandon the output outputConsumer->consumerDisconnect(); IGraphicBufferProducer::QueueBufferInput qbInput(0, false, HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); ASSERT_EQ(OK, inputProducer->queueBuffer(slot, qbInput, &qbOutput)); // Input should be abandoned ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr)); } } // namespace android libs/gui/tests/SurfaceTextureClient_test.cpp0100644 0000000 0000000 00000077017 13300556574 020363 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 "SurfaceTextureClient_test" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); #define CROP_EXT_STR "EGL_ANDROID_image_crop" namespace android { class SurfaceTextureClientTest : public ::testing::Test { protected: SurfaceTextureClientTest(): mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), mEglConfig(NULL) { } virtual void SetUp() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); mST = new GLConsumer(consumer, 123, GLConsumer::TEXTURE_EXTERNAL, true, false); mSTC = new Surface(producer); mANW = mSTC; // We need a valid GL context so we can test updateTexImage() // This initializes EGL and create a dummy GL context with a // pbuffer render target. mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); EGLint majorVersion, minorVersion; EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLConfig myConfig; EGLint numConfigs = 0; EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig, 1, &numConfigs)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mEglConfig = myConfig; EGLint pbufferAttribs[] = { EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE }; mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, pbufferAttribs); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurface); mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); } virtual void TearDown() { mST.clear(); mSTC.clear(); mANW.clear(); eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mEglDisplay, mEglContext); eglDestroySurface(mEglDisplay, mEglSurface); eglTerminate(mEglDisplay); const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); } virtual EGLint const* getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | EGL_WINDOW_BIT, EGL_NONE }; return sDefaultConfigAttribs; } sp mST; sp mSTC; sp mANW; EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLContext mEglContext; EGLConfig mEglConfig; }; TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { sp ist(mSTC->getIGraphicBufferProducer()); ASSERT_TRUE(ist != NULL); } TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { int result = -123; int err = mANW->query(mANW.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(0, result); } TEST_F(SurfaceTextureClientTest, ConcreteTypeIsSurfaceTextureClient) { int result = -123; int err = mANW->query(mANW.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); } TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_DISPLAY, dpy); EGLint majorVersion; EGLint minorVersion; EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLConfig myConfig = {0}; 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_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_NONE }; EXPECT_TRUE(eglChooseConfig(dpy, configAttribs, &myConfig, 1, &numConfigs)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(), NULL); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); if (eglSurface != EGL_NO_SURFACE) { eglDestroySurface(dpy, eglSurface); } eglTerminate(dpy); } TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) { EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); EGLBoolean success = eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext); EXPECT_TRUE(success); glClear(GL_COLOR_BUFFER_BIT); success = eglSwapBuffers(mEglDisplay, eglSurface); EXPECT_TRUE(success); mST->abandon(); glClear(GL_COLOR_BUFFER_BIT); success = eglSwapBuffers(mEglDisplay, eglSurface); EXPECT_FALSE(success); EXPECT_EQ(EGL_BAD_SURFACE, eglGetError()); success = eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); ASSERT_TRUE(success); if (eglSurface != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, eglSurface); } } TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) { EXPECT_GT(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 8)); EXPECT_GT(OK, native_window_set_buffers_dimensions(mANW.get(), 8, 0)); } TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buf; ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buf; EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); sp st(mST); ANativeWindowBuffer* buf; EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) { ANativeWindowBuffer* buf[2]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(16, buf[0]->width); EXPECT_EQ(16, buf[1]->width); EXPECT_EQ(8, buf[0]->height); EXPECT_EQ(8, buf[1]->height); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { ANativeWindowBuffer* buf[2]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(16, buf[0]->width); EXPECT_EQ(16, buf[1]->width); EXPECT_EQ(8, buf[0]->height); EXPECT_EQ(8, buf[1]->height); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 12, 24)); EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(12, buf[0]->width); EXPECT_EQ(12, buf[1]->width); EXPECT_EQ(24, buf[0]->height); EXPECT_EQ(24, buf[1]->height); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 0)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); ASSERT_EQ(OK, mANW->setSwapInterval(mANW.get(), 1)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(OK, mST->updateTexImage()); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); EXPECT_NE(buf[1], buf[2]); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } // XXX: We currently have no hardware that properly handles dequeuing the // buffer that is currently bound to the texture. TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) { android_native_buffer_t* buf[3]; android_native_buffer_t* firstBuf; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &firstBuf)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf, -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), firstBuf); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); EXPECT_EQ(firstBuf, buf[2]); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { android_native_buffer_t* buf[3]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // We should be able to dequeue all the buffers before we've queued mANWy. EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2], -1)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); EXPECT_EQ(OK, mST->updateTexImage()); EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); // Once we've queued a buffer, however we should not be able to dequeue more // than (buffer-count - MIN_UNDEQUEUED_BUFFERS), which is 2 in this case. EXPECT_EQ(INVALID_OPERATION, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2], -1)); } TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); android_native_rect_t rect = {-2, -13, 40, 18}; native_window_set_crop(mANW.get(), &rect); ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 4, 4)); android_native_buffer_t* buf; ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf, -1)); ASSERT_EQ(OK, mST->updateTexImage()); Rect crop = mST->getCurrentCrop(); EXPECT_EQ(0, crop.left); EXPECT_EQ(0, crop.top); EXPECT_EQ(4, crop.right); EXPECT_EQ(4, crop.bottom); } // XXX: This is not expected to pass until the synchronization hacks are removed // from the SurfaceTexture class. TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { class MyThread : public Thread { sp mST; EGLContext ctx; EGLSurface sur; EGLDisplay dpy; bool mBufferRetired; Mutex mLock; virtual bool threadLoop() { eglMakeCurrent(dpy, sur, sur, ctx); usleep(20000); Mutex::Autolock _l(mLock); mST->updateTexImage(); mBufferRetired = true; eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); return false; } public: explicit MyThread(const sp& mST) : mST(mST), mBufferRetired(false) { ctx = eglGetCurrentContext(); sur = eglGetCurrentSurface(EGL_DRAW); dpy = eglGetCurrentDisplay(); eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } ~MyThread() { eglMakeCurrent(dpy, sur, sur, ctx); } void bufferDequeued() { Mutex::Autolock _l(mLock); EXPECT_EQ(true, mBufferRetired); } }; android_native_buffer_t* buf[3]; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // dequeue/queue/update so we have a current buffer ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); mST->updateTexImage(); MyThread* thread = new MyThread(mST); sp threadBase(thread); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); thread->run("MyThread"); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1], -1)); //ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[2])); //ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2], -1)); thread->bufferDequeued(); thread->requestExitAndWait(); } TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) { android_native_buffer_t* buf[3]; float mtx[16] = {}; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mST->updateTexImage()); mST->getTransformMatrix(mtx); EXPECT_EQ(1.f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); EXPECT_EQ(-1.f, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); EXPECT_EQ(0.f, mtx[8]); EXPECT_EQ(0.f, mtx[9]); EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); EXPECT_EQ(0.f, mtx[12]); EXPECT_EQ(1.f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) { android_native_buffer_t* buf[3]; float mtx[16] = {}; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mST->updateTexImage()); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); EXPECT_EQ(1.f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); EXPECT_EQ(-1.f, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); EXPECT_EQ(0.f, mtx[8]); EXPECT_EQ(0.f, mtx[9]); EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); EXPECT_EQ(0.f, mtx[12]); EXPECT_EQ(1.f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { // Query to see if the image crop extension exists EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); size_t cropExtLen = strlen(CROP_EXT_STR); size_t extsLen = strlen(exts); bool equal = !strcmp(CROP_EXT_STR, exts); bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); bool atEnd = (cropExtLen+1) < extsLen && !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); bool hasEglAndroidImageCrop = equal || atStart || atEnd || inMiddle; android_native_buffer_t* buf[3]; float mtx[16] = {}; android_native_rect_t crop; crop.left = 0; crop.top = 0; crop.right = 5; crop.bottom = 5; ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 8, 8)); ASSERT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0)); ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0])); ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &crop)); ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1)); ASSERT_EQ(OK, mST->updateTexImage()); ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); // If the egl image crop extension is not present, this accounts for the // .5 texel shrink for each edge that's included in the transform matrix // to avoid texturing outside the crop region. Otherwise the crop is not // included in the transform matrix. EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); EXPECT_EQ(hasEglAndroidImageCrop ? -1 : -0.5, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); EXPECT_EQ(0.f, mtx[8]); EXPECT_EQ(0.f, mtx[9]); EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); EXPECT_EQ(hasEglAndroidImageCrop ? 0 : 0.0625f, mtx[12]); EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5625f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } // This test verifies that the buffer format can be queried immediately after // it is set. TEST_F(SurfaceTextureClientTest, QueryFormatAfterSettingWorks) { sp anw(mSTC); int fmts[] = { // RGBA_8888 should not come first, as it's the default HAL_PIXEL_FORMAT_RGBX_8888, HAL_PIXEL_FORMAT_RGBA_8888, HAL_PIXEL_FORMAT_RGB_888, HAL_PIXEL_FORMAT_RGB_565, HAL_PIXEL_FORMAT_BGRA_8888, HAL_PIXEL_FORMAT_YV12, }; const int numFmts = (sizeof(fmts) / sizeof(fmts[0])); for (int i = 0; i < numFmts; i++) { int fmt = -1; ASSERT_EQ(OK, native_window_set_buffers_dimensions(anw.get(), 0, 0)); ASSERT_EQ(OK, native_window_set_buffers_format(anw.get(), fmts[i])); ASSERT_EQ(OK, anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt)); EXPECT_EQ(fmts[i], fmt); } } class MultiSurfaceTextureClientTest : public ::testing::Test { public: MultiSurfaceTextureClientTest() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) { for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { mEglSurfaces[i] = EGL_NO_CONTEXT; } } protected: enum { NUM_SURFACE_TEXTURES = 32 }; virtual void SetUp() { mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); EGLint majorVersion, minorVersion; EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLConfig myConfig; EGLint numConfigs = 0; EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &myConfig, 1, &numConfigs)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp st(new GLConsumer(consumer, i, GLConsumer::TEXTURE_EXTERNAL, true, false)); sp stc(new Surface(producer)); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, static_cast(stc.get()), NULL); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]); } } virtual void TearDown() { eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { if (mEglSurfaces[i] != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, mEglSurfaces[i]); } } if (mEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mEglContext); } if (mEglDisplay != EGL_NO_DISPLAY) { eglTerminate(mEglDisplay); } } EGLDisplay mEglDisplay; EGLSurface mEglSurfaces[NUM_SURFACE_TEXTURES]; EGLContext mEglContext; }; // XXX: This test is disabled because it causes a hang on some devices. See bug // 5015672. TEST_F(MultiSurfaceTextureClientTest, DISABLED_MakeCurrentBetweenSurfacesWorks) { for (int iter = 0; iter < 8; iter++) { for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) { eglMakeCurrent(mEglDisplay, mEglSurfaces[i], mEglSurfaces[i], mEglContext); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mEglSurfaces[i]); } } } } // namespace android libs/gui/tests/SurfaceTextureFBO.h0100644 0000000 0000000 00000004315 13300556574 016150 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. */ #ifndef ANDROID_SURFACE_TEXTURE_FBO_H #define ANDROID_SURFACE_TEXTURE_FBO_H #include "SurfaceTextureGL.h" #include namespace android { class SurfaceTextureFBOTest : public SurfaceTextureGLTest { protected: virtual void SetUp() { SurfaceTextureGLTest::SetUp(); glGenFramebuffers(1, &mFbo); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glGenTextures(1, &mFboTex); glBindTexture(GL_TEXTURE_2D, mFboTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glBindFramebuffer(GL_FRAMEBUFFER, mFbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFboTex, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); } virtual void TearDown() { SurfaceTextureGLTest::TearDown(); glDeleteTextures(1, &mFboTex); glDeleteFramebuffers(1, &mFbo); } GLuint mFbo; GLuint mFboTex; }; void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { const size_t PIXEL_SIZE = 4; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { off_t offset = (y * stride + x) * PIXEL_SIZE; buf[offset + 0] = r; buf[offset + 1] = g; buf[offset + 2] = b; buf[offset + 3] = a; } } } } // namespace android #endif libs/gui/tests/SurfaceTextureFBO_test.cpp0100644 0000000 0000000 00000005754 13300556574 017552 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. */ #define LOG_TAG "SurfaceTextureFBO_test" //#define LOG_NDEBUG 0 #include "SurfaceTextureFBO.h" namespace android { // This test is intended to verify that proper synchronization is done when // rendering into an FBO. TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { const int texWidth = 64; const int texHeight = 64; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_RGBA_8888)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); sp buf(GraphicBuffer::from(anb)); // Fill the buffer with green uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, 0, 255); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glBindFramebuffer(GL_FRAMEBUFFER, mFbo); drawTexture(); glBindFramebuffer(GL_FRAMEBUFFER, 0); for (int i = 0; i < 4; i++) { SCOPED_TRACE(String8::format("frame %d", i).string()); ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); buf = GraphicBuffer::from(anb); // Fill the buffer with red ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img))); fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0, 0, 255); ASSERT_EQ(NO_ERROR, buf->unlock()); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); drawTexture(); EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); } glBindFramebuffer(GL_FRAMEBUFFER, mFbo); EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); } } // namespace android libs/gui/tests/SurfaceTextureGL.h0100644 0000000 0000000 00000003646 13300556574 016052 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. */ #ifndef ANDROID_SURFACE_TEXTURE_GL_H #define ANDROID_SURFACE_TEXTURE_GL_H #include "GLTest.h" #include "FrameWaiter.h" #include "TextureRenderer.h" #include #include namespace android { class FrameWaiter; class GLConsumer; class TextureRenderer; class SurfaceTextureGLTest : public GLTest { protected: enum { TEX_ID = 123 }; void SetUp() { GLTest::SetUp(); sp producer; BufferQueue::createBufferQueue(&producer, &mConsumer); mST = new GLConsumer(mConsumer, TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false); mSTC = new Surface(producer); mANW = mSTC; mTextureRenderer = new TextureRenderer(TEX_ID, mST); ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp()); mFW = new FrameWaiter; mST->setFrameAvailableListener(mFW); } void TearDown() { mTextureRenderer.clear(); mANW.clear(); mSTC.clear(); mST.clear(); GLTest::TearDown(); } void drawTexture() { mTextureRenderer->drawTexture(); } sp mConsumer; sp mST; sp mSTC; sp mANW; sp mTextureRenderer; sp mFW; }; } // namespace android #endif libs/gui/tests/SurfaceTextureGLThreadToGL.h0100644 0000000 0000000 00000013774 13300556574 017733 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. */ #ifndef ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H #define ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H #include "SurfaceTextureGLToGL.h" namespace android { /* * This test fixture is for testing GL -> GL texture streaming from one thread * to another. It contains functionality to create a producer thread that will * perform GL rendering to an ANativeWindow that feeds frames to a * GLConsumer. Additionally it supports interlocking the producer and * consumer threads so that a specific sequence of calls can be * deterministically created by the test. * * The intended usage is as follows: * * TEST_F(...) { * class PT : public ProducerThread { * virtual void render() { * ... * swapBuffers(); * } * }; * * runProducerThread(new PT()); * * // The order of these calls will vary from test to test and may include * // multiple frames and additional operations (e.g. GL rendering from the * // texture). * fc->waitForFrame(); * mST->updateTexImage(); * fc->finishFrame(); * } * */ class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { protected: // ProducerThread is an abstract base class to simplify the creation of // OpenGL ES frame producer threads. class ProducerThread : public Thread { public: virtual ~ProducerThread() { } void setEglObjects(EGLDisplay producerEglDisplay, EGLSurface producerEglSurface, EGLContext producerEglContext) { mProducerEglDisplay = producerEglDisplay; mProducerEglSurface = producerEglSurface; mProducerEglContext = producerEglContext; } virtual bool threadLoop() { eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext); render(); eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); return false; } protected: virtual void render() = 0; void swapBuffers() { eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface); } EGLDisplay mProducerEglDisplay; EGLSurface mProducerEglSurface; EGLContext mProducerEglContext; }; // FrameCondition is a utility class for interlocking between the producer // and consumer threads. The FrameCondition object should be created and // destroyed in the consumer thread only. The consumer thread should set // the FrameCondition as the FrameAvailableListener of the GLConsumer, // and should call both waitForFrame and finishFrame once for each expected // frame. // // This interlocking relies on the fact that onFrameAvailable gets called // synchronously from GLConsumer::queueBuffer. class FrameCondition : public GLConsumer::FrameAvailableListener { public: FrameCondition(): mFrameAvailable(false), mFrameFinished(false) { } // waitForFrame waits for the next frame to arrive. This should be // called from the consumer thread once for every frame expected by the // test. void waitForFrame() { Mutex::Autolock lock(mMutex); ALOGV("+waitForFrame"); while (!mFrameAvailable) { mFrameAvailableCondition.wait(mMutex); } mFrameAvailable = false; ALOGV("-waitForFrame"); } // Allow the producer to return from its swapBuffers call and continue // on to produce the next frame. This should be called by the consumer // thread once for every frame expected by the test. void finishFrame() { Mutex::Autolock lock(mMutex); ALOGV("+finishFrame"); mFrameFinished = true; mFrameFinishCondition.signal(); ALOGV("-finishFrame"); } // This should be called by GLConsumer on the producer thread. virtual void onFrameAvailable(const BufferItem& /* item */) { Mutex::Autolock lock(mMutex); ALOGV("+onFrameAvailable"); mFrameAvailable = true; mFrameAvailableCondition.signal(); while (!mFrameFinished) { mFrameFinishCondition.wait(mMutex); } mFrameFinished = false; ALOGV("-onFrameAvailable"); } protected: bool mFrameAvailable; bool mFrameFinished; Mutex mMutex; Condition mFrameAvailableCondition; Condition mFrameFinishCondition; }; virtual void SetUp() { SurfaceTextureGLToGLTest::SetUp(); mFC = new FrameCondition(); mST->setFrameAvailableListener(mFC); } virtual void TearDown() { if (mProducerThread != NULL) { mProducerThread->requestExitAndWait(); } mProducerThread.clear(); mFC.clear(); SurfaceTextureGLToGLTest::TearDown(); } void runProducerThread(const sp producerThread) { ASSERT_TRUE(mProducerThread == NULL); mProducerThread = producerThread; producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, mProducerEglContext); producerThread->run("ProducerThread"); } sp mProducerThread; sp mFC; }; } // namespace android #endif libs/gui/tests/SurfaceTextureGLThreadToGL_test.cpp0100644 0000000 0000000 00000013753 13300556574 021322 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. */ #define LOG_TAG "SurfaceTextureGLThreadToGL_test" //#define LOG_NDEBUG 0 #include "SurfaceTextureGLThreadToGL.h" namespace android { TEST_F(SurfaceTextureGLThreadToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); swapBuffers(); } }; SetUpWindowAndContext(); runProducerThread(new PT()); mFC->waitForFrame(); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); mFC->finishFrame(); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } TEST_F(SurfaceTextureGLThreadToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); swapBuffers(); } }; SetUpWindowAndContext(); runProducerThread(new PT()); mFC->waitForFrame(); mFC->finishFrame(); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } TEST_F(SurfaceTextureGLThreadToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { virtual void render() { for (int i = 0; i < NUM_ITERATIONS; i++) { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ALOGV("+swapBuffers"); swapBuffers(); ALOGV("-swapBuffers"); } } }; SetUpWindowAndContext(); runProducerThread(new PT()); for (int i = 0; i < NUM_ITERATIONS; i++) { mFC->waitForFrame(); ALOGV("+updateTexImage"); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ALOGV("-updateTexImage"); mFC->finishFrame(); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } } TEST_F(SurfaceTextureGLThreadToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { virtual void render() { for (int i = 0; i < NUM_ITERATIONS; i++) { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ALOGV("+swapBuffers"); swapBuffers(); ALOGV("-swapBuffers"); } } }; SetUpWindowAndContext(); runProducerThread(new PT()); for (int i = 0; i < NUM_ITERATIONS; i++) { mFC->waitForFrame(); mFC->finishFrame(); ALOGV("+updateTexImage"); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ALOGV("-updateTexImage"); // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } } // XXX: This test is disabled because it is currently hanging on some devices. TEST_F(SurfaceTextureGLThreadToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { enum { NUM_ITERATIONS = 64 }; class PT : public ProducerThread { virtual void render() { for (int i = 0; i < NUM_ITERATIONS; i++) { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); ALOGV("+swapBuffers"); swapBuffers(); ALOGV("-swapBuffers"); } } }; SetUpWindowAndContext(); runProducerThread(new PT()); // Allow three frames to be rendered and queued before starting the // rendering in this thread. For the latter two frames we don't call // updateTexImage so the next dequeue from the producer thread will block // waiting for a frame to become available. mFC->waitForFrame(); mFC->finishFrame(); // We must call updateTexImage to consume the first frame so that the // SurfaceTexture is able to reduce the buffer count to 2. This is because // the GL driver may dequeue a buffer when the EGLSurface is created, and // that happens before we call setDefaultMaxBufferCount. It's possible that the // driver does not dequeue a buffer at EGLSurface creation time, so we // cannot rely on this to cause the second dequeueBuffer call to block. ASSERT_EQ(NO_ERROR, mST->updateTexImage()); mFC->waitForFrame(); mFC->finishFrame(); mFC->waitForFrame(); mFC->finishFrame(); // Sleep for 100ms to allow the producer thread's dequeueBuffer call to // block waiting for a buffer to become available. usleep(100000); // Render and present a number of images. This thread should not be blocked // by the fact that the producer thread is blocking in dequeue. for (int i = 0; i < NUM_ITERATIONS; i++) { glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mEglSurface); } // Consume the two pending buffers to unblock the producer thread. ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); // Consume the remaining buffers from the producer thread. for (int i = 0; i < NUM_ITERATIONS-3; i++) { mFC->waitForFrame(); mFC->finishFrame(); ALOGV("+updateTexImage"); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ALOGV("-updateTexImage"); } } } // namespace android libs/gui/tests/SurfaceTextureGLToGL.h0100644 0000000 0000000 00000004106 13300556574 016570 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. */ #ifndef ANDROID_SURFACE_TEXTURE_GL_TO_GL_H #define ANDROID_SURFACE_TEXTURE_GL_TO_GL_H #include "SurfaceTextureGL.h" namespace android { /* * This test fixture is for testing GL -> GL texture streaming. It creates an * EGLSurface and an EGLContext for the image producer to use. */ class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { protected: SurfaceTextureGLToGLTest(): mProducerEglSurface(EGL_NO_SURFACE), mProducerEglContext(EGL_NO_CONTEXT) { } virtual void SetUp() { SurfaceTextureGLTest::SetUp(); } void SetUpWindowAndContext() { mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, mANW.get(), NULL); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); mProducerEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext); } virtual void TearDown() { if (mProducerEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mProducerEglContext); } if (mProducerEglSurface != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, mProducerEglSurface); } SurfaceTextureGLTest::TearDown(); } EGLSurface mProducerEglSurface; EGLContext mProducerEglContext; }; } // namespace android #endif libs/gui/tests/SurfaceTextureGLToGL_test.cpp0100644 0000000 0000000 00000044303 13300556574 020165 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. */ #define LOG_TAG "SurfaceTextureGLToGL_test" //#define LOG_NDEBUG 0 #include "SurfaceTextureGLToGL.h" namespace android { TEST_F(SurfaceTextureGLToGLTest, TransformHintGetsRespected) { const uint32_t texWidth = 32; const uint32_t texHeight = 64; mST->setDefaultBufferSize(texWidth, texHeight); mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); // This test requires 3 buffers to avoid deadlock because we're // both producer and consumer, and only using one thread. Set max dequeued // to 2, and max acquired already defaults to 1. ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(2)); SetUpWindowAndContext(); // Do the producer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Start a buffer with our chosen size and transform hint moving // through the system. glClear(GL_COLOR_BUFFER_BIT); // give the driver something to do eglSwapBuffers(mEglDisplay, mProducerEglSurface); mST->updateTexImage(); // consume it // Swap again. glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); mST->updateTexImage(); // The current buffer should either show the effects of the transform // hint (in the form of an inverse transform), or show that the // transform hint has been ignored. sp buf = mST->getCurrentBuffer(); if (mST->getCurrentTransform() == NATIVE_WINDOW_TRANSFORM_ROT_270) { ASSERT_EQ(texWidth, buf->getHeight()); ASSERT_EQ(texHeight, buf->getWidth()); } else { ASSERT_EQ(texWidth, buf->getWidth()); ASSERT_EQ(texHeight, buf->getHeight()); } // Reset the transform hint and confirm that it takes. mST->setTransformHint(0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); mST->updateTexImage(); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); mST->updateTexImage(); buf = mST->getCurrentBuffer(); ASSERT_EQ((uint32_t) 0, mST->getCurrentTransform()); ASSERT_EQ(texWidth, buf->getWidth()); ASSERT_EQ(texHeight, buf->getHeight()); } TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { const int texWidth = 64; const int texHeight = 64; mST->setDefaultBufferSize(texWidth, texHeight); // This test requires 3 buffers to complete run on a single thread. // Set max dequeued to 2, and max acquired already defaults to 1. ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(2)); SetUpWindowAndContext(); // Do the producer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // This is needed to ensure we pick up a buffer of the correct size. eglSwapBuffers(mEglDisplay, mProducerEglSurface); glClearColor(0.6, 0.6, 0.6, 0.6); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(4, 4, 4, 4); glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glScissor(24, 48, 4, 4); glClearColor(0.0, 1.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glScissor(37, 17, 4, 4); glClearColor(0.0, 0.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); glDisable(GL_SCISSOR_TEST); // Skip the first frame, which was empty ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255)); EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255)); EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255)); EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); } TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { SetUpWindowAndContext(); sp buffers[2]; // This test requires async mode to run on a single thread. EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); for (int i = 0; i < 2; i++) { // Produce a frame EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Consume a frame EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mFW->waitForFrame(); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); buffers[i] = mST->getCurrentBuffer(); } // Destroy the GL texture object to release its ref on buffers[2]. GLuint texID = TEX_ID; glDeleteTextures(1, &texID); // Destroy the EGLSurface EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mProducerEglSurface = EGL_NO_SURFACE; // This test should have the only reference to buffer 0. EXPECT_EQ(1, buffers[0]->getStrongCount()); // The GLConsumer should hold one reference to buffer 1 in its // mCurrentTextureImage member and another reference in mEglSlots. The third // reference is in this test. EXPECT_EQ(3, buffers[1]->getStrongCount()); } TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { SetUpWindowAndContext(); sp buffers[3]; // This test requires async mode to run on a single thread. EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); for (int i = 0; i < 3; i++) { // Produce a frame EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); glClear(GL_COLOR_BUFFER_BIT); EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Consume a frame EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mFW->waitForFrame(); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); buffers[i] = mST->getCurrentBuffer(); } // Abandon the GLConsumer, releasing the ref that the GLConsumer has // on buffers[2]. mST->abandon(); // Destroy the GL texture object to release its ref on buffers[2]. GLuint texID = TEX_ID; glDeleteTextures(1, &texID); // Destroy the EGLSurface. EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mProducerEglSurface = EGL_NO_SURFACE; EXPECT_EQ(1, buffers[1]->getStrongCount()); // Depending on how lazily the GL driver dequeues buffers, we may end up // with either two or three total buffers. If there are three, each entry // of the buffers array will be unique and there should only be one // reference (the one in this test). If there are two the first and last // element in the array will be equal meaning that buffer representing both // 0 and 2 will have two references (one for 0 and one for 2). if (buffers[2] != buffers[0]) { EXPECT_EQ(1, buffers[0]->getStrongCount()); EXPECT_EQ(1, buffers[2]->getStrongCount()); } else { EXPECT_EQ(2, buffers[0]->getStrongCount()); } } TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentBeforeConsumerDeathUnrefsBuffers) { SetUpWindowAndContext(); sp buffer; EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); // Produce a frame glClear(GL_COLOR_BUFFER_BIT); EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Destroy the EGLSurface. EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mProducerEglSurface = EGL_NO_SURFACE; mSTC.clear(); mANW.clear(); mTextureRenderer.clear(); // Consume a frame ASSERT_EQ(NO_ERROR, mST->updateTexImage()); buffer = mST->getCurrentBuffer(); // Destroy the GL texture object to release its ref GLuint texID = TEX_ID; glDeleteTextures(1, &texID); // make un-current, all references to buffer should be gone EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); // Destroy consumer mST.clear(); EXPECT_EQ(1, buffer->getStrongCount()); } TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentAfterConsumerDeathUnrefsBuffers) { SetUpWindowAndContext(); sp buffer; EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); // Produce a frame glClear(GL_COLOR_BUFFER_BIT); EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Destroy the EGLSurface. EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mProducerEglSurface = EGL_NO_SURFACE; mSTC.clear(); mANW.clear(); mTextureRenderer.clear(); // Consume a frame ASSERT_EQ(NO_ERROR, mST->updateTexImage()); buffer = mST->getCurrentBuffer(); // Destroy the GL texture object to release its ref GLuint texID = TEX_ID; glDeleteTextures(1, &texID); // Destroy consumer mST.clear(); // make un-current, all references to buffer should be gone EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); EXPECT_EQ(1, buffer->getStrongCount()); } TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 64 }; // This test requires 3 buffers to complete run on a single thread. // Set max dequeued to 2, and max acquired already defaults to 1. ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(2)); SetUpWindowAndContext(); // Set the user buffer size. native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); // Do the producer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // This is needed to ensure we pick up a buffer of the correct size. eglSwapBuffers(mEglDisplay, mProducerEglSurface); glClearColor(0.6, 0.6, 0.6, 0.6); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(4, 4, 1, 1); glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); glDisable(GL_SCISSOR_TEST); // Skip the first frame, which was empty ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 4, 4, 255, 0, 0, 255)); EXPECT_TRUE(checkPixel( 5, 5, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 3, 3, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(45, 52, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(12, 36, 153, 153, 153, 153)); } TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedUserSizedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 16 }; // This test requires 3 buffers to complete run on a single thread. // Set max dequeued to 2, and max acquired already defaults to 1. ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(2)); SetUpWindowAndContext(); // Set the transform hint. mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); // Set the user buffer size. native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); // Do the producer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // This is needed to ensure we pick up a buffer of the correct size and the // new rotation hint. eglSwapBuffers(mEglDisplay, mProducerEglSurface); glClearColor(0.6, 0.6, 0.6, 0.6); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(24, 4, 1, 1); glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); glDisable(GL_SCISSOR_TEST); // Skip the first frame, which was empty ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 15, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 0, 15, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(24, 4, 255, 0, 0, 255)); EXPECT_TRUE(checkPixel(25, 5, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(23, 3, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(45, 13, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(12, 8, 153, 153, 153, 153)); } TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) { enum { texWidth = 64 }; enum { texHeight = 16 }; // This test requires 3 buffers to complete run on a single thread. // Set max dequeued to 2, and max acquired already defaults to 1. ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(2)); SetUpWindowAndContext(); // Set the transform hint. mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); // Set the default buffer size. mST->setDefaultBufferSize(texWidth, texHeight); // Do the producer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // This is needed to ensure we pick up a buffer of the correct size and the // new rotation hint. eglSwapBuffers(mEglDisplay, mProducerEglSurface); glClearColor(0.6, 0.6, 0.6, 0.6); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(24, 4, 1, 1); glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); glDisable(GL_SCISSOR_TEST); // Skip the first frame, which was empty ASSERT_EQ(NO_ERROR, mST->updateTexImage()); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(63, 15, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel( 0, 15, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(24, 4, 255, 0, 0, 255)); EXPECT_TRUE(checkPixel(25, 5, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(23, 3, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(45, 13, 153, 153, 153, 153)); EXPECT_TRUE(checkPixel(12, 8, 153, 153, 153, 153)); } } // namespace android libs/gui/tests/SurfaceTextureGL_test.cpp0100644 0000000 0000000 00000064034 13300556574 017442 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 "SurfaceTextureGL_test" //#define LOG_NDEBUG 0 #include "SurfaceTextureGL.h" #include "DisconnectWaiter.h" #include "FillBuffer.h" namespace android { TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { const int texWidth = 64; const int texHeight = 66; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); sp buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255, 3)); EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255, 3)); EXPECT_TRUE(checkPixel(63, 65, 0, 133, 0, 255, 3)); EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255, 3)); EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255, 3)); EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255, 3)); EXPECT_TRUE(checkPixel(52, 51, 98, 255, 73, 255, 3)); EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255, 3)); EXPECT_TRUE(checkPixel(31, 9, 107, 24, 87, 255, 3)); EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255, 3)); EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255, 3)); } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { const int texWidth = 64; const int texHeight = 64; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); sp buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 0, 133, 0, 255)); EXPECT_TRUE(checkPixel(63, 0, 255, 127, 255, 255)); EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255)); EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255)); EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255, 3)); EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255, 3)); EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255, 3)); EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255, 3)); EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255, 3)); EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255, 3)); EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255, 3)); } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { const int texWidth = 64; const int texHeight = 66; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); android_native_rect_t crops[] = { {4, 6, 22, 36}, {0, 6, 22, 36}, {4, 0, 22, 36}, {4, 6, texWidth, 36}, {4, 6, 22, texHeight}, }; for (int i = 0; i < 5; i++) { const android_native_rect_t& crop(crops[i]); SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, crop.top, crop.right, crop.bottom).string()); ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); sp buf(GraphicBuffer::from(anb)); uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 64, 64); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(63, 0, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(63, 63, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel( 0, 63, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(25, 14, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(35, 31, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(57, 6, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel( 5, 42, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(32, 33, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(16, 26, 82, 255, 35, 255)); EXPECT_TRUE(checkPixel(46, 51, 82, 255, 35, 255)); } } // This test is intended to catch synchronization bugs between the CPU-written // and GPU-read buffers. TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { enum { texWidth = 16 }; enum { texHeight = 16 }; enum { numFrames = 1024 }; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_WRITE_OFTEN)); struct TestPixel { int x; int y; }; const TestPixel testPixels[] = { { 4, 11 }, { 12, 14 }, { 7, 2 }, }; enum {numTestPixels = sizeof(testPixels) / sizeof(testPixels[0])}; class ProducerThread : public Thread { public: ProducerThread(const sp& anw, const TestPixel* testPixels): mANW(anw), mTestPixels(testPixels) { } virtual ~ProducerThread() { } virtual bool threadLoop() { for (int i = 0; i < numFrames; i++) { ANativeWindowBuffer* anb; if (native_window_dequeue_buffer_and_wait(mANW.get(), &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } sp buf(GraphicBuffer::from(anb)); const int yuvTexOffsetY = 0; int stride = buf->getStride(); int yuvTexStrideY = stride; int yuvTexOffsetV = yuvTexStrideY * texHeight; int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; int yuvTexStrideU = yuvTexStrideV; uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); // Gray out all the test pixels first, so we're more likely to // see a failure if GL is still texturing from the buffer we // just dequeued. for (int j = 0; j < numTestPixels; j++) { int x = mTestPixels[j].x; int y = mTestPixels[j].y; uint8_t value = 128; img[y*stride + x] = value; } // Fill the buffer with gray. for (int y = 0; y < texHeight; y++) { for (int x = 0; x < texWidth; x++) { img[yuvTexOffsetY + y*yuvTexStrideY + x] = 128; img[yuvTexOffsetU + (y/2)*yuvTexStrideU + x/2] = 128; img[yuvTexOffsetV + (y/2)*yuvTexStrideV + x/2] = 128; } } // Set the test pixels to either white or black. for (int j = 0; j < numTestPixels; j++) { int x = mTestPixels[j].x; int y = mTestPixels[j].y; uint8_t value = 0; if (j == (i % numTestPixels)) { value = 255; } img[y*stride + x] = value; } buf->unlock(); if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1) != NO_ERROR) { return false; } } return false; } sp mANW; const TestPixel* mTestPixels; }; sp pt(new ProducerThread(mANW, testPixels)); pt->run("ProducerThread"); glViewport(0, 0, texWidth, texHeight); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); // We wait for the first two frames up front so that the producer will be // likely to dequeue the buffer that's currently being textured from. mFW->waitForFrame(); mFW->waitForFrame(); for (int i = 0; i < numFrames; i++) { SCOPED_TRACE(String8::format("frame %d", i).string()); // We must wait for each frame to come in because if we ever do an // updateTexImage call that doesn't consume a newly available buffer // then the producer and consumer will get out of sync, which will cause // a deadlock. if (i > 1) { mFW->waitForFrame(); } ASSERT_EQ(NO_ERROR, mST->updateTexImage()); drawTexture(); for (int j = 0; j < numTestPixels; j++) { int x = testPixels[j].x; int y = testPixels[j].y; uint8_t value = 0; if (j == (i % numTestPixels)) { // We must y-invert the texture coords EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255)); } else { // We must y-invert the texture coords EXPECT_TRUE(checkPixel(x, texHeight-y-1, 0, 0, 0, 255)); } } } pt->requestExitAndWait(); } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferNpot) { const int texWidth = 64; const int texHeight = 66; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_RGBA_8888)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); EXPECT_TRUE(checkPixel(63, 0, 231, 231, 231, 231)); EXPECT_TRUE(checkPixel(63, 65, 231, 231, 231, 231)); EXPECT_TRUE(checkPixel( 0, 65, 35, 35, 35, 35)); EXPECT_TRUE(checkPixel(15, 10, 35, 231, 231, 231)); EXPECT_TRUE(checkPixel(23, 65, 231, 35, 231, 35)); EXPECT_TRUE(checkPixel(19, 40, 35, 231, 35, 35)); EXPECT_TRUE(checkPixel(38, 30, 231, 35, 35, 35)); EXPECT_TRUE(checkPixel(42, 54, 35, 35, 35, 231)); EXPECT_TRUE(checkPixel(37, 34, 35, 231, 231, 231)); EXPECT_TRUE(checkPixel(31, 8, 231, 35, 35, 231)); EXPECT_TRUE(checkPixel(37, 47, 231, 35, 231, 231)); EXPECT_TRUE(checkPixel(25, 38, 35, 35, 35, 35)); EXPECT_TRUE(checkPixel(49, 6, 35, 231, 35, 35)); EXPECT_TRUE(checkPixel(54, 50, 35, 231, 231, 231)); EXPECT_TRUE(checkPixel(27, 26, 231, 231, 231, 231)); EXPECT_TRUE(checkPixel(10, 6, 35, 35, 231, 231)); EXPECT_TRUE(checkPixel(29, 4, 35, 35, 35, 231)); EXPECT_TRUE(checkPixel(55, 28, 35, 35, 231, 35)); EXPECT_TRUE(checkPixel(58, 55, 35, 35, 231, 231)); } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { const int texWidth = 64; const int texHeight = 64; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), texWidth, texHeight)); ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), HAL_PIXEL_FORMAT_RGBA_8888)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); ASSERT_EQ(NO_ERROR, mST->updateTexImage()); glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, texWidth, texHeight); drawTexture(); EXPECT_TRUE(checkPixel( 0, 0, 231, 231, 231, 231)); EXPECT_TRUE(checkPixel(63, 0, 35, 35, 35, 35)); EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231)); EXPECT_TRUE(checkPixel( 0, 63, 35, 35, 35, 35)); EXPECT_TRUE(checkPixel(12, 46, 231, 231, 231, 35)); EXPECT_TRUE(checkPixel(16, 1, 231, 231, 35, 231)); EXPECT_TRUE(checkPixel(21, 12, 231, 35, 35, 231)); EXPECT_TRUE(checkPixel(26, 51, 231, 35, 231, 35)); EXPECT_TRUE(checkPixel( 5, 32, 35, 231, 231, 35)); EXPECT_TRUE(checkPixel(13, 8, 35, 231, 231, 231)); EXPECT_TRUE(checkPixel(46, 3, 35, 35, 231, 35)); EXPECT_TRUE(checkPixel(30, 33, 35, 35, 35, 35)); EXPECT_TRUE(checkPixel( 6, 52, 231, 231, 35, 35)); EXPECT_TRUE(checkPixel(55, 33, 35, 231, 35, 231)); EXPECT_TRUE(checkPixel(16, 29, 35, 35, 231, 231)); EXPECT_TRUE(checkPixel( 1, 30, 35, 35, 35, 231)); EXPECT_TRUE(checkPixel(41, 37, 35, 35, 231, 231)); EXPECT_TRUE(checkPixel(46, 29, 231, 231, 35, 35)); EXPECT_TRUE(checkPixel(15, 25, 35, 231, 35, 231)); EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); } // Tests if GLConsumer and BufferQueue are robust enough // to handle a special case where updateTexImage is called // in the middle of disconnect. This ordering is enforced // by blocking in the disconnect callback. TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { class ProducerThread : public Thread { public: explicit ProducerThread(const sp& anw): mANW(anw) { } virtual ~ProducerThread() { } virtual bool threadLoop() { ANativeWindowBuffer* anb; if (native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU) != NO_ERROR) { return false; } for (int numFrames =0 ; numFrames < 2; numFrames ++) { if (native_window_dequeue_buffer_and_wait(mANW.get(), &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) != NO_ERROR) { return false; } } if (native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU) != NO_ERROR) { return false; } return false; } private: sp mANW; }; sp dw(new DisconnectWaiter()); mConsumer->consumerConnect(dw, false); sp pt(new ProducerThread(mANW)); pt->run("ProducerThread"); // eat a frame so GLConsumer will own an at least one slot dw->waitForFrame(); EXPECT_EQ(OK,mST->updateTexImage()); dw->waitForFrame(); // Could fail here as GLConsumer thinks it still owns the slot // but bufferQueue has released all slots EXPECT_EQ(OK,mST->updateTexImage()); dw->finishDisconnect(); } // This test ensures that the GLConsumer clears the mCurrentTexture // when it is disconnected and reconnected. Otherwise it will // attempt to release a buffer that it does not owned TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer *anb; EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ(OK,mST->updateTexImage()); EXPECT_EQ(OK,mST->updateTexImage()); ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); // Will fail here if mCurrentTexture is not cleared properly mFW->waitForFrame(); EXPECT_EQ(OK,mST->updateTexImage()); ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU)); } TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)); // The producer image size ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512)); // The consumer image size (16 x 9) ratio mST->setDefaultBufferSize(1280, 720); ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer *anb; android_native_rect_t odd = {23, 78, 123, 477}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &odd)); EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); EXPECT_EQ(OK, mST->updateTexImage()); Rect r = mST->getCurrentCrop(); assertRectEq(Rect(23, 78, 123, 477), r); ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU)); } // This test ensures the scaling mode does the right thing // ie NATIVE_WINDOW_SCALING_MODE_CROP should crop // the image such that it has the same aspect ratio as the // default buffer size TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)); // The producer image size ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512)); // The consumer image size (16 x 9) ratio mST->setDefaultBufferSize(1280, 720); native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU); ANativeWindowBuffer *anb; // The crop is in the shape of (320, 180) === 16 x 9 android_native_rect_t standard = {10, 20, 330, 200}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &standard)); EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); EXPECT_EQ(OK, mST->updateTexImage()); Rect r = mST->getCurrentCrop(); // crop should be the same as crop (same aspect ratio) assertRectEq(Rect(10, 20, 330, 200), r); // make this wider then desired aspect 239 x 100 (2.39:1) android_native_rect_t wide = {20, 30, 259, 130}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &wide)); EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); EXPECT_EQ(OK, mST->updateTexImage()); r = mST->getCurrentCrop(); // crop should be the same height, but have cropped left and right borders // offset is 30.6 px L+, R- assertRectEq(Rect(51, 30, 228, 130), r); // This image is taller then desired aspect 400 x 300 (4:3) android_native_rect_t narrow = {0, 0, 400, 300}; ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &narrow)); EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); mFW->waitForFrame(); EXPECT_EQ(OK, mST->updateTexImage()); r = mST->getCurrentCrop(); // crop should be the same width, but have cropped top and bottom borders // offset is 37.5 px assertRectEq(Rect(0, 37, 400, 262), r); native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); } TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { class ProducerThread : public Thread { public: explicit ProducerThread(const sp& anw): mANW(anw), mDequeueError(NO_ERROR) { } virtual ~ProducerThread() { } virtual bool threadLoop() { Mutex::Autolock lock(mMutex); ANativeWindowBuffer* anb; if (native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU) != NO_ERROR) { return false; } // Frame 1 if (native_window_dequeue_buffer_and_wait(mANW.get(), &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) != NO_ERROR) { return false; } // Frame 2 if (native_window_dequeue_buffer_and_wait(mANW.get(), &anb) != NO_ERROR) { return false; } if (anb == NULL) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) != NO_ERROR) { return false; } // Frame 3 - error expected mDequeueError = native_window_dequeue_buffer_and_wait(mANW.get(), &anb); return false; } status_t getDequeueError() { Mutex::Autolock lock(mMutex); return mDequeueError; } private: sp mANW; status_t mDequeueError; Mutex mMutex; }; sp pt(new ProducerThread(mANW)); pt->run("ProducerThread"); mFW->waitForFrame(); mFW->waitForFrame(); // Sleep for 100ms to allow the producer thread's dequeueBuffer call to // block waiting for a buffer to become available. usleep(100000); mST->abandon(); pt->requestExitAndWait(); ASSERT_EQ(NO_INIT, reinterpret_cast(pt.get())->getDequeueError()); } TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { int texHeight = 16; ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); GLint maxTextureSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); // make sure it works with small textures mST->setDefaultBufferSize(16, texHeight); EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(16, anb->width); EXPECT_EQ(texHeight, anb->height); EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ(NO_ERROR, mST->updateTexImage()); // make sure it works with GL_MAX_TEXTURE_SIZE mST->setDefaultBufferSize(maxTextureSize, texHeight); EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(maxTextureSize, anb->width); EXPECT_EQ(texHeight, anb->height); EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); EXPECT_EQ(NO_ERROR, mST->updateTexImage()); // make sure it fails with GL_MAX_TEXTURE_SIZE+1 mST->setDefaultBufferSize(maxTextureSize+1, texHeight); EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); EXPECT_EQ(maxTextureSize+1, anb->width); EXPECT_EQ(texHeight, anb->height); EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); ASSERT_NE(NO_ERROR, mST->updateTexImage()); } } // namespace android libs/gui/tests/SurfaceTextureMultiContextGL.h0100644 0000000 0000000 00000005637 13300556574 020434 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. */ #ifndef ANDROID_SURFACE_TEXTURE_MULTI_CONTEXT_GL_H #define ANDROID_SURFACE_TEXTURE_MULTI_CONTEXT_GL_H #include "SurfaceTextureGL.h" namespace android { class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest { protected: enum { SECOND_TEX_ID = 123 }; enum { THIRD_TEX_ID = 456 }; SurfaceTextureMultiContextGLTest(): mSecondEglContext(EGL_NO_CONTEXT), mThirdEglContext(EGL_NO_CONTEXT) { } virtual void SetUp() { SurfaceTextureGLTest::SetUp(); // Set up the secondary context and texture renderer. mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext); ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mSecondTextureRenderer = new TextureRenderer(SECOND_TEX_ID, mST); ASSERT_NO_FATAL_FAILURE(mSecondTextureRenderer->SetUp()); // Set up the tertiary context and texture renderer. mThirdEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mThirdEglContext); ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mThirdEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); mThirdTextureRenderer = new TextureRenderer(THIRD_TEX_ID, mST); ASSERT_NO_FATAL_FAILURE(mThirdTextureRenderer->SetUp()); // Switch back to the primary context to start the tests. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); } virtual void TearDown() { if (mThirdEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mThirdEglContext); } if (mSecondEglContext != EGL_NO_CONTEXT) { eglDestroyContext(mEglDisplay, mSecondEglContext); } SurfaceTextureGLTest::TearDown(); } EGLContext mSecondEglContext; sp mSecondTextureRenderer; EGLContext mThirdEglContext; sp mThirdTextureRenderer; }; } #endif libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp0100644 0000000 0000000 00000041074 13300556574 022021 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. */ #define LOG_TAG "SurfaceTextureMultiContextGL_test" //#define LOG_NDEBUG 0 #include "SurfaceTextureMultiContextGL.h" #include "FillBuffer.h" #include namespace android { TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Attempt to latch the texture on the secondary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); } TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceeds) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Check that the GL texture was deleted. EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); } TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceedsAfterProducerDisconnect) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); ASSERT_EQ(OK, mST->detachFromContext()); // Check that the GL texture was deleted. EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); } TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenAbandoned) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Attempt to detach from the primary context. mST->abandon(); ASSERT_EQ(NO_INIT, mST->detachFromContext()); } TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenDetached) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attempt to detach from the primary context again. ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); } TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoDisplay) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Make there be no current display. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Attempt to detach from the primary context. ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); } TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoContext) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Make current context be incorrect. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Attempt to detach from the primary context. ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); } TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageFailsWhenDetached) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attempt to latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceeds) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the secondary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); // Verify that the texture object was created and bound. GLint texBinding = -1; glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); EXPECT_EQ(SECOND_TEX_ID, texBinding); // Try to use the texture from the secondary context. glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 1, 1); mSecondTextureRenderer->drawTexture(); ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsAfterProducerDisconnect) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the secondary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); // Verify that the texture object was created and bound. GLint texBinding = -1; glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); EXPECT_EQ(SECOND_TEX_ID, texBinding); // Try to use the texture from the secondary context. glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 1, 1); mSecondTextureRenderer->drawTexture(); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsBeforeUpdateTexImage) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Detach from the primary context. native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the secondary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); // Verify that the texture object was created and bound. GLint texBinding = -1; glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); EXPECT_EQ(SECOND_TEX_ID, texBinding); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Try to use the texture from the secondary context. glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 1, 1); mSecondTextureRenderer->drawTexture(); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAbandoned) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attempt to attach to the secondary context. mST->abandon(); // Attempt to attach to the primary context. ASSERT_EQ(NO_INIT, mST->attachToContext(SECOND_TEX_ID)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttached) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Attempt to attach to the primary context. ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttachedBeforeUpdateTexImage) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Attempt to attach to the primary context. ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWithNoDisplay) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Make there be no current display. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Attempt to attach with no context current. ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwice) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Latch the texture contents on the primary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the secondary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); // Detach from the secondary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the tertiary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mThirdEglContext)); ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); // Verify that the texture object was created and bound. GLint texBinding = -1; glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); EXPECT_EQ(THIRD_TEX_ID, texBinding); // Try to use the texture from the tertiary context. glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 1, 1); mThirdTextureRenderer->drawTexture(); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); } TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwiceBeforeUpdateTexImage) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Detach from the primary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the secondary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); // Detach from the secondary context. ASSERT_EQ(OK, mST->detachFromContext()); // Attach to the tertiary context. ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mThirdEglContext)); ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); // Verify that the texture object was created and bound. GLint texBinding = -1; glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); EXPECT_EQ(THIRD_TEX_ID, texBinding); // Latch the texture contents on the tertiary context. mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // Try to use the texture from the tertiary context. glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 1, 1); mThirdTextureRenderer->drawTexture(); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); } TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); // produce two frames and consume them both on the primary context ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // produce one more frame ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Detach from the primary context and attach to the secondary context ASSERT_EQ(OK, mST->detachFromContext()); ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); // Consume final frame on secondary context mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); } TEST_F(SurfaceTextureMultiContextGLTest, AttachAfterDisplayTerminatedSucceeds) { ASSERT_EQ(OK, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); // produce two frames and consume them both on the primary context ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); // produce one more frame ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); // Detach from the primary context. ASSERT_EQ(OK, mST->releaseTexImage()); ASSERT_EQ(OK, mST->detachFromContext()); // Terminate and then initialize the display. All contexts, surfaces // and images are invalid at this point. EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); EGLint majorVersion = 0; EGLint minorVersion = 0; EXPECT_TRUE(eglTerminate(display)); EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // The surface is invalid so create it again. EGLint pbufferAttribs[] = { EGL_WIDTH, 64, EGL_HEIGHT, 64, EGL_NONE }; mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, pbufferAttribs); // The second context is invalid so create it again. mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext); ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mSecondEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); // Now attach to and consume final frame on secondary context. ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); mFW->waitForFrame(); ASSERT_EQ(OK, mST->updateTexImage()); } } // namespace android libs/gui/tests/Surface_test.cpp0100644 0000000 0000000 00000176242 13300556574 015643 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 "DummyConsumer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { using namespace std::chrono_literals; // retrieve wide-color and hdr settings from configstore using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; static bool hasWideColorDisplay = getBool(false); class FakeSurfaceComposer; class FakeProducerFrameEventHistory; static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits::max(); class SurfaceTest : public ::testing::Test { protected: SurfaceTest() { ProcessState::self()->startThreadPool(); } virtual void SetUp() { mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); // TODO(brianderson): The following sometimes fails and is a source of // test flakiness. mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); ASSERT_TRUE(mSurfaceControl != NULL); ASSERT_TRUE(mSurfaceControl->isValid()); SurfaceComposerClient::openGlobalTransaction(); ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7fffffff)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); SurfaceComposerClient::closeGlobalTransaction(); mSurface = mSurfaceControl->getSurface(); ASSERT_TRUE(mSurface != NULL); } virtual void TearDown() { mComposerClient->dispose(); } sp mSurface; sp mComposerClient; sp mSurfaceControl; }; TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) { sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(1, result); } TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { mSurfaceControl.clear(); // Wait for the async clean-up to complete. std::this_thread::sleep_for(50ms); sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(1, result); } // This test probably doesn't belong here. TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { sp anw(mSurface); // Verify the screenshot works with no protected buffers. sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); sp sf(ComposerService::getComposerService()); sp display(sf->getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 64, 64, 0, 0x7fffffff, false)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); // Set the PROTECTED usage bit and verify that the screenshot fails. Note // that we need to dequeue a buffer in order for it to actually get // allocated in SurfaceFlinger. ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); ANativeWindowBuffer* buf = 0; status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { // we could fail if GRALLOC_USAGE_PROTECTED is not supported. // that's okay as long as this is the reason for the failure. // try again without the GRALLOC_USAGE_PROTECTED bit. ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0)); ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &buf)); return; } ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1)); for (int i = 0; i < 4; i++) { // Loop to make sure SurfaceFlinger has retired a protected buffer. ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 64, 64, 0, 0x7fffffff, false)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); } TEST_F(SurfaceTest, LayerCountIsOne) { sp anw(mSurface); int result = -123; int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(1, result); } TEST_F(SurfaceTest, QueryConsumerUsage) { const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp c = new BufferItemConsumer(consumer, TEST_USAGE_FLAGS); sp s = new Surface(producer); sp anw(s); int flags = -1; int err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &flags); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(TEST_USAGE_FLAGS, flags); } TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { const android_dataspace TEST_DATASPACE = HAL_DATASPACE_SRGB; sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE); sp s = new Surface(producer); sp anw(s); android_dataspace dataSpace; int err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, reinterpret_cast(&dataSpace)); ASSERT_EQ(NO_ERROR, err); ASSERT_EQ(TEST_DATASPACE, dataSpace); } TEST_F(SurfaceTest, SettingGenerationNumber) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp cpuConsumer = new CpuConsumer(consumer, 1); sp surface = new Surface(producer); sp window(surface); // Allocate a buffer with a generation number of 0 ANativeWindowBuffer* buffer; int fenceFd; ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU)); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd)); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fenceFd)); // Detach the buffer and check its generation number sp graphicBuffer; sp fence; ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&graphicBuffer, &fence)); ASSERT_EQ(0U, graphicBuffer->getGenerationNumber()); ASSERT_EQ(NO_ERROR, surface->setGenerationNumber(1)); buffer = static_cast(graphicBuffer.get()); // This should change the generation number of the GraphicBuffer ASSERT_EQ(NO_ERROR, surface->attachBuffer(buffer)); // Check that the new generation number sticks with the buffer ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, -1)); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fenceFd)); graphicBuffer = static_cast(buffer); ASSERT_EQ(1U, graphicBuffer->getGenerationNumber()); } TEST_F(SurfaceTest, GetConsumerName) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp dummyConsumer(new DummyConsumer); consumer->consumerConnect(dummyConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); } TEST_F(SurfaceTest, GetWideColorSupport) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp dummyConsumer(new DummyConsumer); consumer->consumerConnect(dummyConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); bool supported; surface->getWideColorSupport(&supported); // NOTE: This test assumes that device that supports // wide-color (as indicated by BoardConfig) must also // have a wide-color primary display. // That assumption allows this test to cover devices // that advertised a wide-color color mode without // actually supporting wide-color to pass this test // as well as the case of a device that does support // wide-color (via BoardConfig) and has a wide-color // primary display. // NOT covered at this time is a device that supports // wide color in the BoardConfig but does not support // a wide-color color mode on the primary display. ASSERT_EQ(hasWideColorDisplay, supported); } TEST_F(SurfaceTest, DynamicSetBufferCount) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp dummyConsumer(new DummyConsumer); consumer->consumerConnect(dummyConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU)); native_window_set_buffer_count(window.get(), 4); int fence; ANativeWindowBuffer* buffer; ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); native_window_set_buffer_count(window.get(), 3); ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); native_window_set_buffer_count(window.get(), 2); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); } TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { sp producer; sp consumer; BufferQueue::createBufferQueue(&producer, &consumer); sp dummyConsumer(new DummyConsumer); consumer->consumerConnect(dummyConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp surface = new Surface(producer); sp window(surface); sp listener = new DummyProducerListener(); ASSERT_EQ(OK, surface->connect( NATIVE_WINDOW_API_CPU, /*listener*/listener, /*reportBufferRemoval*/true)); const int BUFFER_COUNT = 4; ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); sp detachedBuffer; sp outFence; int fences[BUFFER_COUNT]; ANativeWindowBuffer* buffers[BUFFER_COUNT]; // Allocate buffers because detachNextBuffer requires allocated buffers for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i])); } for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i])); } // Test detached buffer is correctly reported ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); std::vector> removedBuffers; ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(1u, removedBuffers.size()); ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle); // Test the list is flushed one getAndFlushRemovedBuffers returns ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(0u, removedBuffers.size()); // Test removed buffer list is cleanup after next dequeueBuffer call ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[0], &fences[0])); ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(0u, removedBuffers.size()); ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[0], fences[0])); // Test removed buffer list is cleanup after next detachNextBuffer call ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); ASSERT_EQ(1u, removedBuffers.size()); ASSERT_EQ(detachedBuffer->handle, removedBuffers.at(0)->handle); // Re-allocate buffers since all buffers are detached up to now for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffers[i], &fences[i])); } for (int i = 0; i < BUFFER_COUNT; i++) { ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], fences[i])); } ASSERT_EQ(NO_ERROR, surface->detachNextBuffer(&detachedBuffer, &outFence)); ASSERT_EQ(NO_ERROR, surface->attachBuffer(detachedBuffer.get())); ASSERT_EQ(OK, surface->getAndFlushRemovedBuffers(&removedBuffers)); // Depends on which slot GraphicBufferProducer impl pick, the attach call might // get 0 or 1 buffer removed. ASSERT_LE(removedBuffers.size(), 1u); } TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { sp anw(mSurface); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); ANativeWindowBuffer* buffer = nullptr; int32_t fenceFd = -1; nsecs_t before = systemTime(CLOCK_MONOTONIC); anw->dequeueBuffer(anw.get(), &buffer, &fenceFd); nsecs_t after = systemTime(CLOCK_MONOTONIC); nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime(); ASSERT_LE(before, lastDequeueTime); ASSERT_GE(after, lastDequeueTime); } class FakeConsumer : public BnConsumerListener { public: void onFrameAvailable(const BufferItem& /*item*/) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} void addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override { if (newTimestamps) { if (mGetFrameTimestampsEnabled) { EXPECT_GT(mNewFrameEntryOverride.frameNumber, 0u) << "Test should set mNewFrameEntryOverride before queuing " "a frame."; EXPECT_EQ(newTimestamps->frameNumber, mNewFrameEntryOverride.frameNumber) << "Test attempting to add NewFrameEntryOverride with " "incorrect frame number."; mFrameEventHistory.addQueue(mNewFrameEntryOverride); mNewFrameEntryOverride.frameNumber = 0; } mAddFrameTimestampsCount++; mLastAddedFrameNumber = newTimestamps->frameNumber; } if (outDelta) { mFrameEventHistory.getAndResetDelta(outDelta); mGetFrameTimestampsCount++; } mAddAndGetFrameTimestampsCallCount++; } bool mGetFrameTimestampsEnabled = false; ConsumerFrameEventHistory mFrameEventHistory; int mAddAndGetFrameTimestampsCallCount = 0; int mAddFrameTimestampsCount = 0; int mGetFrameTimestampsCount = 0; uint64_t mLastAddedFrameNumber = NO_FRAME_INDEX; NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr }; }; class FakeSurfaceComposer : public ISurfaceComposer{ public: ~FakeSurfaceComposer() override {} void setSupportsPresent(bool supportsPresent) { mSupportsPresent = supportsPresent; } sp createConnection() override { return nullptr; } sp createScopedConnection( const sp& /* parent */) override { return nullptr; } sp createDisplayEventConnection(ISurfaceComposer::VsyncSource) override { return nullptr; } sp createDisplay(const String8& /*displayName*/, bool /*secure*/) override { return nullptr; } void destroyDisplay(const sp& /*display */) override {} sp getBuiltInDisplay(int32_t /*id*/) override { return nullptr; } void setTransactionState(const Vector& /*state*/, const Vector& /*displays*/, uint32_t /*flags*/) override {} void bootFinished() override {} bool authenticateSurfaceTexture( const sp& /*surface*/) const override { return false; } status_t getSupportedFrameTimestamps(std::vector* outSupported) const override { *outSupported = { FrameEvent::REQUESTED_PRESENT, FrameEvent::ACQUIRE, FrameEvent::LATCH, FrameEvent::FIRST_REFRESH_START, FrameEvent::LAST_REFRESH_START, FrameEvent::GPU_COMPOSITION_DONE, FrameEvent::DEQUEUE_READY, FrameEvent::RELEASE }; if (mSupportsPresent) { outSupported->push_back( FrameEvent::DISPLAY_PRESENT); } return NO_ERROR; } void setPowerMode(const sp& /*display*/, int /*mode*/) override {} status_t getDisplayConfigs(const sp& /*display*/, Vector* /*configs*/) override { return NO_ERROR; } status_t getDisplayStats(const sp& /*display*/, DisplayStatInfo* /*stats*/) override { return NO_ERROR; } int getActiveConfig(const sp& /*display*/) override { return 0; } status_t setActiveConfig(const sp& /*display*/, int /*id*/) override { return NO_ERROR; } status_t getDisplayColorModes(const sp& /*display*/, Vector* /*outColorModes*/) override { return NO_ERROR; } android_color_mode_t getActiveColorMode(const sp& /*display*/) override { return HAL_COLOR_MODE_NATIVE; } status_t setActiveColorMode(const sp& /*display*/, android_color_mode_t /*colorMode*/) override { return NO_ERROR; } status_t captureScreen(const sp& /*display*/, const sp& /*producer*/, Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/, bool /*useIdentityTransform*/, Rotation /*rotation*/) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { return NO_ERROR; } status_t getHdrCapabilities(const sp& /*display*/, HdrCapabilities* /*outCapabilities*/) const override { return NO_ERROR; } status_t enableVSyncInjections(bool /*enable*/) override { return NO_ERROR; } status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } private: bool mSupportsPresent{true}; }; class FakeProducerFrameEventHistory : public ProducerFrameEventHistory { public: FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap) : mFenceMap(fenceMap) {} ~FakeProducerFrameEventHistory() {} void updateAcquireFence(uint64_t frameNumber, std::shared_ptr&& acquire) override { // Verify the acquire fence being added isn't the one from the consumer. EXPECT_NE(mConsumerAcquireFence, acquire); // Override the fence, so we can verify this was called by the // producer after the frame is queued. ProducerFrameEventHistory::updateAcquireFence(frameNumber, std::shared_ptr(mAcquireFenceOverride)); } void setAcquireFenceOverride( const std::shared_ptr& acquireFenceOverride, const std::shared_ptr& consumerAcquireFence) { mAcquireFenceOverride = acquireFenceOverride; mConsumerAcquireFence = consumerAcquireFence; } protected: std::shared_ptr createFenceTime(const sp& fence) const override { return mFenceMap->createFenceTimeForTest(fence); } FenceToFenceTimeMap* mFenceMap{nullptr}; std::shared_ptr mAcquireFenceOverride{FenceTime::NO_FENCE}; std::shared_ptr mConsumerAcquireFence{FenceTime::NO_FENCE}; }; class TestSurface : public Surface { public: TestSurface(const sp& bufferProducer, FenceToFenceTimeMap* fenceMap) : Surface(bufferProducer), mFakeSurfaceComposer(new FakeSurfaceComposer) { mFakeFrameEventHistory = new FakeProducerFrameEventHistory(fenceMap); mFrameEventHistory.reset(mFakeFrameEventHistory); } ~TestSurface() override {} sp composerService() const override { return mFakeSurfaceComposer; } nsecs_t now() const override { return mNow; } void setNow(nsecs_t now) { mNow = now; } public: sp mFakeSurfaceComposer; nsecs_t mNow = 0; // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, // but this raw pointer gives access to test functionality. FakeProducerFrameEventHistory* mFakeFrameEventHistory; }; class GetFrameTimestampsTest : public ::testing::Test { protected: struct FenceAndFenceTime { explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap) : mFence(new Fence), mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {} sp mFence { nullptr }; std::shared_ptr mFenceTime { nullptr }; }; struct RefreshEvents { RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) : mFenceMap(fenceMap), kCompositorTiming( {refreshStart, refreshStart + 1, refreshStart + 2 }), kStartTime(refreshStart + 3), kGpuCompositionDoneTime(refreshStart + 4), kPresentTime(refreshStart + 5) {} void signalPostCompositeFences() { mFenceMap.signalAllForTest( mGpuCompositionDone.mFence, kGpuCompositionDoneTime); mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime); } FenceToFenceTimeMap& mFenceMap; FenceAndFenceTime mGpuCompositionDone { mFenceMap }; FenceAndFenceTime mPresent { mFenceMap }; const CompositorTiming kCompositorTiming; const nsecs_t kStartTime; const nsecs_t kGpuCompositionDoneTime; const nsecs_t kPresentTime; }; struct FrameEvents { FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime) : mFenceMap(fenceMap), kPostedTime(frameStartTime + 100), kRequestedPresentTime(frameStartTime + 200), kProducerAcquireTime(frameStartTime + 300), kConsumerAcquireTime(frameStartTime + 301), kLatchTime(frameStartTime + 500), kDequeueReadyTime(frameStartTime + 600), kReleaseTime(frameStartTime + 700), mRefreshes { { mFenceMap, frameStartTime + 410 }, { mFenceMap, frameStartTime + 420 }, { mFenceMap, frameStartTime + 430 } } {} void signalQueueFences() { mFenceMap.signalAllForTest( mAcquireConsumer.mFence, kConsumerAcquireTime); mFenceMap.signalAllForTest( mAcquireProducer.mFence, kProducerAcquireTime); } void signalRefreshFences() { for (auto& re : mRefreshes) { re.signalPostCompositeFences(); } } void signalReleaseFences() { mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime); } FenceToFenceTimeMap& mFenceMap; FenceAndFenceTime mAcquireConsumer { mFenceMap }; FenceAndFenceTime mAcquireProducer { mFenceMap }; FenceAndFenceTime mRelease { mFenceMap }; const nsecs_t kPostedTime; const nsecs_t kRequestedPresentTime; const nsecs_t kProducerAcquireTime; const nsecs_t kConsumerAcquireTime; const nsecs_t kLatchTime; const nsecs_t kDequeueReadyTime; const nsecs_t kReleaseTime; RefreshEvents mRefreshes[3]; }; GetFrameTimestampsTest() {} virtual void SetUp() { BufferQueue::createBufferQueue(&mProducer, &mConsumer); mFakeConsumer = new FakeConsumer; mCfeh = &mFakeConsumer->mFrameEventHistory; mConsumer->consumerConnect(mFakeConsumer, false); mConsumer->setConsumerName(String8("TestConsumer")); mSurface = new TestSurface(mProducer, &mFenceMap); mWindow = mSurface; ASSERT_EQ(NO_ERROR, native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU)); native_window_set_buffer_count(mWindow.get(), 4); } void disableFrameTimestamps() { mFakeConsumer->mGetFrameTimestampsEnabled = false; native_window_enable_frame_timestamps(mWindow.get(), 0); mFrameTimestampsEnabled = false; } void enableFrameTimestamps() { mFakeConsumer->mGetFrameTimestampsEnabled = true; native_window_enable_frame_timestamps(mWindow.get(), 1); mFrameTimestampsEnabled = true; } int getAllFrameTimestamps(uint64_t frameId) { return native_window_get_frame_timestamps(mWindow.get(), frameId, &outRequestedPresentTime, &outAcquireTime, &outLatchTime, &outFirstRefreshStartTime, &outLastRefreshStartTime, &outGpuCompositionDoneTime, &outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime); } void resetTimestamps() { outRequestedPresentTime = -1; outAcquireTime = -1; outLatchTime = -1; outFirstRefreshStartTime = -1; outLastRefreshStartTime = -1; outGpuCompositionDoneTime = -1; outDisplayPresentTime = -1; outDequeueReadyTime = -1; outReleaseTime = -1; } uint64_t getNextFrameId() { uint64_t frameId = -1; int status = native_window_get_next_frame_id(mWindow.get(), &frameId); EXPECT_EQ(status, NO_ERROR); return frameId; } void dequeueAndQueue(uint64_t frameIndex) { int fence = -1; ANativeWindowBuffer* buffer = nullptr; ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); int oldAddFrameTimestampsCount = mFakeConsumer->mAddFrameTimestampsCount; FrameEvents* frame = &mFrames[frameIndex]; uint64_t frameNumber = frameIndex + 1; NewFrameEventsEntry fe; fe.frameNumber = frameNumber; fe.postedTime = frame->kPostedTime; fe.requestedPresentTime = frame->kRequestedPresentTime; fe.acquireFence = frame->mAcquireConsumer.mFenceTime; mFakeConsumer->mNewFrameEntryOverride = fe; mSurface->mFakeFrameEventHistory->setAcquireFenceOverride( frame->mAcquireProducer.mFenceTime, frame->mAcquireConsumer.mFenceTime); ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber); EXPECT_EQ( oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0), mFakeConsumer->mAddFrameTimestampsCount); } void addFrameEvents( bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) { FrameEvents* oldFrame = (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame]; FrameEvents* newFrame = &mFrames[iNewFrame]; uint64_t nOldFrame = iOldFrame + 1; uint64_t nNewFrame = iNewFrame + 1; // Latch, Composite, and Release the frames in a plausible order. // Note: The timestamps won't necessarily match the order, but // that's okay for the purposes of this test. std::shared_ptr gpuDoneFenceTime = FenceTime::NO_FENCE; // Composite the previous frame one more time, which helps verify // LastRefresh is updated properly. if (oldFrame != nullptr) { mCfeh->addPreComposition(nOldFrame, oldFrame->mRefreshes[2].kStartTime); gpuDoneFenceTime = gpuComposited ? oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime, oldFrame->mRefreshes[2].mPresent.mFenceTime, oldFrame->mRefreshes[2].kCompositorTiming); } // Latch the new frame. mCfeh->addLatch(nNewFrame, newFrame->kLatchTime); mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime); gpuDoneFenceTime = gpuComposited ? newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; // HWC2 releases the previous buffer after a new latch just before // calling postComposition. if (oldFrame != nullptr) { mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime, std::shared_ptr(oldFrame->mRelease.mFenceTime)); } mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, newFrame->mRefreshes[0].mPresent.mFenceTime, newFrame->mRefreshes[0].kCompositorTiming); mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[1].kStartTime); gpuDoneFenceTime = gpuComposited ? newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, newFrame->mRefreshes[1].mPresent.mFenceTime, newFrame->mRefreshes[1].kCompositorTiming); } sp mProducer; sp mConsumer; sp mFakeConsumer; ConsumerFrameEventHistory* mCfeh; sp mSurface; sp mWindow; FenceToFenceTimeMap mFenceMap; bool mFrameTimestampsEnabled = false; int64_t outRequestedPresentTime = -1; int64_t outAcquireTime = -1; int64_t outLatchTime = -1; int64_t outFirstRefreshStartTime = -1; int64_t outLastRefreshStartTime = -1; int64_t outGpuCompositionDoneTime = -1; int64_t outDisplayPresentTime = -1; int64_t outDequeueReadyTime = -1; int64_t outReleaseTime = -1; FrameEvents mFrames[3] { { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } }; }; // This test verifies that the frame timestamps are not retrieved when not // explicitly enabled via native_window_enable_frame_timestamps. // We want to check this to make sure there's no overhead for users // that don't need the timestamp information. TEST_F(GetFrameTimestampsTest, DefaultDisabled) { int fence; ANativeWindowBuffer* buffer; EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); const uint64_t fId = getNextFrameId(); // Verify the producer doesn't get frame timestamps piggybacked on dequeue. ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); // Verify the producer doesn't get frame timestamps piggybacked on queue. // It is okay that frame timestamps are added in the consumer since it is // still needed for SurfaceFlinger dumps. ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); // Verify attempts to get frame timestamps fail. int result = getAllFrameTimestamps(fId); EXPECT_EQ(INVALID_OPERATION, result); EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); // Verify compositor timing query fails. nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(INVALID_OPERATION, result); } // This test verifies that the frame timestamps are retrieved if explicitly // enabled via native_window_enable_frame_timestamps. TEST_F(GetFrameTimestampsTest, EnabledSimple) { CompositorTiming initialCompositorTiming { 1000000000, // 1s deadline 16666667, // 16ms interval 50000000, // 50ms present latency }; mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); // Verify the compositor timing query gets the initial compositor values // after timststamps are enabled; even before the first frame is queued // or dequeued. nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; mSurface->setNow(initialCompositorTiming.deadline - 1); int result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); int fence; ANativeWindowBuffer* buffer; EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount); const uint64_t fId1 = getNextFrameId(); // Verify getFrameTimestamps is piggybacked on dequeue. ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount); NewFrameEventsEntry f1; f1.frameNumber = 1; f1.postedTime = mFrames[0].kPostedTime; f1.requestedPresentTime = mFrames[0].kRequestedPresentTime; f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime; mSurface->mFakeFrameEventHistory->setAcquireFenceOverride( mFrames[0].mAcquireProducer.mFenceTime, mFrames[0].mAcquireConsumer.mFenceTime); mFakeConsumer->mNewFrameEntryOverride = f1; mFrames[0].signalQueueFences(); // Verify getFrameTimestamps is piggybacked on queue. ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber); EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount); // Verify queries for timestamps that the producer doesn't know about // triggers a call to see if the consumer has any new timestamps. result = getAllFrameTimestamps(fId1); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount); } TEST_F(GetFrameTimestampsTest, QueryPresentSupported) { bool displayPresentSupported = true; mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); // Verify supported bits are forwarded. int supportsPresent = -1; mWindow.get()->query(mWindow.get(), NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); EXPECT_EQ(displayPresentSupported, supportsPresent); } TEST_F(GetFrameTimestampsTest, QueryPresentNotSupported) { bool displayPresentSupported = false; mSurface->mFakeSurfaceComposer->setSupportsPresent(displayPresentSupported); // Verify supported bits are forwarded. int supportsPresent = -1; mWindow.get()->query(mWindow.get(), NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent); EXPECT_EQ(displayPresentSupported, supportsPresent); } TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) { nsecs_t phase = 4000; nsecs_t interval = 1000; // Timestamp in previous interval. nsecs_t timestamp = 3500; EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp in next interval. timestamp = 4500; EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp multiple intervals before. timestamp = 2500; EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp multiple intervals after. timestamp = 6500; EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp on previous interval. timestamp = 3000; EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp on next interval. timestamp = 5000; EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); // Timestamp equal to phase. timestamp = 4000; EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( timestamp, phase, interval)); } // int(big_timestamp / interval) < 0, which can cause a crash or invalid result // if the number of intervals elapsed is internally stored in an int. TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) { nsecs_t phase = 0; nsecs_t interval = 4000; nsecs_t big_timestamp = 8635916564000; int32_t intervals = big_timestamp / interval; EXPECT_LT(intervals, 0); EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( big_timestamp, phase, interval)); EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( big_timestamp, big_timestamp, interval)); } // This verifies the compositor timing is updated by refresh events // and piggy backed on a queue, dequeue, and enabling of timestamps.. TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { CompositorTiming initialCompositorTiming { 1000000000, // 1s deadline 16666667, // 16ms interval 50000000, // 50ms present latency }; mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); // We get the initial values before any frames are submitted. nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; mSurface->setNow(initialCompositorTiming.deadline - 1); int result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); addFrameEvents(true, NO_FRAME_INDEX, 0); // Still get the initial values because the frame events for frame 0 // didn't get a chance to piggyback on a queue or dequeue yet. result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); addFrameEvents(true, 0, 1); // Now expect the composite values associated with frame 1. mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval, compositeInterval); EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency, compositeToPresentLatency); dequeueAndQueue(2); addFrameEvents(true, 1, 2); // Now expect the composite values associated with frame 2. mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval, compositeInterval); EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency, compositeToPresentLatency); // Re-enabling frame timestamps should get the latest values. disableFrameTimestamps(); enableFrameTimestamps(); // Now expect the composite values associated with frame 3. mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline, compositeDeadline); EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval, compositeInterval); EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency, compositeToPresentLatency); } // This verifies the compositor deadline properly snaps to the the next // deadline based on the current time. TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { CompositorTiming initialCompositorTiming { 1000000000, // 1s deadline 16666667, // 16ms interval 50000000, // 50ms present latency }; mCfeh->initializeCompositorTiming(initialCompositorTiming); enableFrameTimestamps(); nsecs_t compositeDeadline = 0; nsecs_t compositeInterval = 0; nsecs_t compositeToPresentLatency = 0; // A "now" just before the deadline snaps to the deadline. mSurface->setNow(initialCompositorTiming.deadline - 1); int result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); nsecs_t expectedDeadline = initialCompositorTiming.deadline; EXPECT_EQ(expectedDeadline, compositeDeadline); const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); addFrameEvents(true, NO_FRAME_INDEX, 0); // A "now" just after the deadline snaps properly. mSurface->setNow(initialCompositorTiming.deadline + 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = initialCompositorTiming.deadline +initialCompositorTiming.interval; EXPECT_EQ(expectedDeadline, compositeDeadline); const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); addFrameEvents(true, 0, 1); // A "now" just after the next interval snaps properly. mSurface->setNow( mFrames[0].mRefreshes[1].kCompositorTiming.deadline + mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = mFrames[0].mRefreshes[1].kCompositorTiming.deadline + mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2; EXPECT_EQ(expectedDeadline, compositeDeadline); dequeueAndQueue(2); addFrameEvents(true, 1, 2); // A "now" over 1 interval before the deadline snaps properly. mSurface->setNow( mFrames[1].mRefreshes[1].kCompositorTiming.deadline - mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = mFrames[1].mRefreshes[1].kCompositorTiming.deadline - mFrames[1].mRefreshes[1].kCompositorTiming.interval; EXPECT_EQ(expectedDeadline, compositeDeadline); // Re-enabling frame timestamps should get the latest values. disableFrameTimestamps(); enableFrameTimestamps(); // A "now" over 2 intervals before the deadline snaps properly. mSurface->setNow( mFrames[2].mRefreshes[1].kCompositorTiming.deadline - mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1); result = native_window_get_compositor_timing(mWindow.get(), &compositeDeadline, &compositeInterval, &compositeToPresentLatency); EXPECT_EQ(NO_ERROR, result); expectedDeadline = mFrames[2].mRefreshes[1].kCompositorTiming.deadline - mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2; EXPECT_EQ(expectedDeadline, compositeDeadline); } // This verifies the timestamps recorded in the consumer's // FrameTimestampsHistory are properly retrieved by the producer for the // correct frames. TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) { enableFrameTimestamps(); const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); mFrames[0].signalQueueFences(); const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); mFrames[1].signalQueueFences(); addFrameEvents(true, NO_FRAME_INDEX, 0); mFrames[0].signalRefreshFences(); addFrameEvents(true, 0, 1); mFrames[0].signalReleaseFences(); mFrames[1].signalRefreshFences(); // Verify timestamps are correct for frame 1. resetTimestamps(); int result = getAllFrameTimestamps(fId1); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); // Verify timestamps are correct for frame 2. resetTimestamps(); result = getAllFrameTimestamps(fId2); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); } // This test verifies the acquire fence recorded by the consumer is not sent // back to the producer and the producer saves its own fence. TEST_F(GetFrameTimestampsTest, QueueTimestampsNoSync) { enableFrameTimestamps(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); // Verify queue-related timestamps for f1 are available immediately in the // producer without asking the consumer again, even before signaling the // acquire fence. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = native_window_get_frame_timestamps(mWindow.get(), fId1, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime); // Signal acquire fences. Verify a sync call still isn't necessary. mFrames[0].signalQueueFences(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = native_window_get_frame_timestamps(mWindow.get(), fId1, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); // Dequeue and queue frame 2. const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); // Verify queue-related timestamps for f2 are available immediately in the // producer without asking the consumer again, even before signaling the // acquire fence. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = native_window_get_frame_timestamps(mWindow.get(), fId2, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outAcquireTime); // Signal acquire fences. Verify a sync call still isn't necessary. mFrames[1].signalQueueFences(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = native_window_get_frame_timestamps(mWindow.get(), fId2, &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); } TEST_F(GetFrameTimestampsTest, ZeroRequestedTimestampsNoSync) { enableFrameTimestamps(); // Dequeue and queue frame 1. dequeueAndQueue(0); mFrames[0].signalQueueFences(); // Dequeue and queue frame 2. const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); mFrames[1].signalQueueFences(); addFrameEvents(true, NO_FRAME_INDEX, 0); mFrames[0].signalRefreshFences(); addFrameEvents(true, 0, 1); mFrames[0].signalReleaseFences(); mFrames[1].signalRefreshFences(); // Verify a request for no timestamps doesn't result in a sync call. int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = native_window_get_frame_timestamps(mWindow.get(), fId2, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); } // This test verifies that fences can signal and update timestamps producer // side without an additional sync call to the consumer. TEST_F(GetFrameTimestampsTest, FencesInProducerNoSync) { enableFrameTimestamps(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); mFrames[0].signalQueueFences(); // Dequeue and queue frame 2. dequeueAndQueue(1); mFrames[1].signalQueueFences(); addFrameEvents(true, NO_FRAME_INDEX, 0); addFrameEvents(true, 0, 1); // Verify available timestamps are correct for frame 1, before any // fence has been signaled. // Note: A sync call is necessary here since the events triggered by // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = getAllFrameTimestamps(fId1); EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); // Verify available timestamps are correct for frame 1 again, before any // fence has been signaled. // This time a sync call should not be necessary. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId1); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); // Signal the fences for frame 1. mFrames[0].signalRefreshFences(); mFrames[0].signalReleaseFences(); // Verify all timestamps are available without a sync call. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId1); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); } // This test verifies that if the frame wasn't GPU composited but has a refresh // event a sync call isn't made to get the GPU composite done time since it will // never exist. TEST_F(GetFrameTimestampsTest, NoGpuNoSync) { enableFrameTimestamps(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); mFrames[0].signalQueueFences(); // Dequeue and queue frame 2. dequeueAndQueue(1); mFrames[1].signalQueueFences(); addFrameEvents(false, NO_FRAME_INDEX, 0); addFrameEvents(false, 0, 1); // Verify available timestamps are correct for frame 1, before any // fence has been signaled. // Note: A sync call is necessary here since the events triggered by // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = getAllFrameTimestamps(fId1); EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); // Signal the fences for frame 1. mFrames[0].signalRefreshFences(); mFrames[0].signalReleaseFences(); // Verify all timestamps, except GPU composition, are available without a // sync call. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId1); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime); } // This test verifies that if the certain timestamps can't possibly exist for // the most recent frame, then a sync call is not done. TEST_F(GetFrameTimestampsTest, NoReleaseNoSync) { enableFrameTimestamps(); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); mFrames[0].signalQueueFences(); // Dequeue and queue frame 2. const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); mFrames[1].signalQueueFences(); addFrameEvents(false, NO_FRAME_INDEX, 0); addFrameEvents(false, 0, 1); // Verify available timestamps are correct for frame 1, before any // fence has been signaled. // Note: A sync call is necessary here since the events triggered by // addFrameEvents didn't get to piggyback on the earlier queues/dequeues. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = getAllFrameTimestamps(fId1); EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDisplayPresentTime); EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); mFrames[0].signalRefreshFences(); mFrames[0].signalReleaseFences(); mFrames[1].signalRefreshFences(); // Verify querying for all timestmaps of f2 does not do a sync call. Even // though the lastRefresh, dequeueReady, and release times aren't // available, a sync call should not occur because it's not possible for f2 // to encounter the final value for those events until another frame is // queued. resetTimestamps(); oldCount = mFakeConsumer->mGetFrameTimestampsCount; result = getAllFrameTimestamps(fId2); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(NO_ERROR, result); EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime); EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime); EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime); EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_INVALID, outGpuCompositionDoneTime); EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outDequeueReadyTime); EXPECT_EQ(NATIVE_WINDOW_TIMESTAMP_PENDING, outReleaseTime); } // This test verifies there are no sync calls for present times // when they aren't supported and that an error is returned. TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { enableFrameTimestamps(); mSurface->mFakeSurfaceComposer->setSupportsPresent(false); // Dequeue and queue frame 1. const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); // Verify a query for the Present times do not trigger a sync call if they // are not supported. resetTimestamps(); int oldCount = mFakeConsumer->mGetFrameTimestampsCount; int result = native_window_get_frame_timestamps(mWindow.get(), fId1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &outDisplayPresentTime, nullptr, nullptr); EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount); EXPECT_EQ(BAD_VALUE, result); EXPECT_EQ(-1, outDisplayPresentTime); } } // namespace android libs/gui/tests/TextureRenderer.cpp0100644 0000000 0000000 00000007724 13300556574 016341 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 "TextureRenderer.h" #include "GLTest.h" #include #include #include #include namespace android { TextureRenderer::TextureRenderer(GLuint texName, const sp& st) : mTexName(texName), mST(st), mPgm(0), mPositionHandle(-1), mTexSamplerHandle(-1), mTexMatrixHandle(-1) { } void TextureRenderer::SetUp() { const char vsrc[] = "attribute vec4 vPosition;\n" "varying vec2 texCoords;\n" "uniform mat4 texMatrix;\n" "void main() {\n" " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n" " gl_Position = vPosition;\n" "}\n"; const char fsrc[] = "#extension GL_OES_EGL_image_external : require\n" "precision mediump float;\n" "uniform samplerExternalOES texSampler;\n" "varying vec2 texCoords;\n" "void main() {\n" " gl_FragColor = texture2D(texSampler, texCoords);\n" "}\n"; { SCOPED_TRACE("creating shader program"); ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(vsrc, fsrc, &mPgm)); } mPositionHandle = glGetAttribLocation(mPgm, "vPosition"); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_NE(-1, mPositionHandle); mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler"); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_NE(-1, mTexSamplerHandle); mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix"); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); ASSERT_NE(-1, mTexMatrixHandle); } // drawTexture draws the GLConsumer over the entire GL viewport. void TextureRenderer::drawTexture() { static const GLfloat triangleVertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, }; glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glEnableVertexAttribArray(mPositionHandle); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glUseProgram(mPgm); glUniform1i(mTexSamplerHandle, 0); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as // they're setting the defautls for that target, but when hacking // things to use GL_TEXTURE_2D they are needed to achieve the same // behavior. glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); GLfloat texMatrix[16]; mST->getTransformMatrix(texMatrix); glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); } } // namespace android libs/gui/tests/TextureRenderer.h0100644 0000000 0000000 00000002144 13300556574 015775 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. */ #ifndef ANDROID_TEXTURE_RENDERER_H #define ANDROID_TEXTURE_RENDERER_H #include #include namespace android { class GLConsumer; class TextureRenderer : public RefBase { public: TextureRenderer(GLuint texName, const sp& st); void SetUp(); void drawTexture(); private: GLuint mTexName; sp mST; GLuint mPgm; GLint mPositionHandle; GLint mTexSamplerHandle; GLint mTexMatrixHandle; }; } // namespace android #endif libs/gui/view/0040755 0000000 0000000 00000000000 13300556574 012307 5ustar000000000 0000000 libs/gui/view/Surface.cpp0100644 0000000 0000000 00000004773 13300556574 014413 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 "Surface" #include #include #include #include namespace android { namespace view { status_t Surface::writeToParcel(Parcel* parcel) const { return writeToParcel(parcel, false); } status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { if (parcel == nullptr) return BAD_VALUE; status_t res = OK; if (!nameAlreadyWritten) { res = parcel->writeString16(name); if (res != OK) return res; /* isSingleBuffered defaults to no */ res = parcel->writeInt32(0); if (res != OK) return res; } res = parcel->writeStrongBinder( IGraphicBufferProducer::asBinder(graphicBufferProducer)); return res; } status_t Surface::readFromParcel(const Parcel* parcel) { return readFromParcel(parcel, false); } status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { if (parcel == nullptr) return BAD_VALUE; status_t res = OK; if (!nameAlreadyRead) { name = readMaybeEmptyString16(parcel); // Discard this for now int isSingleBuffered; res = parcel->readInt32(&isSingleBuffered); if (res != OK) { ALOGE("Can't read isSingleBuffered"); return res; } } sp binder; res = parcel->readNullableStrongBinder(&binder); if (res != OK) { ALOGE("%s: Can't read strong binder", __FUNCTION__); return res; } graphicBufferProducer = interface_cast(binder); return OK; } String16 Surface::readMaybeEmptyString16(const Parcel* parcel) { size_t len; const char16_t* str = parcel->readString16Inplace(&len); if (str != nullptr) { return String16(str, len); } else { return String16(); } } } // namespace view } // namespace android libs/hwc2on1adapter/0040755 0000000 0000000 00000000000 13300556574 013373 5ustar000000000 0000000 libs/hwc2on1adapter/Android.bp0100644 0000000 0000000 00000004017 13300556574 015275 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_shared { name: "libhwc2on1adapter", vendor: true, clang: true, cppflags: [ "-Weverything", "-Wall", "-Wunused", "-Wunreachable-code", // 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", // hwcomposer2.h features switch covering all cases. "-Wno-covered-switch-default", // hwcomposer.h features zero size array. "-Wno-zero-length-array", // Disabling warning specific to hwc2on1adapter code "-Wno-double-promotion", "-Wno-sign-conversion", "-Wno-switch-enum", "-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-sign-compare", "-Wno-missing-prototypes", ], srcs: [ "HWC2On1Adapter.cpp", "MiniFence.cpp", ], shared_libs: [ "libutils", "libcutils", "liblog", "libhardware", ], export_include_dirs: ["include"], export_shared_lib_headers: ["libutils"], } libs/hwc2on1adapter/CleanSpec.mk0100644 0000000 0000000 00000004664 13300556574 015570 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. # # 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/*) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwc2on1adapter_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwc2on1adapter.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libhwc2on1adapter.so) libs/hwc2on1adapter/HWC2On1Adapter.cpp0100644 0000000 0000000 00000267412 13300556574 016472 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 "hwc2on1adapter/HWC2On1Adapter.h" //#define LOG_NDEBUG 0 #undef LOG_TAG #define LOG_TAG "HWC2On1Adapter" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include #include #include #include using namespace std::chrono_literals; static uint8_t getMinorVersion(struct hwc_composer_device_1* device) { auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK; return (version >> 16) & 0xF; } template static hwc2_function_pointer_t asFP(T function) { static_assert(std::is_same::value, "Incompatible function pointer"); return reinterpret_cast(function); } using namespace HWC2; static constexpr Attribute ColorMode = static_cast(6); namespace android { class HWC2On1Adapter::Callbacks : public hwc_procs_t { public: explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) { invalidate = &invalidateHook; vsync = &vsyncHook; hotplug = &hotplugHook; } static void invalidateHook(const hwc_procs_t* procs) { auto callbacks = static_cast(procs); callbacks->mAdapter.hwc1Invalidate(); } static void vsyncHook(const hwc_procs_t* procs, int display, int64_t timestamp) { auto callbacks = static_cast(procs); callbacks->mAdapter.hwc1Vsync(display, timestamp); } static void hotplugHook(const hwc_procs_t* procs, int display, int connected) { auto callbacks = static_cast(procs); callbacks->mAdapter.hwc1Hotplug(display, connected); } private: HWC2On1Adapter& mAdapter; }; static int closeHook(hw_device_t* /*device*/) { // Do nothing, since the real work is done in the class destructor, but we // need to provide a valid function pointer for hwc2_close to call return 0; } HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device) : mDumpString(), mHwc1Device(hwc1Device), mHwc1MinorVersion(getMinorVersion(hwc1Device)), mHwc1SupportsVirtualDisplays(false), mHwc1SupportsBackgroundColor(false), mHwc1Callbacks(std::make_unique(*this)), mCapabilities(), mLayers(), mHwc1VirtualDisplay(), mStateMutex(), mCallbacks(), mHasPendingInvalidate(false), mPendingVsyncs(), mPendingHotplugs(), mDisplays(), mHwc1DisplayMap() { common.close = closeHook; getCapabilities = getCapabilitiesHook; getFunction = getFunctionHook; populateCapabilities(); populatePrimary(); mHwc1Device->registerProcs(mHwc1Device, static_cast(mHwc1Callbacks.get())); } HWC2On1Adapter::~HWC2On1Adapter() { hwc_close_1(mHwc1Device); } void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount, int32_t* outCapabilities) { if (outCapabilities == nullptr) { *outCount = mCapabilities.size(); return; } auto capabilityIter = mCapabilities.cbegin(); for (size_t written = 0; written < *outCount; ++written) { if (capabilityIter == mCapabilities.cend()) { return; } outCapabilities[written] = static_cast(*capabilityIter); ++capabilityIter; } } hwc2_function_pointer_t HWC2On1Adapter::doGetFunction( FunctionDescriptor descriptor) { switch (descriptor) { // Device functions case FunctionDescriptor::CreateVirtualDisplay: return asFP( createVirtualDisplayHook); case FunctionDescriptor::DestroyVirtualDisplay: return asFP( destroyVirtualDisplayHook); case FunctionDescriptor::Dump: return asFP(dumpHook); case FunctionDescriptor::GetMaxVirtualDisplayCount: return asFP( getMaxVirtualDisplayCountHook); case FunctionDescriptor::RegisterCallback: return asFP(registerCallbackHook); // Display functions case FunctionDescriptor::AcceptDisplayChanges: return asFP( displayHook); case FunctionDescriptor::CreateLayer: return asFP( displayHook); case FunctionDescriptor::DestroyLayer: return asFP( displayHook); case FunctionDescriptor::GetActiveConfig: return asFP( displayHook); case FunctionDescriptor::GetChangedCompositionTypes: return asFP( displayHook); case FunctionDescriptor::GetColorModes: return asFP( displayHook); case FunctionDescriptor::GetDisplayAttribute: return asFP( getDisplayAttributeHook); case FunctionDescriptor::GetDisplayConfigs: return asFP( displayHook); case FunctionDescriptor::GetDisplayName: return asFP( displayHook); case FunctionDescriptor::GetDisplayRequests: return asFP( displayHook); case FunctionDescriptor::GetDisplayType: return asFP( displayHook); case FunctionDescriptor::GetDozeSupport: return asFP( displayHook); case FunctionDescriptor::GetHdrCapabilities: return asFP( displayHook); case FunctionDescriptor::GetReleaseFences: return asFP( displayHook); case FunctionDescriptor::PresentDisplay: return asFP( displayHook); case FunctionDescriptor::SetActiveConfig: return asFP( displayHook); case FunctionDescriptor::SetClientTarget: return asFP( displayHook); case FunctionDescriptor::SetColorMode: return asFP(setColorModeHook); case FunctionDescriptor::SetColorTransform: return asFP(setColorTransformHook); case FunctionDescriptor::SetOutputBuffer: return asFP( displayHook); case FunctionDescriptor::SetPowerMode: return asFP(setPowerModeHook); case FunctionDescriptor::SetVsyncEnabled: return asFP(setVsyncEnabledHook); case FunctionDescriptor::ValidateDisplay: return asFP( displayHook); case FunctionDescriptor::GetClientTargetSupport: return asFP( displayHook); // Layer functions case FunctionDescriptor::SetCursorPosition: return asFP( layerHook); case FunctionDescriptor::SetLayerBuffer: return asFP( layerHook); case FunctionDescriptor::SetLayerSurfaceDamage: return asFP( layerHook); // Layer state functions case FunctionDescriptor::SetLayerBlendMode: return asFP( setLayerBlendModeHook); case FunctionDescriptor::SetLayerColor: return asFP( layerHook); case FunctionDescriptor::SetLayerCompositionType: return asFP( setLayerCompositionTypeHook); case FunctionDescriptor::SetLayerDataspace: return asFP(setLayerDataspaceHook); case FunctionDescriptor::SetLayerDisplayFrame: return asFP( layerHook); case FunctionDescriptor::SetLayerPlaneAlpha: return asFP( layerHook); case FunctionDescriptor::SetLayerSidebandStream: return asFP( layerHook); case FunctionDescriptor::SetLayerSourceCrop: return asFP( layerHook); case FunctionDescriptor::SetLayerTransform: return asFP(setLayerTransformHook); case FunctionDescriptor::SetLayerVisibleRegion: return asFP( layerHook); case FunctionDescriptor::SetLayerZOrder: return asFP(setLayerZOrderHook); default: ALOGE("doGetFunction: Unknown function descriptor: %d (%s)", static_cast(descriptor), to_string(descriptor).c_str()); return nullptr; } } // Device functions Error HWC2On1Adapter::createVirtualDisplay(uint32_t width, uint32_t height, hwc2_display_t* outDisplay) { std::unique_lock lock(mStateMutex); if (mHwc1VirtualDisplay) { // We have already allocated our only HWC1 virtual display ALOGE("createVirtualDisplay: HWC1 virtual display already allocated"); return Error::NoResources; } mHwc1VirtualDisplay = std::make_shared(*this, HWC2::DisplayType::Virtual); mHwc1VirtualDisplay->populateConfigs(width, height); const auto displayId = mHwc1VirtualDisplay->getId(); mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId; mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL); mDisplays.emplace(displayId, mHwc1VirtualDisplay); *outDisplay = displayId; return Error::None; } Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) { std::unique_lock lock(mStateMutex); if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) { return Error::BadDisplay; } mHwc1VirtualDisplay.reset(); mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL); mDisplays.erase(displayId); return Error::None; } void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) { if (outBuffer != nullptr) { auto copiedBytes = mDumpString.copy(outBuffer, *outSize); *outSize = static_cast(copiedBytes); return; } std::stringstream output; output << "-- HWC2On1Adapter --\n"; output << "Adapting to a HWC 1." << static_cast(mHwc1MinorVersion) << " device\n"; // Attempt to acquire the lock for 1 second, but proceed without the lock // after that, so we can still get some information if we're deadlocked std::unique_lock lock(mStateMutex, std::defer_lock); lock.try_lock_for(1s); if (mCapabilities.empty()) { output << "Capabilities: None\n"; } else { output << "Capabilities:\n"; for (auto capability : mCapabilities) { output << " " << to_string(capability) << '\n'; } } output << "Displays:\n"; for (const auto& element : mDisplays) { const auto& display = element.second; output << display->dump(); } output << '\n'; // Release the lock before calling into HWC1, and since we no longer require // mutual exclusion to access mCapabilities or mDisplays lock.unlock(); if (mHwc1Device->dump) { output << "HWC1 dump:\n"; std::vector hwc1Dump(4096); // Call with size - 1 to preserve a null character at the end mHwc1Device->dump(mHwc1Device, hwc1Dump.data(), static_cast(hwc1Dump.size() - 1)); output << hwc1Dump.data(); } mDumpString = output.str(); *outSize = static_cast(mDumpString.size()); } uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() { return mHwc1SupportsVirtualDisplays ? 1 : 0; } static bool isValid(Callback descriptor) { switch (descriptor) { case Callback::Hotplug: // Fall-through case Callback::Refresh: // Fall-through case Callback::Vsync: return true; default: return false; } } Error HWC2On1Adapter::registerCallback(Callback descriptor, hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) { if (!isValid(descriptor)) { return Error::BadParameter; } ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(), callbackData, pointer); std::unique_lock lock(mStateMutex); if (pointer != nullptr) { mCallbacks[descriptor] = {callbackData, pointer}; } else { ALOGI("unregisterCallback(%s)", to_string(descriptor).c_str()); mCallbacks.erase(descriptor); return Error::None; } bool hasPendingInvalidate = false; std::vector displayIds; std::vector> pendingVsyncs; std::vector> pendingHotplugs; if (descriptor == Callback::Refresh) { hasPendingInvalidate = mHasPendingInvalidate; if (hasPendingInvalidate) { for (auto& displayPair : mDisplays) { displayIds.emplace_back(displayPair.first); } } mHasPendingInvalidate = false; } else if (descriptor == Callback::Vsync) { for (auto pending : mPendingVsyncs) { auto hwc1DisplayId = pending.first; if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId); continue; } auto displayId = mHwc1DisplayMap[hwc1DisplayId]; auto timestamp = pending.second; pendingVsyncs.emplace_back(displayId, timestamp); } mPendingVsyncs.clear(); } else if (descriptor == Callback::Hotplug) { // Hotplug the primary display pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY], static_cast(Connection::Connected)); for (auto pending : mPendingHotplugs) { auto hwc1DisplayId = pending.first; if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d", hwc1DisplayId); continue; } auto displayId = mHwc1DisplayMap[hwc1DisplayId]; auto connected = pending.second; pendingHotplugs.emplace_back(displayId, connected); } } // Call pending callbacks without the state lock held lock.unlock(); if (hasPendingInvalidate) { auto refresh = reinterpret_cast(pointer); for (auto displayId : displayIds) { refresh(callbackData, displayId); } } if (!pendingVsyncs.empty()) { auto vsync = reinterpret_cast(pointer); for (auto& pendingVsync : pendingVsyncs) { vsync(callbackData, pendingVsync.first, pendingVsync.second); } } if (!pendingHotplugs.empty()) { auto hotplug = reinterpret_cast(pointer); for (auto& pendingHotplug : pendingHotplugs) { hotplug(callbackData, pendingHotplug.first, pendingHotplug.second); } } return Error::None; } // Display functions std::atomic HWC2On1Adapter::Display::sNextId(1); HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type) : mId(sNextId++), mDevice(device), mStateMutex(), mHwc1RequestedContents(nullptr), mRetireFence(), mChanges(), mHwc1Id(-1), mConfigs(), mActiveConfig(nullptr), mActiveColorMode(static_cast(-1)), mName(), mType(type), mPowerMode(PowerMode::Off), mVsyncEnabled(Vsync::Invalid), mClientTarget(), mOutputBuffer(), mHasColorTransform(false), mLayers(), mHwc1LayerMap(), mNumAvailableRects(0), mNextAvailableRect(nullptr), mGeometryChanged(false) {} Error HWC2On1Adapter::Display::acceptChanges() { std::unique_lock lock(mStateMutex); if (!mChanges) { ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId); return Error::NotValidated; } ALOGV("[%" PRIu64 "] acceptChanges", mId); for (auto& change : mChanges->getTypeChanges()) { auto layerId = change.first; auto type = change.second; if (mDevice.mLayers.count(layerId) == 0) { // This should never happen but somehow does. ALOGW("Cannot accept change for unknown layer (%" PRIu64 ")", layerId); continue; } auto layer = mDevice.mLayers[layerId]; layer->setCompositionType(type); } mChanges->clearTypeChanges(); return Error::None; } Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) { std::unique_lock lock(mStateMutex); auto layer = *mLayers.emplace(std::make_shared(*this)); mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer)); *outLayerId = layer->getId(); ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId); markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) { std::unique_lock lock(mStateMutex); const auto mapLayer = mDevice.mLayers.find(layerId); if (mapLayer == mDevice.mLayers.end()) { ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer", mId, layerId); return Error::BadLayer; } const auto layer = mapLayer->second; mDevice.mLayers.erase(mapLayer); const auto zRange = mLayers.equal_range(layer); for (auto current = zRange.first; current != zRange.second; ++current) { if (**current == *layer) { current = mLayers.erase(current); break; } } ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId); markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) { std::unique_lock lock(mStateMutex); if (!mActiveConfig) { ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId, to_string(Error::BadConfig).c_str()); return Error::BadConfig; } auto configId = mActiveConfig->getId(); ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId); *outConfig = configId; return Error::None; } Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId, Attribute attribute, int32_t* outValue) { std::unique_lock lock(mStateMutex); if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId, configId); return Error::BadConfig; } *outValue = mConfigs[configId]->getAttribute(attribute); ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId, to_string(attribute).c_str(), *outValue); return Error::None; } Error HWC2On1Adapter::Display::getChangedCompositionTypes( uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) { std::unique_lock lock(mStateMutex); if (!mChanges) { ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated", mId); return Error::NotValidated; } if ((outLayers == nullptr) || (outTypes == nullptr)) { *outNumElements = mChanges->getTypeChanges().size(); return Error::None; } uint32_t numWritten = 0; for (const auto& element : mChanges->getTypeChanges()) { if (numWritten == *outNumElements) { break; } auto layerId = element.first; auto intType = static_cast(element.second); ALOGV("Adding %" PRIu64 " %s", layerId, to_string(element.second).c_str()); outLayers[numWritten] = layerId; outTypes[numWritten] = intType; ++numWritten; } *outNumElements = numWritten; return Error::None; } Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes, int32_t* outModes) { std::unique_lock lock(mStateMutex); if (!outModes) { *outNumModes = mColorModes.size(); return Error::None; } uint32_t numModes = std::min(*outNumModes, static_cast(mColorModes.size())); std::copy_n(mColorModes.cbegin(), numModes, outModes); *outNumModes = numModes; return Error::None; } Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs, hwc2_config_t* outConfigs) { std::unique_lock lock(mStateMutex); if (!outConfigs) { *outNumConfigs = mConfigs.size(); return Error::None; } uint32_t numWritten = 0; for (const auto& config : mConfigs) { if (numWritten == *outNumConfigs) { break; } outConfigs[numWritten] = config->getId(); ++numWritten; } *outNumConfigs = numWritten; return Error::None; } Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) { std::unique_lock lock(mStateMutex); if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) { *outSupport = 0; } else { *outSupport = 1; } return Error::None; } Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes, int32_t* /*outTypes*/, float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) { // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0 *outNumTypes = 0; return Error::None; } Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) { std::unique_lock lock(mStateMutex); if (!outName) { *outSize = mName.size(); return Error::None; } auto numCopied = mName.copy(outName, *outSize); *outSize = numCopied; return Error::None; } Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outFences) { std::unique_lock lock(mStateMutex); uint32_t numWritten = 0; bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr); for (const auto& layer : mLayers) { if (outputsNonNull && (numWritten == *outNumElements)) { break; } auto releaseFence = layer->getReleaseFence(); if (releaseFence != MiniFence::NO_FENCE) { if (outputsNonNull) { outLayers[numWritten] = layer->getId(); outFences[numWritten] = releaseFence->dup(); } ++numWritten; } } *outNumElements = numWritten; return Error::None; } Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests, uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outLayerRequests) { std::unique_lock lock(mStateMutex); if (!mChanges) { return Error::NotValidated; } if (outLayers == nullptr || outLayerRequests == nullptr) { *outNumElements = mChanges->getNumLayerRequests(); return Error::None; } // Display requests (HWC2::DisplayRequest) are not supported by hwc1: // A hwc1 has always zero requests for the client. *outDisplayRequests = 0; uint32_t numWritten = 0; for (const auto& request : mChanges->getLayerRequests()) { if (numWritten == *outNumElements) { break; } outLayers[numWritten] = request.first; outLayerRequests[numWritten] = static_cast(request.second); ++numWritten; } return Error::None; } Error HWC2On1Adapter::Display::getType(int32_t* outType) { std::unique_lock lock(mStateMutex); *outType = static_cast(mType); return Error::None; } Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) { std::unique_lock lock(mStateMutex); if (mChanges) { Error error = mDevice.setAllDisplays(); if (error != Error::None) { ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId, to_string(error).c_str()); return error; } } *outRetireFence = mRetireFence.get()->dup(); ALOGV("[%" PRIu64 "] present returning retire fence %d", mId, *outRetireFence); return Error::None; } Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) { std::unique_lock lock(mStateMutex); auto config = getConfig(configId); if (!config) { return Error::BadConfig; } if (config == mActiveConfig) { return Error::None; } if (mDevice.mHwc1MinorVersion >= 4) { uint32_t hwc1Id = 0; auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id); if (error != Error::None) { return error; } int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, static_cast(hwc1Id)); if (intError != 0) { ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)", intError); return Error::BadConfig; } mActiveConfig = config; } return Error::None; } Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target, int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) { std::unique_lock lock(mStateMutex); ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence); mClientTarget.setBuffer(target); mClientTarget.setFence(acquireFence); // dataspace and damage can't be used by HWC1, so ignore them return Error::None; } Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) { std::unique_lock lock (mStateMutex); ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode); if (mode == mActiveColorMode) { return Error::None; } if (mColorModes.count(mode) == 0) { ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode); return Error::Unsupported; } uint32_t hwc1Config = 0; auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config); if (error != Error::None) { return error; } ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config); int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, hwc1Config); if (intError != 0) { ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError); return Error::Unsupported; } mActiveColorMode = mode; return Error::None; } Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint) { std::unique_lock lock(mStateMutex); ALOGV("%" PRIu64 "] setColorTransform(%d)", mId, static_cast(hint)); mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY); return Error::None; } Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer, int32_t releaseFence) { std::unique_lock lock(mStateMutex); ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence); mOutputBuffer.setBuffer(buffer); mOutputBuffer.setFence(releaseFence); return Error::None; } static bool isValid(PowerMode mode) { switch (mode) { case PowerMode::Off: // Fall-through case PowerMode::DozeSuspend: // Fall-through case PowerMode::Doze: // Fall-through case PowerMode::On: return true; } } static int getHwc1PowerMode(PowerMode mode) { switch (mode) { case PowerMode::Off: return HWC_POWER_MODE_OFF; case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND; case PowerMode::Doze: return HWC_POWER_MODE_DOZE; case PowerMode::On: return HWC_POWER_MODE_NORMAL; } } Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) { if (!isValid(mode)) { return Error::BadParameter; } if (mode == mPowerMode) { return Error::None; } std::unique_lock lock(mStateMutex); int error = 0; if (mDevice.mHwc1MinorVersion < 4) { error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id, mode == PowerMode::Off); } else { error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device, mHwc1Id, getHwc1PowerMode(mode)); } ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)", error); ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str()); mPowerMode = mode; return Error::None; } static bool isValid(Vsync enable) { switch (enable) { case Vsync::Enable: // Fall-through case Vsync::Disable: return true; case Vsync::Invalid: return false; } } Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) { if (!isValid(enable)) { return Error::BadParameter; } if (enable == mVsyncEnabled) { return Error::None; } std::unique_lock lock(mStateMutex); int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device, mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable); ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)", error); mVsyncEnabled = enable; return Error::None; } Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests) { std::unique_lock lock(mStateMutex); if (!mChanges) { if (!mDevice.prepareAllDisplays()) { return Error::BadDisplay; } } else { ALOGE("Validate was called more than once!"); } *outNumTypes = mChanges->getNumTypes(); *outNumRequests = mChanges->getNumLayerRequests(); ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes, *outNumRequests); for (auto request : mChanges->getTypeChanges()) { ALOGV("Layer %" PRIu64 " --> %s", request.first, to_string(request.second).c_str()); } return *outNumTypes > 0 ? Error::HasChanges : Error::None; } Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) { std::unique_lock lock(mStateMutex); const auto mapLayer = mDevice.mLayers.find(layerId); if (mapLayer == mDevice.mLayers.end()) { ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId); return Error::BadLayer; } const auto layer = mapLayer->second; const auto zRange = mLayers.equal_range(layer); bool layerOnDisplay = false; for (auto current = zRange.first; current != zRange.second; ++current) { if (**current == *layer) { if ((*current)->getZ() == z) { // Don't change anything if the Z hasn't changed return Error::None; } current = mLayers.erase(current); layerOnDisplay = true; break; } } if (!layerOnDisplay) { ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display", mId); return Error::BadLayer; } layer->setZ(z); mLayers.emplace(std::move(layer)); markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Display::getClientTargetSupport(uint32_t width, uint32_t height, int32_t format, int32_t dataspace){ if (mActiveConfig == nullptr) { return Error::Unsupported; } if (width == mActiveConfig->getAttribute(Attribute::Width) && height == mActiveConfig->getAttribute(Attribute::Height) && format == HAL_PIXEL_FORMAT_RGBA_8888 && dataspace == HAL_DATASPACE_UNKNOWN) { return Error::None; } return Error::Unsupported; } static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = { HWC_DISPLAY_VSYNC_PERIOD, HWC_DISPLAY_WIDTH, HWC_DISPLAY_HEIGHT, HWC_DISPLAY_DPI_X, HWC_DISPLAY_DPI_Y, HWC_DISPLAY_COLOR_TRANSFORM, HWC_DISPLAY_NO_ATTRIBUTE, }; static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = { HWC_DISPLAY_VSYNC_PERIOD, HWC_DISPLAY_WIDTH, HWC_DISPLAY_HEIGHT, HWC_DISPLAY_DPI_X, HWC_DISPLAY_DPI_Y, HWC_DISPLAY_NO_ATTRIBUTE, }; static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR = sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t); static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR), "Attribute tables have unexpected sizes"); static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = { 6, // HWC_DISPLAY_NO_ATTRIBUTE = 0 0, // HWC_DISPLAY_VSYNC_PERIOD = 1, 1, // HWC_DISPLAY_WIDTH = 2, 2, // HWC_DISPLAY_HEIGHT = 3, 3, // HWC_DISPLAY_DPI_X = 4, 4, // HWC_DISPLAY_DPI_Y = 5, 5, // HWC_DISPLAY_COLOR_TRANSFORM = 6, }; static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = { 5, // HWC_DISPLAY_NO_ATTRIBUTE = 0 0, // HWC_DISPLAY_VSYNC_PERIOD = 1, 1, // HWC_DISPLAY_WIDTH = 2, 2, // HWC_DISPLAY_HEIGHT = 3, 3, // HWC_DISPLAY_DPI_X = 4, 4, // HWC_DISPLAY_DPI_Y = 5, }; template static constexpr bool attributesMatch() { bool match = (attribute == ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]); if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) { return match; } return match && (attribute == ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]); } static_assert(attributesMatch(), "Tables out of sync"); static_assert(attributesMatch(), "Tables out of sync"); static_assert(attributesMatch(), "Tables out of sync"); static_assert(attributesMatch(), "Tables out of sync"); static_assert(attributesMatch(), "Tables out of sync"); static_assert(attributesMatch(), "Tables out of sync"); void HWC2On1Adapter::Display::populateConfigs() { std::unique_lock lock(mStateMutex); ALOGV("[%" PRIu64 "] populateConfigs", mId); if (mHwc1Id == -1) { ALOGE("populateConfigs: HWC1 ID not set"); return; } const size_t MAX_NUM_CONFIGS = 128; uint32_t configs[MAX_NUM_CONFIGS] = {}; size_t numConfigs = MAX_NUM_CONFIGS; mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id, configs, &numConfigs); for (size_t c = 0; c < numConfigs; ++c) { uint32_t hwc1ConfigId = configs[c]; auto newConfig = std::make_shared(*this); int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {}; bool hasColor = true; auto result = mDevice.mHwc1Device->getDisplayAttributes( mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITH_COLOR, values); if (result != 0) { mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values); hasColor = false; } auto attributeMap = hasColor ? ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR; newConfig->setAttribute(Attribute::VsyncPeriod, values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]); newConfig->setAttribute(Attribute::Width, values[attributeMap[HWC_DISPLAY_WIDTH]]); newConfig->setAttribute(Attribute::Height, values[attributeMap[HWC_DISPLAY_HEIGHT]]); newConfig->setAttribute(Attribute::DpiX, values[attributeMap[HWC_DISPLAY_DPI_X]]); newConfig->setAttribute(Attribute::DpiY, values[attributeMap[HWC_DISPLAY_DPI_Y]]); if (hasColor) { // In HWC1, color modes are referred to as color transforms. To avoid confusion with // the HWC2 concept of color transforms, we internally refer to them as color modes for // both HWC1 and 2. newConfig->setAttribute(ColorMode, values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]); } // We can only do this after attempting to read the color mode newConfig->setHwc1Id(hwc1ConfigId); for (auto& existingConfig : mConfigs) { if (existingConfig->merge(*newConfig)) { ALOGV("Merged config %d with existing config %u: %s", hwc1ConfigId, existingConfig->getId(), existingConfig->toString().c_str()); newConfig.reset(); break; } } // If it wasn't merged with any existing config, add it to the end if (newConfig) { newConfig->setId(static_cast(mConfigs.size())); ALOGV("Found new config %u: %s", newConfig->getId(), newConfig->toString().c_str()); mConfigs.emplace_back(std::move(newConfig)); } } initializeActiveConfig(); populateColorModes(); } void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) { std::unique_lock lock(mStateMutex); mConfigs.emplace_back(std::make_shared(*this)); auto& config = mConfigs[0]; config->setAttribute(Attribute::Width, static_cast(width)); config->setAttribute(Attribute::Height, static_cast(height)); config->setHwc1Id(0); config->setId(0); mActiveConfig = config; } bool HWC2On1Adapter::Display::prepare() { std::unique_lock lock(mStateMutex); // Only prepare display contents for displays HWC1 knows about if (mHwc1Id == -1) { return true; } // It doesn't make sense to prepare a display for which there is no active // config, so return early if (!mActiveConfig) { ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId); return false; } allocateRequestedContents(); assignHwc1LayerIds(); mHwc1RequestedContents->retireFenceFd = -1; mHwc1RequestedContents->flags = 0; if (mGeometryChanged) { mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED; } mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer(); mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence(); // +1 is for framebuffer target layer. mHwc1RequestedContents->numHwLayers = mLayers.size() + 1; for (auto& layer : mLayers) { auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()]; hwc1Layer.releaseFenceFd = -1; hwc1Layer.acquireFenceFd = -1; ALOGV("Applying states for layer %" PRIu64 " ", layer->getId()); layer->applyState(hwc1Layer); } prepareFramebufferTarget(); resetGeometryMarker(); return true; } void HWC2On1Adapter::Display::generateChanges() { std::unique_lock lock(mStateMutex); mChanges.reset(new Changes); size_t numLayers = mHwc1RequestedContents->numHwLayers; for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) { const auto& receivedLayer = mHwc1RequestedContents->hwLayers[hwc1Id]; if (mHwc1LayerMap.count(hwc1Id) == 0) { ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET, "generateChanges: HWC1 layer %zd doesn't have a" " matching HWC2 layer, and isn't the framebuffer target", hwc1Id); continue; } Layer& layer = *mHwc1LayerMap[hwc1Id]; updateTypeChanges(receivedLayer, layer); updateLayerRequests(receivedLayer, layer); } } bool HWC2On1Adapter::Display::hasChanges() const { std::unique_lock lock(mStateMutex); return mChanges != nullptr; } Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) { std::unique_lock lock(mStateMutex); if (!mChanges || (mChanges->getNumTypes() > 0)) { ALOGE("[%" PRIu64 "] set failed: not validated", mId); return Error::NotValidated; } // Set up the client/framebuffer target auto numLayers = hwcContents.numHwLayers; // Close acquire fences on FRAMEBUFFER layers, since they will not be used // by HWC for (size_t l = 0; l < numLayers - 1; ++l) { auto& layer = hwcContents.hwLayers[l]; if (layer.compositionType == HWC_FRAMEBUFFER) { ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l); close(layer.acquireFenceFd); layer.acquireFenceFd = -1; } } auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1]; if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) { clientTargetLayer.handle = mClientTarget.getBuffer(); clientTargetLayer.acquireFenceFd = mClientTarget.getFence(); } else { ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET", mId); } mChanges.reset(); return Error::None; } void HWC2On1Adapter::Display::addRetireFence(int fenceFd) { std::unique_lock lock(mStateMutex); mRetireFence.add(fenceFd); } void HWC2On1Adapter::Display::addReleaseFences( const hwc_display_contents_1_t& hwcContents) { std::unique_lock lock(mStateMutex); size_t numLayers = hwcContents.numHwLayers; for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) { const auto& receivedLayer = hwcContents.hwLayers[hwc1Id]; if (mHwc1LayerMap.count(hwc1Id) == 0) { if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) { ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a" " matching HWC2 layer, and isn't the framebuffer" " target", hwc1Id); } // Close the framebuffer target release fence since we will use the // display retire fence instead if (receivedLayer.releaseFenceFd != -1) { close(receivedLayer.releaseFenceFd); } continue; } Layer& layer = *mHwc1LayerMap[hwc1Id]; ALOGV("Adding release fence %d to layer %" PRIu64, receivedLayer.releaseFenceFd, layer.getId()); layer.addReleaseFence(receivedLayer.releaseFenceFd); } } bool HWC2On1Adapter::Display::hasColorTransform() const { std::unique_lock lock(mStateMutex); return mHasColorTransform; } static std::string hwc1CompositionString(int32_t type) { switch (type) { case HWC_FRAMEBUFFER: return "Framebuffer"; case HWC_OVERLAY: return "Overlay"; case HWC_BACKGROUND: return "Background"; case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget"; case HWC_SIDEBAND: return "Sideband"; case HWC_CURSOR_OVERLAY: return "CursorOverlay"; default: return std::string("Unknown (") + std::to_string(type) + ")"; } } static std::string hwc1TransformString(int32_t transform) { switch (transform) { case 0: return "None"; case HWC_TRANSFORM_FLIP_H: return "FlipH"; case HWC_TRANSFORM_FLIP_V: return "FlipV"; case HWC_TRANSFORM_ROT_90: return "Rotate90"; case HWC_TRANSFORM_ROT_180: return "Rotate180"; case HWC_TRANSFORM_ROT_270: return "Rotate270"; case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90"; case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90"; default: return std::string("Unknown (") + std::to_string(transform) + ")"; } } static std::string hwc1BlendModeString(int32_t mode) { switch (mode) { case HWC_BLENDING_NONE: return "None"; case HWC_BLENDING_PREMULT: return "Premultiplied"; case HWC_BLENDING_COVERAGE: return "Coverage"; default: return std::string("Unknown (") + std::to_string(mode) + ")"; } } static std::string rectString(hwc_rect_t rect) { std::stringstream output; output << "[" << rect.left << ", " << rect.top << ", "; output << rect.right << ", " << rect.bottom << "]"; return output.str(); } static std::string approximateFloatString(float f) { if (static_cast(f) == f) { return std::to_string(static_cast(f)); } int32_t truncated = static_cast(f * 10); bool approximate = (static_cast(truncated) != f * 10); const size_t BUFFER_SIZE = 32; char buffer[BUFFER_SIZE] = {}; auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%s%.1f", approximate ? "~" : "", f); return std::string(buffer, bytesWritten); } static std::string frectString(hwc_frect_t frect) { std::stringstream output; output << "[" << approximateFloatString(frect.left) << ", "; output << approximateFloatString(frect.top) << ", "; output << approximateFloatString(frect.right) << ", "; output << approximateFloatString(frect.bottom) << "]"; return output.str(); } static std::string colorString(hwc_color_t color) { std::stringstream output; output << "RGBA ["; output << static_cast(color.r) << ", "; output << static_cast(color.g) << ", "; output << static_cast(color.b) << ", "; output << static_cast(color.a) << "]"; return output.str(); } static std::string alphaString(float f) { const size_t BUFFER_SIZE = 8; char buffer[BUFFER_SIZE] = {}; auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f); return std::string(buffer, bytesWritten); } static std::string to_string(const hwc_layer_1_t& hwcLayer, int32_t hwc1MinorVersion) { const char* fill = " "; std::stringstream output; output << " Composition: " << hwc1CompositionString(hwcLayer.compositionType); if (hwcLayer.compositionType == HWC_BACKGROUND) { output << " Color: " << colorString(hwcLayer.backgroundColor) << '\n'; } else if (hwcLayer.compositionType == HWC_SIDEBAND) { output << " Stream: " << hwcLayer.sidebandStream << '\n'; } else { output << " Buffer: " << hwcLayer.handle << "/" << hwcLayer.acquireFenceFd << '\n'; } output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) << '\n'; output << fill << "Source crop: "; if (hwc1MinorVersion >= 3) { output << frectString(hwcLayer.sourceCropf) << '\n'; } else { output << rectString(hwcLayer.sourceCropi) << '\n'; } output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform); output << " Blend mode: " << hwc1BlendModeString(hwcLayer.blending); if (hwcLayer.planeAlpha != 0xFF) { output << " Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f); } output << '\n'; if (hwcLayer.hints != 0) { output << fill << "Hints:"; if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) { output << " TripleBuffer"; } if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) { output << " ClearFB"; } output << '\n'; } if (hwcLayer.flags != 0) { output << fill << "Flags:"; if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) { output << " SkipLayer"; } if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) { output << " IsCursorLayer"; } output << '\n'; } return output.str(); } static std::string to_string(const hwc_display_contents_1_t& hwcContents, int32_t hwc1MinorVersion) { const char* fill = " "; std::stringstream output; output << fill << "Geometry changed: " << ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n"); output << fill << hwcContents.numHwLayers << " Layer" << ((hwcContents.numHwLayers == 1) ? "\n" : "s\n"); for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) { output << fill << " Layer " << layer; output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion); } if (hwcContents.outbuf != nullptr) { output << fill << "Output buffer: " << hwcContents.outbuf << "/" << hwcContents.outbufAcquireFenceFd << '\n'; } return output.str(); } std::string HWC2On1Adapter::Display::dump() const { std::unique_lock lock(mStateMutex); std::stringstream output; output << " Display " << mId << ": "; output << to_string(mType) << " "; output << "HWC1 ID: " << mHwc1Id << " "; output << "Power mode: " << to_string(mPowerMode) << " "; output << "Vsync: " << to_string(mVsyncEnabled) << '\n'; output << " Color modes [active]:"; for (const auto& mode : mColorModes) { if (mode == mActiveColorMode) { output << " [" << mode << ']'; } else { output << " " << mode; } } output << '\n'; output << " " << mConfigs.size() << " Config" << (mConfigs.size() == 1 ? "" : "s") << " (* active)\n"; for (const auto& config : mConfigs) { output << (config == mActiveConfig ? " * " : " "); output << config->toString(true) << '\n'; } output << " " << mLayers.size() << " Layer" << (mLayers.size() == 1 ? "" : "s") << '\n'; for (const auto& layer : mLayers) { output << layer->dump(); } output << " Client target: " << mClientTarget.getBuffer() << '\n'; if (mOutputBuffer.getBuffer() != nullptr) { output << " Output buffer: " << mOutputBuffer.getBuffer() << '\n'; } if (mHwc1RequestedContents) { output << " Last requested HWC1 state\n"; output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion); } return output.str(); } hwc_rect_t* HWC2On1Adapter::Display::GetRects(size_t numRects) { if (numRects == 0) { return nullptr; } if (numRects > mNumAvailableRects) { // This should NEVER happen since we calculated how many rects the // display would need. ALOGE("Rect allocation failure! SF is likely to crash soon!"); return nullptr; } hwc_rect_t* rects = mNextAvailableRect; mNextAvailableRect += numRects; mNumAvailableRects -= numRects; return rects; } hwc_display_contents_1* HWC2On1Adapter::Display::getDisplayContents() { return mHwc1RequestedContents.get(); } void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute, int32_t value) { mAttributes[attribute] = value; } int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const { if (mAttributes.count(attribute) == 0) { return -1; } return mAttributes.at(attribute); } void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) { android_color_mode_t colorMode = static_cast(getAttribute(ColorMode)); mHwc1Ids.emplace(colorMode, id); } bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const { for (const auto& idPair : mHwc1Ids) { if (id == idPair.second) { return true; } } return false; } Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id( uint32_t id, android_color_mode_t* outMode) const { for (const auto& idPair : mHwc1Ids) { if (id == idPair.second) { *outMode = idPair.first; return Error::None; } } ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId); return Error::BadParameter; } Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode, uint32_t* outId) const { for (const auto& idPair : mHwc1Ids) { if (mode == idPair.first) { *outId = idPair.second; return Error::None; } } ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId); return Error::BadParameter; } bool HWC2On1Adapter::Display::Config::merge(const Config& other) { auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height, HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX, HWC2::Attribute::DpiY}; for (auto attribute : attributes) { if (getAttribute(attribute) != other.getAttribute(attribute)) { return false; } } android_color_mode_t otherColorMode = static_cast(other.getAttribute(ColorMode)); if (mHwc1Ids.count(otherColorMode) != 0) { ALOGE("Attempted to merge two configs (%u and %u) which appear to be " "identical", mHwc1Ids.at(otherColorMode), other.mHwc1Ids.at(otherColorMode)); return false; } mHwc1Ids.emplace(otherColorMode, other.mHwc1Ids.at(otherColorMode)); return true; } std::set HWC2On1Adapter::Display::Config::getColorModes() const { std::set colorModes; for (const auto& idPair : mHwc1Ids) { colorModes.emplace(idPair.first); } return colorModes; } std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const { std::string output; const size_t BUFFER_SIZE = 100; char buffer[BUFFER_SIZE] = {}; auto writtenBytes = snprintf(buffer, BUFFER_SIZE, "%u x %u", mAttributes.at(HWC2::Attribute::Width), mAttributes.at(HWC2::Attribute::Height)); output.append(buffer, writtenBytes); if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) { std::memset(buffer, 0, BUFFER_SIZE); writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz", 1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod)); output.append(buffer, writtenBytes); } if (mAttributes.count(HWC2::Attribute::DpiX) != 0 && mAttributes.at(HWC2::Attribute::DpiX) != -1) { std::memset(buffer, 0, BUFFER_SIZE); writtenBytes = snprintf(buffer, BUFFER_SIZE, ", DPI: %.1f x %.1f", mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f, mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f); output.append(buffer, writtenBytes); } std::memset(buffer, 0, BUFFER_SIZE); if (splitLine) { writtenBytes = snprintf(buffer, BUFFER_SIZE, "\n HWC1 ID/Color transform:"); } else { writtenBytes = snprintf(buffer, BUFFER_SIZE, ", HWC1 ID/Color transform:"); } output.append(buffer, writtenBytes); for (const auto& id : mHwc1Ids) { android_color_mode_t colorMode = id.first; uint32_t hwc1Id = id.second; std::memset(buffer, 0, BUFFER_SIZE); if (colorMode == mDisplay.mActiveColorMode) { writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id, colorMode); } else { writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id, colorMode); } output.append(buffer, writtenBytes); } return output; } std::shared_ptr HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const { if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { return nullptr; } return mConfigs[configId]; } void HWC2On1Adapter::Display::populateColorModes() { mColorModes = mConfigs[0]->getColorModes(); for (const auto& config : mConfigs) { std::set intersection; auto configModes = config->getColorModes(); std::set_intersection(mColorModes.cbegin(), mColorModes.cend(), configModes.cbegin(), configModes.cend(), std::inserter(intersection, intersection.begin())); std::swap(intersection, mColorModes); } } void HWC2On1Adapter::Display::initializeActiveConfig() { if (mDevice.mHwc1Device->getActiveConfig == nullptr) { ALOGV("getActiveConfig is null, choosing config 0"); mActiveConfig = mConfigs[0]; mActiveColorMode = HAL_COLOR_MODE_NATIVE; return; } auto activeConfig = mDevice.mHwc1Device->getActiveConfig( mDevice.mHwc1Device, mHwc1Id); // Some devices startup without an activeConfig: // We need to set one ourselves. if (activeConfig == HWC_ERROR) { ALOGV("There is no active configuration: Picking the first one: 0."); const int defaultIndex = 0; mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex); activeConfig = defaultIndex; } for (const auto& config : mConfigs) { if (config->hasHwc1Id(activeConfig)) { ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig); mActiveConfig = config; if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) { // This should never happen since we checked for the config's presence before // setting it as active. ALOGE("Unable to find color mode for active HWC1 config %d", config->getId()); mActiveColorMode = HAL_COLOR_MODE_NATIVE; } break; } } if (!mActiveConfig) { ALOGV("Unable to find active HWC1 config %u, defaulting to " "config 0", activeConfig); mActiveConfig = mConfigs[0]; mActiveColorMode = HAL_COLOR_MODE_NATIVE; } } void HWC2On1Adapter::Display::allocateRequestedContents() { // What needs to be allocated: // 1 hwc_display_contents_1_t // 1 hwc_layer_1_t for each layer // 1 hwc_rect_t for each layer's surfaceDamage // 1 hwc_rect_t for each layer's visibleRegion // 1 hwc_layer_1_t for the framebuffer // 1 hwc_rect_t for the framebuffer's visibleRegion // Count # of surfaceDamage size_t numSurfaceDamages = 0; for (const auto& layer : mLayers) { numSurfaceDamages += layer->getNumSurfaceDamages(); } // Count # of visibleRegions (start at 1 for mandatory framebuffer target // region) size_t numVisibleRegion = 1; for (const auto& layer : mLayers) { numVisibleRegion += layer->getNumVisibleRegions(); } size_t numRects = numVisibleRegion + numSurfaceDamages; auto numLayers = mLayers.size() + 1; size_t size = sizeof(hwc_display_contents_1_t) + sizeof(hwc_layer_1_t) * numLayers + sizeof(hwc_rect_t) * numRects; auto contents = static_cast(std::calloc(size, 1)); mHwc1RequestedContents.reset(contents); mNextAvailableRect = reinterpret_cast(&contents->hwLayers[numLayers]); mNumAvailableRects = numRects; } void HWC2On1Adapter::Display::assignHwc1LayerIds() { mHwc1LayerMap.clear(); size_t nextHwc1Id = 0; for (auto& layer : mLayers) { mHwc1LayerMap[nextHwc1Id] = layer; layer->setHwc1Id(nextHwc1Id++); } } void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer, const Layer& layer) { auto layerId = layer.getId(); switch (hwc1Layer.compositionType) { case HWC_FRAMEBUFFER: if (layer.getCompositionType() != Composition::Client) { mChanges->addTypeChange(layerId, Composition::Client); } break; case HWC_OVERLAY: if (layer.getCompositionType() != Composition::Device) { mChanges->addTypeChange(layerId, Composition::Device); } break; case HWC_BACKGROUND: ALOGE_IF(layer.getCompositionType() != Composition::SolidColor, "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2" " wasn't expecting SolidColor"); break; case HWC_FRAMEBUFFER_TARGET: // Do nothing, since it shouldn't be modified by HWC1 break; case HWC_SIDEBAND: ALOGE_IF(layer.getCompositionType() != Composition::Sideband, "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2" " wasn't expecting Sideband"); break; case HWC_CURSOR_OVERLAY: ALOGE_IF(layer.getCompositionType() != Composition::Cursor, "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but" " HWC2 wasn't expecting Cursor"); break; } } void HWC2On1Adapter::Display::updateLayerRequests( const hwc_layer_1_t& hwc1Layer, const Layer& layer) { if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) { mChanges->addLayerRequest(layer.getId(), LayerRequest::ClearClientTarget); } } void HWC2On1Adapter::Display::prepareFramebufferTarget() { // We check that mActiveConfig is valid in Display::prepare int32_t width = mActiveConfig->getAttribute(Attribute::Width); int32_t height = mActiveConfig->getAttribute(Attribute::Height); auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()]; hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET; hwc1Target.releaseFenceFd = -1; hwc1Target.hints = 0; hwc1Target.flags = 0; hwc1Target.transform = 0; hwc1Target.blending = HWC_BLENDING_PREMULT; if (mDevice.getHwc1MinorVersion() < 3) { hwc1Target.sourceCropi = {0, 0, width, height}; } else { hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast(width), static_cast(height)}; } hwc1Target.displayFrame = {0, 0, width, height}; hwc1Target.planeAlpha = 255; hwc1Target.visibleRegionScreen.numRects = 1; hwc_rect_t* rects = GetRects(1); rects[0].left = 0; rects[0].top = 0; rects[0].right = width; rects[0].bottom = height; hwc1Target.visibleRegionScreen.rects = rects; // We will set this to the correct value in set hwc1Target.acquireFenceFd = -1; } // Layer functions std::atomic HWC2On1Adapter::Layer::sNextId(1); HWC2On1Adapter::Layer::Layer(Display& display) : mId(sNextId++), mDisplay(display), mBuffer(), mSurfaceDamage(), mBlendMode(BlendMode::None), mColor({0, 0, 0, 0}), mCompositionType(Composition::Invalid), mDisplayFrame({0, 0, -1, -1}), mPlaneAlpha(0.0f), mSidebandStream(nullptr), mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}), mTransform(Transform::None), mVisibleRegion(), mZ(0), mReleaseFence(), mHwc1Id(0), mHasUnsupportedPlaneAlpha(false) {} bool HWC2On1Adapter::SortLayersByZ::operator()( const std::shared_ptr& lhs, const std::shared_ptr& rhs) { return lhs->getZ() < rhs->getZ(); } Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer, int32_t acquireFence) { ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId); mBuffer.setBuffer(buffer); mBuffer.setFence(acquireFence); return Error::None; } Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) { if (mCompositionType != Composition::Cursor) { return Error::BadLayer; } if (mDisplay.hasChanges()) { return Error::NotValidated; } auto displayId = mDisplay.getHwc1Id(); auto hwc1Device = mDisplay.getDevice().getHwc1Device(); hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y); return Error::None; } Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) { // HWC1 supports surface damage starting only with version 1.5. if (mDisplay.getDevice().mHwc1MinorVersion < 5) { return Error::None; } mSurfaceDamage.resize(damage.numRects); std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin()); return Error::None; } // Layer state functions Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) { mBlendMode = mode; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) { mColor = color; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setCompositionType(Composition type) { mCompositionType = type; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t) { return Error::None; } Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) { mDisplayFrame = frame; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) { mPlaneAlpha = alpha; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) { mSidebandStream = stream; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) { mSourceCrop = crop; mDisplay.markGeometryChanged(); return Error::None; } Error HWC2On1Adapter::Layer::setTransform(Transform transform) { mTransform = transform; mDisplay.markGeometryChanged(); return Error::None; } static bool compareRects(const hwc_rect_t& rect1, const hwc_rect_t& rect2) { return rect1.left == rect2.left && rect1.right == rect2.right && rect1.top == rect2.top && rect1.bottom == rect2.bottom; } Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) { if ((getNumVisibleRegions() != visible.numRects) || !std::equal(mVisibleRegion.begin(), mVisibleRegion.end(), visible.rects, compareRects)) { mVisibleRegion.resize(visible.numRects); std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin()); mDisplay.markGeometryChanged(); } return Error::None; } Error HWC2On1Adapter::Layer::setZ(uint32_t z) { mZ = z; return Error::None; } void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) { ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId); mReleaseFence.add(fenceFd); } const sp& HWC2On1Adapter::Layer::getReleaseFence() const { return mReleaseFence.get(); } void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer) { applyCommonState(hwc1Layer); applyCompositionType(hwc1Layer); switch (mCompositionType) { case Composition::SolidColor : applySolidColorState(hwc1Layer); break; case Composition::Sideband : applySidebandState(hwc1Layer); break; default: applyBufferState(hwc1Layer); break; } } static std::string regionStrings(const std::vector& visibleRegion, const std::vector& surfaceDamage) { std::string regions; regions += " Visible Region"; regions.resize(40, ' '); regions += "Surface Damage\n"; size_t numPrinted = 0; size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size()); while (numPrinted < maxSize) { std::string line(" "); if (visibleRegion.empty() && numPrinted == 0) { line += "None"; } else if (numPrinted < visibleRegion.size()) { line += rectString(visibleRegion[numPrinted]); } line.resize(40, ' '); if (surfaceDamage.empty() && numPrinted == 0) { line += "None"; } else if (numPrinted < surfaceDamage.size()) { line += rectString(surfaceDamage[numPrinted]); } line += '\n'; regions += line; ++numPrinted; } return regions; } std::string HWC2On1Adapter::Layer::dump() const { std::stringstream output; const char* fill = " "; output << fill << to_string(mCompositionType); output << " Layer HWC2/1: " << mId << "/" << mHwc1Id << " "; output << "Z: " << mZ; if (mCompositionType == HWC2::Composition::SolidColor) { output << " " << colorString(mColor); } else if (mCompositionType == HWC2::Composition::Sideband) { output << " Handle: " << mSidebandStream << '\n'; } else { output << " Buffer: " << mBuffer.getBuffer() << "/" << mBuffer.getFence() << '\n'; output << fill << " Display frame [LTRB]: " << rectString(mDisplayFrame) << '\n'; output << fill << " Source crop: " << frectString(mSourceCrop) << '\n'; output << fill << " Transform: " << to_string(mTransform); output << " Blend mode: " << to_string(mBlendMode); if (mPlaneAlpha != 1.0f) { output << " Alpha: " << alphaString(mPlaneAlpha) << '\n'; } else { output << '\n'; } output << regionStrings(mVisibleRegion, mSurfaceDamage); } return output.str(); } static int getHwc1Blending(HWC2::BlendMode blendMode) { switch (blendMode) { case BlendMode::Coverage: return HWC_BLENDING_COVERAGE; case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT; default: return HWC_BLENDING_NONE; } } void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer) { auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion(); hwc1Layer.blending = getHwc1Blending(mBlendMode); hwc1Layer.displayFrame = mDisplayFrame; auto pendingAlpha = mPlaneAlpha; if (minorVersion < 2) { mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f; } else { hwc1Layer.planeAlpha = static_cast(255.0f * pendingAlpha + 0.5f); } if (minorVersion < 3) { auto pending = mSourceCrop; hwc1Layer.sourceCropi.left = static_cast(std::ceil(pending.left)); hwc1Layer.sourceCropi.top = static_cast(std::ceil(pending.top)); hwc1Layer.sourceCropi.right = static_cast(std::floor(pending.right)); hwc1Layer.sourceCropi.bottom = static_cast(std::floor(pending.bottom)); } else { hwc1Layer.sourceCropf = mSourceCrop; } hwc1Layer.transform = static_cast(mTransform); auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen; hwc1VisibleRegion.numRects = mVisibleRegion.size(); hwc_rect_t* rects = mDisplay.GetRects(hwc1VisibleRegion.numRects); hwc1VisibleRegion.rects = rects; for (size_t i = 0; i < mVisibleRegion.size(); i++) { rects[i] = mVisibleRegion[i]; } } void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer) { // If the device does not support background color it is likely to make // assumption regarding backgroundColor and handle (both fields occupy // the same location in hwc_layer_1_t union). // To not confuse these devices we don't set background color and we // make sure handle is a null pointer. if (hasUnsupportedBackgroundColor()) { hwc1Layer.handle = nullptr; } else { hwc1Layer.backgroundColor = mColor; } } void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer) { hwc1Layer.sidebandStream = mSidebandStream; } void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) { hwc1Layer.handle = mBuffer.getBuffer(); hwc1Layer.acquireFenceFd = mBuffer.getFence(); } void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer) { // HWC1 never supports color transforms or dataspaces and only sometimes // supports plane alpha (depending on the version). These require us to drop // some or all layers to client composition. if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() || hasUnsupportedBackgroundColor()) { hwc1Layer.compositionType = HWC_FRAMEBUFFER; hwc1Layer.flags = HWC_SKIP_LAYER; return; } hwc1Layer.flags = 0; switch (mCompositionType) { case Composition::Client: hwc1Layer.compositionType = HWC_FRAMEBUFFER; hwc1Layer.flags |= HWC_SKIP_LAYER; break; case Composition::Device: hwc1Layer.compositionType = HWC_FRAMEBUFFER; break; case Composition::SolidColor: // In theory the following line should work, but since the HWC1 // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1 // devices may not work correctly. To be on the safe side, we // fall back to client composition. // // hwc1Layer.compositionType = HWC_BACKGROUND; hwc1Layer.compositionType = HWC_FRAMEBUFFER; hwc1Layer.flags |= HWC_SKIP_LAYER; break; case Composition::Cursor: hwc1Layer.compositionType = HWC_FRAMEBUFFER; if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) { hwc1Layer.hints |= HWC_IS_CURSOR_LAYER; } break; case Composition::Sideband: if (mDisplay.getDevice().getHwc1MinorVersion() < 4) { hwc1Layer.compositionType = HWC_SIDEBAND; } else { hwc1Layer.compositionType = HWC_FRAMEBUFFER; hwc1Layer.flags |= HWC_SKIP_LAYER; } break; default: hwc1Layer.compositionType = HWC_FRAMEBUFFER; hwc1Layer.flags |= HWC_SKIP_LAYER; break; } ALOGV("Layer %" PRIu64 " %s set to %d", mId, to_string(mCompositionType).c_str(), hwc1Layer.compositionType); ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, " and skipping"); } // Adapter helpers void HWC2On1Adapter::populateCapabilities() { if (mHwc1MinorVersion >= 3U) { int supportedTypes = 0; auto result = mHwc1Device->query(mHwc1Device, HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes); if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) { ALOGI("Found support for HWC virtual displays"); mHwc1SupportsVirtualDisplays = true; } } if (mHwc1MinorVersion >= 4U) { mCapabilities.insert(Capability::SidebandStream); } // Check for HWC background color layer support. if (mHwc1MinorVersion >= 1U) { int backgroundColorSupported = 0; auto result = mHwc1Device->query(mHwc1Device, HWC_BACKGROUND_LAYER_SUPPORTED, &backgroundColorSupported); if ((result == 0) && (backgroundColorSupported == 1)) { ALOGV("Found support for HWC background color"); mHwc1SupportsBackgroundColor = true; } } // Some devices might have HWC1 retire fences that accurately emulate // HWC2 present fences when they are deferred, but it's not very reliable. // To be safe, we indicate PresentFenceIsNotReliable for all HWC1 devices. mCapabilities.insert(Capability::PresentFenceIsNotReliable); } HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) { std::unique_lock lock(mStateMutex); auto display = mDisplays.find(id); if (display == mDisplays.end()) { return nullptr; } return display->second.get(); } std::tuple HWC2On1Adapter::getLayer( hwc2_display_t displayId, hwc2_layer_t layerId) { auto display = getDisplay(displayId); if (!display) { return std::make_tuple(static_cast(nullptr), Error::BadDisplay); } auto layerEntry = mLayers.find(layerId); if (layerEntry == mLayers.end()) { return std::make_tuple(static_cast(nullptr), Error::BadLayer); } auto layer = layerEntry->second; if (layer->getDisplay().getId() != displayId) { return std::make_tuple(static_cast(nullptr), Error::BadLayer); } return std::make_tuple(layer.get(), Error::None); } void HWC2On1Adapter::populatePrimary() { std::unique_lock lock(mStateMutex); auto display = std::make_shared(*this, HWC2::DisplayType::Physical); mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId(); display->setHwc1Id(HWC_DISPLAY_PRIMARY); display->populateConfigs(); mDisplays.emplace(display->getId(), std::move(display)); } bool HWC2On1Adapter::prepareAllDisplays() { ATRACE_CALL(); std::unique_lock lock(mStateMutex); for (const auto& displayPair : mDisplays) { auto& display = displayPair.second; if (!display->prepare()) { return false; } } if (mHwc1DisplayMap.count(HWC_DISPLAY_PRIMARY) == 0) { ALOGE("prepareAllDisplays: Unable to find primary HWC1 display"); return false; } // Build an array of hwc_display_contents_1 to call prepare() on HWC1. mHwc1Contents.clear(); // Always push the primary display auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY]; auto& primaryDisplay = mDisplays[primaryDisplayId]; mHwc1Contents.push_back(primaryDisplay->getDisplayContents()); // Push the external display, if present if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) { auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL]; auto& externalDisplay = mDisplays[externalDisplayId]; mHwc1Contents.push_back(externalDisplay->getDisplayContents()); } else { // Even if an external display isn't present, we still need to send // at least two displays down to HWC1 mHwc1Contents.push_back(nullptr); } // Push the hardware virtual display, if supported and present if (mHwc1MinorVersion >= 3) { if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) { auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL]; auto& virtualDisplay = mDisplays[virtualDisplayId]; mHwc1Contents.push_back(virtualDisplay->getDisplayContents()); } else { mHwc1Contents.push_back(nullptr); } } for (auto& displayContents : mHwc1Contents) { if (!displayContents) { continue; } ALOGV("Display %zd layers:", mHwc1Contents.size() - 1); for (size_t l = 0; l < displayContents->numHwLayers; ++l) { auto& layer = displayContents->hwLayers[l]; ALOGV(" %zd: %d", l, layer.compositionType); } } ALOGV("Calling HWC1 prepare"); { ATRACE_NAME("HWC1 prepare"); mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data()); } for (size_t c = 0; c < mHwc1Contents.size(); ++c) { auto& contents = mHwc1Contents[c]; if (!contents) { continue; } ALOGV("Display %zd layers:", c); for (size_t l = 0; l < contents->numHwLayers; ++l) { ALOGV(" %zd: %d", l, contents->hwLayers[l].compositionType); } } // Return the received contents to their respective displays for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { if (mHwc1Contents[hwc1Id] == nullptr) { continue; } auto displayId = mHwc1DisplayMap[hwc1Id]; auto& display = mDisplays[displayId]; display->generateChanges(); } return true; } void dumpHWC1Message(hwc_composer_device_1* device, size_t numDisplays, hwc_display_contents_1_t** displays) { ALOGV("*****************************"); size_t displayId = 0; while (displayId < numDisplays) { hwc_display_contents_1_t* display = displays[displayId]; ALOGV("hwc_display_contents_1_t[%zu] @0x%p", displayId, display); if (display == nullptr) { displayId++; continue; } ALOGV(" retirefd:0x%08x", display->retireFenceFd); ALOGV(" outbuf :0x%p", display->outbuf); ALOGV(" outbuffd:0x%08x", display->outbufAcquireFenceFd); ALOGV(" flags :0x%08x", display->flags); for(size_t layerId=0 ; layerId < display->numHwLayers ; layerId++) { hwc_layer_1_t& layer = display->hwLayers[layerId]; ALOGV(" Layer[%zu]:", layerId); ALOGV(" composition : 0x%08x", layer.compositionType); ALOGV(" hints : 0x%08x", layer.hints); ALOGV(" flags : 0x%08x", layer.flags); ALOGV(" handle : 0x%p", layer.handle); ALOGV(" transform : 0x%08x", layer.transform); ALOGV(" blending : 0x%08x", layer.blending); ALOGV(" sourceCropf : %f, %f, %f, %f", layer.sourceCropf.left, layer.sourceCropf.top, layer.sourceCropf.right, layer.sourceCropf.bottom); ALOGV(" displayFrame : %d, %d, %d, %d", layer.displayFrame.left, layer.displayFrame.left, layer.displayFrame.left, layer.displayFrame.left); hwc_region_t& visReg = layer.visibleRegionScreen; ALOGV(" visibleRegionScreen: #0x%08zx[@0x%p]", visReg.numRects, visReg.rects); for (size_t visRegId=0; visRegId < visReg.numRects ; visRegId++) { if (layer.visibleRegionScreen.rects == nullptr) { ALOGV(" null"); } else { ALOGV(" visibleRegionScreen[%zu] %d, %d, %d, %d", visRegId, visReg.rects[visRegId].left, visReg.rects[visRegId].top, visReg.rects[visRegId].right, visReg.rects[visRegId].bottom); } } ALOGV(" acquireFenceFd : 0x%08x", layer.acquireFenceFd); ALOGV(" releaseFenceFd : 0x%08x", layer.releaseFenceFd); ALOGV(" planeAlpha : 0x%08x", layer.planeAlpha); if (getMinorVersion(device) < 5) continue; ALOGV(" surfaceDamage : #0x%08zx[@0x%p]", layer.surfaceDamage.numRects, layer.surfaceDamage.rects); for (size_t sdId=0; sdId < layer.surfaceDamage.numRects ; sdId++) { if (layer.surfaceDamage.rects == nullptr) { ALOGV(" null"); } else { ALOGV(" surfaceDamage[%zu] %d, %d, %d, %d", sdId, layer.surfaceDamage.rects[sdId].left, layer.surfaceDamage.rects[sdId].top, layer.surfaceDamage.rects[sdId].right, layer.surfaceDamage.rects[sdId].bottom); } } } displayId++; } ALOGV("-----------------------------"); } Error HWC2On1Adapter::setAllDisplays() { ATRACE_CALL(); std::unique_lock lock(mStateMutex); // Make sure we're ready to validate for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { if (mHwc1Contents[hwc1Id] == nullptr) { continue; } auto displayId = mHwc1DisplayMap[hwc1Id]; auto& display = mDisplays[displayId]; Error error = display->set(*mHwc1Contents[hwc1Id]); if (error != Error::None) { ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id, to_string(error).c_str()); return error; } } ALOGV("Calling HWC1 set"); { ATRACE_NAME("HWC1 set"); //dumpHWC1Message(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data()); mHwc1Device->set(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data()); } // Add retire and release fences for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { if (mHwc1Contents[hwc1Id] == nullptr) { continue; } auto displayId = mHwc1DisplayMap[hwc1Id]; auto& display = mDisplays[displayId]; auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd; ALOGV("setAllDisplays: Adding retire fence %d to display %zd", retireFenceFd, hwc1Id); display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd); display->addReleaseFences(*mHwc1Contents[hwc1Id]); } return Error::None; } void HWC2On1Adapter::hwc1Invalidate() { ALOGV("Received hwc1Invalidate"); std::unique_lock lock(mStateMutex); // If the HWC2-side callback hasn't been registered yet, buffer this until // it is registered. if (mCallbacks.count(Callback::Refresh) == 0) { mHasPendingInvalidate = true; return; } const auto& callbackInfo = mCallbacks[Callback::Refresh]; std::vector displays; for (const auto& displayPair : mDisplays) { displays.emplace_back(displayPair.first); } // Call back without the state lock held. lock.unlock(); auto refresh = reinterpret_cast(callbackInfo.pointer); for (auto display : displays) { refresh(callbackInfo.data, display); } } void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) { ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp); std::unique_lock lock(mStateMutex); // If the HWC2-side callback hasn't been registered yet, buffer this until // it is registered. if (mCallbacks.count(Callback::Vsync) == 0) { mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp); return; } if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId); return; } const auto& callbackInfo = mCallbacks[Callback::Vsync]; auto displayId = mHwc1DisplayMap[hwc1DisplayId]; // Call back without the state lock held. lock.unlock(); auto vsync = reinterpret_cast(callbackInfo.pointer); vsync(callbackInfo.data, displayId, timestamp); } void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) { ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected); if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) { ALOGE("hwc1Hotplug: Received hotplug for non-external display"); return; } std::unique_lock lock(mStateMutex); // If the HWC2-side callback hasn't been registered yet, buffer this until // it is registered if (mCallbacks.count(Callback::Hotplug) == 0) { mPendingHotplugs.emplace_back(hwc1DisplayId, connected); return; } hwc2_display_t displayId = UINT64_MAX; if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { if (connected == 0) { ALOGW("hwc1Hotplug: Received disconnect for unconnected display"); return; } // Create a new display on connect auto display = std::make_shared(*this, HWC2::DisplayType::Physical); display->setHwc1Id(HWC_DISPLAY_EXTERNAL); display->populateConfigs(); displayId = display->getId(); mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId; mDisplays.emplace(displayId, std::move(display)); } else { if (connected != 0) { ALOGW("hwc1Hotplug: Received connect for previously connected " "display"); return; } // Disconnect an existing display displayId = mHwc1DisplayMap[hwc1DisplayId]; mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL); mDisplays.erase(displayId); } const auto& callbackInfo = mCallbacks[Callback::Hotplug]; // Call back without the state lock held lock.unlock(); auto hotplug = reinterpret_cast(callbackInfo.pointer); auto hwc2Connected = (connected == 0) ? HWC2::Connection::Disconnected : HWC2::Connection::Connected; hotplug(callbackInfo.data, displayId, static_cast(hwc2Connected)); } } // namespace android libs/hwc2on1adapter/MiniFence.cpp0100644 0000000 0000000 00000002010 13300556574 015722 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 "hwc2on1adapter/MiniFence.h" #include namespace android { const sp MiniFence::NO_FENCE = sp(new MiniFence); MiniFence::MiniFence() : mFenceFd(-1) { } MiniFence::MiniFence(int fenceFd) : mFenceFd(fenceFd) { } MiniFence::~MiniFence() { if (mFenceFd != -1) { close(mFenceFd); } } int MiniFence::dup() const { return ::dup(mFenceFd); } } libs/hwc2on1adapter/include/0040755 0000000 0000000 00000000000 13300556574 015016 5ustar000000000 0000000 libs/hwc2on1adapter/include/hwc2on1adapter/0040755 0000000 0000000 00000000000 13300556574 017640 5ustar000000000 0000000 libs/hwc2on1adapter/include/hwc2on1adapter/HWC2On1Adapter.h0100644 0000000 0000000 00000072325 13300556574 022401 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_SF_HWC2_ON_1_ADAPTER_H #define ANDROID_SF_HWC2_ON_1_ADAPTER_H #define HWC2_INCLUDE_STRINGIFICATION #define HWC2_USE_CPP11 #include #undef HWC2_INCLUDE_STRINGIFICATION #undef HWC2_USE_CPP11 #include "MiniFence.h" #include #include #include #include #include #include #include #include struct hwc_composer_device_1; struct hwc_display_contents_1; struct hwc_layer_1; namespace android { // For devices unable to provide an implementation of HWC2 (see hwcomposer2.h), // we provide an adapter able to talk to HWC1 (see hwcomposer.h). It translates // streamed function calls ala HWC2 model to batched array of structs calls ala // HWC1 model. class HWC2On1Adapter : public hwc2_device_t { public: explicit HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device); ~HWC2On1Adapter(); struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; } uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; } private: static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) { return static_cast(device); } // getCapabilities void doGetCapabilities(uint32_t* outCount, int32_t* /*hwc2_capability_t*/ outCapabilities); static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount, int32_t* /*hwc2_capability_t*/ outCapabilities) { getAdapter(device)->doGetCapabilities(outCount, outCapabilities); } bool supportsBackgroundColor() { return mHwc1SupportsBackgroundColor; } // getFunction hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor); static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device, int32_t intDesc) { auto descriptor = static_cast(intDesc); return getAdapter(device)->doGetFunction(descriptor); } // Device functions HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height, hwc2_display_t* outDisplay); static int32_t createVirtualDisplayHook(hwc2_device_t* device, uint32_t width, uint32_t height, int32_t* /*format*/, hwc2_display_t* outDisplay) { // HWC1 implementations cannot override the buffer format requested by // the consumer auto error = getAdapter(device)->createVirtualDisplay(width, height, outDisplay); return static_cast(error); } HWC2::Error destroyVirtualDisplay(hwc2_display_t display); static int32_t destroyVirtualDisplayHook(hwc2_device_t* device, hwc2_display_t display) { auto error = getAdapter(device)->destroyVirtualDisplay(display); return static_cast(error); } std::string mDumpString; void dump(uint32_t* outSize, char* outBuffer); static void dumpHook(hwc2_device_t* device, uint32_t* outSize, char* outBuffer) { getAdapter(device)->dump(outSize, outBuffer); } uint32_t getMaxVirtualDisplayCount(); static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) { return getAdapter(device)->getMaxVirtualDisplayCount(); } HWC2::Error registerCallback(HWC2::Callback descriptor, hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer); static int32_t registerCallbackHook(hwc2_device_t* device, int32_t intDesc, hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) { auto descriptor = static_cast(intDesc); auto error = getAdapter(device)->registerCallback(descriptor, callbackData, pointer); return static_cast(error); } // Display functions class Layer; class SortLayersByZ { public: bool operator()(const std::shared_ptr& lhs, const std::shared_ptr& rhs); }; // The semantics of the fences returned by the device differ between // hwc1.set() and hwc2.present(). Read hwcomposer.h and hwcomposer2.h // for more information. // // Release fences in hwc1 are obtained on set() for a frame n and signaled // when the layer buffer is not needed for read operations anymore // (typically on frame n+1). In HWC2, release fences are obtained with a // special call after present() for frame n. These fences signal // on frame n: More specifically, the fence for a given buffer provided in // frame n will signal when the prior buffer is no longer required. // // A retire fence (HWC1) is signaled when a composition is replaced // on the panel whereas a present fence (HWC2) is signaled when a // composition starts to be displayed on a panel. // // The HWC2to1Adapter emulates the new fence semantics for a frame // n by returning the fence from frame n-1. For frame 0, the adapter // returns NO_FENCE. class DeferredFence { public: DeferredFence() : mFences({MiniFence::NO_FENCE, MiniFence::NO_FENCE}) {} void add(int32_t fenceFd) { mFences.emplace(new MiniFence(fenceFd)); mFences.pop(); } const sp& get() const { return mFences.front(); } private: // There are always two fences in this queue. std::queue> mFences; }; class FencedBuffer { public: FencedBuffer() : mBuffer(nullptr), mFence(MiniFence::NO_FENCE) {} void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; } void setFence(int fenceFd) { mFence = new MiniFence(fenceFd); } buffer_handle_t getBuffer() const { return mBuffer; } int getFence() const { return mFence->dup(); } private: buffer_handle_t mBuffer; sp mFence; }; class Display { public: Display(HWC2On1Adapter& device, HWC2::DisplayType type); hwc2_display_t getId() const { return mId; } HWC2On1Adapter& getDevice() const { return mDevice; } // Does not require locking because it is set before adding the // Displays to the Adapter's list of displays void setHwc1Id(int32_t id) { mHwc1Id = id; } int32_t getHwc1Id() const { return mHwc1Id; } // HWC2 Display functions HWC2::Error acceptChanges(); HWC2::Error createLayer(hwc2_layer_t* outLayerId); HWC2::Error destroyLayer(hwc2_layer_t layerId); HWC2::Error getActiveConfig(hwc2_config_t* outConfigId); HWC2::Error getAttribute(hwc2_config_t configId, HWC2::Attribute attribute, int32_t* outValue); HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes); HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes); HWC2::Error getConfigs(uint32_t* outNumConfigs, hwc2_config_t* outConfigIds); HWC2::Error getDozeSupport(int32_t* outSupport); HWC2::Error getHdrCapabilities(uint32_t* outNumTypes, int32_t* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance); HWC2::Error getName(uint32_t* outSize, char* outName); HWC2::Error getReleaseFences(uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outFences); HWC2::Error getRequests(int32_t* outDisplayRequests, uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outLayerRequests); HWC2::Error getType(int32_t* outType); // Since HWC1 "presents" (called "set" in HWC1) all Displays // at once, the first call to any Display::present will trigger // present() on all Displays in the Device. Subsequent calls without // first calling validate() are noop (except for duping/returning // the retire fence). HWC2::Error present(int32_t* outRetireFence); HWC2::Error setActiveConfig(hwc2_config_t configId); HWC2::Error setClientTarget(buffer_handle_t target, int32_t acquireFence, int32_t dataspace, hwc_region_t damage); HWC2::Error setColorMode(android_color_mode_t mode); HWC2::Error setColorTransform(android_color_transform_t hint); HWC2::Error setOutputBuffer(buffer_handle_t buffer, int32_t releaseFence); HWC2::Error setPowerMode(HWC2::PowerMode mode); HWC2::Error setVsyncEnabled(HWC2::Vsync enabled); // Since HWC1 "validates" (called "prepare" in HWC1) all Displays // at once, the first call to any Display::validate() will trigger // validate() on all other Displays in the Device. HWC2::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests); HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z); HWC2::Error getClientTargetSupport(uint32_t width, uint32_t height, int32_t format, int32_t dataspace); // Read configs from HWC1 device void populateConfigs(); // Set configs for a virtual display void populateConfigs(uint32_t width, uint32_t height); bool prepare(); // Called after hwc.prepare() with responses from the device. void generateChanges(); bool hasChanges() const; HWC2::Error set(hwc_display_contents_1& hwcContents); void addRetireFence(int fenceFd); void addReleaseFences(const hwc_display_contents_1& hwcContents); bool hasColorTransform() const; std::string dump() const; // Return a rect from the pool allocated during validate() hwc_rect_t* GetRects(size_t numRects); hwc_display_contents_1* getDisplayContents(); void markGeometryChanged() { mGeometryChanged = true; } void resetGeometryMarker() { mGeometryChanged = false;} private: class Config { public: Config(Display& display) : mDisplay(display), mId(0), mAttributes() {} bool isOnDisplay(const Display& display) const { return display.getId() == mDisplay.getId(); } void setAttribute(HWC2::Attribute attribute, int32_t value); int32_t getAttribute(HWC2::Attribute attribute) const; void setHwc1Id(uint32_t id); bool hasHwc1Id(uint32_t id) const; HWC2::Error getColorModeForHwc1Id(uint32_t id, android_color_mode_t *outMode) const; HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode, uint32_t* outId) const; void setId(hwc2_config_t id) { mId = id; } hwc2_config_t getId() const { return mId; } // Attempts to merge two configs that differ only in color // mode. Returns whether the merge was successful bool merge(const Config& other); std::set getColorModes() const; // splitLine divides the output into two lines suitable for // dumpsys SurfaceFlinger std::string toString(bool splitLine = false) const; private: Display& mDisplay; hwc2_config_t mId; std::unordered_map mAttributes; // Maps from color transform to HWC1 config ID std::unordered_map mHwc1Ids; }; // Stores changes requested from the device upon calling prepare(). // Handles change request to: // - Layer composition type. // - Layer hints. class Changes { public: uint32_t getNumTypes() const { return static_cast(mTypeChanges.size()); } uint32_t getNumLayerRequests() const { return static_cast(mLayerRequests.size()); } const std::unordered_map& getTypeChanges() const { return mTypeChanges; } const std::unordered_map& getLayerRequests() const { return mLayerRequests; } void addTypeChange(hwc2_layer_t layerId, HWC2::Composition type) { mTypeChanges.insert({layerId, type}); } void clearTypeChanges() { mTypeChanges.clear(); } void addLayerRequest(hwc2_layer_t layerId, HWC2::LayerRequest request) { mLayerRequests.insert({layerId, request}); } private: std::unordered_map mTypeChanges; std::unordered_map mLayerRequests; }; std::shared_ptr getConfig(hwc2_config_t configId) const; void populateColorModes(); void initializeActiveConfig(); // Creates a bi-directional mapping between index in HWC1 // prepare/set array and Layer object. Stores mapping in // mHwc1LayerMap and also updates Layer's attribute mHwc1Id. void assignHwc1LayerIds(); // Called after a response to prepare() has been received: // Ingest composition type changes requested by the device. void updateTypeChanges(const struct hwc_layer_1& hwc1Layer, const Layer& layer); // Called after a response to prepare() has been received: // Ingest layer hint changes requested by the device. void updateLayerRequests(const struct hwc_layer_1& hwc1Layer, const Layer& layer); // Set all fields in HWC1 comm array for layer containing the // HWC_FRAMEBUFFER_TARGET (always the last layer). void prepareFramebufferTarget(); // Display ID generator. static std::atomic sNextId; const hwc2_display_t mId; HWC2On1Adapter& mDevice; // The state of this display should only be modified from // SurfaceFlinger's main loop, with the exception of when dump is // called. To prevent a bad state from crashing us during a dump // call, all public calls into Display must acquire this mutex. // // It is recursive because we don't want to deadlock in validate // (or present) when we call HWC2On1Adapter::prepareAllDisplays // (or setAllDisplays), which calls back into Display functions // which require locking. mutable std::recursive_mutex mStateMutex; // Allocate RAM able to store all layers and rects used for // communication with HWC1. Place allocated RAM in variable // mHwc1RequestedContents. void allocateRequestedContents(); // Array of structs exchanged between client and hwc1 device. // Sent to device upon calling prepare(). std::unique_ptr mHwc1RequestedContents; private: DeferredFence mRetireFence; // Will only be non-null after the Display has been validated and // before it has been presented std::unique_ptr mChanges; int32_t mHwc1Id; std::vector> mConfigs; std::shared_ptr mActiveConfig; std::set mColorModes; android_color_mode_t mActiveColorMode; std::string mName; HWC2::DisplayType mType; HWC2::PowerMode mPowerMode; HWC2::Vsync mVsyncEnabled; // Used to populate HWC1 HWC_FRAMEBUFFER_TARGET layer FencedBuffer mClientTarget; FencedBuffer mOutputBuffer; bool mHasColorTransform; // All layers this Display is aware of. std::multiset, SortLayersByZ> mLayers; // Mapping between layer index in array of hwc_display_contents_1* // passed to HWC1 during validate/set and Layer object. std::unordered_map> mHwc1LayerMap; // All communication with HWC1 via prepare/set is done with one // alloc. This pointer is pointing to a pool of hwc_rect_t. size_t mNumAvailableRects; hwc_rect_t* mNextAvailableRect; // True if any of the Layers contained in this Display have been // updated with anything other than a buffer since last call to // Display::set() bool mGeometryChanged; }; // Utility template calling a Display object method directly based on the // hwc2_display_t displayId parameter. template static int32_t callDisplayFunction(hwc2_device_t* device, hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...), Args... args) { auto display = getAdapter(device)->getDisplay(displayId); if (!display) { return static_cast(HWC2::Error::BadDisplay); } auto error = ((*display).*member)(std::forward(args)...); return static_cast(error); } template static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId, Args... args) { return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc, std::forward(args)...); } static int32_t getDisplayAttributeHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config, int32_t intAttribute, int32_t* outValue) { auto attribute = static_cast(intAttribute); return callDisplayFunction(device, display, &Display::getAttribute, config, attribute, outValue); } static int32_t setColorTransformHook(hwc2_device_t* device, hwc2_display_t display, const float* /*matrix*/, int32_t /*android_color_transform_t*/ intHint) { // We intentionally throw away the matrix, because if the hint is // anything other than IDENTITY, we have to fall back to client // composition anyway auto hint = static_cast(intHint); return callDisplayFunction(device, display, &Display::setColorTransform, hint); } static int32_t setColorModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) { auto mode = static_cast(intMode); return callDisplayFunction(device, display, &Display::setColorMode, mode); } static int32_t setPowerModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t intMode) { auto mode = static_cast(intMode); return callDisplayFunction(device, display, &Display::setPowerMode, mode); } static int32_t setVsyncEnabledHook(hwc2_device_t* device, hwc2_display_t display, int32_t intEnabled) { auto enabled = static_cast(intEnabled); return callDisplayFunction(device, display, &Display::setVsyncEnabled, enabled); } class Layer { public: explicit Layer(Display& display); bool operator==(const Layer& other) { return mId == other.mId; } bool operator!=(const Layer& other) { return !(*this == other); } hwc2_layer_t getId() const { return mId; } Display& getDisplay() const { return mDisplay; } // HWC2 Layer functions HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence); HWC2::Error setCursorPosition(int32_t x, int32_t y); HWC2::Error setSurfaceDamage(hwc_region_t damage); // HWC2 Layer state functions HWC2::Error setBlendMode(HWC2::BlendMode mode); HWC2::Error setColor(hwc_color_t color); HWC2::Error setCompositionType(HWC2::Composition type); HWC2::Error setDataspace(android_dataspace_t dataspace); HWC2::Error setDisplayFrame(hwc_rect_t frame); HWC2::Error setPlaneAlpha(float alpha); HWC2::Error setSidebandStream(const native_handle_t* stream); HWC2::Error setSourceCrop(hwc_frect_t crop); HWC2::Error setTransform(HWC2::Transform transform); HWC2::Error setVisibleRegion(hwc_region_t visible); HWC2::Error setZ(uint32_t z); HWC2::Composition getCompositionType() const { return mCompositionType; } uint32_t getZ() const { return mZ; } void addReleaseFence(int fenceFd); const sp& getReleaseFence() const; void setHwc1Id(size_t id) { mHwc1Id = id; } size_t getHwc1Id() const { return mHwc1Id; } // Write state to HWC1 communication struct. void applyState(struct hwc_layer_1& hwc1Layer); std::string dump() const; std::size_t getNumVisibleRegions() { return mVisibleRegion.size(); } std::size_t getNumSurfaceDamages() { return mSurfaceDamage.size(); } // True if a layer cannot be properly rendered by the device due // to usage of SolidColor (a.k.a BackgroundColor in HWC1). bool hasUnsupportedBackgroundColor() { return (mCompositionType == HWC2::Composition::SolidColor && !mDisplay.getDevice().supportsBackgroundColor()); } private: void applyCommonState(struct hwc_layer_1& hwc1Layer); void applySolidColorState(struct hwc_layer_1& hwc1Layer); void applySidebandState(struct hwc_layer_1& hwc1Layer); void applyBufferState(struct hwc_layer_1& hwc1Layer); void applyCompositionType(struct hwc_layer_1& hwc1Layer); static std::atomic sNextId; const hwc2_layer_t mId; Display& mDisplay; FencedBuffer mBuffer; std::vector mSurfaceDamage; HWC2::BlendMode mBlendMode; hwc_color_t mColor; HWC2::Composition mCompositionType; hwc_rect_t mDisplayFrame; float mPlaneAlpha; const native_handle_t* mSidebandStream; hwc_frect_t mSourceCrop; HWC2::Transform mTransform; std::vector mVisibleRegion; uint32_t mZ; DeferredFence mReleaseFence; size_t mHwc1Id; bool mHasUnsupportedPlaneAlpha; }; // Utility tempate calling a Layer object method based on ID parameters: // hwc2_display_t displayId // and // hwc2_layer_t layerId template static int32_t callLayerFunction(hwc2_device_t* device, hwc2_display_t displayId, hwc2_layer_t layerId, HWC2::Error (Layer::*member)(Args...), Args... args) { auto result = getAdapter(device)->getLayer(displayId, layerId); auto error = std::get(result); if (error == HWC2::Error::None) { auto layer = std::get(result); error = ((*layer).*member)(std::forward(args)...); } return static_cast(error); } template static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId, hwc2_layer_t layerId, Args... args) { return HWC2On1Adapter::callLayerFunction(device, displayId, layerId, memFunc, std::forward(args)...); } // Layer state functions static int32_t setLayerBlendModeHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) { auto mode = static_cast(intMode); return callLayerFunction(device, display, layer, &Layer::setBlendMode, mode); } static int32_t setLayerCompositionTypeHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, int32_t intType) { auto type = static_cast(intType); return callLayerFunction(device, display, layer, &Layer::setCompositionType, type); } static int32_t setLayerDataspaceHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) { auto dataspace = static_cast(intDataspace); return callLayerFunction(device, display, layer, &Layer::setDataspace, dataspace); } static int32_t setLayerTransformHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) { auto transform = static_cast(intTransform); return callLayerFunction(device, display, layer, &Layer::setTransform, transform); } static int32_t setLayerZOrderHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, uint32_t z) { return callDisplayFunction(device, display, &Display::updateLayerZ, layer, z); } // Adapter internals void populateCapabilities(); Display* getDisplay(hwc2_display_t id); std::tuple getLayer(hwc2_display_t displayId, hwc2_layer_t layerId); void populatePrimary(); bool prepareAllDisplays(); std::vector mHwc1Contents; HWC2::Error setAllDisplays(); // Callbacks void hwc1Invalidate(); void hwc1Vsync(int hwc1DisplayId, int64_t timestamp); void hwc1Hotplug(int hwc1DisplayId, int connected); // These are set in the constructor and before any asynchronous events are // possible struct hwc_composer_device_1* const mHwc1Device; const uint8_t mHwc1MinorVersion; bool mHwc1SupportsVirtualDisplays; bool mHwc1SupportsBackgroundColor; class Callbacks; const std::unique_ptr mHwc1Callbacks; std::unordered_set mCapabilities; // These are only accessed from the main SurfaceFlinger thread (not from // callbacks or dump std::map> mLayers; // A HWC1 supports only one virtual display. std::shared_ptr mHwc1VirtualDisplay; // These are potentially accessed from multiple threads, and are protected // by this mutex. This needs to be recursive, since the HWC1 implementation // can call back into the invalidate callback on the same thread that is // calling prepare. std::recursive_timed_mutex mStateMutex; struct CallbackInfo { hwc2_callback_data_t data; hwc2_function_pointer_t pointer; }; std::unordered_map mCallbacks; bool mHasPendingInvalidate; // There is a small gap between the time the HWC1 module is started and // when the callbacks for vsync and hotplugs are registered by the // HWC2on1Adapter. To prevent losing events they are stored in these arrays // and fed to the callback as soon as possible. std::vector> mPendingVsyncs; std::vector> mPendingHotplugs; // Mapping between HWC1 display id and Display objects. std::map> mDisplays; // Map HWC1 display type (HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL, // HWC_DISPLAY_VIRTUAL) to Display IDs generated by HWC2on1Adapter objects. std::unordered_map mHwc1DisplayMap; }; } // namespace android #endif libs/hwc2on1adapter/include/hwc2on1adapter/MiniFence.h0100644 0000000 0000000 00000003553 13300556574 021651 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 MINIFENCE_H #define MINIFENCE_H #include namespace android { /* MiniFence is a minimal re-implementation of Fence from libui. It exists to * avoid linking the HWC2on1Adapter to libui and satisfy Treble requirements. */ class MiniFence : public LightRefBase { public: static const sp NO_FENCE; // Construct a new MiniFence object with an invalid file descriptor. MiniFence(); // Construct a new MiniFence object to manage a given fence file descriptor. // When the new MiniFence object is destructed the file descriptor will be // closed. explicit MiniFence(int fenceFd); // Not copyable or movable. MiniFence(const MiniFence& rhs) = delete; MiniFence& operator=(const MiniFence& rhs) = delete; MiniFence(MiniFence&& rhs) = delete; MiniFence& operator=(MiniFence&& rhs) = delete; // 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; private: // Only allow instantiation using ref counting. friend class LightRefBase; ~MiniFence(); int mFenceFd; }; } #endif //MINIFENCE_H libs/input/0040755 0000000 0000000 00000000000 13300556574 011710 5ustar000000000 0000000 libs/input/Android.bp0100644 0000000 0000000 00000003202 13300556574 013605 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", "VirtualKeyMap.cpp", ], clang: true, shared_libs: [ "libbase", "liblog", "libcutils", ], target: { android: { srcs: [ "IInputFlinger.cpp", "InputTransport.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], shared_libs: [ "libutils", "libbinder", ], sanitize: { misc_undefined: ["integer"], }, }, host: { shared: { enabled: false, }, }, }, } subdirs = ["tests"] libs/input/IInputFlinger.cpp0100644 0000000 0000000 00000003262 13300556574 015133 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 status_t doSomething() { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply); return reply.readInt32(); } }; 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 DO_SOMETHING_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); reply->writeInt32(0); break; } default: return BBinder::onTransact(code, data, reply, flags); } return NO_ERROR; } }; libs/input/Input.cpp0100644 0000000 0000000 00000043361 13300556574 013517 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 { // --- InputEvent --- void InputEvent::initialize(int32_t deviceId, int32_t source) { mDeviceId = deviceId; mSource = source; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; } // --- 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 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); 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 scaleFactor) { // No need to scale pressure or size since they are normalized. // No need to scale orientation since it is meaningless to do so. scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); } 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 action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, 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); mAction = action; mActionButton = actionButton; mFlags = flags; mEdgeFlags = edgeFlags; mMetaState = metaState; mButtonState = buttonState; 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); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; mEdgeFlags = other->mEdgeFlags; mMetaState = other->mMetaState; mButtonState = other->mButtonState; 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 scaleFactor) { mXOffset *= scaleFactor; mYOffset *= scaleFactor; mXPrecision *= scaleFactor; mYPrecision *= scaleFactor; size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { mSamplePointerCoords.editItemAt(i).scale(scaleFactor); } } 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(); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); mEdgeFlags = parcel->readInt32(); mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); 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(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); parcel->writeInt32(mEdgeFlags); parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); 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/InputDevice.cpp0100644 0000000 0000000 00000014320 13300556574 014630 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 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(String8& path, const String8& name, InputDeviceConfigurationFileType type) { path.append(CONFIGURATION_FILE_DIR[type]); for (size_t i = 0; i < name.length(); i++) { char ch = name[i]; if (!isValidNameChar(ch)) { ch = '_'; } path.append(&ch, 1); } path.append(CONFIGURATION_FILE_EXTENSION[type]); } String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. String8 versionPath(getInputDeviceConfigurationFilePathByName( String8::format("Vendor_%04x_Product_%04x_Version_%04x", deviceIdentifier.vendor, deviceIdentifier.product, deviceIdentifier.version), type)); if (!versionPath.isEmpty()) { return versionPath; } } // Try vendor product. String8 productPath(getInputDeviceConfigurationFilePathByName( String8::format("Vendor_%04x_Product_%04x", deviceIdentifier.vendor, deviceIdentifier.product), type)); if (!productPath.isEmpty()) { return productPath; } } // Try device name. return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); } String8 getInputDeviceConfigurationFilePathByName( const String8& name, InputDeviceConfigurationFileType type) { // Search system repository. String8 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++) { path.setTo(rootsForPartition[i]); path.append("/usr/"); appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system provided input device configuration file: path='%s'", path.string()); #endif if (!access(path.string(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif return path; } } // Search user repository. // TODO Should only look here if not in safe mode. path.setTo(getenv("ANDROID_DATA")); path.append("/system/devices/"); appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); #endif if (!access(path.string(), 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.string(), type); #endif return String8(); } // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), 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 String8& 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.itemAt(i); if (range.axis == axis && range.source == source) { return ⦥ } } return NULL; } 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.add(range); } void InputDeviceInfo::addMotionRange(const MotionRange& range) { mMotionRanges.add(range); } } // namespace android libs/input/InputTransport.cpp0100644 0000000 0000000 00000103473 13300556574 015435 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 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; 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); } // --- 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); } // --- InputChannel --- InputChannel::InputChannel(const String8& name, int fd) : mName(name), mFd(fd) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.string(), fd); #endif 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.string(), errno); } InputChannel::~InputChannel() { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel destroyed: name='%s', fd=%d", mName.string(), mFd); #endif ::close(mFd); } status_t InputChannel::openInputChannelPair(const String8& 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.string(), 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)); String8 serverChannelName = name; serverChannelName.append(" (server)"); outServerChannel = new InputChannel(serverChannelName, sockets[0]); String8 clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = new InputChannel(clientChannelName, sockets[1]); return OK; } status_t InputChannel::sendMessage(const InputMessage* msg) { size_t msgLength = msg->size(); ssize_t nWrite; do { nWrite = ::send(mFd, msg, 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.string(), 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.string(), msg->header.type); #endif return DEAD_OBJECT; } #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ sent message of type %d", mName.string(), 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.string(), 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.string()); #endif return DEAD_OBJECT; } if (!msg->isValid(nRead)) { #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ received invalid message", mName.string()); #endif return BAD_VALUE; } #if DEBUG_CHANNEL_MESSAGES ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type); #endif return OK; } sp InputChannel::dup() const { int fd = ::dup(getFd()); return fd >= 0 ? new InputChannel(getName(), fd) : NULL; } // --- 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 action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { #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=%lld, eventTime=%lld", mChannel->getName().string(), 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.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, float xOffset, float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " "pointerCount=%" PRIu32, mChannel->getName().string(), seq, deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState, 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().string(), 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.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().string()); #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().string(), 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() { char value[PROPERTY_VALUE_MAX]; int length = property_get("ro.input.noresample", value, NULL); if (length > 0) { if (!strcmp("1", value)) { return false; } if (strcmp("0", value)) { ALOGD("Unrecognized property value for 'ro.input.noresample'. " "Use '1' or '0'."); } } return true; } status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld", mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime); #endif *outSeq = 0; *outEvent = NULL; *displayId = -1; // Invalid display. // 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, displayId); if (*outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", mChannel->getName().string(), *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().string(), *outSeq); #endif break; } case AINPUT_EVENT_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().string()); #endif break; } 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, displayId); 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().string(), *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().string()); #endif break; } MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; updateTouchState(mMsg); initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; *displayId = mMsg.body.motion.displayId; #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", mChannel->getName().string(), *outSeq); #endif break; } default: ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", mChannel->getName().string(), mMsg.header.type); return UNKNOWN_ERROR; } } return OK; } status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { 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, displayId); 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, displayId); const InputMessage* next; if (batch.samples.isEmpty()) { mBatches.removeAt(i); next = NULL; } 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, int32_t* displayId) { 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 { *displayId = msg.body.motion.displayId; 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 || !(msg.body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { 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); bool messageRewritten = rewriteMessage(touchState, msg); if (!messageRewritten) { touchState.lastResample.idBits.clear(); } } 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) { const TouchState& touchState = mTouchStates.itemAt(index); rewriteMessage(touchState, msg); } break; } case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_CANCEL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { const TouchState& touchState = mTouchStates.itemAt(index); rewriteMessage(touchState, msg); mTouchStates.removeAt(index); } break; } } } bool InputConsumer::rewriteMessage(const TouchState& state, InputMessage& msg) { bool messageRewritten = false; 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)) { PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); if (eventTime < state.lastResample.eventTime || state.recentCoordinatesAreIdentical(id)) { msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); #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 messageRewritten = true; } } } return messageRewritten; } void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, const InputMessage* next) { if (!mResampleTouch || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER) || 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. // Also ensure that the past two "real" touch events do not contain duplicate coordinates 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; } if (touchState.recentCoordinatesAreIdentical(id)) { #if DEBUG_RESAMPLING ALOGD("Not resampled, past two historical events have duplicate coordinates"); #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. 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); PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; const PointerCoords& currentCoords = current->getPointerById(id); if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { const PointerCoords& otherCoords = other->getPointerById(id); resampledCoords.copyFrom(currentCoords); 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 { resampledCoords.copyFrom(currentCoords); #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().string(), 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.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.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, 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/KeyCharacterMap.cpp0100644 0000000 0000000 00000127457 13300556574 015434 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 String8& filename, Format format, sp* outMap) { outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::open(filename, &tokenizer); if (status) { ALOGE("Error %d opening key character map file %s.", status, filename.string()); } else { status = load(tokenizer, format, outMap); delete tokenizer; } return status; } status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, Format format, sp* outMap) { outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::fromContents(filename, 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 == NULL) { return base; } if (base == NULL) { 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 = NULL; 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, 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 NULL; } if (numKeys > MAX_KEYS) { ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS); return NULL; } 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 NULL; } Key* key = new Key(); key->label = label; key->number = number; map->mKeys.add(keyCode, key); Behavior* lastBehavior = NULL; 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 NULL; } 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 NULL; } } 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 != NULL; 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(NULL) { } KeyCharacterMap::Key::Key(const Key& other) : label(other.label), number(other.number), firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { } KeyCharacterMap::Key::~Key() { Behavior* behavior = firstBehavior; while (behavior) { Behavior* next = behavior->next; delete behavior; behavior = next; } } // --- KeyCharacterMap::Behavior --- KeyCharacterMap::Behavior::Behavior() : next(NULL), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { } KeyCharacterMap::Behavior::Behavior(const Behavior& other) : next(other.next ? new Behavior(*other.next) : NULL), 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") { 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, &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 String8& token, int32_t* outMetaState) { if (token == "base") { *outMetaState = 0; return NO_ERROR; } int32_t combinedMeta = 0; const char* str = token.string(); 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.string()); 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 00000035574 13300556574 015013 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 String8& filename, sp* outMap) { outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::open(filename, &tokenizer); if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.string()); } 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 NULL; } status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector* outScanCodes) const { const size_t N = mKeysByScanCode.size(); for (size_t i=0; iadd(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 00000020542 13300556574 014154 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); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " "it was not found.", deviceIdenfifier.name.string(), keyLayoutName.string()); } } String8 keyCharacterMapName; if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), keyCharacterMapName)) { status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " "map '%s' but it was not found.", deviceIdenfifier.name.string(), keyLayoutName.string()); } } if (isComplete()) { return OK; } } // Try searching by device identifier. if (probeKeyMap(deviceIdenfifier, String8::empty())) { 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, String8("Generic"))) { return OK; } // Try the Virtual key map as a last resort. if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { return OK; } // Give up! ALOGE("Could not determine key map for device '%s' and no default key maps were found!", deviceIdenfifier.name.string()); return NAME_NOT_FOUND; } bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& keyMapName) { if (!haveKeyLayout()) { loadKeyLayout(deviceIdentifier, keyMapName); } if (!haveKeyCharacterMap()) { loadKeyCharacterMap(deviceIdentifier, keyMapName); } return isComplete(); } status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name) { String8 path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); if (path.isEmpty()) { return NAME_NOT_FOUND; } status_t status = KeyLayoutMap::load(path, &keyLayoutMap); if (status) { return status; } keyLayoutFile.setTo(path); return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name) { String8 path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); if (path.isEmpty()) { return NAME_NOT_FOUND; } status_t status = KeyCharacterMap::load(path, KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); if (status) { return status; } keyCharacterMapFile.setTo(path); return OK; } String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, const String8& name, InputDeviceConfigurationFileType type) { return name.isEmpty() ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) : getInputDeviceConfigurationFilePathByName(name, type); } // --- Global functions --- bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { if (!keyMap->haveKeyCharacterMap() || 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.string(), "-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/VelocityControl.cpp0100644 0000000 0000000 00000006715 13300556574 015561 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 00000100510 13300556574 015520 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 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; } 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("debug.velocitytracker.strategy", value, NULL); 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 != NULL; } VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { 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 NULL; } 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 --- const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON; const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE; 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 (++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; } 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 >= 1) { 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 --- const nsecs_t LegacyVelocityTrackerStrategy::HORIZON; const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE; const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION; 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; } } // namespace android libs/input/VirtualKeyMap.cpp0100644 0000000 0000000 00000012444 13300556574 015153 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 // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 namespace android { static const char* WHITESPACE = " \t\r"; static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:"; // --- VirtualKeyMap --- VirtualKeyMap::VirtualKeyMap() { } VirtualKeyMap::~VirtualKeyMap() { } status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { *outMap = NULL; Tokenizer* tokenizer; status_t status = Tokenizer::open(filename, &tokenizer); if (status) { ALOGE("Error %d opening virtual key map file %s.", status, filename.string()); } else { VirtualKeyMap* map = new VirtualKeyMap(); if (!map) { ALOGE("Error allocating virtual key map."); status = NO_MEMORY; } else { #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif Parser parser(map, tokenizer); 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) { delete map; } else { *outMap = map; } } delete tokenizer; } return status; } // --- 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(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 13300556574 013052 5ustar000000000 0000000 libs/input/tests/Android.bp0100644 0000000 0000000 00000001165 13300556574 014755 0ustar000000000 0000000 // Build the unit tests. cc_test { name: "libinput_tests", test_per_src: true, srcs: [ "InputChannel_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", ], shared_libs: [ "libinput", "libcutils", "libutils", "libbinder", "libui", ] } // 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", ], } libs/input/tests/InputChannel_test.cpp0100644 0000000 0000000 00000014025 13300556574 017204 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 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(String8("channel name"), pipe.sendFd); EXPECT_STREQ("channel name", inputChannel->getName().string()) << "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(String8("channel name"), serverChannel, clientChannel); ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; // Name EXPECT_STREQ("channel name (server)", serverChannel->getName().string()) << "server channel should have suffixed name"; EXPECT_STREQ("channel name (client)", clientChannel->getName().string()) << "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(String8("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(String8("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(String8("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"; } } // namespace android libs/input/tests/InputEvent_test.cpp0100644 0000000 0000000 00000056417 13300556574 016730 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 namespace android { class BaseTest : public testing::Test { protected: virtual void SetUp() { } virtual void TearDown() { } }; // --- 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, 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(AINPUT_SOURCE_GAMEPAD, event.getSource()); 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(AINPUT_SOURCE_JOYSTICK, event.getSource()); } // --- 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, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, 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(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); 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(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(AINPUT_SOURCE_JOYSTICK, event.getSource()); // 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, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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); } } // namespace android libs/input/tests/InputPublisherAndConsumer_test.cpp0100644 0000000 0000000 00000027257 13300556574 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(String8("channel name"), serverChannel, clientChannel); mPublisher = new InputPublisher(serverChannel); mConsumer = new InputConsumer(clientChannel); } virtual void TearDown() { if (mPublisher) { delete mPublisher; mPublisher = NULL; } if (mConsumer) { delete mConsumer; mConsumer = NULL; } 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; const uint32_t seq = 15; const int32_t deviceId = 1; const int32_t source = AINPUT_SOURCE_KEYBOARD; const int32_t action = AKEY_EVENT_ACTION_DOWN; const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; const int32_t keyCode = AKEYCODE_ENTER; const int32_t scanCode = 13; const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; const int32_t repeatCount = 1; const nsecs_t downTime = 3; const nsecs_t eventTime = 4; status = mPublisher->publishKeyEvent(seq, deviceId, source, 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, 0); ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != NULL) << "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(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; const uint32_t seq = 15; const int32_t deviceId = 1; const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; const int32_t displayId = 0; const int32_t action = AMOTION_EVENT_ACTION_MOVE; const int32_t actionButton = 0; const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; const float xOffset = -10; const float yOffset = -20; const float xPrecision = 0.25; const float yPrecision = 0.5; const nsecs_t downTime = 3; const size_t pointerCount = 3; const 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, 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, 0); ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != NULL) << "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(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(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_WhenPointerCountLessThan1_ReturnsError) { status_t status; const size_t pointerCount = 0; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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/StructLayout_test.cpp0100644 0000000 0000000 00000005302 13300556574 017274 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, edgeFlags, 48); 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); } } // namespace android libs/input/tests/TestHelpers.h0100644 0000000 0000000 00000003324 13300556574 015464 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: 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/math/0040755 0000000 0000000 00000000000 13300556574 011502 5ustar000000000 0000000 libs/math/Android.bp0100644 0000000 0000000 00000001402 13300556574 013377 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 13300556574 014622 0ustar000000000 0000000 libs/math/NOTICE0100644 0000000 0000000 00000024707 13300556574 012415 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/include/0040755 0000000 0000000 00000000000 13300556574 013125 5ustar000000000 0000000 libs/math/include/math/0040755 0000000 0000000 00000000000 13300556574 014056 5ustar000000000 0000000 libs/math/include/math/TMatHelpers.h0100644 0000000 0000000 00000046744 13300556574 016433 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 13300556574 016610 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 13300556574 016424� 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 13300556574 015146� 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 13300556574 015070� 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 13300556574 015077 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 13300556574 015100 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 13300556574 015211 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 13300556574 015470� 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 13300556574 015070� 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 13300556574 015072� 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 13300556574 015077� 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 13300556574 012644� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/math/tests/Android.bp��������������������������������������������������������������������������0100644 0000000 0000000 00000001743 13300556574 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"], } cc_test { name: "mat_test", srcs: ["mat_test.cpp"], static_libs: ["libmath"], } cc_test { name: "half_test", srcs: ["half_test.cpp"], static_libs: ["libmath"], } cc_test { name: "quat_test", srcs: ["quat_test.cpp"], static_libs: ["libmath"], } �����������������������������libs/math/tests/half_test.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000005366 13300556574 015330� 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 00000047510 13300556574 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 "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(mat4), 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)); } 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(mat3), 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(mat2), 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 13300556574 015374� 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 13300556574 015170� 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 13300556574 012672� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/Android.bp��������������������������������������������������������������������������0100644 0000000 0000000 00000001610 13300556574 014570� 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 13300556574 016012� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/NOTICE������������������������������������������������������������������������������0100644 0000000 0000000 00000024707 13300556574 013605� 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 13300556574 014315� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/include/nativebase/�����������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016436� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativebase/include/nativebase/nativebase.h�����������������������������������������������������0100644 0000000 0000000 00000006063 13300556574 020732� 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 13300556574 013267� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/AHardwareBuffer.cpp���������������������������������������������������������������0100644 0000000 0000000 00000043575 13300556574 016776� 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_isValidPixelFormat(desc->format)) { ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format); return BAD_VALUE; } int format = AHardwareBuffer_convertToPixelFormat(desc->format); if (desc->rfu0 != 0 || desc->rfu1 != 0) { ALOGE("AHardwareBuffer_Desc::rfu fields must be 0"); return BAD_VALUE; } if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) { ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format"); return BAD_VALUE; } 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_lock(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress) { 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); 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); } 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; } // ---------------------------------------------------------------------------- // 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; } // ---------------------------------------------------------------------------- // Helpers implementation // ---------------------------------------------------------------------------- namespace android { // A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks. struct UsageMaskMapping { uint64_t hardwareBufferMask; uint64_t grallocMask; }; static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) { return (mask & bitsToCheck) == bitsToCheck && bitsToCheck; } 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_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_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_YCBCR_444_888 == AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_FLEX_RGB_888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8, "HAL and AHardwareBuffer pixel format don't match"); static_assert(HAL_PIXEL_FORMAT_FLEX_RGBA_8888 == AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8, "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: // 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_Y8Cb8Cr8_420: case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422: case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444: case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8: case AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8: 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_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_0::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_COLOR_OUTPUT == (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"); return usage; } uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage) { return usage; } const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) { return reinterpret_cast(buffer); } GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) { return reinterpret_cast(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 reinterpret_cast(buffer); } } // namespace android �����������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/ANativeWindow.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000017157 13300556574 016522� 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; } /************************************************************************************************** * 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; if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) return -EINVAL; if ((transform & ~kAllTransformBits) != 0) return -EINVAL; return native_window_set_buffers_transform(window, transform); } /************************************************************************************************** * 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_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace) { return native_window_set_buffers_data_space(window, dataSpace); } 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 00000003405 13300556574 015171� 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_headers", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } ndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", // Android O first_version: "26", } cc_library { name: "libnativewindow", export_include_dirs: ["include"], clang: true, cppflags: [ "-std=c++1z" ], srcs: [ "AHardwareBuffer.cpp", "ANativeWindow.cpp", ], shared_libs: [ "libhardware", "libcutils", "liblog", "libutils", "libui", "android.hardware.graphics.common@1.0", ], static_libs: [ "libarect", "libgrallocusage", ], header_libs: [ "libnativebase_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 13300556574 016407� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/NOTICE����������������������������������������������������������������������������0100644 0000000 0000000 00000024707 13300556574 014202� 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/��������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 014712� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016332� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/hardware_buffer.h�������������������������������������������������0100644 0000000 0000000 00000021457 13300556574 021637� 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 */ #ifndef ANDROID_HARDWARE_BUFFER_H #define ANDROID_HARDWARE_BUFFER_H #include #include #include __BEGIN_DECLS /** * Buffer pixel formats. */ enum { /** * Corresponding formats: * Vulkan: VK_FORMAT_R8G8B8A8_UNORM * OpenGL ES: GL_RGBA8 */ AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1, /** * Corresponding formats: * Vulkan: VK_FORMAT_R8G8B8A8_UNORM * OpenGL ES: GL_RGBA8 */ 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, /** * An opaque binary blob format that must have height 1, with width equal to * the buffer size in bytes. */ AHARDWAREBUFFER_FORMAT_BLOB = 0x21, }; enum { /* The buffer will never be read by the CPU */ AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL, /* The buffer will sometimes be read by the CPU */ AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL, /* The buffer will often be read by the CPU */ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL, /* CPU read value mask */ AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL, /* The buffer will never be written by the CPU */ AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4, /* The buffer will sometimes be written to by the CPU */ AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4, /* The buffer will often be written to by the CPU */ 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 */ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, /* The buffer will be written to by the GPU */ AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9, /* The buffer must not be used outside of a protected hardware path */ 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 sensor direct data */ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, /* The buffer will be used as a shader storage or uniform buffer object*/ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, 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, }; typedef struct AHardwareBuffer_Desc { uint32_t width; // width in pixels uint32_t height; // height in pixels uint32_t layers; // number of images uint32_t format; // One of AHARDWAREBUFFER_FORMAT_* uint64_t usage; // Combination of AHARDWAREBUFFER_USAGE_* uint32_t stride; // 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; typedef struct AHardwareBuffer AHardwareBuffer; /** * Allocates a buffer that backs an AHardwareBuffer using the passed * AHardwareBuffer_Desc. * * Returns NO_ERROR on success, or an error number of the allocation fails for * any reason. */ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer); /** * 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); /** * Remove a reference that was previously acquired with * AHardwareBuffer_acquire(). */ void AHardwareBuffer_release(AHardwareBuffer* buffer); /** * Return a description of the AHardwareBuffer in the passed * AHardwareBuffer_Desc struct. */ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc); /* * Lock the AHardwareBuffer for reading or writing, depending on the usage flags * passed. This call may block if the hardware needs to finish rendering or if * CPU caches need to be synchronized, or possibly for other implementation- * specific reasons. If fence is not negative, then it specifies a fence file * descriptor that will be signaled when the buffer is locked, otherwise the * caller will block until the buffer is available. * * If 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. * * The buffer usage may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set, then * outVirtualAddress is filled with the address of the buffer in virtual memory, * otherwise this function will fail. * * THREADING CONSIDERATIONS: * * 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 into an indeterminate * state. * * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL or if the usage * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error * number of the lock fails for any reason. */ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress); /* * Unlock the AHardwareBuffer; must be called after all changes to the buffer * are completed by the caller. If fence is not NULL then it will be set to a * file descriptor that is signaled when all pending work on the buffer is * completed. The caller is responsible for closing the fence when it is no * longer needed. * * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error * number of the lock fails for any reason. */ int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence); /* * Send the AHardwareBuffer to an AF_UNIX socket. * * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error * number of the lock fails for any reason. */ int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd); /* * Receive the AHardwareBuffer from an AF_UNIX socket. * * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error * number of the lock fails for any reason. */ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer); __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/android/native_window.h���������������������������������������������������0100644 0000000 0000000 00000014353 13300556574 021363� 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. */ /** * @addtogroup NativeActivity Native Activity * @{ */ /** * @file native_window.h * @brief API for accessing a native window. */ #ifndef ANDROID_NATIVE_WINDOW_H #define ANDROID_NATIVE_WINDOW_H #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 { // 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 show 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 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__ >= __ANDROID_API_O__ /** * 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); #endif // __ANDROID_API__ >= __ANDROID_API_O__ #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_WINDOW_H /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/private/������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016364� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/private/android/����������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 020004� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/private/android/AHardwareBufferHelpers.h����������������������������������0100644 0000000 0000000 00000004426 13300556574 024473� 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 ANativeWindowBuffer; namespace android { // whether this AHardwareBuffer format is valid bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_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/system/�������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016236� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/system/window.h�����������������������������������������������������������0100644 0000000 0000000 00000106401 13300556574 017715� 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, }; /* 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, // 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_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 13300556574 015654� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/include/vndk/hardware_buffer.h����������������������������������������������������0100644 0000000 0000000 00000005571 13300556574 021160� 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); /** * 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_420_888 */ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23, /* same as HAL_PIXEL_FORMAT_YCBCR_422_888 */ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_422 = 0x27, /* same as HAL_PIXEL_FORMAT_YCBCR_444_888 */ AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_444 = 0x28, /* same as HAL_PIXEL_FORMAT_FLEX_RGB_888 */ AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8 = 0x29, /* same as HAL_PIXEL_FORMAT_FLEX_RGBA_8888 */ AHARDWAREBUFFER_FORMAT_FLEX_R8G8B8A8 = 0x2A, /* 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 00000031561 13300556574 017337� 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); /* * 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. */ int ANativeWindow_setBufferDataSpace(ANativeWindow* window, android_dataspace_t dataSpace); /* * 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 00000002700 13300556574 020005� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; AHardwareBuffer_describe; AHardwareBuffer_fromHardwareBuffer; AHardwareBuffer_getNativeHandle; # vndk AHardwareBuffer_lock; AHardwareBuffer_recvHandleFromUnixSocket; AHardwareBuffer_release; AHardwareBuffer_sendHandleToUnixSocket; AHardwareBuffer_toHardwareBuffer; AHardwareBuffer_unlock; ANativeWindowBuffer_getHardwareBuffer; # vndk ANativeWindow_OemStorageGet; # vndk ANativeWindow_OemStorageSet; # vndk ANativeWindow_acquire; ANativeWindow_cancelBuffer; # vndk ANativeWindow_dequeueBuffer; # vndk ANativeWindow_fromSurface; ANativeWindow_fromSurfaceTexture; 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_setBufferDataSpace; # vndk 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: *; }; ����������������������������������������������������������������libs/nativewindow/tests/����������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 014431� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/tests/AHardwareBufferTest.cpp�����������������������������������������������������0100644 0000000 0000000 00000013033 13300556574 020762� 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 00000001513 13300556574 016331� 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"], } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/nativewindow/tests/c_compatibility.c�����������������������������������������������������������0100644 0000000 0000000 00000001450 13300556574 017745� 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/sensor/����������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 012062� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/Android.bp������������������������������������������������������������������������������0100644 0000000 0000000 00000003243 13300556574 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, cppflags: [ "-Weverything", "-Werror", // 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 13300556574 014116� 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 13300556574 017174� 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 00000020270 13300556574 015335� 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 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--) { 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--) { 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; } } return BBinder::onTransact(code, data, reply, flags); } // ---------------------------------------------------------------------------- }; // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/Sensor.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000051461 13300556574 014043� 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 // ---------------------------------------------------------------------------- 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(SENSOR_PERMISSION_BODY_SENSORS)); 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; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; break; case SENSOR_TYPE_STEP_DETECTOR: mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR; 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 != 0) { 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, FlattenableUtils::align<4>(len)); } 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 00000014103 13300556574 016042� 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 using std::min; // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp& connection) : mSensorEventConnection(connection), mRecBuffer(NULL), 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 == 0) { mLooper = new Looper(true); mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL); } return mLooper; } status_t SensorEventQueue::waitForEvent() const { const int fd = getFd(); sp looper(getLooper()); int events; int32_t result; do { result = looper->pollOnce(-1, NULL, &events, NULL); 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; } // ---------------------------------------------------------------------------- }; // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/SensorManager.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000025615 13300556574 015340� 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 // ---------------------------------------------------------------------------- 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 != 0) { 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(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { // okay we're not locked here, but it's not needed during construction 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 = NULL; mSensors.clear(); } status_t SensorManager::assertStateLocked() { bool initSensorManager = false; if (mSensorServer == NULL) { 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 == NULL, "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 NULL; } 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 == NULL) { // SensorService just died or the app doesn't have required permissions. ALOGE("createEventQueue: connection is NULL."); return NULL; } 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 13300556574 013505� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 015016� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/BitTube.h����������������������������������������������������������������0100644 0000000 0000000 00000005357 13300556574 016534� 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 13300556574 021603� 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 00000004431 13300556574 017737� 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 onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; // ---------------------------------------------------------------------------- }; // namespace android ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/include/sensor/Sensor.h�����������������������������������������������������������������0100644 0000000 0000000 00000011233 13300556574 016435� 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]; }; 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; Sensor(const char * name = ""); 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 00000006775 13300556574 020463� 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; }; // ---------------------------------------------------------------------------- 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; 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); 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 00000006077 13300556574 017742� 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); 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 13300556574 013224� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/tests/Android.bp������������������������������������������������������������������������0100644 0000000 0000000 00000001441 13300556574 015124� 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, srcs: [ "Sensor_test.cpp", ], shared_libs: [ "liblog", "libsensor", "libutils", ], } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/sensor/tests/Sensor_test.cpp�������������������������������������������������������������������0100644 0000000 0000000 00000006424 13300556574 016243� 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/ui/��������������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 011166� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Android.bp����������������������������������������������������������������������������������0100644 0000000 0000000 00000005633 13300556574 013075� 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, }, clang: true, cppflags: [ "-Weverything", "-Werror", // 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", ], sanitize: { //misc_undefined: ["integer"], }, srcs: [ "ColorSpace.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", "Gralloc2.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", "HdrCapabilities.cpp", "PixelFormat.cpp", "Rect.cpp", "Region.cpp", "UiConfig.cpp", ], include_dirs: [ "frameworks/native/include", ], shared_libs: [ "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.mapper@2.0", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", "libbase", "libcutils", "libhardware", "libhidlbase", "libhidltransport", "libhwbinder", "libsync", "libutils", "liblog", ], static_libs: [ "libarect", "libgrallocusage", "libmath", ], header_libs: [ "libnativebase_headers", "libhardware_headers", ], export_include_dirs: ["include"], export_static_lib_headers: [ "libarect", "libmath", ], export_header_lib_headers: [ "libnativebase_headers", "libhardware_headers", ], } cc_library_headers { name: "libui_headers", export_include_dirs: ["include"], vendor_available: true, } subdirs = ["tests"] �����������������������������������������������������������������������������������������������������libs/ui/ColorSpace.cpp������������������������������������������������������������������������������0100644 0000000 0000000 00000031710 13300556574 013723� 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 00000022001 13300556574 013731� 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 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_V0_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", dataspaceSelect); } } 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_V0_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_V0_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 (0)"; } return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(), decodeTransfer(dataspace).c_str(), decodeRange(dataspace).c_str()); } std::string decodeColorMode(android_color_mode colorMode) { switch (colorMode) { case HAL_COLOR_MODE_NATIVE: return std::string("HAL_COLOR_MODE_NATIVE"); case HAL_COLOR_MODE_STANDARD_BT601_625: return std::string("HAL_COLOR_MODE_BT601_625"); case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED: return std::string("HAL_COLOR_MODE_BT601_625_UNADJUSTED"); case HAL_COLOR_MODE_STANDARD_BT601_525: return std::string("HAL_COLOR_MODE_BT601_525"); case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED: return std::string("HAL_COLOR_MODE_BT601_525_UNADJUSTED"); case HAL_COLOR_MODE_STANDARD_BT709: return std::string("HAL_COLOR_MODE_BT709"); case HAL_COLOR_MODE_DCI_P3: return std::string("HAL_COLOR_MODE_DCI_P3"); case HAL_COLOR_MODE_SRGB: return std::string("HAL_COLOR_MODE_SRGB"); case HAL_COLOR_MODE_ADOBE_RGB: return std::string("HAL_COLOR_MODE_ADOBE_RGB"); case HAL_COLOR_MODE_DISPLAY_P3: return std::string("HAL_COLOR_MODE_DISPLAY_P3"); } return android::base::StringPrintf("Unknown color mode %d", colorMode); } // 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); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/Fence.cpp�����������������������������������������������������������������������������������0100644 0000000 0000000 00000011761 13300556574 012715� 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() : mFenceFd(-1) { } Fence::Fence(int fenceFd) : mFenceFd(fenceFd) { } Fence::~Fence() { if (mFenceFd != -1) { close(mFenceFd); } } 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, 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, f2->mFenceFd, 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_fence_info_data* finfo = sync_fence_info(mFenceFd); if (finfo == NULL) { ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd); return SIGNAL_TIME_INVALID; } if (finfo->status != 1) { sync_fence_info_free(finfo); return SIGNAL_TIME_PENDING; } struct sync_pt_info* pinfo = NULL; uint64_t timestamp = 0; while ((pinfo = sync_pt_info(finfo, pinfo)) != NULL) { if (pinfo->timestamp_ns > timestamp) { timestamp = pinfo->timestamp_ns; } } sync_fence_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 = *fds++; count--; } return NO_ERROR; } } // namespace android ���������������libs/ui/FenceTime.cpp�������������������������������������������������������������������������������0100644 0000000 0000000 00000030643 13300556574 013534� 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); void* FenceTime::operator new(size_t byteCount) noexcept { void *p = nullptr; if (posix_memalign(&p, alignof(FenceTime), byteCount)) { return nullptr; } return p; } void FenceTime::operator delete(void *p) { free(p); } 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() { while (!mQueue.empty()) { std::lock_guard lock(mMutex); 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 13300556574 013737� 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/Gralloc2.cpp��������������������������������������������������������������������������������0100644 0000000 0000000 00000016735 13300556574 013350� 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 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wzero-length-array" #include #pragma clang diagnostic pop namespace android { namespace Gralloc2 { static constexpr Error kTransactionError = Error::NO_RESOURCES; void Mapper::preload() { android::hardware::preloadPassthroughService(); } Mapper::Mapper() { mMapper = IMapper::getService(); if (mMapper == nullptr || mMapper->isRemote()) { LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); } } Error Mapper::createDescriptor( const IMapper::BufferDescriptorInfo& descriptorInfo, BufferDescriptor* outDescriptor) const { Error error; auto ret = mMapper->createDescriptor(descriptorInfo, [&](const auto& tmpError, const auto& tmpDescriptor) { error = tmpError; if (error != Error::NONE) { return; } *outDescriptor = tmpDescriptor; }); return (ret.isOk()) ? error : kTransactionError; } Error Mapper::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 (ret.isOk()) ? error : kTransactionError; } void Mapper::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); } Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const IMapper::Rect& accessRegion, int acquireFence, void** outData) const { auto buffer = const_cast(bufferHandle); // 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); } return (ret.isOk()) ? error : kTransactionError; } Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const IMapper::Rect& accessRegion, int acquireFence, YCbCrLayout* outLayout) const { auto buffer = const_cast(bufferHandle); // 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->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle, [&](const auto& tmpError, const auto& tmpLayout) { error = tmpError; if (error != Error::NONE) { return; } *outLayout = tmpLayout; }); // we own acquireFence even on errors if (acquireFence >= 0) { close(acquireFence); } return (ret.isOk()) ? error : kTransactionError; } int Mapper::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; } Allocator::Allocator(const Mapper& mapper) : mMapper(mapper) { mAllocator = IAllocator::getService(); if (mAllocator == nullptr) { LOG_ALWAYS_FATAL("gralloc-alloc is missing"); } } std::string Allocator::dumpDebugInfo() const { std::string debugInfo; mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); }); return debugInfo; } Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count, uint32_t* outStride, buffer_handle_t* outBufferHandles) const { Error error; auto ret = mAllocator->allocate(descriptor, count, [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) { error = tmpError; if (tmpError != Error::NONE) { return; } // import buffers for (uint32_t i = 0; i < count; i++) { error = mMapper.importBuffer(tmpBuffers[i], &outBufferHandles[i]); if (error != Error::NONE) { 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 : kTransactionError; } } // namespace Gralloc2 } // namespace android �����������������������������������libs/ui/GraphicBuffer.cpp���������������������������������������������������������������������������0100644 0000000 0000000 00000036501 13300556574 014403� 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" #include #include #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() : 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 = NULL; } // 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 usage, std::string requestorName) : GraphicBuffer() { mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, usage, 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* handle, HandleWrapMethod method, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) : GraphicBuffer() { mInitCheck = initWithHandle(handle, method, width, height, format, layerCount, usage, stride); } GraphicBuffer::~GraphicBuffer() { if (handle) { free_handle(); } } void GraphicBuffer::free_handle() { if (mOwner == ownHandle) { mBufferMapper.freeBuffer(handle); } else if (mOwner == ownData) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); } handle = NULL; } status_t GraphicBuffer::initCheck() const { return static_cast(mInitCheck); } void GraphicBuffer::dumpAllocationsToSystemLog() { GraphicBufferAllocator::dumpToSystemLog(); } ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const { LOG_ALWAYS_FATAL_IF(this == NULL, "getNativeBuffer() called on NULL GraphicBuffer"); 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 = 0; } 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; 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) { 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* handle, HandleWrapMethod method, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride) { ANativeWindowBuffer::width = static_cast(width); ANativeWindowBuffer::height = static_cast(height); ANativeWindowBuffer::stride = static_cast(stride); ANativeWindowBuffer::format = format; ANativeWindowBuffer::usage = usage; ANativeWindowBuffer::usage_deprecated = int(usage); ANativeWindowBuffer::layerCount = layerCount; mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle; if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) { buffer_handle_t importedHandle; status_t err = mBufferMapper.importBuffer(handle, &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(handle); native_handle_delete(const_cast(handle)); } handle = importedHandle; } ANativeWindowBuffer::handle = handle; return NO_ERROR; } status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr) { const Rect lockBounds(width, height); status_t res = lock(inUsage, lockBounds, vaddr); return res; } status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr) { 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); 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) { const Rect lockBounds(width, height); status_t res = lockAsync(inUsage, lockBounds, vaddr, fenceFd); return res; } status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd) { return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd); } status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect, void** vaddr, 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().lockAsync(handle, inProducerUsage, inConsumerUsage, rect, vaddr, fenceFd); 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; } size_t GraphicBuffer::getFlattenedSize() const { return static_cast(13 + (handle ? handle->numInts : 0)) * sizeof(int); } size_t GraphicBuffer::getFdCount() const { return static_cast(handle ? handle->numFds : 0); } status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { 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] = handle->numFds; buf[11] = handle->numInts; memcpy(fds, handle->data, static_cast(handle->numFds) * sizeof(int)); memcpy(buf + 13, handle->data + handle->numFds, static_cast(handle->numInts) * sizeof(int)); } buffer = static_cast(static_cast(buffer) + sizeNeeded); size -= sizeNeeded; if (handle) { fds += handle->numFds; count -= static_cast(handle->numFds); } return NO_ERROR; } status_t GraphicBuffer::unflatten( void const*& buffer, size_t& size, int const*& fds, size_t& count) { 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 { return BAD_TYPE; } 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 = NULL; 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 = NULL; 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 = NULL; } mId = static_cast(buf[7]) << 32; mId |= static_cast(buf[8]); mGenerationNumber = static_cast(buf[9]); mOwner = ownHandle; if (handle != 0) { buffer_handle_t importedHandle; status_t err = mBufferMapper.importBuffer(handle, &importedHandle); if (err != NO_ERROR) { width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; handle = NULL; ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err); return err; } native_handle_close(handle); native_handle_delete(const_cast(handle)); handle = importedHandle; } buffer = static_cast(static_cast(buffer) + sizeNeeded); size -= sizeNeeded; fds += numFds; count -= numFds; return NO_ERROR; } // --------------------------------------------------------------------------- }; // namespace android �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/GraphicBufferAllocator.cpp������������������������������������������������������������������0100644 0000000 0000000 00000012232 13300556574 016237� 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 namespace android { // --------------------------------------------------------------------------- ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator ) Mutex GraphicBufferAllocator::sLock; KeyedVector GraphicBufferAllocator::sAllocList; GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()), mAllocator(std::make_unique( mMapper.getGrallocMapper())) { } GraphicBufferAllocator::~GraphicBufferAllocator() {} void GraphicBufferAllocator::dump(String8& result) const { Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); size_t total = 0; const size_t SIZE = 4096; char buffer[SIZE]; snprintf(buffer, SIZE, "Allocated buffers:\n"); result.append(buffer); const size_t c = list.size(); for (size_t i=0 ; idumpDebugInfo(); result.append(deviceDump.c_str(), deviceDump.size()); } void GraphicBufferAllocator::dumpToSystemLog() { String8 s; GraphicBufferAllocator::getInstance().dump(s); ALOGD("%s", s.string()); } 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; // Ensure that layerCount is valid. if (layerCount < 1) layerCount = 1; Gralloc2::IMapper::BufferDescriptorInfo info = {}; info.width = width; info.height = height; info.layerCount = layerCount; info.format = static_cast(format); info.usage = usage; Gralloc2::Error error = mAllocator->allocate(info, stride, handle); if (error == Gralloc2::Error::NONE) { Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); uint32_t bpp = bytesPerPixel(format); alloc_rec_t rec; rec.width = width; rec.height = height; rec.stride = *stride; rec.format = format; rec.layerCount = layerCount; rec.usage = usage; rec.size = static_cast(height * (*stride) * bpp); 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 00000012552 13300556574 015550� 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 namespace android { // --------------------------------------------------------------------------- ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) void GraphicBufferMapper::preloadHal() { Gralloc2::Mapper::preload(); } GraphicBufferMapper::GraphicBufferMapper() : mMapper(std::make_unique()) { } status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, buffer_handle_t* outHandle) { ATRACE_CALL(); Gralloc2::Error error = mMapper->importBuffer( hardware::hidl_handle(rawHandle), outHandle); ALOGW_IF(error != Gralloc2::Error::NONE, "importBuffer(%p) failed: %d", rawHandle, error); return static_cast(error); } status_t GraphicBufferMapper::freeBuffer(buffer_handle_t handle) { ATRACE_CALL(); mMapper->freeBuffer(handle); return NO_ERROR; } static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) { Gralloc2::IMapper::Rect outRect{}; outRect.left = rect.left; outRect.top = rect.top; outRect.width = rect.width(); outRect.height = rect.height(); return outRect; } status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr) { return lockAsync(handle, usage, bounds, vaddr, -1); } 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) { 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) { return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd); } status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, void** vaddr, int fenceFd) { ATRACE_CALL(); const uint64_t usage = static_cast( android_convertGralloc1To0Usage(producerUsage, consumerUsage)); Gralloc2::Error error = mMapper->lock(handle, usage, asGralloc2Rect(bounds), fenceFd, vaddr); ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d", handle, error); return static_cast(error); } static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) { if (plane.bits_per_component != 8) { ALOGV("Invalid number of bits per component: %d", plane.bits_per_component); return false; } if (plane.bits_used != 8) { ALOGV("Invalid number of bits used: %d", plane.bits_used); return false; } bool hasValidIncrement = plane.h_increment == 1 || (plane.component != FLEX_COMPONENT_Y && plane.h_increment == 2); hasValidIncrement = hasValidIncrement && plane.v_increment > 0; if (!hasValidIncrement) { ALOGV("Invalid increment: h %d v %d", plane.h_increment, plane.v_increment); return false; } return true; } status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd) { ATRACE_CALL(); Gralloc2::YCbCrLayout layout; Gralloc2::Error error = mMapper->lock(handle, usage, asGralloc2Rect(bounds), fenceFd, &layout); if (error == Gralloc2::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); } return static_cast(error); } status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) { ATRACE_CALL(); *fenceFd = mMapper->unlock(handle); return NO_ERROR; } // --------------------------------------------------------------------------- }; // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/HdrCapabilities.cpp�������������������������������������������������������������������������0100644 0000000 0000000 00000005460 13300556574 014723� 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) = default; HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) = default; size_t HdrCapabilities::getFlattenedSize() const { return sizeof(mMaxLuminance) + sizeof(mMaxAverageLuminance) + sizeof(mMinLuminance) + sizeof(int32_t) + mSupportedHdrTypes.size() * sizeof(int32_t); } 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] = 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] = 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 13300556574 014306� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/NOTICE��������������������������������������������������������������������������������������0100644 0000000 0000000 00000024707 13300556574 012101� 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/PixelFormat.cpp�����������������������������������������������������������������������������0100644 0000000 0000000 00000003766 13300556574 014135� 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/Rect.cpp������������������������������������������������������������������������������������0100644 0000000 0000000 00000007643 13300556574 012576� 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; } 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 00000063413 13300556574 013121� 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 #include // ---------------------------------------------------------------------------- #define VALIDATE_REGIONS (false) #define VALIDATE_WITH_CORECG (false) // ---------------------------------------------------------------------------- #if VALIDATE_WITH_CORECG #include #endif namespace android { // ---------------------------------------------------------------------------- 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 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 VALIDATE_REGIONS validate(outputRegion, "T-Junction free region"); #endif return outputRegion; } Region& Region::operator = (const Region& rhs) { #if 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; } // ---------------------------------------------------------------------------- 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) { 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 (result == false && !silent) { reg.dump(name); CallStack stack(LOG_TAG); } return result; } void Region::boolean_operation(uint32_t op, Region& dst, const Region& lhs, const Region& rhs, int dx, int dy) { #if 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 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 || 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 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 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 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, NULL, 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 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 { 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(String8& out, const char* what, uint32_t /* flags */) const { const_iterator head = begin(); const_iterator const tail = end(); out.appendFormat(" Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail - head); while (head != tail) { out.appendFormat(" [%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/UiConfig.cpp��������������������������������������������������������������������������������0100644 0000000 0000000 00000001476 13300556574 013402� 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(String8& configStr) { static const char* config = " [libui]"; configStr.append(config); } }; // namespace android ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 012611� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/���������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 013226� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/ANativeObjectBase.h��������������������������������������������������������������0100644 0000000 0000000 00000005013 13300556574 016644� 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/BufferQueueDefs.h����������������������������������������������������������������0100644 0000000 0000000 00000002010 13300556574 016405� 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; } // namespace BufferQueueDefs } // namespace android #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/ColorSpace.h���������������������������������������������������������������������0100644 0000000 0000000 00000022746 13300556574 015441� 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/DebugUtils.h���������������������������������������������������������������������0100644 0000000 0000000 00000002036 13300556574 015444� 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 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_color_mode colormode); std::string decodePixelFormat(android::PixelFormat format); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/DisplayInfo.h��������������������������������������������������������������������0100644 0000000 0000000 00000002457 13300556574 015625� 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}; }; /* 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 13300556574 016451� 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/Fence.h��������������������������������������������������������������������������0100644 0000000 0000000 00000012727 13300556574 014425� 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 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(); // 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); // 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; // 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(); int mFenceFd; }; }; // namespace android #endif // ANDROID_FENCE_H �����������������������������������������libs/ui/include/ui/FenceTime.h����������������������������������������������������������������������0100644 0000000 0000000 00000016530 13300556574 015240� 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 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); // Override new and delete since this needs 8-byte alignment, which // is not guaranteed on x86. static void* operator new(size_t nbytes) noexcept; static void operator delete(void *p); 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; }; // 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 00000002327 13300556574 015263� 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; } 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 13300556574 015440� 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/Gralloc2.h�����������������������������������������������������������������������0100644 0000000 0000000 00000010044 13300556574 015040� 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 namespace android { namespace Gralloc2 { using hardware::graphics::allocator::V2_0::IAllocator; using hardware::graphics::common::V1_0::BufferUsage; using hardware::graphics::common::V1_0::PixelFormat; using hardware::graphics::mapper::V2_0::BufferDescriptor; using hardware::graphics::mapper::V2_0::Error; using hardware::graphics::mapper::V2_0::IMapper; using hardware::graphics::mapper::V2_0::YCbCrLayout; // A wrapper to IMapper class Mapper { public: static void preload(); Mapper(); Error createDescriptor( const IMapper::BufferDescriptorInfo& descriptorInfo, BufferDescriptor* outDescriptor) const; // Import a buffer that is from another HAL, another process, or is // cloned. // // The returned handle must be freed with freeBuffer. Error importBuffer(const hardware::hidl_handle& rawHandle, buffer_handle_t* outBufferHandle) const; void freeBuffer(buffer_handle_t bufferHandle) const; // The ownership of acquireFence is always transferred to the callee, even // on errors. Error lock(buffer_handle_t bufferHandle, uint64_t usage, const IMapper::Rect& accessRegion, int acquireFence, void** outData) const; // The ownership of acquireFence is always transferred to the callee, even // on errors. Error lock(buffer_handle_t bufferHandle, uint64_t usage, const IMapper::Rect& accessRegion, int acquireFence, YCbCrLayout* outLayout) const; // unlock returns a fence sync object (or -1) and the fence sync object is // owned by the caller int unlock(buffer_handle_t bufferHandle) const; private: sp mMapper; }; // A wrapper to IAllocator class Allocator { public: // An allocator relies on a mapper, and that mapper must be alive at all // time. Allocator(const Mapper& mapper); std::string dumpDebugInfo() const; /* * The returned buffers are already imported and must not be imported * again. outBufferHandles must point to a space that can contain at * least "count" buffer_handle_t. */ Error allocate(BufferDescriptor descriptor, uint32_t count, uint32_t* outStride, buffer_handle_t* outBufferHandles) const; Error allocate(BufferDescriptor descriptor, uint32_t* outStride, buffer_handle_t* outBufferHandle) const { return allocate(descriptor, 1, outStride, outBufferHandle); } Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count, uint32_t* outStride, buffer_handle_t* outBufferHandles) const { BufferDescriptor descriptor; Error error = mMapper.createDescriptor(descriptorInfo, &descriptor); if (error == Error::NONE) { error = allocate(descriptor, count, outStride, outBufferHandles); } return error; } Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t* outStride, buffer_handle_t* outBufferHandle) const { return allocate(descriptorInfo, 1, outStride, outBufferHandle); } private: const Mapper& mMapper; sp mAllocator; }; } // namespace Gralloc2 } // namespace android #endif // ANDROID_UI_GRALLOC2_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicBuffer.h������������������������������������������������������������������0100644 0000000 0000000 00000022273 13300556574 016111� 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 namespace android { class GraphicBufferMapper; // =========================================================================== // 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 *); // 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* handle, HandleWrapMethod method, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride); // These functions are deprecated because they only take 32 bits of usage GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint32_t usage, uint32_t stride) : GraphicBuffer(handle, method, width, height, format, layerCount, static_cast(usage), stride) {} 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 = ""); // 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; } 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); status_t lock(uint32_t inUsage, void** vaddr); status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr); // 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(); status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd); status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd); status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd); 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); 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); 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* handle, HandleWrapMethod method, uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t stride); void free_handle(); GraphicBufferMapper& mBufferMapper; ssize_t mInitCheck; uint64_t mId; // 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; }; }; // namespace android #endif // ANDROID_GRAPHIC_BUFFER_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicBufferAllocator.h���������������������������������������������������������0100644 0000000 0000000 00000004277 13300556574 017756� 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 { namespace Gralloc2 { class Allocator; } class GraphicBufferMapper; class String8; 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); void dump(String8& res) const; static void dumpToSystemLog(); private: 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; const std::unique_ptr mAllocator; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_BUFFER_ALLOCATOR_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/GraphicBufferMapper.h������������������������������������������������������������0100644 0000000 0000000 00000005242 13300556574 017253� 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 // 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 { // --------------------------------------------------------------------------- namespace Gralloc2 { class Mapper; } class Rect; class GraphicBufferMapper : public Singleton { public: 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, buffer_handle_t* outHandle); status_t freeBuffer(buffer_handle_t handle); status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr); 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); status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, void** vaddr, int fenceFd); 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); const Gralloc2::Mapper& getGrallocMapper() const { return *mMapper; } private: friend class Singleton; GraphicBufferMapper(); const std::unique_ptr mMapper; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_UI_BUFFER_MAPPER_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/include/ui/HdrCapabilities.h����������������������������������������������������������������0100644 0000000 0000000 00000004330 13300556574 016423� 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 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); HdrCapabilities& operator=(HdrCapabilities&& other); 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 13300556574 015630� 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 13300556574 014470� 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 #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; } 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 inline int32_t getWidth() const { return right - left; } // rectangle's height inline int32_t getHeight() const { return bottom - top; } 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); 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 00000017606 13300556574 014631� 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 namespace android { // --------------------------------------------------------------------------- class String8; // --------------------------------------------------------------------------- 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& 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; const Region merge(const Region& rhs, int dx, int dy) const; const Region mergeExclusive(const Region& rhs, int dx, int dy) const; const Region intersect(const Region& rhs, int dx, int dy) const; const Region subtract(const Region& rhs, int dx, int dy) const; // 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(String8& 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/UiConfig.h�����������������������������������������������������������������������0100644 0000000 0000000 00000001567 13300556574 015110� 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(String8& configStr); }; // namespace android #endif /*ANDROID_UI_CONFIG_H*/ �����������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/��������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 012330� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/Android.bp����������������������������������������������������������������������������0100644 0000000 0000000 00000001465 13300556574 014236� 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"], } cc_test { name: "colorspace_test", shared_libs: ["libui"], srcs: ["colorspace_test.cpp"], } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tests/Region_test.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000010022 13300556574 015306� 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/colorspace_test.cpp�������������������������������������������������������������������0100644 0000000 0000000 00000013152 13300556574 016224� 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/tools/��������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 012326� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/ui/tools/Android.bp����������������������������������������������������������������������������0100644 0000000 0000000 00000001626 13300556574 014233� 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 00000015324 13300556574 014332� 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, 0, 'h' }, { "dimension", required_argument, 0, 'd' }, { "source", required_argument, 0, 's' }, { "target", required_argument, 0, 't' }, { 0, 0, 0, 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/vr/��������������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 011200� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/.clang-format�������������������������������������������������������������������������������0100644 0000000 0000000 00000000231 13300556574 013544� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������BasedOnStyle: Google DerivePointerAlignment: false PointerAlignment: Left AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/Android.bp����������������������������������������������������������������������������������0100644 0000000 0000000 00000000027 13300556574 013077� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������subdirs = [ "*", ] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/CPPLINT.cfg���������������������������������������������������������������������������������0100644 0000000 0000000 00000000153 13300556574 012766� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������set noparent filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/���������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 014511� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/Android.bp�����������������������������������������������������������������0100644 0000000 0000000 00000001103 13300556574 016404� 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 13300556574 021050� 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 13300556574 016134� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/include/libbroadcastring/��������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 021445� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h����������������������������������0100644 0000000 0000000 00000062620 13300556574 024602� 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 13300556574 013637� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/Android.bp���������������������������������������������������������������������0100644 0000000 0000000 00000003204 13300556574 015536� 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_client.cpp", "buffer_hub_rpc.cpp", "ion_buffer.cpp", ] localIncludeFiles = [ "include", ] staticLibraries = [ "libdvrcommon", "libpdx_default_transport", ] sharedLibraries = [ "libbase", "libcutils", "libhardware", "liblog", "libui", "libutils", "libnativewindow" ] headerLibraries = [ "libdvr_headers", "libnativebase_headers", ] cc_library { srcs: sourceFiles, cflags: [ "-DLOG_TAG=\"libbufferhub\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", ], export_include_dirs: localIncludeFiles, static_libs: staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, name: "libbufferhub", export_header_lib_headers: [ "libnativebase_headers", ], } cc_test { tags: ["optional"], srcs: ["bufferhub_tests.cpp"], static_libs: ["libbufferhub"] + staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, name: "bufferhub_tests", } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/buffer_hub_client.cpp����������������������������������������������������������0100644 0000000 0000000 00000064134 13300556574 020015� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #include "include/private/dvr/bufferhub_rpc.h" using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; namespace android { namespace dvr { BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle) : Client{pdx::default_transport::ClientChannel::Create( std::move(channel_handle))}, id_(-1) {} BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path) : Client{pdx::default_transport::ClientChannelFactory::Create( endpoint_path)}, id_(-1) {} BufferHubBuffer::~BufferHubBuffer() { if (metadata_header_ != nullptr) { metadata_buffer_.Unlock(); } } Status BufferHubBuffer::CreateConsumer() { Status status = InvokeRemoteMethod(); ALOGE_IF(!status, "BufferHub::CreateConsumer: Failed to create consumer channel: %s", status.GetErrorMessage().c_str()); return status; } int BufferHubBuffer::ImportBuffer() { ATRACE_NAME("BufferHubBuffer::ImportBuffer"); Status> status = InvokeRemoteMethod(); if (!status) { ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s", status.GetErrorMessage().c_str()); return -status.error(); } else if (status.get().id() < 0) { ALOGE("BufferHubBuffer::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, "BufferHubBuffer::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("BufferHubBuffer::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("BufferHubBuffer::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("BufferHubBuffer::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; buffer_state_bit_ = buffer_desc.buffer_state_bit(); // 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 will be preserved. buffer_state_ = &metadata_header_->buffer_state; ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".", id(), buffer_state_->load()); fence_state_ = &metadata_header_->fence_state; ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".", id(), fence_state_->load()); return 0; } inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const { if (user_metadata_size && !user_metadata_ptr_) { ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata."); return -EINVAL; } if (user_metadata_size > user_metadata_size_) { ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.", user_metadata_size, user_metadata_size_); return -E2BIG; } return 0; } int BufferHubBuffer::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, "BufferHubBuffer::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.u64 = buffer_state_bit(); 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( "BufferHubBuffer::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(buffer_state_bit()); } 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(~buffer_state_bit()); } } return 0; } int BufferHubBuffer::Poll(int timeout_ms) { ATRACE_NAME("BufferHubBuffer::Poll"); pollfd p = {event_fd(), POLLIN, 0}; return poll(&p, 1, timeout_ms); } int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height, void** address) { return buffer_.Lock(usage, x, y, width, height, address); } int BufferHubBuffer::Unlock() { return buffer_.Unlock(); } int BufferHubBuffer::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; } int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) { return GetBlobReadWritePointer(size, addr); } void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const { size_t numFds = static_cast(native_handle()->numFds); *fds_count = std::min(max_fds_count, numFds); std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); } BufferConsumer::BufferConsumer(LocalChannelHandle channel) : BASE(std::move(channel)) { const int ret = ImportBuffer(); if (ret < 0) { ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s", strerror(-ret)); Close(ret); } } std::unique_ptr BufferConsumer::Import( LocalChannelHandle channel) { ATRACE_NAME("BufferConsumer::Import"); ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value()); return BufferConsumer::Create(std::move(channel)); } std::unique_ptr BufferConsumer::Import( Status status) { return Import(status ? status.take() : LocalChannelHandle{nullptr, -status.error()}); } int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { if (!out_meta) return -EINVAL; // Only check producer bit and this consumer buffer's particular consumer bit. // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit // is not set. uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) { ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64 " buffer_state_bit=%" PRIx64 ".", id(), buffer_state, buffer_state_bit()); return -EBUSY; } // 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; } uint64_t fence_state = fence_state_->load(); // If there is an acquire fence from producer, we need to return it. if (fence_state & BufferHubDefs::kProducerStateBit) { *out_fence = shared_acquire_fence_.Duplicate(); } // Set the consumer bit unique to this consumer. BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit()); return 0; } int BufferConsumer::Acquire(LocalHandle* ready_fence) { return Acquire(ready_fence, nullptr, 0); } int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size) { ATRACE_NAME("BufferConsumer::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("BufferConsumer::Acquire: no user-defined metadata."); } } auto status = InvokeRemoteMethod(); if (!status) return -status.error(); return 0; } int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { ATRACE_NAME("BufferConsumer::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 BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence) { if (const int error = CheckMetadata(meta->user_metadata_size)) return error; // Check invalid state transition. uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferAcquired(buffer_state)) { ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".", id(), buffer_state); return -EBUSY; } // 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; // For release operation, the client don't need to change the state as it's // bufferhubd's job to flip the produer bit once all consumers are released. return 0; } int BufferConsumer::Release(const LocalHandle& release_fence) { ATRACE_NAME("BufferConsumer::Release"); DvrNativeBufferMetadata meta; if (const int error = LocalRelease(&meta, release_fence)) return error; return ReturnStatusOrError(InvokeRemoteMethod( BorrowedFence(release_fence.Borrow()))); } int BufferConsumer::ReleaseAsync() { DvrNativeBufferMetadata meta; return ReleaseAsync(&meta, LocalHandle()); } int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& release_fence) { ATRACE_NAME("BufferConsumer::ReleaseAsync"); if (const int error = LocalRelease(meta, release_fence)) return error; return ReturnStatusOrError( SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); } int BufferConsumer::Discard() { return Release(LocalHandle()); } int BufferConsumer::SetIgnore(bool ignore) { return ReturnStatusOrError( InvokeRemoteMethod(ignore)); } BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, uint32_t usage, size_t user_metadata_size) : BufferProducer(width, height, format, usage, usage, user_metadata_size) {} BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, uint64_t consumer_usage, size_t user_metadata_size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u " "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " user_metadata_size=%zu", event_fd(), width, height, format, producer_usage, consumer_usage, user_metadata_size); // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod( width, height, format, (producer_usage | consumer_usage), user_metadata_size); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to create producer buffer: %s", status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "BufferProducer::BufferProducer: Failed to import producer buffer: %s", strerror(-ret)); Close(ret); } } BufferProducer::BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint32_t usage, size_t user_metadata_size) : BufferProducer(name, user_id, group_id, width, height, format, usage, usage, user_metadata_size) {} BufferProducer::BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, uint64_t consumer_usage, size_t user_metadata_size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d " "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " user_metadata_size=%zu", event_fd(), name.c_str(), user_id, group_id, width, height, format, producer_usage, consumer_usage, user_metadata_size); // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod( name, user_id, group_id, width, height, format, (producer_usage | consumer_usage), user_metadata_size); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to create/get persistent " "buffer \"%s\": %s", name.c_str(), status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "BufferProducer::BufferProducer: Failed to import producer buffer " "\"%s\": %s", name.c_str(), strerror(-ret)); Close(ret); } } BufferProducer::BufferProducer(uint32_t usage, size_t size) : BufferProducer(usage, usage, size) {} BufferProducer::BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, size_t size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " size=%zu", producer_usage, consumer_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; // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod( width, height, format, (producer_usage | consumer_usage), user_metadata_size); if (!status) { ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s", status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "BufferProducer::BufferProducer: Failed to import producer buffer: %s", strerror(-ret)); Close(ret); } } BufferProducer::BufferProducer(const std::string& name, int user_id, int group_id, uint32_t usage, size_t size) : BufferProducer(name, user_id, group_id, usage, usage, size) {} BufferProducer::BufferProducer(const std::string& name, int user_id, int group_id, uint64_t producer_usage, uint64_t consumer_usage, size_t size) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s user_id=%d group=%d " "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64 " size=%zu", name.c_str(), user_id, group_id, producer_usage, consumer_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; // (b/37881101) Deprecate producer/consumer usage auto status = InvokeRemoteMethod( name, user_id, group_id, width, height, format, (producer_usage | consumer_usage), user_metadata_size); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to create persistent " "buffer \"%s\": %s", name.c_str(), status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "BufferProducer::BufferProducer: Failed to import producer buffer " "\"%s\": %s", name.c_str(), strerror(-ret)); Close(ret); } } BufferProducer::BufferProducer(const std::string& name) : BASE(BufferHubRPC::kClientPath) { ATRACE_NAME("BufferProducer::BufferProducer"); ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str()); auto status = InvokeRemoteMethod(name); if (!status) { ALOGE( "BufferProducer::BufferProducer: Failed to get producer buffer by name " "\"%s\": %s", name.c_str(), status.GetErrorMessage().c_str()); Close(-status.error()); return; } const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "BufferProducer::BufferProducer: Failed to import producer buffer " "\"%s\": %s", name.c_str(), strerror(-ret)); Close(ret); } } BufferProducer::BufferProducer(LocalChannelHandle channel) : BASE(std::move(channel)) { const int ret = ImportBuffer(); if (ret < 0) { ALOGE( "BufferProducer::BufferProducer: Failed to import producer buffer: %s", strerror(-ret)); Close(ret); } } int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence) { if (const int error = CheckMetadata(meta->user_metadata_size)) return error; // Check invalid state transition. uint64_t buffer_state = buffer_state_->load(); if (!BufferHubDefs::IsBufferGained(buffer_state)) { ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".", id(), 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; // Set the producer bit atomically to transit into posted state. BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, BufferHubDefs::kProducerStateBit); return 0; } int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta, size_t user_metadata_size) { ATRACE_NAME("BufferProducer::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 BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence) { ATRACE_NAME("BufferProducer::PostAsync"); if (const int error = LocalPost(meta, ready_fence)) return error; return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); } int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence) { uint64_t buffer_state = buffer_state_->load(); ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".", id(), buffer_state); if (!out_meta) return -EINVAL; if (!BufferHubDefs::IsBufferReleased(buffer_state)) { if (BufferHubDefs::IsBufferGained(buffer_state)) { // We don't want to log error when gaining a newly allocated // buffer. ALOGI("BufferProducer::LocalGain: already gained id=%d.", id()); return -EALREADY; } ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".", id(), 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; } uint64_t fence_state = fence_state_->load(); // If there is an release fence from consumer, we need to return it. if (fence_state & BufferHubDefs::kConsumerStateMask) { *out_fence = shared_release_fence_.Duplicate(); out_meta->release_fence_mask = fence_state & BufferHubDefs::kConsumerStateMask; } // Clear out all bits and the buffer is now back to gained state. buffer_state_->store(0ULL); return 0; } int BufferProducer::Gain(LocalHandle* release_fence) { ATRACE_NAME("BufferProducer::Gain"); DvrNativeBufferMetadata meta; if (const int error = LocalGain(&meta, release_fence)) return error; auto status = InvokeRemoteMethod(); if (!status) return -status.error(); return 0; } int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* release_fence) { ATRACE_NAME("BufferProducer::GainAsync"); if (const int error = LocalGain(out_meta, release_fence)) return error; return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); } int BufferProducer::GainAsync() { DvrNativeBufferMetadata meta; LocalHandle fence; return GainAsync(&meta, &fence); } std::unique_ptr BufferProducer::Import( LocalChannelHandle channel) { ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value()); return BufferProducer::Create(std::move(channel)); } std::unique_ptr BufferProducer::Import( Status status) { return Import(status ? status.take() : LocalChannelHandle{nullptr, -status.error()}); } int BufferProducer::MakePersistent(const std::string& name, int user_id, int group_id) { ATRACE_NAME("BufferProducer::MakePersistent"); return ReturnStatusOrError( InvokeRemoteMethod(name, user_id, group_id)); } int BufferProducer::RemovePersistence() { ATRACE_NAME("BufferProducer::RemovePersistence"); return ReturnStatusOrError( InvokeRemoteMethod()); } } // namespace dvr } // namespace android ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/buffer_hub_rpc.cpp�������������������������������������������������������������0100644 0000000 0000000 00000000256 13300556574 017316� 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/bufferhub_tests.cpp������������������������������������������������������������0100644 0000000 0000000 00000045352 13300556574 017543� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#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::dvr::BufferConsumer; using android::dvr::BufferHubDefs::kConsumerStateMask; using android::dvr::BufferHubDefs::kProducerStateBit; using android::dvr::BufferProducer; using android::pdx::LocalHandle; const int kWidth = 640; const int kHeight = 480; const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; const uint64_t kContext = 42; using LibBufferHubTest = ::testing::Test; TEST_F(LibBufferHubTest, TestBasicUsage) { std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); // Check that consumers can spawn other consumers. std::unique_ptr c2 = BufferConsumer::Import(c->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); // Producer state mask is unique, i.e. 1. EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit); // Consumer state mask cannot have producer bit on. EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0); // Consumer state mask must be a single, i.e. power of 2. EXPECT_NE(c->buffer_state_bit(), 0); EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0); // Consumer state mask cannot have producer bit on. EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0); // Consumer state mask must be a single, i.e. power of 2. EXPECT_NE(c2->buffer_state_bit(), 0); EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0); // Each consumer should have unique bit. EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0); // Initial state: producer not available, consumers not available. EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100))); EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); // New state: producer not available, consumers available. EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(1, RETRY_EINTR(c->Poll(100))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(100))); uint64_t context; LocalHandle fence; EXPECT_EQ(0, c->Acquire(&fence, &context)); EXPECT_EQ(kContext, context); EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(100))); EXPECT_EQ(0, c2->Acquire(&fence, &context)); EXPECT_EQ(kContext, context); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100))); EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(0, c2->Discard()); EXPECT_EQ(1, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(0, p->Gain(&fence)); EXPECT_EQ(0, RETRY_EINTR(p->Poll(100))); EXPECT_EQ(0, RETRY_EINTR(c->Poll(100))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(100))); } TEST_F(LibBufferHubTest, TestEpoll) { std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::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)); // Post the producer and check for consumer signal. EXPECT_EQ(0, p->Post({}, kContext)); ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); 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(), 100)); EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); EXPECT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 100)); // 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(), 100)); } TEST_F(LibBufferHubTest, TestStateMask) { std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); // It's ok to create up to 63 consumer buffers. uint64_t buffer_state_bits = p->buffer_state_bit(); std::array, 63> cs; for (size_t i = 0; i < 63; i++) { cs[i] = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // Expect all buffers have unique state mask. EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0); buffer_state_bits |= cs[i]->buffer_state_bit(); } EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); // 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 < 63; i++) { buffer_state_bits &= ~cs[i]->buffer_state_bit(); cs[i] = nullptr; cs[i] = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // The released state mask will be reused. EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0); buffer_state_bits |= cs[i]->buffer_state_bit(); EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); } } TEST_F(LibBufferHubTest, TestStateTransitions) { std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); uint64_t context; LocalHandle fence; // The producer buffer starts in gained state. // Acquire, release, and gain in gained state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EALREADY, p->Gain(&fence)); // Post in gained state should succeed. EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); // Post, release, and gain in posted state should fail. EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Acquire in posted state should succeed. EXPECT_LE(0, c->Acquire(&fence, &context)); // Acquire, post, and gain in acquired state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Release in acquired state should succeed. EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); // Release, acquire, and post in released state should fail. EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); // Gain in released state should succeed. EXPECT_EQ(0, p->Gain(&fence)); // Acquire, release, and gain in gained state should fail. EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EALREADY, p->Gain(&fence)); } TEST_F(LibBufferHubTest, TestWithCustomMetadata) { struct Metadata { int64_t field1; int64_t field2; }; std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); Metadata m = {1, 3}; EXPECT_EQ(0, p->Post(LocalHandle(), m)); EXPECT_LE(0, RETRY_EINTR(c->Poll(10))); LocalHandle fence; Metadata m2 = {}; EXPECT_EQ(0, c->Acquire(&fence, &m2)); EXPECT_EQ(m.field1, m2.field1); EXPECT_EQ(m.field2, m2.field2); EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(p->Poll(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 = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); // It is illegal to post metadata larger than originally requested during // buffer allocation. OverSizedMetadata evil_meta = {}; EXPECT_NE(0, p->Post(LocalHandle(), evil_meta)); EXPECT_GE(0, RETRY_EINTR(c->Poll(10))); // It is ok to post metadata smaller than originally requested during // buffer allocation. int64_t sequence = 42; EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); } 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 = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); Metadata m = {1, 3}; EXPECT_EQ(0, p->Post(LocalHandle(), 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)); // It is ok to acquire metadata smaller than originally requested during // buffer allocation. EXPECT_EQ(0, c->Acquire(&fence, &sequence)); EXPECT_EQ(m.field1, sequence); } TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) { std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); int64_t sequence = 3; EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); LocalHandle fence; EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestWithNoMeta) { std::unique_ptr p = BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); LocalHandle fence; EXPECT_EQ(0, p->Post(LocalHandle())); EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) { std::unique_ptr p = BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); int64_t sequence = 3; EXPECT_NE(0, p->Post(LocalHandle(), sequence)); } TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) { auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth, kHeight, kFormat, kUsage); ASSERT_NE(nullptr, p); // Record the original buffer id for later comparison. const int buffer_id = p->id(); auto c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_NE(nullptr, c); EXPECT_EQ(0, p->Post(LocalHandle())); // Close the connection to the producer. This should not affect the consumer. p = nullptr; LocalHandle fence; EXPECT_EQ(0, c->Acquire(&fence)); EXPECT_EQ(0, c->Release(LocalHandle())); // Attempt to reconnect to the persistent buffer. p = BufferProducer::Create("TestPersistentBuffer"); ASSERT_NE(nullptr, p); EXPECT_EQ(buffer_id, p->id()); EXPECT_EQ(0, p->Gain(&fence)); } TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) { auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth, kHeight, kFormat, kUsage); ASSERT_NE(nullptr, p); // Close the connection to the producer. p = nullptr; // Mismatch the params. p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2, kHeight, kFormat, kUsage); ASSERT_EQ(nullptr, p); } TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) { auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth, kHeight, kFormat, kUsage); ASSERT_NE(nullptr, p); LocalHandle fence; auto c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_NE(nullptr, c); EXPECT_EQ(0, p->Post(LocalHandle())); EXPECT_EQ(0, c->Acquire(&fence)); EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); // Test that removing persistence and closing the producer orphans the // consumer. EXPECT_EQ(0, p->Gain(&fence)); EXPECT_EQ(0, p->Post(LocalHandle())); EXPECT_EQ(0, p->RemovePersistence()); p = nullptr; // Orphaned consumer can acquire the posted buffer one more time in // asynchronous manner. But synchronous call will fail. DvrNativeBufferMetadata meta; EXPECT_EQ(0, c->AcquireAsync(&meta, &fence)); EXPECT_EQ(-EPIPE, c->Release(LocalHandle())); } 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 = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); 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(c->Poll(10))); 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(), 10)); // 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(p->Poll(10))); 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(c->Poll(10))); EXPECT_EQ(0, c->AcquireAsync(&meta, &f4)); EXPECT_TRUE(f4.IsValid()); EXPECT_LT(0, PollFd(f4.Get(), 10)); // 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(p->Poll(10))); EXPECT_EQ(0, p->GainAsync(&meta, &f6)); EXPECT_TRUE(f6.IsValid()); EXPECT_LT(0, PollFd(f6.Get(), 10)); } TEST_F(LibBufferHubTest, TestOrphanedAcquire) { std::unique_ptr p = BufferProducer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); std::unique_ptr c1 = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); const uint64_t consumer_state_bit1 = c1->buffer_state_bit(); DvrNativeBufferMetadata meta; EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); LocalHandle fence; EXPECT_LT(0, RETRY_EINTR(c1->Poll(10))); EXPECT_LE(0, c1->AcquireAsync(&meta, &fence)); // Destroy the consumer now will make it orphaned and the buffer is still // acquired. c1 = nullptr; EXPECT_GE(0, RETRY_EINTR(p->Poll(10))); std::unique_ptr c2 = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); const uint64_t consumer_state_bit2 = c2->buffer_state_bit(); EXPECT_NE(consumer_state_bit1, consumer_state_bit2); // The new consumer is available for acquire. EXPECT_LT(0, RETRY_EINTR(c2->Poll(10))); EXPECT_LE(0, c2->AcquireAsync(&meta, &fence)); // Releasing the consumer makes the buffer gainable. EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle())); // The buffer is now available for the producer to gain. EXPECT_LT(0, RETRY_EINTR(p->Poll(10))); // But if another consumer is created in released state. std::unique_ptr c3 = BufferConsumer::Import(p->CreateConsumer()); ASSERT_TRUE(c3.get() != nullptr); const uint64_t consumer_state_bit3 = c3->buffer_state_bit(); EXPECT_NE(consumer_state_bit2, consumer_state_bit3); // The consumer buffer is not acquirable. EXPECT_GE(0, RETRY_EINTR(c3->Poll(10))); EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence)); // Producer should be able to gain no matter what. EXPECT_EQ(0, p->GainAsync(&meta, &fence)); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/�����������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 015262� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/���������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016734� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/�����������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 017527� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h����������������������������������������0100644 0000000 0000000 00000040240 13300556574 023342� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_ #define ANDROID_DVR_BUFFER_HUB_CLIENT_H_ #include #include #include #include #include #include #include #include "bufferhub_rpc.h" namespace android { namespace dvr { class BufferHubBuffer : 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(); // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). int Poll(int timeout_ms); // 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(); // Gets a blob buffer that was created with BufferProducer::CreateBlob. // Locking and Unlocking is handled internally. There's no need to Unlock // after calling this method. int GetBlobReadWritePointer(size_t size, void** addr); // Gets a blob buffer that was created with BufferProducer::CreateBlob. // Locking and Unlocking is handled internally. There's no need to Unlock // after calling this method. int GetBlobReadOnlyPointer(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])); } // Get up to |max_fds_count| file descriptors for accessing the blob shared // memory. |fds_count| will contain the actual number of file descriptors. void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const; 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_; } int id() const { return id_; } // A state mask which is unique to a buffer hub client among all its siblings // sharing the same concrete graphic buffer. uint64_t buffer_state_bit() const { return buffer_state_bit_; } // The following methods return settings of the first buffer. Currently, // it is only possible to create multi-buffer BufferHubBuffers 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(); } // TODO(b/37881101) Clean up producer/consumer usage. uint64_t producer_usage() const { return buffer_.usage(); } uint64_t consumer_usage() const { return buffer_.usage(); } uint64_t GetQueueIndex() const { return metadata_header_->queue_index; } void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; } protected: explicit BufferHubBuffer(LocalChannelHandle channel); explicit BufferHubBuffer(const std::string& endpoint_path); virtual ~BufferHubBuffer(); // 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); // 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}; 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: BufferHubBuffer(const BufferHubBuffer&) = delete; void operator=(const BufferHubBuffer&) = 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_; uint64_t buffer_state_bit_{0ULL}; IonBuffer buffer_; IonBuffer metadata_buffer_; }; // 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 BufferProducer 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 (BufferConsumers) 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 BufferProducer : 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); template ::value>::type> int Post(const LocalHandle& ready_fence) { return Post(ready_fence, nullptr, 0); } template ::value>::type> int Post(const LocalHandle& ready_fence, const Meta& meta) { return Post(ready_fence, &meta, sizeof(meta)); } // 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 only succeed if the buffer // is in the released state. // This returns zero or a negative unix error code. int Gain(LocalHandle* release_fence); int GainAsync(); // 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. int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); // Attaches the producer to |name| so that it becomes a persistent buffer that // may be retrieved by name at a later time. This may be used in cases where a // shared memory buffer should persist across the life of the producer process // (i.e. the buffer may be held by clients across a service restart). The // buffer may be associated with a user and/or group id to restrict access to // the buffer. If user_id or group_id is -1 then checks for the respective id // are disabled. If user_id or group_id is 0 then the respective id of the // calling process is used instead. int MakePersistent(const std::string& name, int user_id, int group_id); // Removes the persistence of the producer. int RemovePersistence(); private: friend BASE; // Constructors are automatically exposed through BufferProducer::Create(...) // static template methods inherited from ClientBase, which take the same // arguments as the constructors. // Constructs a buffer with the given geometry and parameters. BufferProducer(uint32_t width, uint32_t height, uint32_t format, uint32_t usage, size_t metadata_size = 0); BufferProducer(uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, uint64_t consumer_usage, size_t metadata_size); // Constructs a persistent buffer with the given geometry and parameters and // binds it to |name| in one shot. If a persistent buffer with the same name // and settings already exists and matches the given geometry and parameters, // that buffer is connected to this client instead of creating a new buffer. // If the name matches but the geometry or settings do not match then // construction fails and BufferProducer::Create() returns nullptr. // // Access to the persistent buffer may be restricted by |user_id| and/or // |group_id|; these settings are established only when the buffer is first // created and cannot be changed. A user or group id of -1 disables checks for // that respective id. A user or group id of 0 is substituted with the // effective user or group id of the calling process. BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint32_t usage, size_t metadata_size = 0); BufferProducer(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint64_t producer_usage, uint64_t consumer_usage, size_t user_metadata_size); // Constructs a blob (flat) buffer with the given usage flags. BufferProducer(uint32_t usage, size_t size); BufferProducer(uint64_t producer_usage, uint64_t consumer_usage, size_t size); // Constructs a persistent blob (flat) buffer and binds it to |name|. BufferProducer(const std::string& name, int user_id, int group_id, uint32_t usage, size_t size); BufferProducer(const std::string& name, int user_id, int group_id, uint64_t producer_usage, uint64_t consumer_usage, size_t size); // Constructs a channel to persistent buffer by name only. The buffer must // have been previously created or made persistent. explicit BufferProducer(const std::string& name); // Imports the given file handle to a producer channel, taking ownership. explicit BufferProducer(LocalChannelHandle channel); // Local state transition helpers. int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); int LocalPost(const DvrNativeBufferMetadata* meta, const LocalHandle& ready_fence); }; // 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 BufferConsumer : 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); // Attempt to retrieve a post event from buffer hub. If successful, // |ready_fence| is set to a fence to wait on until the buffer is ready. This // call will only succeed after the fd is signaled. This returns zero or a // negative unix error code. template int Acquire(LocalHandle* ready_fence, Meta* meta) { return Acquire(ready_fence, meta, sizeof(*meta)); } // Asynchronously acquires a bufer. int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); // This should be called after a successful Acquire call. 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(); // When set, this consumer is no longer notified when this buffer is // available. The system behaves as if Discard() is immediately called // whenever the buffer is posted. If ignore is set to true while a buffer is // pending, it will act as if Discard() was also called. // This returns zero or a negative unix error code. int SetIgnore(bool ignore); private: friend BASE; explicit BufferConsumer(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_BUFFER_HUB_CLIENT_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h��������������������������������������������0100644 0000000 0000000 00000036407 13300556574 022523� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_ #define ANDROID_DVR_BUFFERHUB_RPC_H_ #include #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; // Single producuer multiple (up to 63) consumers ownership signal. // 64-bit atomic unsigned int. // // MSB LSB // | | // v v // [P|C62|...|C1|C0] // Gain'ed state: [0|..|0|0] -> Exclusively Writable. // Post'ed state: [1|..|0|0] // Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits // Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits static constexpr uint64_t kProducerStateBit = 1ULL << 63; static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1; static inline void ModifyBufferState(std::atomic* buffer_state, uint64_t clear_mask, uint64_t set_mask) { uint64_t old_state; uint64_t new_state; do { old_state = buffer_state->load(); new_state = (old_state & ~clear_mask) | set_mask; } while (!buffer_state->compare_exchange_weak(old_state, new_state)); } static inline bool IsBufferGained(uint64_t state) { return state == 0; } static inline bool IsBufferPosted(uint64_t state, uint64_t consumer_bit = kConsumerStateMask) { return (state & kProducerStateBit) && !(state & consumer_bit); } static inline bool IsBufferAcquired(uint64_t state) { return (state & kProducerStateBit) && (state & kConsumerStateMask); } static inline bool IsBufferReleased(uint64_t state) { return !(state & kProducerStateBit) && (state & kConsumerStateMask); } struct __attribute__((packed, 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). std::atomic buffer_state; std::atomic fence_state; uint64_t queue_index; // 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); } // namespace BufferHubDefs 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) = default; NativeBufferHandle& operator=(NativeBufferHandle&& other) = 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, uint64_t buffer_state_bit, const FileHandleType& acquire_fence_fd, const FileHandleType& release_fence_fd) : id_(id), buffer_state_bit_(buffer_state_bit), buffer_(buffer, id), metadata_(metadata, id), acquire_fence_fd_(acquire_fence_fd.Borrow()), release_fence_fd_(release_fence_fd.Borrow()) {} BufferDescription(BufferDescription&& other) = default; BufferDescription& operator=(BufferDescription&& 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 kProducerStateBit; for a consumer the bit // must be one of the kConsumerStateMask. uint64_t buffer_state_bit() const { return buffer_state_bit_; } 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}; uint64_t buffer_state_bit_{0}; // 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_state_bit_, 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&&) = default; FenceHandle& operator=(FenceHandle&&) = 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, kOpCreatePersistentBuffer, kOpGetPersistentBuffer, kOpGetBuffer, kOpNewConsumer, kOpProducerMakePersistent, kOpProducerRemovePersistence, kOpProducerPost, kOpProducerGain, kOpConsumerAcquire, kOpConsumerRelease, kOpConsumerSetIgnore, kOpCreateProducerQueue, kOpCreateConsumerQueue, kOpGetQueueInfo, kOpProducerQueueAllocateBuffers, kOpProducerQueueRemoveBuffer, kOpConsumerQueueImportBuffers, }; // 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(CreatePersistentBuffer, kOpCreatePersistentBuffer, void(const std::string& name, int user_id, int group_id, uint32_t width, uint32_t height, uint32_t format, uint64_t usage, size_t user_metadata_size)); PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer, void(const std::string& name)); PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer, BufferDescription(Void)); PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void)); PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent, void(const std::string& name, int user_id, int group_id)); PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence, void(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)); PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore)); // 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(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/ion_buffer.h�����������������������������������������������0100644 0000000 0000000 00000007321 13300556574 022016� 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); IonBuffer& operator=(IonBuffer&& other); // 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(); 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_buffer.h��������������������������������������������0100644 0000000 0000000 00000014276 13300556574 022526� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_NATIVE_BUFFER_H_ #define ANDROID_DVR_NATIVE_BUFFER_H_ #include #include #include #include #include #include #include namespace android { namespace dvr { // ANativeWindowBuffer is the abstraction Android HALs and frameworks use to // pass around hardware graphics buffers. The following classes implement this // abstraction with different DVR backing buffers, all of which provide // different semantics on top of ion/gralloc buffers. // An implementation of ANativeWindowBuffer backed by an IonBuffer. class NativeBuffer : public android::ANativeObjectBase> { public: static constexpr int kEmptyFence = -1; explicit NativeBuffer(const std::shared_ptr& buffer) : BASE(), buffer_(buffer), fence_(kEmptyFence) { ANativeWindowBuffer::width = buffer->width(); ANativeWindowBuffer::height = buffer->height(); ANativeWindowBuffer::stride = buffer->stride(); ANativeWindowBuffer::format = buffer->format(); ANativeWindowBuffer::usage = buffer->usage(); handle = buffer_->handle(); } virtual ~NativeBuffer() {} std::shared_ptr buffer() { return buffer_; } int fence() const { return fence_.Get(); } void SetFence(int fence) { fence_.Reset(fence); } private: friend class android::LightRefBase; std::shared_ptr buffer_; pdx::LocalHandle fence_; NativeBuffer(const NativeBuffer&) = delete; void operator=(NativeBuffer&) = delete; }; class NativeBufferProducer : public android::ANativeObjectBase< ANativeWindowBuffer, NativeBufferProducer, android::LightRefBase> { public: static constexpr int kEmptyFence = -1; NativeBufferProducer(const std::shared_ptr& buffer, EGLDisplay display, uint32_t surface_buffer_index) : BASE(), buffer_(buffer), surface_buffer_index_(surface_buffer_index), display_(display) { ANativeWindowBuffer::width = buffer_->width(); ANativeWindowBuffer::height = buffer_->height(); ANativeWindowBuffer::stride = buffer_->stride(); ANativeWindowBuffer::format = buffer_->format(); ANativeWindowBuffer::usage = buffer_->usage(); ANativeWindowBuffer::handle = buffer_->native_handle(); if (display_) { image_khr_ = eglCreateImageKHR(display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, static_cast(this), nullptr); } else { image_khr_ = EGL_NO_IMAGE_KHR; } } explicit NativeBufferProducer(const std::shared_ptr& buffer) : NativeBufferProducer(buffer, nullptr, 0) {} virtual ~NativeBufferProducer() { if (image_khr_ != EGL_NO_IMAGE_KHR) eglDestroyImageKHR(display_, image_khr_); } EGLImageKHR image_khr() const { return image_khr_; } std::shared_ptr buffer() const { return buffer_; } int release_fence() const { return release_fence_.Get(); } uint32_t surface_buffer_index() const { return surface_buffer_index_; } // Return the release fence, passing ownership to the caller. pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); } // Post the buffer consumer, closing the acquire and release fences. int Post(int acquire_fence, uint64_t sequence) { release_fence_.Close(); return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence); } // Gain the buffer producer, closing the previous release fence if valid. int Gain() { return buffer_->Gain(&release_fence_); } // Asynchronously gain the buffer, closing the previous release fence. int GainAsync() { release_fence_.Close(); return buffer_->GainAsync(); } private: friend class android::LightRefBase; std::shared_ptr buffer_; pdx::LocalHandle release_fence_; EGLImageKHR image_khr_; uint32_t surface_buffer_index_; EGLDisplay display_; NativeBufferProducer(const NativeBufferProducer&) = delete; void operator=(NativeBufferProducer&) = delete; }; // NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a // BufferConsumer. class NativeBufferConsumer : public android::ANativeObjectBase< ANativeWindowBuffer, NativeBufferConsumer, android::LightRefBase> { public: static constexpr int kEmptyFence = -1; explicit NativeBufferConsumer(const std::shared_ptr& buffer) : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) { ANativeWindowBuffer::width = buffer_->width(); ANativeWindowBuffer::height = buffer_->height(); ANativeWindowBuffer::stride = buffer_->stride(); ANativeWindowBuffer::format = buffer_->format(); ANativeWindowBuffer::usage = buffer_->usage(); handle = buffer_->native_handle(); } virtual ~NativeBufferConsumer() {} std::shared_ptr buffer() const { return buffer_; } int acquire_fence() const { return acquire_fence_.Get(); } uint64_t sequence() const { return sequence_; } // Return the acquire fence, passing ownership to the caller. pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); } // Acquire the underlying buffer consumer, closing the previous acquire fence // if valid. int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); } // Release the buffer consumer, closing the acquire and release fences if // valid. int Release(int release_fence) { acquire_fence_.Close(); sequence_ = 0; return buffer_->Release(pdx::LocalHandle(release_fence)); } private: friend class android::LightRefBase; std::shared_ptr buffer_; pdx::LocalHandle acquire_fence_; uint64_t sequence_; NativeBufferConsumer(const NativeBufferConsumer&) = delete; void operator=(NativeBufferConsumer&) = delete; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_NATIVE_BUFFER_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhub/ion_buffer.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000016753 13300556574 016472� 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) : IonBuffer() { *this = std::move(other); } IonBuffer& IonBuffer::operator=(IonBuffer&& other) { 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 != NO_ERROR) 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 != NO_ERROR) 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 != NO_ERROR) return -EINVAL; else return 0; } } // namespace dvr } // namespace android ���������������������libs/vr/libbufferhubqueue/��������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 014704� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/Android.bp����������������������������������������������������������������0100644 0000000 0000000 00000002671 13300556574 016612� 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_producer.cpp", ] includeFiles = [ "include", ] staticLibraries = [ "libbufferhub", "libdvrcommon", "libpdx_default_transport", ] sharedLibraries = [ "libbase", "libbinder", "libcutils", "libhardware", "liblog", "libui", "libutils", "libgui", ] headerLibraries = [ "libdvr_headers", "libnativebase_headers", ] cc_library { name: "libbufferhubqueue", cflags: [ "-DLOG_TAG=\"libbufferhubqueue\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", ], srcs: sourceFiles, export_include_dirs: includeFiles, export_static_lib_headers: staticLibraries, static_libs: staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, } subdirs = ["tests"] �����������������������������������������������������������������������libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp�����������������������������������������������0100644 0000000 0000000 00000055732 13300556574 022272� 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 { // Polls an fd for the given events. Status PollEvents(int fd, short events) { const int kTimeoutMs = 0; pollfd pfd{fd, events, 0}; const int count = RETRY_EINTR(poll(&pfd, 1, kTimeoutMs)); if (count < 0) { return ErrorStatus(errno); } else if (count == 0) { return ErrorStatus(ETIMEDOUT); } else { return {pfd.revents}; } } 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("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s", strerror(-ret)); } } Status BufferHubQueue::ImportQueue() { auto status = InvokeRemoteMethod(); if (!status) { ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s", 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; } 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("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s", 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("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s", buffer_status.GetErrorMessage().c_str()); } } else if (events & EPOLLHUP) { ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!"); hung_up_ = true; } else { ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events); } return {}; } Status BufferHubQueue::AddBuffer( const std::shared_ptr& buffer, size_t slot) { ALOGD_IF(TRACE, "BufferHubQueue::AddBuffer: buffer_id=%d slot=%zu", buffer->id(), slot); if (is_full()) { ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu", 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("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", strerror(-ret)); return ErrorStatus(-ret); } } buffers_[slot] = buffer; capacity_++; return {}; } Status BufferHubQueue::RemoveBuffer(size_t slot) { ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", 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( "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll " "set: %s", 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()) { available_buffers_.push(std::move(entry)); // Trigger OnBufferAvailable callback if registered. if (on_buffer_available_) on_buffer_available_(); return {}; } else { ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!"); return ErrorStatus(E2BIG); } } Status> BufferHubQueue::Dequeue(int timeout, size_t* slot) { ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(), timeout); PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", 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(); 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 (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(BufferProducer::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() == 0) { // Error out if no buffer is allocated and improted. ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffers: no buffer allocated."); 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(); } if (status.get().size() == 0) { ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated."); ErrorStatus(ENOMEM); } 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::RemoveBuffer(size_t slot) { auto status = InvokeRemoteMethod(slot); if (!status) { ALOGE("ProducerQueue::RemoveBuffer: Failed to remove producer buffer: %s", 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) { ATRACE_NAME("ProducerQueue::Dequeue"); if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { ALOGE("ProducerQueue::Dequeue: Invalid parameter."); 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->GainAsync(out_meta, release_fence); if (ret < 0 && ret != -EALREADY) return ErrorStatus(-ret); return {std::move(buffer)}; } ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) : BufferHubQueue(std::move(handle)) { auto status = ImportQueue(); if (!status) { ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s", status.GetErrorMessage().c_str()); Close(-status.error()); } auto import_status = ImportBuffers(); if (import_status) { ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.", import_status.get()); } else { ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s", import_status.GetErrorMessage().c_str()); } } Status ConsumerQueue::ImportBuffers() { auto status = InvokeRemoteMethod(); if (!status) { if (status.error() == EBADR) { ALOGI( "ConsumerQueue::ImportBuffers: Queue is silent, no buffers " "imported."); return {0}; } else { ALOGE( "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s", 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, "ConsumerQueue::ImportBuffers: buffer_handle=%d", buffer_handle_slot.first.value()); std::unique_ptr buffer_consumer = BufferConsumer::Import(std::move(buffer_handle_slot.first)); if (!buffer_consumer) { ALOGE("ConsumerQueue::ImportBuffers: Failed to import buffer: slot=%zu", buffer_handle_slot.second); last_error = ErrorStatus(EPIPE); continue; } auto add_status = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second); if (!add_status) { ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s", 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, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", id(), buffer->id(), slot); auto status = BufferHubQueue::AddBuffer(buffer, slot); if (!status) return status; // Check to see if the buffer is already signaled. This is necessary to catch // cases where buffers are already available; epoll edge triggered mode does // not fire until an edge transition when adding new buffers to the epoll // set. Note that we only poll the fd events because HandleBufferEvent() takes // care of checking the translated buffer events. auto poll_status = PollEvents(buffer->event_fd(), POLLIN); if (!poll_status && poll_status.error() != ETIMEDOUT) { ALOGE("ConsumerQueue::AddBuffer: Failed to poll consumer buffer: %s", poll_status.GetErrorMessage().c_str()); return poll_status.error_status(); } // Update accounting if the buffer is available. if (poll_status) return HandleBufferEvent(slot, buffer->event_fd(), poll_status.get()); else return {}; } 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( "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer " "does not match metadata size (%zu) for the queue.", 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("ConsumerQueue::Dequeue: no user-defined metadata."); } } 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("ConsumerQueue::Dequeue: Invalid parameter."); 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, "ConsumerQueue::OnBufferAllocated: queue_id=%d", id()); auto status = ImportBuffers(); if (!status) { ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s", status.GetErrorMessage().c_str()); return ErrorStatus(status.error()); } else if (status.get() == 0) { ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!"); return ErrorStatus(ENOBUFS); } else { ALOGD_IF(TRACE, "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.", status.get()); return {}; } } } // namespace dvr } // namespace android ��������������������������������������libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp���������������������������������������������0100644 0000000 0000000 00000056026 13300556574 022634� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/buffer_hub_queue_producer.h" #include #include #include #include namespace android { namespace dvr { /* static */ sp BufferHubQueueProducer::Create() { sp producer = new BufferHubQueueProducer; auto config = ProducerQueueConfigBuilder() .SetMetadata() .Build(); producer->queue_ = ProducerQueue::Create(config, UsagePolicy{}); return producer; } /* static */ sp BufferHubQueueProducer::Create( const std::shared_ptr& queue) { if (queue->metadata_size() != sizeof(DvrNativeBufferMetadata)) { ALOGE( "BufferHubQueueProducer::Create producer's metadata size is different " "than the size of DvrNativeBufferMetadata"); return nullptr; } sp producer = new BufferHubQueueProducer; producer->queue_ = queue; return producer; } status_t BufferHubQueueProducer::requestBuffer(int slot, sp* buf) { ALOGD_IF(TRACE, "requestBuffer: slot=%d", slot); std::unique_lock lock(mutex_); if (connected_api_ == kNoConnectedApi) { ALOGE("requestBuffer: BufferHubQueueProducer 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].mBufferProducer == nullptr) { ALOGE("requestBuffer: slot %d is not dequeued.", slot); return BAD_VALUE; } const auto& buffer_producer = buffers_[slot].mBufferProducer; sp graphic_buffer = buffer_producer->buffer()->buffer(); buffers_[slot].mGraphicBuffer = graphic_buffer; buffers_[slot].mRequestBufferCalled = true; *buf = graphic_buffer; return NO_ERROR; } status_t BufferHubQueueProducer::setMaxDequeuedBufferCount( int max_dequeued_buffers) { ALOGD_IF(TRACE, "setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers); std::unique_lock lock(mutex_); if (max_dequeued_buffers <= 0 || max_dequeued_buffers > static_cast(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 BufferHubQueueProducer::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( "BufferHubQueueProducer::setAsyncMode: BufferHubQueue should always be " "asynchronous. This call makes no effact."); return NO_ERROR; } return NO_ERROR; } status_t BufferHubQueueProducer::dequeueBuffer( int* out_slot, sp* out_fence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* /*outBufferAge*/, FrameEventHistoryDelta* /* out_timestamps */) { ALOGD_IF(TRACE, "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 (static_cast(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; std::shared_ptr buffer_producer; 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; buffer_producer = buffer_status.take(); if (!buffer_producer) return NO_MEMORY; if (width == buffer_producer->width() && height == buffer_producer->height() && static_cast(format) == buffer_producer->format()) { // The producer queue returns a buffer producer 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, buffer_producer->width(), buffer_producer->height(), buffer_producer->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(); ALOGD_IF(TRACE, "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 = slot; ret = NO_ERROR; if (buffers_[slot].mIsReallocating) { ret |= BUFFER_NEEDS_REALLOCATION; buffers_[slot].mIsReallocating = false; } return ret; } status_t BufferHubQueueProducer::detachBuffer(int /* slot */) { ALOGE("BufferHubQueueProducer::detachBuffer not implemented."); return INVALID_OPERATION; } status_t BufferHubQueueProducer::detachNextBuffer( sp* /* out_buffer */, sp* /* out_fence */) { ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented."); return INVALID_OPERATION; } status_t BufferHubQueueProducer::attachBuffer( int* /* out_slot */, const sp& /* buffer */) { // With this BufferHub backed implementation, we assume (for now) all buffers // are allocated and owned by the BufferHub. Thus the attempt of transfering // ownership of a buffer to the buffer queue is intentionally unsupported. LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported."); return INVALID_OPERATION; } status_t BufferHubQueueProducer::queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) { ALOGD_IF(TRACE, "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; } status_t ret; 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 buffer producer with timestamp in the metadata. const auto& buffer_producer = buffers_[slot].mBufferProducer; // Check input crop is not out of boundary of current buffer. Rect buffer_rect(buffer_producer->width(), buffer_producer->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 = static_cast(is_auto_timestamp); meta_data.dataspace = static_cast(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 = static_cast(scaling_mode); meta_data.transform = static_cast(transform); buffer_producer->PostAsync(&meta_data, fence_fd); buffers_[slot].mBufferState.queue(); output->width = buffer_producer->width(); output->height = buffer_producer->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 BufferHubQueueProducer::cancelBuffer(int slot, const sp& fence) { ALOGD_IF(TRACE, __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 buffer_producer = buffers_[slot].mBufferProducer; queue_->Enqueue(buffer_producer, slot, 0ULL); buffers_[slot].mBufferState.cancel(); buffers_[slot].mFence = fence; ALOGD_IF(TRACE, "cancelBuffer: slot %d", slot); return NO_ERROR; } status_t BufferHubQueueProducer::query(int what, int* out_value) { ALOGD_IF(TRACE, __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 = queue_->default_width(); break; case NATIVE_WINDOW_HEIGHT: value = queue_->default_height(); break; case NATIVE_WINDOW_FORMAT: value = 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; } ALOGD_IF(TRACE, "query: key=%d, v=%d", what, value); *out_value = value; return NO_ERROR; } status_t BufferHubQueueProducer::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. ALOGD_IF(TRACE, __FUNCTION__); if (output == nullptr) { return BAD_VALUE; } std::unique_lock lock(mutex_); if (connected_api_ != kNoConnectedApi) { 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("BufferHubQueueProducer::connect: unknow API %d", api); return BAD_VALUE; } return NO_ERROR; } status_t BufferHubQueueProducer::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. ALOGD_IF(TRACE, __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 BufferHubQueueProducer::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 BufferHubQueueProducer::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("BufferHubQueueProducer::allocateBuffers not implemented."); } status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) { ALOGE("BufferHubQueueProducer::allowAllocation not implemented."); return INVALID_OPERATION; } status_t BufferHubQueueProducer::setGenerationNumber( uint32_t generation_number) { ALOGD_IF(TRACE, __FUNCTION__); std::unique_lock lock(mutex_); generation_number_ = generation_number; return NO_ERROR; } String8 BufferHubQueueProducer::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("BufferHubQueueProducer::getConsumerName not supported."); return String8("BufferHubQueue::DummyConsumer"); } status_t BufferHubQueueProducer::setSharedBufferMode(bool shared_buffer_mode) { if (shared_buffer_mode) { ALOGE( "BufferHubQueueProducer::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 BufferHubQueueProducer::setAutoRefresh(bool auto_refresh) { if (auto_refresh) { ALOGE("BufferHubQueueProducer::setAutoRefresh(true) is not supported."); return INVALID_OPERATION; } // Setting to default should just work as a no-op. return NO_ERROR; } status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) { ALOGD_IF(TRACE, __FUNCTION__); std::unique_lock lock(mutex_); dequeue_timeout_ms_ = static_cast(timeout / (1000 * 1000)); return NO_ERROR; } status_t BufferHubQueueProducer::getLastQueuedBuffer( sp* /* out_buffer */, sp* /* out_fence */, float /*out_transform_matrix*/[16]) { ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented."); return INVALID_OPERATION; } void BufferHubQueueProducer::getFrameTimestamps( FrameEventHistoryDelta* /*outDelta*/) { ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented."); } status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const { ALOGD_IF(TRACE, __FUNCTION__); *out_id = unique_id_; return NO_ERROR; } status_t BufferHubQueueProducer::getConsumerUsage(uint64_t* out_usage) const { ALOGD_IF(TRACE, __FUNCTION__); // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS *out_usage = 0; return NO_ERROR; } status_t BufferHubQueueProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, PixelFormat format, uint64_t usage) { auto status = queue_->AllocateBuffer(width, height, layer_count, format, usage); if (!status) { ALOGE( "BufferHubQueueProducer::AllocateBuffer: Failed to allocate buffer: %s", status.GetErrorMessage().c_str()); return NO_MEMORY; } size_t slot = status.get(); auto buffer_producer = queue_->GetBuffer(slot); LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, "Failed to get buffer producer at slot: %zu", slot); buffers_[slot].mBufferProducer = buffer_producer; return NO_ERROR; } status_t BufferHubQueueProducer::RemoveBuffer(size_t slot) { auto status = queue_->RemoveBuffer(slot); if (!status) { ALOGE("BufferHubQueueProducer::RemoveBuffer: Failed to remove buffer: %s", status.GetErrorMessage().c_str()); return INVALID_OPERATION; } // Reset in memory objects related the the buffer. buffers_[slot].mBufferProducer = nullptr; buffers_[slot].mGraphicBuffer = nullptr; buffers_[slot].mBufferState.detachProducer(); return NO_ERROR; } status_t BufferHubQueueProducer::FreeAllBuffers() { for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { // Reset in memory objects related the the buffer. buffers_[slot].mGraphicBuffer = nullptr; buffers_[slot].mBufferState.reset(); buffers_[slot].mRequestBufferCalled = false; buffers_[slot].mBufferProducer = nullptr; buffers_[slot].mFence = Fence::NO_FENCE; } auto status = queue_->FreeAllBuffers(); if (!status) { ALOGE( "BufferHubQueueProducer::FreeAllBuffers: Failed to free all buffers on " "the queue: %s", status.GetErrorMessage().c_str()); } if (queue_->capacity() != 0 || queue_->count() != 0) { LOG_ALWAYS_FATAL( "BufferHubQueueProducer::FreeAllBuffers: Not all buffers are freed."); } return NO_ERROR; } } // namespace dvr } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016327� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/����������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 020001� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/dvr/������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 020574� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h�����������������������������0100644 0000000 0000000 00000040766 13300556574 025630� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ #define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_ #include #include #include #include #include #include #include #include #include #include namespace android { namespace dvr { class ConsumerQueue; // |BufferHubQueue| manages a queue of |BufferHubBuffer|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); // 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; } explicit operator bool() const { return epoll_fd_.IsValid(); } 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: BufferHubQueue(pdx::LocalChannelHandle channel); 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 ring buffer of available buffers that stores related // per-buffer data. struct Entry { Entry() : slot(0) {} Entry(const std::shared_ptr& buffer, size_t slot, uint64_t index) : buffer(buffer), slot(slot), index(index) {} Entry(const std::shared_ptr& buffer, std::unique_ptr metadata, pdx::LocalHandle fence, size_t slot) : buffer(buffer), metadata(std::move(metadata)), fence(std::move(fence)), slot(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}; 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. size_t default_width_{1}; // Default buffer height that is set during ProducerQueue's creation. size_t default_height_{1}; // Default buffer format that is set during ProducerQueue's creation. int32_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_; // Buffers and related data that are available for dequeue. // RingBuffer available_buffers_{kMaxQueueCapacity}; std::priority_queue, EntryComparator> available_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 buffer producer. 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 BufferProducer. 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); // 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. pdx::Status> Dequeue( int timeout, size_t* slot, pdx::LocalHandle* release_fence); pdx::Status> Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, pdx::LocalHandle* release_fence); // 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}); } 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); }; class ConsumerQueue : public BufferHubQueue { public: // Get a buffer consumer. 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 BufferConsumer. 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) { return std::unique_ptr( new ConsumerQueue(std::move(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 BufferProducer 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; 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_producer.h���������������������������0100644 0000000 0000000 00000016554 13300556574 026173� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_ #define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_ #include #include namespace android { namespace dvr { class BufferHubQueueProducer : public BnGraphicBufferProducer { public: static constexpr int kNoConnectedApi = -1; // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer // side logic doesn't limit the number of buffer it can acquire // simultaneously. We need a way for consumer logic to configure and enforce // that. static constexpr int kDefaultUndequeuedBuffers = 1; // Create a BufferHubQueueProducer instance by creating a new producer queue. static sp Create(); // Create a BufferHubQueueProducer instance by importing an existing prodcuer // queue. static sp Create( const std::shared_ptr& producer); // See |IGraphicBufferProducer::requestBuffer| status_t requestBuffer(int slot, sp* buf) override; // For the BufferHub based implementation. All buffers in the queue are // allowed to be dequeued from the consumer side. It call always returns // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting // |max_dequeued_buffers| here can be considered the same as setting queue // capacity. // // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override; // See |IGraphicBufferProducer::setAsyncMode| status_t setAsyncMode(bool async) override; // See |IGraphicBufferProducer::dequeueBuffer| status_t dequeueBuffer(int* out_slot, sp* out_fence, uint32_t width, uint32_t height, PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) override; // See |IGraphicBufferProducer::detachBuffer| status_t detachBuffer(int slot) override; // See |IGraphicBufferProducer::detachNextBuffer| status_t detachNextBuffer(sp* out_buffer, sp* out_fence) override; // See |IGraphicBufferProducer::attachBuffer| status_t attachBuffer(int* out_slot, const sp& buffer) override; // See |IGraphicBufferProducer::queueBuffer| status_t queueBuffer(int slot, const QueueBufferInput& input, QueueBufferOutput* output) override; // See |IGraphicBufferProducer::cancelBuffer| status_t cancelBuffer(int slot, const sp& fence) override; // See |IGraphicBufferProducer::query| status_t query(int what, int* out_value) override; // See |IGraphicBufferProducer::connect| status_t connect(const sp& listener, int api, bool producer_controlled_by_app, QueueBufferOutput* output) override; // See |IGraphicBufferProducer::disconnect| status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override; // See |IGraphicBufferProducer::setSidebandStream| status_t setSidebandStream(const sp& stream) override; // See |IGraphicBufferProducer::allocateBuffers| void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) override; // See |IGraphicBufferProducer::allowAllocation| status_t allowAllocation(bool allow) override; // See |IGraphicBufferProducer::setGenerationNumber| status_t setGenerationNumber(uint32_t generation_number) override; // See |IGraphicBufferProducer::getConsumerName| String8 getConsumerName() const override; // See |IGraphicBufferProducer::setSharedBufferMode| status_t setSharedBufferMode(bool shared_buffer_mode) override; // See |IGraphicBufferProducer::setAutoRefresh| status_t setAutoRefresh(bool auto_refresh) override; // See |IGraphicBufferProducer::setDequeueTimeout| status_t setDequeueTimeout(nsecs_t timeout) override; // See |IGraphicBufferProducer::getLastQueuedBuffer| status_t getLastQueuedBuffer(sp* out_buffer, sp* out_fence, float out_transform_matrix[16]) override; // See |IGraphicBufferProducer::getFrameTimestamps| void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override; // See |IGraphicBufferProducer::getUniqueId| status_t getUniqueId(uint64_t* out_id) const override; // See |IGraphicBufferProducer::getConsumerUsage| status_t getConsumerUsage(uint64_t* out_usage) const override; private: using LocalHandle = pdx::LocalHandle; // Private constructor to force use of |Create|. BufferHubQueueProducer() {} static uint64_t genUniqueId() { static std::atomic counter{0}; static uint64_t id = static_cast(getpid()) << 32; return id | counter++; } // Allocate new buffer through BufferHub and add it into |queue_| for // bookkeeping. status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count, PixelFormat format, uint64_t usage); // Remove a buffer via BufferHubRPC. status_t RemoveBuffer(size_t slot); // Free all buffers which are owned by the prodcuer. Note that if graphic // buffers are acquired by the consumer, we can't . status_t FreeAllBuffers(); // Concreate implementation backed by BufferHubBuffer. std::shared_ptr queue_; // Mutex for thread safety. std::mutex mutex_; // Connect client API, should be one of the NATIVE_WINDOW_API_* flags. int connected_api_{kNoConnectedApi}; // |max_buffer_count_| sets the capacity of the underlying buffer queue. int32_t max_buffer_count_{BufferHubQueue::kMaxQueueCapacity}; // |max_dequeued_buffer_count_| set the maximum number of buffers that can // be dequeued at the same momment. int32_t max_dequeued_buffer_count_{1}; // Sets how long dequeueBuffer or attachBuffer will block if a buffer or // slot is not yet available. The timeout is stored in milliseconds. int dequeue_timeout_ms_{BufferHubQueue::kNoTimeOut}; // |generation_number_| stores the current generation number of the attached // producer. Any attempt to attach a buffer with a different generation // number will fail. // TOOD(b/38137191) Currently not used as we don't support // IGraphicBufferProducer::detachBuffer. uint32_t generation_number_{0}; // |buffers_| stores the buffers that have been dequeued from // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets // filled in with the result of |Dequeue|. // TODO(jwcai) The buffer allocated to a slot will also be replaced if the // requested buffer usage or geometry differs from that of the buffer // allocated to a slot. struct BufferHubSlot : public BufferSlot { BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {} // BufferSlot comes from android framework, using m prefix to comply with // the name convention with the reset of data fields from BufferSlot. std::shared_ptr mBufferProducer; bool mIsReallocating; }; BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity]; // A uniqueId used by IGraphicBufferProducer interface. const uint64_t unique_id_{genUniqueId()}; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_ ����������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/��������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016046� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/Android.bp����������������������������������������������������������0100644 0000000 0000000 00000002010 13300556574 017737� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� header_libraries = [ "libdvr_headers", ] shared_libraries = [ "libbase", "libbinder", "libcutils", "libgui", "liblog", "libhardware", "libui", "libutils", ] static_libraries = [ "libbufferhubqueue", "libbufferhub", "libchrome", "libdvrcommon", "libpdx_default_transport", ] 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", ], name: "buffer_hub_queue-test", tags: ["optional"], } 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", ], name: "buffer_hub_queue_producer-test", tags: ["optional"], } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp�������������������������������������������0100644 0000000 0000000 00000057554 13300556574 023057� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include // Enable/disable debug logging. #define TRACE 0 namespace android { namespace dvr { 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; 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 size_t nb_dequeue_times = 16; ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); // Allocate only one buffer. AllocateBuffer(); // But dequeue multiple times. for (size_t i = 0; i < nb_dequeue_times; i++) { size_t slot; LocalHandle fence; auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); size_t mi = i; ASSERT_EQ(p1->Post(LocalHandle(), &mi, sizeof(mi)), 0); size_t mo; auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_TRUE(c1_status.ok()); auto c1 = c1_status.take(); ASSERT_NE(nullptr, c1); ASSERT_EQ(mi, mo); c1->Release(LocalHandle()); } } TEST_F(BufferHubQueueTest, TestProducerConsumer) { const size_t kBufferCount = 16; size_t slot; uint64_t seq; ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().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|. LocalHandle fence; auto status = consumer_queue_->Dequeue(100, &slot, &seq, &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++) { LocalHandle fence; // First time there is no buffer available to dequeue. auto consumer_status = consumer_queue_->Dequeue(100, &slot, &seq, &fence); ASSERT_FALSE(consumer_status.ok()); ASSERT_EQ(ETIMEDOUT, consumer_status.error()); // 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(100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); auto producer = producer_status.take(); ASSERT_NE(nullptr, producer); uint64_t seq_in = static_cast(i); ASSERT_EQ(producer->Post(post_fence, &seq_in, sizeof(seq_in)), 0); // Second time the just the POSTED buffer should be dequeued. uint64_t seq_out = 0; consumer_status = consumer_queue_->Dequeue(100, &slot, &seq_out, &fence); ASSERT_TRUE(consumer_status.ok()); EXPECT_TRUE(fence.IsValid()); auto consumer = consumer_status.take(); ASSERT_NE(nullptr, consumer); ASSERT_EQ(seq_in, seq_out); } } TEST_F(BufferHubQueueTest, TestRemoveBuffer) { 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()); 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( /*timeout_ms=*/100, &entry->slot, &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(), /*timeout_ms=*/100)); 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(0, silent_queue->capacity()); // Dequeue and post a buffer. size_t slot; LocalHandle fence; auto producer_status = producer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); auto producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post({})); // After post, check the number of remaining available buffers. EXPECT_EQ(kBufferCount - 1, producer_queue_->count()); // 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(0u, silent_queue->count()); EXPECT_FALSE(silent_queue->HandleQueueEvents()); EXPECT_EQ(0u, silent_queue->count()); // Build a new consumer queue to test multi-consumer queue features. consumer_queue_ = silent_queue->CreateConsumerQueue(); ASSERT_NE(nullptr, consumer_queue_); // Check that buffers are correctly imported on construction. EXPECT_EQ(kBufferCount, consumer_queue_->capacity()); EXPECT_EQ(1u, consumer_queue_->count()); // Reclaim released/ignored buffers. ASSERT_EQ(kBufferCount - 1, producer_queue_->count()); usleep(10000); WaitAndHandleOnce(producer_queue_.get(), /*timeout_ms=*/100); ASSERT_EQ(kBufferCount - 1, producer_queue_->count()); // Post another buffer. producer_status = producer_queue_->Dequeue(/*timeout_ms=*/100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post({})); // Verify that the consumer queue receives it. size_t consumer_queue_count = consumer_queue_->count(); WaitAndHandleOnce(consumer_queue_.get(), /*timeout_ms=*/100); EXPECT_LT(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(/*timeout_ms=*/100, &slot, &fence); ASSERT_TRUE(consumer_status.ok()); auto consumer_buffer = consumer_status.take(); ASSERT_NE(nullptr, consumer_buffer); consumer_buffer->Discard(); // Buffer should be returned to the producer queue without being handled by // the silent consumer queue. EXPECT_GT(consumer_queue_count, consumer_queue_->count()); EXPECT_EQ(kBufferCount - 2, producer_queue_->count()); EXPECT_TRUE(producer_queue_->HandleQueueEvents()); EXPECT_EQ(kBufferCount - 1, producer_queue_->count()); } struct TestMetadata { char a; int32_t b; int64_t c; }; TEST_F(BufferHubQueueTest, TestMetadata) { ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); AllocateBuffer(); std::vector ms = { {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}}; for (auto mi : ms) { size_t slot; LocalHandle fence; auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0); TestMetadata mo; auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_TRUE(c1_status.ok()); auto c1 = c1_status.take(); ASSERT_EQ(mi.a, mo.a); ASSERT_EQ(mi.b, mo.b); ASSERT_EQ(mi.c, mo.c); c1->Release(LocalHandle(-1)); } } TEST_F(BufferHubQueueTest, TestMetadataMismatch) { ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); AllocateBuffer(); int64_t mi = 3; size_t slot; LocalHandle fence; auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0); int32_t mo; // Acquire a buffer with mismatched metadata is not OK. auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_FALSE(c1_status.ok()); } TEST_F(BufferHubQueueTest, TestEnqueue) { ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); AllocateBuffer(); size_t slot; LocalHandle fence; auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); int64_t mo; producer_queue_->Enqueue(p1, slot, 0ULL); auto c1_status = consumer_queue_->Dequeue(100, &slot, &mo, &fence); ASSERT_FALSE(c1_status.ok()); } TEST_F(BufferHubQueueTest, TestAllocateBuffer) { ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().Build(), UsagePolicy{})); size_t s1; AllocateBuffer(); LocalHandle fence; auto p1_status = producer_queue_->Dequeue(100, &s1, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_NE(nullptr, p1); // producer queue is exhausted size_t s2; auto p2_status = producer_queue_->Dequeue(100, &s2, &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(100, &s2, &fence); ASSERT_TRUE(p2_status.ok()); auto p2 = p2_status.take(); ASSERT_NE(nullptr, p2); ASSERT_EQ(producer_queue_->count(), 0U); // p1 and p2 should have different slot number ASSERT_NE(s1, s2); // 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; ASSERT_EQ(p1->Post(LocalHandle(), seq), 0); size_t cs1, cs2; auto c1_status = consumer_queue_->Dequeue(100, &cs1, &seq, &fence); ASSERT_TRUE(c1_status.ok()); auto c1 = c1_status.take(); ASSERT_NE(nullptr, c1); ASSERT_EQ(consumer_queue_->count(), 0U); ASSERT_EQ(consumer_queue_->capacity(), 2U); ASSERT_EQ(cs1, s1); ASSERT_EQ(p2->Post(LocalHandle(), seq), 0); auto c2_status = consumer_queue_->Dequeue(100, &cs2, &seq, &fence); ASSERT_TRUE(c2_status.ok()); auto c2 = c2_status.take(); ASSERT_NE(nullptr, c2); ASSERT_EQ(cs2, s2); } TEST_F(BufferHubQueueTest, TestUsageSetMask) { const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().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; auto p1_status = producer_queue_->Dequeue(100, &slot, &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_.SetMetadata().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; auto p1_status = producer_queue_->Dequeue(100, &slot, &fence); ASSERT_TRUE(p1_status.ok()); auto p1 = p1_status.take(); ASSERT_EQ(0u, p1->usage() & clear_mask); } 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; uint64_t seq; LocalHandle fence; pdx::Status status; pdx::Status> consumer_status; pdx::Status> producer_status; std::shared_ptr consumer_buffer; std::shared_ptr producer_buffer; ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata().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(100, &slot, &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(100, &slot, &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(100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq))); 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(100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq))); } 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(100, &slot, &fence); ASSERT_TRUE(producer_status.ok()); producer_buffer = producer_status.take(); ASSERT_NE(nullptr, producer_buffer); ASSERT_EQ(0, producer_buffer->Post(fence, &seq, sizeof(seq))); consumer_status = consumer_queue_->Dequeue(100, &slot, &seq, &fence); ASSERT_TRUE(consumer_status.ok()); } 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 } } // namespace } // namespace dvr } // namespace android ����������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp����������������������������������0100644 0000000 0000000 00000045441 13300556574 024752� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include namespace android { namespace dvr { 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()); mProducer = BufferHubQueueProducer::Create(); 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(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); EXPECT_EQ(kDefaultWidth, static_cast(value)); EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); EXPECT_EQ(kDefaultHeight, static_cast(value)); EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); EXPECT_EQ(kDefaultFormat, value); EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); EXPECT_LE(0, value); EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value); EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue EXPECT_EQ(NO_ERROR, 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(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; // Queue the buffer back into the BQ ASSERT_EQ(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, mProducer->cancelBuffer(slot, fence)); } TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { return; ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) << "async mode: " << false; ASSERT_EQ(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); ASSERT_EQ(NO_ERROR, 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(NO_ERROR, mProducer->cancelBuffer(slot, fence)); // Should now be able to decrease the max dequeued count by 1 ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); } TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; ASSERT_EQ(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, 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(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); // Shouldn't be able to cancel buffer after disconnect. ASSERT_EQ(NO_ERROR, 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(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers)); EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false)); EXPECT_EQ(NO_ERROR, 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(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); } // Disconnect then reconnect. EXPECT_EQ(NO_ERROR, 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(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); } EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); } } // namespace } // namespace dvr } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/���������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 013334� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/Android.bp�����������������������������������������������������������������������0100644 0000000 0000000 00000003247 13300556574 015242� 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", "vsync_client.cpp", "shared_buffer_helpers.cpp", ] localIncludeFiles = [ "include", ] sharedLibraries = [ "libbase", "libcutils", "liblog", "libutils", "libui", "libgui", "libhardware", "libsync", "libnativewindow", ] staticLibraries = [ "libdvrcommon", "libbufferhubqueue", "libbufferhub", "libbroadcastring", "libpdx_default_transport", ] headerLibraries = [ "vulkan_headers", "libdvr_headers", ] cc_library { tags: ["tests"], srcs: sourceFiles, cflags: ["-DLOG_TAG=\"libdisplay\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", ], // + [ "-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 00000020021 13300556574 017033� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/display_client.h" #include #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 00000003056 13300556574 020536� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/display_manager_client.h" #include #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 13300556574 017421� 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 13300556574 014757� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/CPPLINT.cfg��������������������������������������������������������������0100644 0000000 0000000 00000000033 13300556574 016542� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������filter=-build/header_guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/�����������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016431� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/�������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 017224� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/include/private/dvr/display_client.h���������������������������������������������0100644 0000000 0000000 00000006772 13300556574 022411� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_ #define ANDROID_DVR_DISPLAY_CLIENT_H_ #include #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 13300556574 024066� 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 00000021316 13300556574 022763� 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; Flags(const Integer& value) : value_{value} {} Flags(const Flags&) = default; Flags& operator=(const Flags&) = default; Integer value() const { return value_; } 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 13300556574 023721� 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_client.h�����������������������������������������������0100644 0000000 0000000 00000003431 13300556574 022073� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_VSYNC_CLIENT_H_ #define ANDROID_DVR_VSYNC_CLIENT_H_ #include #include struct dvr_vsync_client {}; namespace android { namespace dvr { /* * VSyncClient is a remote interface to the vsync service in displayd. * This class is used to wait for and retrieve information about the * display vsync. */ class VSyncClient : public pdx::ClientBase, public dvr_vsync_client { public: /* * Wait for the next vsync signal. * The timestamp (in ns) is written into *ts when ts is non-NULL. */ int Wait(int64_t* timestamp_ns); /* * Returns the file descriptor used to communicate with the vsync system * service or -1 on error. */ int GetFd(); /* * Clears the select/poll/epoll event so that subsequent calls to * these will not signal until the next vsync. */ int Acknowledge(); /* * Get the timestamp of the last vsync event in ns. This call has * the same side effect on events as Acknowledge(), which saves * an IPC message. */ int GetLastTimestamp(int64_t* timestamp_ns); /* * Get vsync scheduling info. * Get the estimated timestamp of the next GPU lens warp preemption event in * ns. Also returns the corresponding vsync count that the next lens warp * operation will target. This call has the same side effect on events as * Acknowledge(), which saves an IPC message. */ int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns, uint32_t* next_vsync_count); private: friend BASE; VSyncClient(); explicit VSyncClient(long timeout_ms); VSyncClient(const VSyncClient&) = delete; void operator=(const VSyncClient&) = delete; }; } // namespace dvr } // namespace android #endif // ANDROID_DVR_VSYNC_CLIENT_H_ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/shared_buffer_helpers.cpp��������������������������������������������������������0100644 0000000 0000000 00000005450 13300556574 020362� 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 13300556574 014660� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/system/CPPLINT.cfg���������������������������������������������������������������0100644 0000000 0000000 00000000033 13300556574 016443� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������filter=-build/header_guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdisplay/vsync_client.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000004345 13300556574 016543� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/private/dvr/vsync_client.h" #include #include #include using android::dvr::display::VSyncProtocol; using android::pdx::Transaction; namespace android { namespace dvr { VSyncClient::VSyncClient(long timeout_ms) : BASE(pdx::default_transport::ClientChannelFactory::Create( VSyncProtocol::kClientPath), timeout_ms) {} VSyncClient::VSyncClient() : BASE(pdx::default_transport::ClientChannelFactory::Create( VSyncProtocol::kClientPath)) {} int VSyncClient::Wait(int64_t* timestamp_ns) { auto status = InvokeRemoteMethod(); if (!status) { ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s", status.GetErrorMessage().c_str()); return -status.error(); } if (timestamp_ns != nullptr) { *timestamp_ns = status.get(); } return 0; } int VSyncClient::GetFd() { return event_fd(); } int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) { auto status = InvokeRemoteMethod(); if (!status) { ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s", status.GetErrorMessage().c_str()); return -status.error(); } *timestamp_ns = status.get(); return 0; } int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns, uint32_t* next_vsync_count) { if (!vsync_period_ns || !timestamp_ns || !next_vsync_count) return -EINVAL; auto status = InvokeRemoteMethod(); if (!status) { ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s", status.GetErrorMessage().c_str()); return -status.error(); } *vsync_period_ns = status.get().vsync_period_ns; *timestamp_ns = status.get().timestamp_ns; *next_vsync_count = status.get().next_vsync_count; return 0; } int VSyncClient::Acknowledge() { auto status = InvokeRemoteMethod(); ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s", status.GetErrorMessage().c_str()); return ReturnStatusOrError(status); } } // namespace dvr } // namespace android �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/�������������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 012462� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/Android.bp���������������������������������������������������������������������������0100644 0000000 0000000 00000004411 13300556574 014362� 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", owner: "google", export_include_dirs: ["include"], } cflags = [ "-DLOG_TAG=\"libdvr\"", "-DTRACE=0", ] 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_vsync.cpp", ] static_libs = [ "libbroadcastring", "libbufferhub", "libbufferhubqueue", "libvrsensor", "libdisplay", "libvirtualtouchpadclient", "libvr_hwc-impl", "libvr_hwc-binder", "libgrallocusage", "libperformance", "libpdx_default_transport", ] shared_libs = [ "android.hardware.graphics.bufferqueue@1.0", "android.hidl.token@1.0-utils", "libbase", "libbinder", "liblog", "libcutils", "libutils", "libnativewindow", "libgui", "libui", ] cc_library_shared { name: "libdvr", 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", 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 00000003441 13300556574 014611� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_api.h" #include #include #include // Headers from libdvr #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) #include "include/dvr/dvr_api_entries.h" // Undefine macro definitions to play nice with Google3 style rules. #undef DVR_V1_API_ENTRY return 0; } ALOGE("dvrGetApi: Unknown API version=%d", version); return -EINVAL; } } // extern "C" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_buffer.cpp�����������������������������������������������������������������������0100644 0000000 0000000 00000014037 13300556574 015314� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_buffer.h" #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 dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer) { if (write_buffer) *write_buffer = new DvrWriteBuffer; } 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."); delete write_buffer; } } int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) { return write_buffer && write_buffer->write_buffer; } int dvrWriteBufferClear(DvrWriteBuffer* write_buffer) { if (!write_buffer) return -EINVAL; write_buffer->write_buffer = nullptr; return 0; } 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); } int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd, const void* meta, size_t meta_size_bytes) { if (!write_buffer || !write_buffer->write_buffer) return -EINVAL; pdx::LocalHandle fence(ready_fence_fd); int result = write_buffer->write_buffer->Post(fence, meta, meta_size_bytes); return result; } int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd) { if (!write_buffer || !write_buffer->write_buffer || !release_fence_fd) return -EINVAL; pdx::LocalHandle release_fence; int result = write_buffer->write_buffer->Gain(&release_fence); *release_fence_fd = release_fence.Release(); return result; } int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer) { if (!write_buffer || !write_buffer->write_buffer) return -EINVAL; return write_buffer->write_buffer->GainAsync(); } void dvrReadBufferCreateEmpty(DvrReadBuffer** read_buffer) { if (read_buffer) *read_buffer = new DvrReadBuffer; } 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."); delete read_buffer; } } int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) { return read_buffer && read_buffer->read_buffer; } int dvrReadBufferClear(DvrReadBuffer* read_buffer) { if (!read_buffer) return -EINVAL; read_buffer->read_buffer = nullptr; return 0; } 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); } int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd, void* meta, size_t meta_size_bytes) { if (!read_buffer || !read_buffer->read_buffer) return -EINVAL; pdx::LocalHandle ready_fence; int result = read_buffer->read_buffer->Acquire(&ready_fence, meta, meta_size_bytes); *ready_fence_fd = ready_fence.Release(); return result; } int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd) { if (!read_buffer || !read_buffer->read_buffer) return -EINVAL; pdx::LocalHandle fence(release_fence_fd); int result = read_buffer->read_buffer->Release(fence); return result; } int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer) { if (!read_buffer || !read_buffer->read_buffer) return -EINVAL; return read_buffer->read_buffer->ReleaseAsync(); } 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; } const struct native_handle* dvrWriteBufferGetNativeHandle( DvrWriteBuffer* write_buffer) { if (!write_buffer || !write_buffer->write_buffer) return nullptr; return write_buffer->write_buffer->native_handle(); } const struct native_handle* dvrReadBufferGetNativeHandle( DvrReadBuffer* read_buffer) { if (!read_buffer || !read_buffer->read_buffer) return nullptr; return read_buffer->read_buffer->native_handle(); } const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer) { if (!buffer || !buffer->buffer) return nullptr; return buffer->buffer->handle(); } } // extern "C" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/dvr_buffer_queue.cpp�����������������������������������������������������������������0100644 0000000 0000000 00000051312 13300556574 016515� 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::BufferConsumer; using android::dvr::BufferHubBuffer; using android::dvr::BufferHubQueueProducer; using android::dvr::BufferProducer; using android::dvr::ConsumerQueue; 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 = BufferHubQueueProducer::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& buffer_producer = write_buffers_[slot]->write_buffer; if (!buffer_producer) return -ENOMEM; if (width_ == buffer_producer->width() && height_ == buffer_producer->height() && format_ == buffer_producer->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_, buffer_producer->width(), buffer_producer->height(), buffer_producer->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 = buffer_producer->layer_count(); uint64_t old_usage = buffer_producer->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) { LOG_FATAL_IF( (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()), "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); // Some basic sanity checks before we put the buffer back into a slot. size_t slot = static_cast(write_buffer->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 dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue, ANativeWindow** out_window) { ALOGW( "dvrWriteBufferQueueGetExternalSurface: This API has been deprecated and " "renamed to dvrWriteBufferQueueGetANativeWindow."); return dvrWriteBufferQueueGetANativeWindow(write_queue, out_window); } 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 dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd) { if (!write_queue || !write_buffer || !out_fence_fd) return -EINVAL; return write_queue->Dequeue(timeout, write_buffer, out_fence_fd); } 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::Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd, void* out_meta, size_t meta_size_bytes) { if (meta_size_bytes != consumer_queue_->metadata_size()) { ALOGE( "DvrReadBufferQueue::Dequeue: Invalid metadata size, expected (%zu), " "but actual (%zu).", consumer_queue_->metadata_size(), meta_size_bytes); return -EINVAL; } size_t slot; pdx::LocalHandle acquire_fence; auto buffer_status = consumer_queue_->Dequeue( timeout, &slot, out_meta, meta_size_bytes, &acquire_fence); if (!buffer_status) { ALOGE_IF(buffer_status.error() != ETIMEDOUT, "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s", buffer_status.GetErrorMessage().c_str()); return -buffer_status.error(); } read_buffer->read_buffer = buffer_status.take(); *out_fence_fd = acquire_fence.Release(); 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) { LOG_FATAL_IF( (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()), "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot); // Some basic sanity checks before we put the buffer back into a slot. size_t slot = static_cast(read_buffer->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)) { ALOGE( "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released does not " "belong to this buffer queue. Releasing buffer: id=%d, buffer in " "queue: id=%d", read_buffer->read_buffer->id(), consumer_queue_->GetBufferId(slot)); return -EINVAL; } 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 13300556574 020056� 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 13300556574 017703� 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 00000021140 13300556574 017173� 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::BufferConsumer; using android::dvr::display::DisplayManagerClient; using android::dvr::display::SurfaceAttributes; using android::dvr::display::SurfaceAttribute; 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 13300556574 021077� 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 00000002766 13300556574 015332� 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 BufferProducer; class BufferConsumer; class IonBuffer; DvrBuffer* CreateDvrBufferFromIonBuffer( const std::shared_ptr& ion_buffer); DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer( const std::shared_ptr& buffer_consumer); DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer( const std::shared_ptr& buffer_producer); } // 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 13300556574 016343� 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 13300556574 015011� 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 00000021223 13300556574 015466� 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::dvr::CreateDvrReadBufferFromBufferConsumer; 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_vsync.cpp������������������������������������������������������������������������0100644 0000000 0000000 00000001606 13300556574 015203� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "include/dvr/dvr_vsync.h" #include #include extern "C" { struct DvrVSyncClient { std::unique_ptr client; }; int dvrVSyncClientCreate(DvrVSyncClient** client_out) { auto client = android::dvr::VSyncClient::Create(); if (!client) { ALOGE("dvrVSyncClientCreate: Failed to create vsync client!"); return -EIO; } *client_out = new DvrVSyncClient{std::move(client)}; return 0; } void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; } int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, int64_t* next_timestamp_ns, uint32_t* next_vsync_count) { return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns, next_vsync_count); } } // extern "C" ��������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/exported_apis.lds��������������������������������������������������������������������0100644 0000000 0000000 00000000175 13300556574 016034� 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 13300556574 014105� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/CPPLINT.cfg������������������������������������������������������������������0100644 0000000 0000000 00000000033 13300556574 015670� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������filter=-build/header_guard �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 014700� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_api.h����������������������������������������������������������������0100644 0000000 0000000 00000054170 13300556574 016501� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_API_H_ #define ANDROID_DVR_API_H_ #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; // 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); // 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. 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. uint64_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[8]; }; #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 #include "dvr_api_entries.h" // Undefine macro definitions to play nice with Google3 style rules. #undef DVR_V1_API_ENTRY }; 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 00000014215 13300556574 020226� 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 // 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(WriteBufferCreateEmpty); DVR_V1_API_ENTRY(WriteBufferDestroy); DVR_V1_API_ENTRY(WriteBufferIsValid); DVR_V1_API_ENTRY(WriteBufferClear); DVR_V1_API_ENTRY(WriteBufferGetId); DVR_V1_API_ENTRY(WriteBufferGetAHardwareBuffer); DVR_V1_API_ENTRY(WriteBufferPost); DVR_V1_API_ENTRY(WriteBufferGain); DVR_V1_API_ENTRY(WriteBufferGainAsync); DVR_V1_API_ENTRY(WriteBufferGetNativeHandle); // Read buffer DVR_V1_API_ENTRY(ReadBufferCreateEmpty); DVR_V1_API_ENTRY(ReadBufferDestroy); DVR_V1_API_ENTRY(ReadBufferIsValid); DVR_V1_API_ENTRY(ReadBufferClear); DVR_V1_API_ENTRY(ReadBufferGetId); DVR_V1_API_ENTRY(ReadBufferGetAHardwareBuffer); DVR_V1_API_ENTRY(ReadBufferAcquire); DVR_V1_API_ENTRY(ReadBufferRelease); DVR_V1_API_ENTRY(ReadBufferReleaseAsync); DVR_V1_API_ENTRY(ReadBufferGetNativeHandle); // Buffer DVR_V1_API_ENTRY(BufferDestroy); DVR_V1_API_ENTRY(BufferGetAHardwareBuffer); DVR_V1_API_ENTRY(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(WriteBufferQueueGetExternalSurface); // deprecated DVR_V1_API_ENTRY(WriteBufferQueueCreateReadQueue); DVR_V1_API_ENTRY(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(ReadBufferQueueDequeue); DVR_V1_API_ENTRY(ReadBufferQueueSetBufferAvailableCallback); DVR_V1_API_ENTRY(ReadBufferQueueSetBufferRemovedCallback); DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents); // V-Sync client DVR_V1_API_ENTRY(VSyncClientCreate); DVR_V1_API_ENTRY(VSyncClientDestroy); DVR_V1_API_ENTRY(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); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_buffer.h�������������������������������������������������������������0100644 0000000 0000000 00000010071 13300556574 017171� 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; // Creates an empty write buffer that may be filled with an acutal buffer by // other functions. void dvrWriteBufferCreateEmpty(DvrWriteBuffer** write_buffer); // 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); // Clears the contents of the buffer object. After a call to this function // dvrWriteBufferIsValid on the same buffer object returns 0. int dvrWriteBufferClear(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); // Posts the buffer, notifying any connected read buffers. Takes ownership of // |ready_fence_fd|. int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd, const void* meta, size_t meta_size_bytes); // Gains a buffer that has been released by all connected read buffers. int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd); int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer); // TODO(eieio): Switch to return int and take an out parameter for the native // handle. const struct native_handle* dvrWriteBufferGetNativeHandle( DvrWriteBuffer* write_buffer); // Creates an empty read buffer that may be filled with and actual buffer by // other functions. void dvrReadBufferCreateEmpty(DvrReadBuffer** read_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); // Clears the contents of the buffer object. After a call to this function // dvrReadBufferIsValid on the same buffer object returns 0. int dvrReadBufferClear(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); // Acquires the read buffer after it has been posted by the write buffer it is // connected to. int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd, void* meta, size_t meta_size_bytes); // Releases the read buffer, notifying the write buffer it is connected to. // Takes ownership of |release_fence_fd|. int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd); int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer); // TODO(eieio): Switch to return int and take an out parameter for the native // handle. const struct native_handle* dvrReadBufferGetNativeHandle( DvrReadBuffer* read_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(); // TODO(eieio): Switch to return int and take an out parameter for the native // handle. const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer); __END_DECLS #endif // ANDROID_DVR_BUFFER_H_ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/include/dvr/dvr_buffer_queue.h�������������������������������������������������������0100644 0000000 0000000 00000032513 13300556574 020402� 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); // @deprecated Please use dvrWriteBufferQueueGetANativeWindow instead. int dvrWriteBufferQueueGetExternalSurface(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); // @deprecated Please use dvrWriteBufferQueueGainBuffer instead. int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout, DvrWriteBuffer* out_buffer, int* out_fence_fd); // 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); // @deprecated Please use dvrReadBufferQueueAcquireBuffer instead. int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout, DvrReadBuffer* out_buffer, int* out_fence_fd, void* out_meta, size_t meta_size_bytes); // 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 13300556574 017170� 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 13300556574 021553� 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 00000006646 13300556574 017361� 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; typedef struct DvrVSyncClient DvrVSyncClient; 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); void dvrVSyncClientDestroy(DvrVSyncClient* client); __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); } void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(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; using UniqueDvrVSyncClient = 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 13300556574 021070� 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 00000004403 13300556574 020613� 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]; }; #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 13300556574 022772� 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 13300556574 022647� 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 13300556574 020225� 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 13300556574 016674� 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 13300556574 020713� 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 13300556574 017355� 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_vsync.h��������������������������������������������������������������0100644 0000000 0000000 00000002661 13300556574 017070� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_DVR_VSYNC_H_ #define ANDROID_DVR_VSYNC_H_ #include #include __BEGIN_DECLS typedef struct DvrVSyncClient DvrVSyncClient; // 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; // Creates a new client to the system vsync service. int dvrVSyncClientCreate(DvrVSyncClient** client_out); // Destroys the vsync client. void dvrVSyncClientDestroy(DvrVSyncClient* client); // Get the estimated timestamp of the next GPU lens warp preemption event in/ // ns. Also returns the corresponding vsync count that the next lens warp // operation will target. int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, int64_t* next_timestamp_ns, uint32_t* next_vsync_count); __END_DECLS #endif // ANDROID_DVR_VSYNC_H_ �������������������������������������������������������������������������������libs/vr/libdvr/tests/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 013624� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/Android.bp���������������������������������������������������������������������0100644 0000000 0000000 00000002612 13300556574 015525� 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. shared_libraries = [ "libbase", "libbinder", "libcutils", "libgui", "liblog", "libhardware", "libui", "libutils", "libnativewindow", ] static_libraries = [ "libdvr_static", "libbufferhubqueue", "libbufferhub", "libchrome", "libdvrcommon", "libdisplay", "libpdx_default_transport", "libbroadcastring", ] cc_test { srcs: [ "dvr_buffer_queue-test.cpp", "dvr_display_manager-test.cpp", "dvr_named_buffer-test.cpp", ], header_libs: ["libdvr_headers"], static_libs: static_libraries, shared_libs: shared_libraries, cflags: [ "-DLOG_TAG=\"dvr_api-test\"", "-DTRACE=0", "-Wno-missing-field-initializers", "-O0", "-g", ], name: "dvr_api-test", } ����������������������������������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp������������������������������������������������������0100644 0000000 0000000 00000043645 13300556574 020646� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #include #include #include #include #include #include #include #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 ::testing::Test { 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) { dvrWriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } } 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 = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); dvrWriteBufferQueueDestroy(write_queue_); write_queue_ = nullptr; } TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) { int ret = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_); ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); } TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) { int ret = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); dvrReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) { int ret = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue1 = nullptr; DvrReadBufferQueue* read_queue2 = nullptr; ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue1); ret = dvrReadBufferQueueCreateReadQueue(read_queue1, &read_queue2); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue2); ASSERT_NE(read_queue1, read_queue2); dvrReadBufferQueueDestroy(read_queue1); dvrReadBufferQueueDestroy(read_queue2); } TEST_F(DvrBufferQueueTest, GainBuffer) { int ret = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(ret, 0); DvrWriteBuffer* wb = nullptr; EXPECT_FALSE(dvrWriteBufferIsValid(wb)); DvrNativeBufferMetadata meta; int fence_fd = -1; ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta, &fence_fd); ASSERT_EQ(ret, 0); EXPECT_EQ(fence_fd, -1); EXPECT_NE(wb, nullptr); EXPECT_TRUE(dvrWriteBufferIsValid(wb)); } TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) { int ret = dvrWriteBufferQueueCreate( 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 = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(ret, 0); ASSERT_NE(read_queue, nullptr); dvrReadBufferQueueSetBufferAvailableCallback(read_queue, &BufferAvailableCallback, this); // Gain buffer for writing. ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta1, &fence_fd); ASSERT_EQ(ret, 0); ASSERT_NE(wb, nullptr); ASSERT_TRUE(dvrWriteBufferIsValid(wb)); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d", wb, fence_fd); android::base::unique_fd release_fence(fence_fd); // Post buffer to the read_queue. meta1.timestamp = 42; ret = dvrWriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1); ASSERT_EQ(ret, 0); ASSERT_FALSE(dvrWriteBufferIsValid(wb)); wb = nullptr; // Acquire buffer for reading. ret = dvrReadBufferQueueAcquireBuffer(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(dvrReadBufferIsValid(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); android::base::unique_fd acquire_fence(fence_fd); // Release buffer to the write_queue. ret = dvrReadBufferQueueReleaseBuffer(read_queue, rb, &meta2, /*release_fence_fd=*/-1); ASSERT_EQ(ret, 0); ASSERT_FALSE(dvrReadBufferIsValid(rb)); rb = nullptr; // TODO(b/34387835) Currently buffer allocation has to happen after all queues // are initialized. size_t capacity = dvrReadBufferQueueGetCapacity(read_queue); ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); dvrReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, GetANativeWindow) { int ret = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, write_queue_); ANativeWindow* window = nullptr; ret = dvrWriteBufferQueueGetANativeWindow(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 = dvrWriteBufferQueueCreate( 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 = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); dvrReadBufferQueueSetBufferRemovedCallback(read_queue, &BufferRemovedCallback, this); // Handle all pending events on the read queue. ret = dvrReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); size_t capacity = dvrReadBufferQueueGetCapacity(read_queue); ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity); ASSERT_EQ(kQueueCapacity, capacity); // Resize before dequeuing. constexpr uint32_t w1 = 10; ret = dvrWriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight); ASSERT_EQ(0, ret); // Gain first buffer for writing. All buffers will be resized. ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(dvrWriteBufferIsValid(wb1)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1); android::base::unique_fd release_fence1(fence_fd); // Check the buffer dimension. ret = dvrWriteBufferGetAHardwareBuffer(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 = dvrReadBufferQueueHandleEvents(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 = dvrWriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight); ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(dvrWriteBufferIsValid(wb2)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2, fence_fd); android::base::unique_fd release_fence2(fence_fd); // Check the buffer dimension, should be new width ret = dvrWriteBufferGetAHardwareBuffer(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 = dvrReadBufferQueueHandleEvents(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 = dvrWriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight); ASSERT_EQ(0, ret); // The next buffer we dequeued should have new width. ret = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3, &meta, &fence_fd); ASSERT_EQ(0, ret); ASSERT_TRUE(dvrWriteBufferIsValid(wb3)); ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3, fence_fd); android::base::unique_fd release_fence3(fence_fd); // Check the buffer dimension, should be new width ret = dvrWriteBufferGetAHardwareBuffer(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 = dvrReadBufferQueueHandleEvents(read_queue); ASSERT_EQ(0, ret); ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_); dvrReadBufferQueueDestroy(read_queue); } TEST_F(DvrBufferQueueTest, ReadQueueEventFd) { int ret = dvrWriteBufferQueueCreate( kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage, kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_); ASSERT_EQ(0, ret); DvrReadBufferQueue* read_queue = nullptr; ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue); ASSERT_EQ(0, ret); ASSERT_NE(nullptr, read_queue); int event_fd = dvrReadBufferQueueGetEventFd(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 = dvrWriteBufferQueueCreate( 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, dvrWriteBufferQueueCreateReadQueue(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 = dvrWriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10, &wbs[i], &metas[i], &fence_fd); ASSERT_EQ(ret, 0); ASSERT_LT(fence_fd, 0); // expect invalid fence. ASSERT_TRUE(dvrWriteBufferIsValid(wbs[i])); int buffer_id = dvrWriteBufferGetId(wbs[i]); ASSERT_GT(buffer_id, 0); AHardwareBuffer* hb = nullptr; ASSERT_EQ(0, dvrWriteBufferGetAHardwareBuffer(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(dvrWriteBufferIsValid(wbs[i])); metas[i].timestamp++; int ret = dvrWriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i], /*fence=*/-1); ASSERT_EQ(ret, 0); }; std::function Acquire = [&](size_t i) { int ret = dvrReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rbs[i], &metas[i], &fence_fd); ASSERT_EQ(ret, 0); ASSERT_LT(fence_fd, 0); // expect invalid fence. ASSERT_TRUE(dvrReadBufferIsValid(rbs[i])); int buffer_id = dvrReadBufferGetId(rbs[i]); ASSERT_GT(buffer_id, 0); AHardwareBuffer* hb = nullptr; ASSERT_EQ(0, dvrReadBufferGetAHardwareBuffer(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(dvrReadBufferIsValid(rbs[i])); int ret = dvrReadBufferQueueReleaseBuffer(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)); } } } } // namespace �������������������������������������������������������������������������������������������libs/vr/libdvr/tests/dvr_display_manager-test.cpp���������������������������������������������������0100644 0000000 0000000 00000074201 13300556574 021320� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#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 { 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)}; } 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)}; } 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)}; } 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 ::testing::Test { protected: void SetUp() override { 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(DvrDisplayManagerTest, 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(DvrDisplayManagerTest, 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; dvrWriteBufferCreateEmpty(&buffer); int fence_fd = -1; int error = dvrWriteBufferQueueDequeue(write_queue.get(), 1000, buffer, &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(DvrDisplayManagerTest, ConfigurationData) { // TODO(hendrikw): Move this out of the display manager tests. auto data1 = manager_->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 = manager_->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 00000022015 13300556574 020572� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#include #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, ret1); 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(0, 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(0, 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(0, 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(0, 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/libdvrcommon/�������������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 013673� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/Android.bp���������������������������������������������������������������������0100644 0000000 0000000 00000003006 13300556574 015572� 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", ] staticLibraries = ["libpdx_default_transport", "libbroadcastring"] headerLibraries = [ "libeigen", ] cc_library { local_include_dirs: localIncludeFiles, cflags: [ "-DLOG_TAG=\"libdvrcommon\"", "-DTRACE=0", ], 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", tags: ["optional"], srcs: testFiles, shared_libs: sharedLibraries, static_libs: [ "libgmock_main", "libgmock", "libgtest", "libdvrcommon", ] + staticLibraries, } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/�����������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 015316� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/���������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 016770� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/�����������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 017563� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/benchmark.h������������������������������������������������0100644 0000000 0000000 00000006205 13300556574 021666� 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 13300556574 021525� 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 13300556574 021021� 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 13300556574 021020� 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/epoll_file_descriptor.h������������������������������������0100644 0000000 0000000 00000002666 13300556574 024313� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_ #define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_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_create(64)); 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 // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_ ��������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/field_of_view.h��������������������������������������������0100644 0000000 0000000 00000006273 13300556574 022542� 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 13300556574 022233� 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 13300556574 022760� 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 13300556574 021367� 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 13300556574 021064� 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 13300556574 020710� 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 13300556574 021025� 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 00000004416 13300556574 022226� 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) = default; RingBuffer& operator=(const RingBuffer& other) = default; RingBuffer& operator=(RingBuffer&& other) = 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 13300556574 020542� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/include/private/dvr/test/test_macros.h�����������������������������������������0100644 0000000 0000000 00000011056 13300556574 023236� 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 13300556574 021103� 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 13300556574 015035� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libdvrcommon/tests/numeric_test.cpp���������������������������������������������������������0100644 0000000 0000000 00000003012 13300556574 020233� 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 13300556574 017547� 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 13300556574 012462� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/Android.bp���������������������������������������������������������������������������0100644 0000000 0000000 00000002243 13300556574 014363� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������cc_library_static { name: "libpdx", clang: true, cflags: [ "-Wall", "-Wextra", "-Werror", "-DLOG_TAG=\"libpdx\"", "-DTRACE=0", ], export_include_dirs: ["private"], local_include_dirs: ["private"], srcs: [ "client.cpp", "service.cpp", "service_dispatcher.cpp", "status.cpp", ], } 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 00000017651 13300556574 014453� 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(); } ///////////////////////////// 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 13300556574 015664� 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 13300556574 020237� 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 13300556574 015334� 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 13300556574 014134� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/�������������������������������������������������������������������������0040755 0000000 0000000 00000000000 13300556574 014727� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/channel_handle.h���������������������������������������������������������0100644 0000000 0000000 00000006336 13300556574 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; 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) : ChannelHandleBase{other.value_} { other.value_ = kEmptyHandle; } ~ChannelHandle() = default; ChannelHandle Duplicate() const { return ChannelHandle{value_}; } ChannelHandle& operator=(ChannelHandle&& other) { 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) : ChannelHandleBase{other.value_}, manager_{other.manager_} { other.manager_ = nullptr; other.value_ = kEmptyHandle; } ChannelHandle& operator=(ChannelHandle&& other) { 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/client.h�����������������������������������������������������������������0100644 0000000 0000000 00000024047 13300556574 016362� 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(); 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: 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 00000005137 13300556574 020051� 0����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ANDROID_PDX_CLIENT_CHANNEL_H_ #define ANDROID_PDX_CLIENT_CHANNEL_H_ #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 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; }; } // namespace pdx } // namespace android #endif // ANDROID_PDX_CLIENT_CHANNEL_H_ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/client_channel_factory.h�������������������������������������������������0100644 0000000 0000000 00000000720 13300556574 021571� 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 00000010507 13300556574 017332� 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) { 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) { 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 13300556574 020044� 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 13300556574 020115� 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 00000005164 13300556574 021062� 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_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)); }; } // 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 13300556574 022612� 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 13300556574 021056� 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 13300556574 021123� 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 13300556574 021461� 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 13300556574 015513� 5����������������������������������������������������������������������������������������������������ustar�00����������������������������������������������������������������0000000 0000000 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������libs/vr/libpdx/private/pdx/rpc/argument_encoder.h���������������������������������������������������0100644 0000000 0000000 00000013305 13300556574 021204� 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 00000006121 13300556574 020537� 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) { *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) { 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 00000012703 13300556574 020675� 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) { *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) { 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() {} BufferWrapper(const BufferType& buffer) : buffer_(buffer) {} BufferWrapper(const BufferType& buffer, const Allocator& allocator) : buffer_(buffer, allocator) {} BufferWrapper(BufferType&& buffer) : buffer_(std::move(buffer)) {} BufferWrapper(BufferType&& buffer, const Allocator& allocator) : buffer_(std::move(buffer), allocator) {} BufferWrapper(const BufferWrapper&) = default; BufferWrapper(BufferWrapper&&) = default; BufferWrapper& operator=(const BufferWrapper&) = default; BufferWrapper& operator=(BufferWrapper&&) = 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 13300556574 021344� 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 13300556574 024456 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 13300556574 017456 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 13300556574 020213 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 13300556574 020302 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 13300556574 021104 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