diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ec5fff5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +gen/ +bin/ +build/ +.gradle/ +.idea/ +user.gradle +local.properties +.directory +play-services-core/release + +.settings +.classpath +.project \ No newline at end of file diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..2f7c68b6 --- /dev/null +++ b/Android.mk @@ -0,0 +1,41 @@ +# Copyright (c) 2015 μg Project Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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_MODULE := GmsCore +LOCAL_MODULE_TAGS := optional +LOCAL_PACKAGE_NAME := GmsCore + +gmscore_root := $(LOCAL_PATH) +gmscore_dir := play-services-core +gmscore_out := $(TARGET_COMMON_OUT_ROOT)/obj/APPS/$(LOCAL_MODULE)_intermediates +gmscore_build := $(gmscore_dir)/build +gmscore_apk := $(gmscore_build)/outputs/apk/release/play-services-core-release-unsigned.apk + +$(gmscore_root)/$(gmscore_apk): + rm -Rf $(gmscore_root)/$(gmscore_build) + mkdir -p $(ANDROID_BUILD_TOP)/$(gmscore_out) + ln -s $(ANDROID_BUILD_TOP)/$(gmscore_out) $(ANDROID_BUILD_TOP)/$(gmscore_root)/$(gmscore_build) + echo "sdk.dir=$(ANDROID_HOME)" > $(gmscore_root)/local.properties + cd $(gmscore_root) && git submodule update --recursive --init + cd $(gmscore_root)/$(gmscore_dir) && JAVA_TOOL_OPTIONS="$(JAVA_TOOL_OPTIONS) -Dfile.encoding=UTF8" ../gradlew assembleRelease + +LOCAL_CERTIFICATE := platform +LOCAL_SRC_FILES := $(gmscore_apk) +LOCAL_MODULE_CLASS := APPS +LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) + +include $(BUILD_PREBUILT) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + 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 diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 00000000..4ed90b95 --- /dev/null +++ b/LICENSES/Apache-2.0.txt @@ -0,0 +1,208 @@ +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 + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 00000000..3f92dfc5 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,324 @@ +Creative Commons Attribution 4.0 International Creative Commons Corporation +("Creative Commons") is not a law firm and does not provide legal services +or legal advice. Distribution of Creative Commons public licenses does not +create a lawyer-client or other relationship. Creative Commons makes its licenses +and related information available on an "as-is" basis. Creative Commons gives +no warranties regarding its licenses, any material licensed under their terms +and conditions, or any related information. Creative Commons disclaims all +liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions +that creators and other rights holders may use to share original works of +authorship and other material subject to copyright and certain other rights +specified in the public license below. The following considerations are for +informational purposes only, are not exhaustive, and do not form part of our +licenses. + +Considerations for licensors: Our public licenses are intended for use by +those authorized to give the public permission to use material in ways otherwise +restricted by copyright and certain other rights. Our licenses are irrevocable. +Licensors should read and understand the terms and conditions of the license +they choose before applying it. Licensors should also secure all rights necessary +before applying our licenses so that the public can reuse the material as +expected. Licensors should clearly mark any material not subject to the license. +This includes other CC-licensed material, or material used under an exception +or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a licensor +grants the public permission to use the licensed material under specified +terms and conditions. If the licensor's permission is not necessary for any +reason–for example, because of any applicable exception or limitation to copyright–then +that use is not regulated by the license. Our licenses grant only permissions +under copyright and certain other rights that a licensor has authority to +grant. Use of the licensed material may still be restricted for other reasons, +including because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be marked +or described. Although not required by our licenses, you are encouraged to +respect those requests where reasonable. More considerations for the public +: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution +4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to +be bound by the terms and conditions of this Creative Commons Attribution +4.0 International Public License ("Public License"). To the extent this Public +License may be interpreted as a contract, You are granted the Licensed Rights +in consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the Licensor +receives from making the Licensed Material available under these terms and +conditions. + +Section 1 – Definitions. + +a. Adapted Material means material subject to Copyright and Similar Rights +that is derived from or based upon the Licensed Material and in which the +Licensed Material is translated, altered, arranged, transformed, or otherwise +modified in a manner requiring permission under the Copyright and Similar +Rights held by the Licensor. For purposes of this Public License, where the +Licensed Material is a musical work, performance, or sound recording, Adapted +Material is always produced where the Licensed Material is synched in timed +relation with a moving image. + +b. Adapter's License means the license You apply to Your Copyright and Similar +Rights in Your contributions to Adapted Material in accordance with the terms +and conditions of this Public License. + +c. Copyright and Similar Rights means copyright and/or similar rights closely +related to copyright including, without limitation, performance, broadcast, +sound recording, and Sui Generis Database Rights, without regard to how the +rights are labeled or categorized. For purposes of this Public License, the +rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. Effective Technological Measures means those measures that, in the absence +of proper authority, may not be circumvented under laws fulfilling obligations +under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, +and/or similar international agreements. + +e. Exceptions and Limitations means fair use, fair dealing, and/or any other +exception or limitation to Copyright and Similar Rights that applies to Your +use of the Licensed Material. + +f. Licensed Material means the artistic or literary work, database, or other +material to which the Licensor applied this Public License. + +g. Licensed Rights means the rights granted to You subject to the terms and +conditions of this Public License, which are limited to all Copyright and +Similar Rights that apply to Your use of the Licensed Material and that the +Licensor has authority to license. + +h. Licensor means the individual(s) or entity(ies) granting rights under this +Public License. + +i. Share means to provide material to the public by any means or process that +requires permission under the Licensed Rights, such as reproduction, public +display, public performance, distribution, dissemination, communication, or +importation, and to make material available to the public including in ways +that members of the public may access the material from a place and at a time +individually chosen by them. + +j. Sui Generis Database Rights means rights other than copyright resulting +from Directive 96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, as amended and/or succeeded, +as well as other essentially equivalent rights anywhere in the world. + +k. You means the individual or entity exercising the Licensed Rights under +this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + +1. Subject to the terms and conditions of this Public License, the Licensor +hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, +irrevocable license to exercise the Licensed Rights in the Licensed Material +to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + +2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions +and Limitations apply to Your use, this Public License does not apply, and +You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + +4. Media and formats; technical modifications allowed. The Licensor authorizes +You to exercise the Licensed Rights in all media and formats whether now known +or hereafter created, and to make technical modifications necessary to do +so. The Licensor waives and/or agrees not to assert any right or authority +to forbid You from making technical modifications necessary to exercise the +Licensed Rights, including technical modifications necessary to circumvent +Effective Technological Measures. For purposes of this Public License, simply +making modifications authorized by this Section 2(a)(4) never produces Adapted +Material. + + 5. Downstream recipients. + +A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed +Material automatically receives an offer from the Licensor to exercise the +Licensed Rights under the terms and conditions of this Public License. + +B. No downstream restrictions. You may not offer or impose any additional +or different terms or conditions on, or apply any Effective Technological +Measures to, the Licensed Material if doing so restricts exercise of the Licensed +Rights by any recipient of the Licensed Material. + +6. No endorsement. Nothing in this Public License constitutes or may be construed +as permission to assert or imply that You are, or that Your use of the Licensed +Material is, connected with, or sponsored, endorsed, or granted official status +by, the Licensor or others designated to receive attribution as provided in +Section 3(a)(1)(A)(i). + + b. Other rights. + +1. Moral rights, such as the right of integrity, are not licensed under this +Public License, nor are publicity, privacy, and/or other similar personality +rights; however, to the extent possible, the Licensor waives and/or agrees +not to assert any such rights held by the Licensor to the limited extent necessary +to allow You to exercise the Licensed Rights, but not otherwise. + +2. Patent and trademark rights are not licensed under this Public License. + +3. To the extent possible, the Licensor waives any right to collect royalties +from You for the exercise of the Licensed Rights, whether directly or through +a collecting society under any voluntary or waivable statutory or compulsory +licensing scheme. In all other cases the Licensor expressly reserves any right +to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following +conditions. + + a. Attribution. + +1. If You Share the Licensed Material (including in modified form), You must: + +A. retain the following if it is supplied by the Licensor with the Licensed +Material: + +i. identification of the creator(s) of the Licensed Material and any others +designated to receive attribution, in any reasonable manner requested by the +Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + +v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + +B. indicate if You modified the Licensed Material and retain an indication +of any previous modifications; and + +C. indicate the Licensed Material is licensed under this Public License, and +include the text of, or the URI or hyperlink to, this Public License. + +2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner +based on the medium, means, and context in which You Share the Licensed Material. +For example, it may be reasonable to satisfy the conditions by providing a +URI or hyperlink to a resource that includes the required information. + +3. If requested by the Licensor, You must remove any of the information required +by Section 3(a)(1)(A) to the extent reasonably practicable. + +4. If You Share Adapted Material You produce, the Adapter's License You apply +must not prevent recipients of the Adapted Material from complying with this +Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to +Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, +reuse, reproduce, and Share all or a substantial portion of the contents of +the database; + +b. if You include all or a substantial portion of the database contents in +a database in which You have Sui Generis Database Rights, then the database +in which You have Sui Generis Database Rights (but not its individual contents) +is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or +a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace +Your obligations under this Public License where the Licensed Rights include +other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. Unless otherwise separately undertaken by the Licensor, to the extent possible, +the Licensor offers the Licensed Material as-is and as-available, and makes +no representations or warranties of any kind concerning the Licensed Material, +whether express, implied, statutory, or other. This includes, without limitation, +warranties of title, merchantability, fitness for a particular purpose, non-infringement, +absence of latent or other defects, accuracy, or the presence or absence of +errors, whether or not known or discoverable. Where disclaimers of warranties +are not allowed in full or in part, this disclaimer may not apply to You. + +b. To the extent possible, in no event will the Licensor be liable to You +on any legal theory (including, without limitation, negligence) or otherwise +for any direct, special, indirect, incidental, consequential, punitive, exemplary, +or other losses, costs, expenses, or damages arising out of this Public License +or use of the Licensed Material, even if the Licensor has been advised of +the possibility of such losses, costs, expenses, or damages. Where a limitation +of liability is not allowed in full or in part, this limitation may not apply +to You. + +c. The disclaimer of warranties and limitation of liability provided above +shall be interpreted in a manner that, to the extent possible, most closely +approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights +licensed here. However, if You fail to comply with this Public License, then +Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section +6(a), it reinstates: + +1. automatically as of the date the violation is cured, provided it is cured +within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + +c. For the avoidance of doubt, this Section 6(b) does not affect any right +the Licensor may have to seek remedies for Your violations of this Public +License. + +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material +under separate terms or conditions or stop distributing the Licensed Material +at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or +conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed +Material not stated herein are separate from and independent of the terms +and conditions of this Public License. + +Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not +be interpreted to, reduce, limit, restrict, or impose conditions on any use +of the Licensed Material that could lawfully be made without permission under +this Public License. + +b. To the extent possible, if any provision of this Public License is deemed +unenforceable, it shall be automatically reformed to the minimum extent necessary +to make it enforceable. If the provision cannot be reformed, it shall be severed +from this Public License without affecting the enforceability of the remaining +terms and conditions. + +c. No term or condition of this Public License will be waived and no failure +to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation +upon, or waiver of, any privileges and immunities that apply to the Licensor +or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative +Commons may elect to apply one of its public licenses to material it publishes +and in those instances will be considered the "Licensor." The text of the +Creative Commons public licenses is dedicated to the public domain under the +CC0 Public Domain Dedication. Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as otherwise +permitted by the Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative Commons" +or any other trademark or logo of Creative Commons without its prior written +consent including, without limitation, in connection with any unauthorized +modifications to any of its public licenses or any other arrangements, understandings, +or agreements concerning use of licensed material. For the avoidance of doubt, +this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..2c713c7c --- /dev/null +++ b/build.gradle @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2013, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +buildscript { + ext.kotlinVersion = '1.8.20' + ext.coroutineVersion = '1.6.4' + + ext.annotationVersion = '1.6.0' + ext.appcompatVersion = '1.6.1' + ext.coreVersion = '1.9.0' + ext.collectionVersion = '1.3.0-dev01' + ext.fragmentVersion = '1.5.5' + ext.lifecycleVersion = '2.5.1' + ext.materialVersion = '1.8.0' + ext.navigationVersion = '2.5.3' + ext.preferenceVersion = '1.2.0' + ext.webkitVersion = '1.6.0' + + ext.circleimageviewVersion = '3.1.0' + ext.volleyVersion = '1.2.1' + ext.wireVersion = '4.5.5' + + + ext.androidBuildGradleVersion = '8.0.0' + ext.androidBuildVersionTools = '33.0.2' + + ext.androidMinSdk = 23 + ext.androidTargetSdk = 30 + ext.androidCompileSdk = 33 + + repositories { + mavenCentral() + google() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.0.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion" + } +} + + +def gmsVersion = "24.09.13" +def gmsVersionCode = Integer.parseInt(gmsVersion.replaceAll('\\.', '')) + +def ourVersionBase = "0.3.0" + +def ourVersionCode = gmsVersionCode * 1000 +def ourVersionName = "$ourVersionBase.$gmsVersionCode" + +allprojects { + apply plugin: 'idea' + + group = 'org.microg.gms' + version = ourVersionName + ext.appVersionCode = ourVersionCode + ext.isReleaseVersion = false +} + +subprojects { + repositories { + mavenCentral() + google() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..86630672 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +android.useAndroidX=true +org.gradle.jvmargs=-Xmx4096m -XX:+UseParallelGC +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..62d4c053 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e736c04d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jun 29 16:45:22 ICT 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-rc-3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..60cf9e8c --- /dev/null +++ b/gradlew @@ -0,0 +1,233 @@ +#!/bin/sh + +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" \ No newline at end of file diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/play-services-api/build.gradle b/play-services-api/build.gradle new file mode 100644 index 00000000..f78981e9 --- /dev/null +++ b/play-services-api/build.gradle @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' + +android { + compileSdk androidCompileSdk + buildToolsVersion '33.0.2' + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = "1.8" + } + namespace 'org.microg.gms.api' + buildFeatures { + aidl true + } +} + +dependencies { + api project(':play-services-basement') + api project(':play-services-iid-api') + api project(':play-services-base-api') +} \ No newline at end of file diff --git a/play-services-api/gradle.properties b/play-services-api/gradle.properties new file mode 100644 index 00000000..cac2a521 --- /dev/null +++ b/play-services-api/gradle.properties @@ -0,0 +1,33 @@ +# +# Copyright 2013-2016 microG Project Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +POM_NAME=Play Services Internal API +POM_DESCRIPTION=Interfaces and objects for IPC between Play Services Library and Play Services Core + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsApi + +POM_SCM_URL=https://github.com/microg/android_external_GmsApi +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl b/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl new file mode 100644 index 00000000..ae33a334 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl @@ -0,0 +1,23 @@ +package com.google.android.auth; + +import android.os.Bundle; +import android.accounts.Account; + +import com.google.android.gms.auth.AccountChangeEventsResponse; +import com.google.android.gms.auth.AccountChangeEventsRequest; +import com.google.android.gms.auth.GetHubTokenRequest; +import com.google.android.gms.auth.GetHubTokenInternalResponse; +import com.google.android.gms.auth.HasCababilitiesRequest; + +interface IAuthManagerService { + Bundle getToken(String accountName, String scope, in Bundle extras) = 0; + Bundle clearToken(String token, in Bundle extras) = 1; + AccountChangeEventsResponse getChangeEvents(in AccountChangeEventsRequest request) = 2; + + Bundle getTokenWithAccount(in Account account, String scope, in Bundle extras) = 4; + Bundle getAccounts(in Bundle extras) = 5; + Bundle removeAccount(in Account account) = 6; + Bundle requestGoogleAccountsAccess(String packageName) = 7; + int hasCapabilities(in HasCababilitiesRequest request) = 8; + GetHubTokenInternalResponse getHubToken(in GetHubTokenRequest request, in Bundle extras) = 9; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl new file mode 100644 index 00000000..206b1a9d --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable AccountChangeEventsRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl new file mode 100644 index 00000000..832e5859 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable AccountChangeEventsResponse; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl new file mode 100644 index 00000000..f73f91bb --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable GetHubTokenInternalResponse; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl new file mode 100644 index 00000000..74de2776 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable GetHubTokenRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl new file mode 100644 index 00000000..c0c080cd --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable HasCababilitiesRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl new file mode 100644 index 00000000..82536b93 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials; + +parcelable Credential; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl new file mode 100644 index 00000000..d000f32e --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials; + +parcelable CredentialRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl new file mode 100644 index 00000000..7fa0d4ab --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials.internal; + +parcelable DeleteRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl new file mode 100644 index 00000000..814f68da --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials.internal; + +parcelable GeneratePasswordRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl new file mode 100644 index 00000000..da248c33 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl @@ -0,0 +1,10 @@ +package com.google.android.gms.auth.api.credentials.internal; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.auth.api.credentials.Credential; + +interface ICredentialsCallbacks { + void onStatusAndCredential(in Status status, in Credential credential) = 0; + void onStatus(in Status status) = 1; + void onStatusAndString(in Status status, String string) = 2; +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl new file mode 100644 index 00000000..89c6c7e2 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl @@ -0,0 +1,15 @@ +package com.google.android.gms.auth.api.credentials.internal; + +import com.google.android.gms.auth.api.credentials.CredentialRequest; +import com.google.android.gms.auth.api.credentials.internal.ICredentialsCallbacks; +import com.google.android.gms.auth.api.credentials.internal.DeleteRequest; +import com.google.android.gms.auth.api.credentials.internal.GeneratePasswordRequest; +import com.google.android.gms.auth.api.credentials.internal.SaveRequest; + +interface ICredentialsService { + void request(ICredentialsCallbacks callbacks, in CredentialRequest request) = 0; + void save(ICredentialsCallbacks callbacks, in SaveRequest request) = 1; + void delete(ICredentialsCallbacks callbacks, in DeleteRequest request) = 2; + void disableAutoSignIn(ICredentialsCallbacks callbacks) = 3; + void generatePassword(ICredentialsCallbacks callbacks, in GeneratePasswordRequest request) = 4; +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl new file mode 100644 index 00000000..b5bcb593 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials.internal; + +parcelable SaveRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl new file mode 100644 index 00000000..ae8e79c5 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.auth.api.internal; + +import com.google.android.gms.auth.api.proxy.ProxyResponse; + +interface IAuthCallbacks { + void onProxyResponse(in ProxyResponse response) = 0; + void onSpatulaHeader(String spatulaHeader) = 1; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl new file mode 100644 index 00000000..a5f6a537 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl @@ -0,0 +1,11 @@ +package com.google.android.gms.auth.api.internal; + +import com.google.android.gms.auth.api.internal.IAuthCallbacks; +//import com.google.android.gms.auth.api.proxy.ProxyGrpcRequest; +import com.google.android.gms.auth.api.proxy.ProxyRequest; + +interface IAuthService { + void performProxyRequest(IAuthCallbacks callbacks, in ProxyRequest request) = 0; +// void performProxyGrpcRequest(IAuthCallback callbacks, in ProxyGrpcRequest request) = 1; + void getSpatulaHeader(IAuthCallbacks callbacks) = 2; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl new file mode 100644 index 00000000..fa5885f8 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.proxy; + +parcelable ProxyRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl new file mode 100644 index 00000000..eae21f3c --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.proxy; + +parcelable ProxyResponse; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl new file mode 100644 index 00000000..ee7f7338 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl @@ -0,0 +1,6 @@ +package com.google.android.gms.auth.appcert; + +interface IAppCertService { + boolean fetchDeviceKey() = 0; + String getSpatulaHeader(String packageName) = 1; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl new file mode 100644 index 00000000..973de92f --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.checkin.internal; + +interface ICheckinService { + String getDeviceDataVersionInfo(); + long getLastCheckinSuccessTime(); + String getLastSimOperator(); +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl new file mode 100644 index 00000000..ae02e068 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.internal; + +parcelable GoogleCertificatesQuery; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl new file mode 100644 index 00000000..19839d7d --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.dynamic.IObjectWrapper; + +interface ICertData { + IObjectWrapper getWrappedBytes(); + int remoteHashCode(); +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl new file mode 100644 index 00000000..da539f32 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl @@ -0,0 +1,12 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.common.internal.GoogleCertificatesQuery; +import com.google.android.gms.dynamic.IObjectWrapper; + +interface IGoogleCertificatesApi { + IObjectWrapper getGoogleCertficates(); + IObjectWrapper getGoogleReleaseCertificates(); + boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData); + boolean isGoogleSigned(String packageName, IObjectWrapper certData); + boolean isGoogleOrPlatformSigned(in GoogleCertificatesQuery query, IObjectWrapper packageManager); +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl new file mode 100644 index 00000000..f6c53996 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.dynamic.IObjectWrapper; + +interface ISignInButtonCreator { + IObjectWrapper createSignInButton(IObjectWrapper context, int size, int color); // returns View +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl new file mode 100644 index 00000000..69b856bc --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.server; + +parcelable FavaDiagnosticsEntity; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl new file mode 100644 index 00000000..58ea0ba5 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.http; + +import android.os.Bundle; + +interface IGoogleHttpService { + Bundle checkUrl(String url); +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl new file mode 100644 index 00000000..75db9fae --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl @@ -0,0 +1,12 @@ +package com.google.android.gms.people.internal; + +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +import com.google.android.gms.common.data.DataHolder; + +interface IPeopleCallbacks { + void onDataHolder(int code, in Bundle resolution, in DataHolder holder) = 1; + void onDataHolders(int code, in Bundle resolution, in DataHolder[] holders) = 3; + void onParcelFileDescriptor(int code, in Bundle resolution, in ParcelFileDescriptor fileDescriptor, in Bundle extras) = 4; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl new file mode 100644 index 00000000..62ae1478 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl @@ -0,0 +1,44 @@ +package com.google.android.gms.people.internal; + +import android.os.Bundle; + +import com.google.android.gms.people.internal.IPeopleCallbacks; +import com.google.android.gms.common.server.FavaDiagnosticsEntity; +import com.google.android.gms.common.internal.ICancelToken; + +interface IPeopleService { + // void loadOwners1(IPeopleCallbacks var1, boolean var2, boolean var3, String var4, String var5); + // void loadCirclesOld(IPeopleCallbacks var1, String var2, String var3, String var4, int var5, String var6); + // void loadPeopleOld(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, int var6, boolean var7, long var8); + // void loadAvatarLegacy(IPeopleCallbacks var1, String var2, int var3, int var4); + // void loadContactImageLegacy(IPeopleCallbacks var1, long var2, boolean var4); + // void blockPerson(IPeopleCallbacks var1, String var2, String var3, String var4, boolean var5); + // Bundle syncRawContact(in Uri var1); + // void loadPeopleForAggregation8(IPeopleCallbacks var1, String var2, String var3, String var4, boolean var5, int var6); + // void setSyncToContactsSettings(IPeopleCallbacks var1, String var2, boolean var3, in String[] var4); + + // Bundle startSync(String var1, String var2); + // void requestSync(IPeopleCallbacks var1, String var2, String var3, in Uri var4); + // void updatePersonCirclesOld(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, in List var6); + // boolean isSyncToContactsEnabled(); + // Bundle requestSyncOld(String var1, String var2); + // void setAvatar(IPeopleCallbacks var1, String var2, String var3, in Uri var4, boolean var5); + // void loadCircles(IPeopleCallbacks var1, String var2, String var3, String var4, int var5, String var6, boolean var7); + // Bundle requestSyncOld19(String var1, String var2, long var3); + // void loadPeople20(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, int var6, boolean var7, long var8, String var10, int var11); + // void loadPeopleLive(IPeopleCallbacks var1, String var2, String var3, String var4, int var5, String var6); + // void updatePersonCircles(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, in List var6, in FavaDiagnosticsEntity var7); + // void loadRemoteImageLegacy(IPeopleCallbacks var1, String var2); + // void loadContactsGaiaIds24(IPeopleCallbacks var1, String var2, String var3); + // Bundle requestSyncOld25(String var1, String var2, long var3, boolean var5); + // void addCircle(IPeopleCallbacks var1, String var2, String var3, String var4, String var5); + // void addPeopleToCircle(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5); + + Bundle registerDataChangedListener(IPeopleCallbacks callbacks, boolean register, String var3, String var4, int scopes) = 10; + void loadCircles(IPeopleCallbacks callbacks, String account, String pageGaiaId, String circleId, int type, String var6, boolean var7) = 18; + Bundle requestSync(String account, String var2, long var3, boolean var5, boolean var6) = 204; + void loadOwners(IPeopleCallbacks callbacks, boolean var2, boolean var3, String account, String var5, int sortOrder) = 304; + void loadPeopleForAggregation(IPeopleCallbacks callbacks, String account, String var3, String filter, int var5, boolean var6, int var7, int var8, String var9, boolean var10, int var11, int var12) = 401; + ICancelToken loadOwnerAvatar(IPeopleCallbacks callbacks, String account, String pageId, int size, int flags) = 504; + ICancelToken loadAutocompleteList(IPeopleCallbacks callbacks, String account, String pageId, boolean directorySearch, String var5, String query, int autocompleteType, int var8, int numberOfResults, boolean var10) = 506; +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java new file mode 100644 index 00000000..d9a19568 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountChangeEvent extends AutoSafeParcelable { + public static Creator CREATOR = new AutoCreator<>(AccountChangeEvent.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java new file mode 100644 index 00000000..cf8561df --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import static org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT_TYPE; + +import android.accounts.Account; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountChangeEventsRequest extends AutoSafeParcelable { + @Field(3) + @Deprecated + private final String accountName; + @Field(4) + private final Account account; + + public AccountChangeEventsRequest(String accountName, Account account) { + this.accountName = accountName; + this.account = account; + } + + public Account getAccount() { + if (account != null) return account; + if (accountName != null) return new Account(accountName, DEFAULT_ACCOUNT_TYPE); + return null; + } + + public static Creator CREATOR = new AutoCreator<>(AccountChangeEventsRequest.class); + +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java new file mode 100644 index 00000000..10491379 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountChangeEventsResponse extends AutoSafeParcelable { + + public AccountChangeEventsResponse() { + } + + public static Creator CREATOR = new AutoCreator<>(AccountChangeEventsResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java new file mode 100644 index 00000000..4845ed41 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GetHubTokenInternalResponse extends AutoSafeParcelable { + @Field(2) + public String status; + public static final Creator CREATOR = new AutoCreator<>(GetHubTokenInternalResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java new file mode 100644 index 00000000..2c4e973c --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GetHubTokenRequest extends AutoSafeParcelable { + @Field(1) + public String accountName; + @Field(2) + public String service; + @Field(3) + public String packageName; + public static final Creator CREATOR = new AutoCreator<>(GetHubTokenRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java new file mode 100644 index 00000000..c9907fa8 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth; + +import android.accounts.Account; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class HasCababilitiesRequest extends AutoSafeParcelable { + @Field(1) + public Account account; + public static final Creator CREATOR = new AutoCreator<>(HasCababilitiesRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java b/play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java new file mode 100644 index 00000000..df3768c8 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import com.google.android.gms.common.api.Scope; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +import java.util.List; + +public class TokenData extends AutoSafeParcelable { + + @SafeParceled(2) + public final String token; + + @SafeParceled(3) + public final Long expiry; + + @SafeParceled(5) + public final boolean isOAuth; + + @SafeParceled(value = 6, subClass = Scope.class) + public final List scopes; + + public TokenData() { + token = null; + expiry = null; + isOAuth = false; + scopes = null; + } + + public TokenData(String token, Long expiry, boolean isOAuth, List scopes) { + this.token = token; + this.expiry = expiry; + this.isOAuth = isOAuth; + this.scopes = scopes; + } + + public TokenData(String token, Long expiry) { + this.token = token; + this.expiry = expiry; + this.isOAuth = false; + this.scopes = null; + } + + public static final Creator CREATOR = new AutoCreator<>(TokenData.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java new file mode 100644 index 00000000..9ade5cc0 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java @@ -0,0 +1,160 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import android.net.Uri; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +@PublicApi +public class Credential extends AutoSafeParcelable { + + @Field(1) + private String id; + @Field(2) + private String name; + @Field(3) + private Uri profilePictureUri; + @Field(value = 4, subClass = IdToken.class) + private List tokens; + @Field(5) + private String password; + @Field(6) + private String accountType; + @Field(7) + private String generatedPassword; + + private Credential() { + } + + /** + * Returns the type of federated identity account used to sign in the user. While this may be + * any string, it is strongly recommended that values from {@link com.google.android.gms.auth.api.credentials.IdentityProviders} + * are used, which are the login domains for common identity providers. + * + * @return A string identifying the federated identity provider associated with this account, + * typically in the form of the identity provider's login domain. null will be returned if the + * credential is a password credential. + */ + public String getAccountType() { + return accountType; + } + + /** + * Returns the credential identifier, typically an email address or user name, though it may + * also be some encoded unique identifier for a federated identity account. + */ + public String getId() { + return id; + } + + /** + * Returns the display name of the credential, if available. Typically, the display name will + * be the name of the user, or some other string which the user can easily recognize and + * distinguish from other accounts they may have. + */ + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Credential)) return false; + + Credential that = (Credential) o; + + if (!Objects.equals(id, that.id)) return false; + if (!Objects.equals(name, that.name)) return false; + if (!Objects.equals(profilePictureUri, that.profilePictureUri)) + return false; + if (!Objects.equals(password, that.password)) + return false; + if (!Objects.equals(accountType, that.accountType)) + return false; + return Objects.equals(generatedPassword, that.generatedPassword); + + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{id, name, profilePictureUri, password, accountType, generatedPassword}); + } + + public static class Builder { + private final String id; + private String name; + private final Uri profilePictureUri; + private final String password; + private String accountType; + + @PublicApi(exclude = true) + public List tokens; + @PublicApi(exclude = true) + private final String generatedPassword; + + /** + * Copies the information stored in an existing credential, in order to allow that information to be modified. + * + * @param credential the existing credential + */ + public Builder(Credential credential) { + this.id = credential.id; + this.name = credential.name; + this.profilePictureUri = credential.profilePictureUri; + this.password = credential.password; + this.accountType = credential.accountType; + this.tokens = credential.tokens; + this.generatedPassword = credential.generatedPassword; + } + + public Credential build() { + Credential credential = new Credential(); + credential.id = id; + credential.name = name; + credential.profilePictureUri = profilePictureUri; + credential.password = password; + credential.accountType = accountType; + credential.tokens = tokens; + credential.generatedPassword = generatedPassword; + return credential; + } + + /** + * Specifies the account type for a federated credential. The value should be set to + * identity provider's login domain, such as "https://accounts.google.com" for Google + * accounts. The login domains for common identity providers are listed in {@link IdentityProviders}. + * + * @param accountType The type of the account. Typically, one of the values in {@link IdentityProviders}. + */ + public Builder setAccountType(String accountType) { + this.accountType = accountType; + return this; + } + + /** + * Sets the display name for the credential, which should be easy for the user to recognize + * as associated to the credential, and distinguishable from other credentials they may + * have. This string will be displayed more prominently than, or instead of, the account ID + * whenever available. In most cases, the name of the user is sufficient. + */ + public Builder setName(String name) { + this.name = name; + return this; + } + + } + + public static final Creator CREATOR = new AutoCreator<>(Credential.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java new file mode 100644 index 00000000..41ca2191 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +@PublicApi +public class CredentialPickerConfig extends AutoSafeParcelable { + + @Field(1) + private final boolean showAddAccountButton; + @Field(2) + private final boolean showCancelButton; + @Field(3) + private final boolean forNewAccount; + + public CredentialPickerConfig(boolean showAddAccountButton, boolean showCancelButton, boolean forNewAccount) { + this.showAddAccountButton = showAddAccountButton; + this.showCancelButton = showCancelButton; + this.forNewAccount = forNewAccount; + } + + /** + * @deprecated It was determined that this method was not useful for developers. + */ + @Deprecated + public boolean isForNewAccount() { + return forNewAccount; + } + + @NonNull + @Override + public String toString() { + return "CredentialPickerConfig{" + + "showAddAccountButton=" + showAddAccountButton + + ", showCancelButton=" + showCancelButton + + '}'; + } + + public static class Builder { + private final boolean showAddAccountButton; + private final boolean showCancelButton; + private final boolean forNewAccount; + + public Builder(boolean showAddAccountButton, boolean showCancelButton, boolean forNewAccount) { + this.showAddAccountButton = showAddAccountButton; + this.showCancelButton = showCancelButton; + this.forNewAccount = forNewAccount; + } + + public CredentialPickerConfig build() { + return new CredentialPickerConfig(showAddAccountButton, showCancelButton, forNewAccount); + } + + } + + public static final Creator CREATOR = new AutoCreator<>(CredentialPickerConfig.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java new file mode 100644 index 00000000..c4961d14 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class CredentialRequest extends AutoSafeParcelable { + + @Field(1) + private final boolean passwordLoginSupported; + + public CredentialRequest(boolean passwordLoginSupported) { + this.passwordLoginSupported = passwordLoginSupported; + } + + /** + * @deprecated Use {@link #isPasswordLoginSupported()} + */ + @Deprecated + public boolean getSupportsPasswordLogin() { + return isPasswordLoginSupported(); + } + + public boolean isPasswordLoginSupported() { + return passwordLoginSupported; + } + + public static final Creator CREATOR = new AutoCreator(CredentialRequest.class); + + public static class Builder { + + public Builder() { + } + + public void setAccountTypes(String... accountTypes) { + accountTypes.clone(); + } + } +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java new file mode 100644 index 00000000..3f9336f4 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +import java.util.Arrays; + +/** + * Parameters for requesting the display of the hint picker, via {@link CredentalsApi#getHintPickerIntent()}. + * Instances can be created using {@link HintRequest.Builder}. + */ +@PublicApi +public class HintRequest extends AutoSafeParcelable { + + @Field(1) + private final CredentialPickerConfig hintPickerConfig; + @Field(2) + private final boolean emailAddressIdentifierSupported; + @Field(3) + private final boolean phoneNumberIdentifierSupported; + @Field(4) + private final String[] accountTypes; + @Field(5) + private final boolean idTokenRequested; + @Field(6) + private final String serverClientId; + @Field(7) + private final String idTokenNonce; + + public HintRequest(CredentialPickerConfig hintPickerConfig, boolean emailAddressIdentifierSupported, boolean phoneNumberIdentifierSupported, String[] accountTypes, boolean idTokenRequested, String serverClientId, String idTokenNonce) { + this.hintPickerConfig = hintPickerConfig; + this.emailAddressIdentifierSupported = emailAddressIdentifierSupported; + this.phoneNumberIdentifierSupported = phoneNumberIdentifierSupported; + this.accountTypes = accountTypes; + this.idTokenRequested = idTokenRequested; + this.serverClientId = serverClientId; + this.idTokenNonce = idTokenNonce; + } + + public static final Creator CREATOR = new AutoCreator<>(HintRequest.class); + + @NonNull + @Override + public String toString() { + return "HintRequest{" + + "hintPickerConfig=" + hintPickerConfig + + ", emailAddressIdentifierSupported=" + emailAddressIdentifierSupported + + ", phoneNumberIdentifierSupported=" + phoneNumberIdentifierSupported + + ", accountTypes=" + Arrays.toString(accountTypes) + + ", idTokenRequested=" + idTokenRequested + + ", serverClientId='" + serverClientId + '\'' + + ", idTokenNonce='" + idTokenNonce + '\'' + + '}'; + } + + public static class Builder { + private final CredentialPickerConfig hintPickerConfig; + private final boolean emailAddressIdentifierSupported; + private final boolean phoneNumberIdentifierSupported; + private final String[] accountTypes; + private final String serverClientId; + private final String idTokenNonce; + + public Builder(CredentialPickerConfig hintPickerConfig, boolean emailAddressIdentifierSupported, boolean phoneNumberIdentifierSupported, String[] accountTypes, String serverClientId, String idTokenNonce) { + this.hintPickerConfig = hintPickerConfig; + this.emailAddressIdentifierSupported = emailAddressIdentifierSupported; + this.phoneNumberIdentifierSupported = phoneNumberIdentifierSupported; + this.accountTypes = accountTypes; + this.serverClientId = serverClientId; + this.idTokenNonce = idTokenNonce; + } + + /** + * Builds a {@link HintRequest}. + */ + public HintRequest build() { + boolean idTokenRequested = true; + return new HintRequest(hintPickerConfig, emailAddressIdentifierSupported, phoneNumberIdentifierSupported, accountTypes, idTokenRequested, serverClientId, idTokenNonce); + } + + } +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java new file mode 100644 index 00000000..5d84423e --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +@PublicApi +public class IdToken extends AutoSafeParcelable { + + @Field(1) + private final String accountType; + + @Field(2) + private final String id; + + public IdToken(String accountType, String id) { + this.accountType = accountType; + this.id = id; + } + + /** + * Returns {@code AccountManager} account type for the token. + */ + public String getAccountType() { + return accountType; + } + + public static final Creator CREATOR = new AutoCreator<>(IdToken.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java new file mode 100644 index 00000000..1d974993 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +/** + * Identity provider constants for use with {@link CredentialRequest.Builder#setAccountTypes(String...)} + */ +public final class IdentityProviders { +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java new file mode 100644 index 00000000..e560ddc5 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.api.credentials.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class DeleteRequest extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(DeleteRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java new file mode 100644 index 00000000..5fd6fcb7 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.api.credentials.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GeneratePasswordRequest extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(GeneratePasswordRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java new file mode 100644 index 00000000..24a9d92f --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.api.credentials.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class SaveRequest extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(SaveRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java new file mode 100644 index 00000000..dc1fbd39 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.proxy; + +import androidx.annotation.NonNull; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ProxyRequest extends AutoSafeParcelable { + + @Field(1) + public String url; + + @NonNull + @Override + public String toString() { + return url; + } + + public static final Creator CREATOR = new AutoCreator<>(ProxyRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java new file mode 100644 index 00000000..263d2e20 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.proxy; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ProxyResponse extends AutoSafeParcelable { + @Field(1) + public int gmsStatusCode; + + public static final Creator CREATOR = new AutoCreator<>(ProxyResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java new file mode 100644 index 00000000..1c267ed1 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.accounts.Account; + +import org.microg.gms.auth.AuthConstants; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class AccountRecoveryGuidanceRequest extends AutoSafeParcelable { + + @SafeParceled(2) + @Deprecated + public final String accountName; + @SafeParceled(3) + public final Account account; + + public AccountRecoveryGuidanceRequest(String accountName) { + this.accountName = accountName; + this.account = new Account(accountName, AuthConstants.DEFAULT_ACCOUNT_TYPE); + } + + public AccountRecoveryGuidanceRequest(Account account) { + this.accountName = account.name; + this.account = account; + } + + public static final Creator CREATOR = new AutoCreator<>(AccountRecoveryGuidanceRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java new file mode 100644 index 00000000..dc90a7d0 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.accounts.Account; + +import org.microg.gms.auth.AuthConstants; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class AccountRemovalRequest extends AutoSafeParcelable { + + @SafeParceled(2) + @Deprecated + public final String accountName; + @SafeParceled(3) + public final Account account; + + public AccountRemovalRequest(String accountName) { + this.accountName = accountName; + this.account = new Account(accountName, AuthConstants.DEFAULT_ACCOUNT_TYPE); + } + + public AccountRemovalRequest(Account account) { + this.accountName = account.name; + this.account = account; + } + + public static final Creator CREATOR = new AutoCreator(AccountRemovalRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java new file mode 100644 index 00000000..9edad8b2 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ConfirmCredentialsRequest extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(ConfirmCredentialsRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java new file mode 100644 index 00000000..73062668 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.accounts.Account; +import android.os.Bundle; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +// TODO +public class TokenRequest extends AutoSafeParcelable{ + + @SafeParceled(3) + public String accountName; + @SafeParceled(4) + public Bundle extras; + @SafeParceled(9) + public String consent; + @SafeParceled(15) + public String accountType; + + public Account getAccount() { + return new Account(accountName, accountType); + } + + public static final Creator CREATOR = new AutoCreator<>(TokenRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java new file mode 100644 index 00000000..b8e5ed63 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.delegate; + +import android.accounts.Account; +import android.os.Bundle; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class ConfirmCredentialsWorkflowRequest extends AutoSafeParcelable { + @SafeParceled(2) + public String accountName; + @SafeParceled(4) + public Bundle extras; + @SafeParceled(5) + public Account account; + + public static final Creator CREATOR = new AutoCreator<>(ConfirmCredentialsWorkflowRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java new file mode 100644 index 00000000..67e7c7db --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.shared; + +import android.accounts.Account; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +// TODO +public class AccountCredentials extends AutoSafeParcelable { + @SafeParceled(3) + public String accountName; + @SafeParceled(9) + public String accountType; + + public Account getAccount() { + return new Account(accountName, accountType); + } + + public static final Creator CREATOR = new AutoCreator<>(AccountCredentials.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java new file mode 100644 index 00000000..1ccdf4f6 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.shared; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AppDescription extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(AppDescription.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java new file mode 100644 index 00000000..ce1d3f61 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.shared; + +import org.microg.safeparcel.AutoSafeParcelable; + +// TODO +public class CaptchaSolution extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(CaptchaSolution.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java b/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java new file mode 100644 index 00000000..62b1abfd --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import android.os.RemoteException; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; + +import java.util.Arrays; + +public class CertData extends ICertData.Stub { + private final byte[] bytes; + private final int hashCode; + + public CertData(byte[] bytes) { + this.bytes = bytes; + if (bytes.length < 25) throw new RuntimeException("CertData to small"); + hashCode = Arrays.hashCode(Arrays.copyOfRange(bytes, 0, 25)); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ICertData)) return false; + ICertData cert = (ICertData) obj; + try { + if (cert.remoteHashCode() != hashCode()) return false; + return Arrays.equals(ObjectWrapper.unwrapTyped(cert.getWrappedBytes(), byte[].class), getBytes()); + } catch (RemoteException e) { + return false; + } + } + + public byte[] getBytes() { + return bytes; + } + + @Override + public IObjectWrapper getWrappedBytes() { + return ObjectWrapper.wrap(getBytes()); + } + + @Override + public int remoteHashCode() { + return hashCode(); + } +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java b/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java new file mode 100644 index 00000000..02dcc3e9 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import android.os.IBinder; +import android.os.RemoteException; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class GoogleCertificatesQuery extends AutoSafeParcelable { + @SafeParceled(1) + private final String packageName; + @SafeParceled(2) + private final IBinder certDataBinder; + private CertData certData; + @SafeParceled(3) + private final boolean allowNonRelease; + + public GoogleCertificatesQuery(String packageName, IBinder certDataBinder, boolean allowNonRelease) { + this.packageName = packageName; + this.certDataBinder = certDataBinder; + this.allowNonRelease = allowNonRelease; + } + + public String getPackageName() { + return packageName; + } + + public CertData getCertData() { + if (certData == null && certDataBinder != null) { + ICertData iCertData = null; + if (certDataBinder instanceof CertData) { + certData = (CertData) certDataBinder; + } else if (certDataBinder instanceof IObjectWrapper) { + certData = ObjectWrapper.unwrapTyped((IObjectWrapper) certDataBinder, CertData.class); + if (certData == null) { + byte[] bytes = ObjectWrapper.unwrapTyped((IObjectWrapper) certDataBinder, byte[].class); + if (bytes != null) { + certData = new CertData(bytes); + } + } + if (certData == null) { + iCertData = ObjectWrapper.unwrapTyped((IObjectWrapper) certDataBinder, ICertData.class); + } + } else if (certDataBinder instanceof ICertData) { + iCertData = (ICertData) certDataBinder; + } + if (iCertData != null) { + try { + byte[] bytes = ObjectWrapper.unwrapTyped(iCertData.getWrappedBytes(), byte[].class); + if (bytes != null) { + certData = new CertData(bytes); + } + } catch (RemoteException e) { + // Ignore + } + } + } + return certData; + } + + public static final Creator CREATOR = new AutoCreator(GoogleCertificatesQuery.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java b/play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java new file mode 100644 index 00000000..d32e668b --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.server; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class FavaDiagnosticsEntity extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(FavaDiagnosticsEntity.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java b/play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java new file mode 100644 index 00000000..64322959 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.people.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ParcelableLoadImageOptions extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(ParcelableLoadImageOptions.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java b/play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java new file mode 100644 index 00000000..dfb64544 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.people.model; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountMetadata extends AutoSafeParcelable { + + public static Creator CREATOR = new AutoCreator<>(AccountMetadata.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java b/play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java new file mode 100644 index 00000000..6e3da8e2 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.people.model; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AvatarReference extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(AvatarReference.class); + + public AvatarReference() { + } +} diff --git a/play-services-base-api/build.gradle b/play-services-base-api/build.gradle new file mode 100644 index 00000000..59dc1be7 --- /dev/null +++ b/play-services-base-api/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' + +dependencies { + api project(':play-services-basement') +} + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + aidlPackageWhiteList "com/google/android/gms/common/data/DataHolder.aidl" + aidlPackageWhiteList "com/google/android/gms/common/images/WebImage.aidl" + aidlPackageWhiteList "com/google/android/gms/common/api/internal/IStatusCallback.aidl" + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + namespace 'org.microg.gms.base.api' + buildFeatures { + aidl true + } +} \ No newline at end of file diff --git a/play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl b/play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl new file mode 100644 index 00000000..6355a13b --- /dev/null +++ b/play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.common.api.internal; + +import com.google.android.gms.common.api.Status; + +interface IStatusCallback { + void onResult(in Status status); +} diff --git a/play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl b/play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl new file mode 100644 index 00000000..3bbd1174 --- /dev/null +++ b/play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.data; + +parcelable DataHolder; diff --git a/play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl b/play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl new file mode 100644 index 00000000..e31f15ab --- /dev/null +++ b/play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.images; + +parcelable WebImage; diff --git a/play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java b/play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java new file mode 100644 index 00000000..031c5b37 --- /dev/null +++ b/play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.signin; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GoogleSignInAccount extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(GoogleSignInAccount.class); +} diff --git a/play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java b/play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java new file mode 100644 index 00000000..77acbb1b --- /dev/null +++ b/play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.data; + +import android.annotation.SuppressLint; +import android.database.Cursor; +import android.database.CursorWindow; +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Class for accessing collections of data, organized into columns. This provides the backing + * support for DataBuffer. Much like a cursor, the holder supports the notion of a current + * position, and has methods for extracting various types of data from named columns. + */ +@PublicApi(until = "1") +public class DataHolder extends AutoSafeParcelable implements Closeable { + @SafeParceled(1) + private final String[] columns; + + @SafeParceled(2) + private final CursorWindow[] windows; + + @SafeParceled(3) + private final int statusCode; + + @SafeParceled(4) + private final Bundle metadata; + + private boolean closed = false; + private Map columnIndices; + + protected static final int FIELD_TYPE_NULL = 0; + protected static final int FIELD_TYPE_INTEGER = 1; + protected static final int FIELD_TYPE_FLOAT = 2; + protected static final int FIELD_TYPE_STRING = 3; + protected static final int FIELD_TYPE_BLOB = 4; + + /** + * Creates a data holder with the specified data. + * + * @param columns The column names corresponding to the data in the given windows. + * @param windows The {@link CursorWindow} instances holding the data. + * @param statusCode The status code of this {@link DataHolder}. + * @param metadata The metadata associated with this {@link DataHolder} (may be null). + */ + public DataHolder(String[] columns, CursorWindow[] windows, int statusCode, Bundle metadata) { + this.columns = columns; + this.windows = windows; + this.statusCode = statusCode; + this.metadata = metadata; + validateContents(); + } + + /** + * Creates a data holder wrapping the provided cursor, with provided status code and metadata. + * + * @param cursor The cursor containing the data. + * @param statusCode The status code of this {@link DataHolder}. + * @param metadata The metadata associated with this {@link DataHolder} (may be null). + */ + public DataHolder(Cursor cursor, int statusCode, Bundle metadata) { + this(cursor.getColumnNames(), createCursorWindows(cursor), statusCode, metadata); + } + + /** + * Get a {@link DataHolder.Builder} to create a new {@link DataHolder} manually. + * + * @param columns The array of column names that the object supports. + * @return {@link DataHolder.Builder} object to work with. + */ + public static Builder builder(String[] columns) { + return new Builder(columns); + } + + @SuppressLint({"NewApi", "ObsoleteSdkInt"}) + static int getCursorType(Cursor cursor, int i) { + return cursor.getType(i); + } + + /** + * Closes the data holder, releasing all of its resources and making it completely invalid. + */ + @Override + public void close() { + synchronized (this) { + if (!closed) { + closed = true; + for (CursorWindow window : windows) { + window.close(); + } + } + } + } + + @SuppressWarnings("deprecation") + private static CursorWindow[] createCursorWindows(Builder builder) { + if (builder.columns.length == 0) return new CursorWindow[0]; + List windows = new ArrayList<>(); + try { + CursorWindow current = null; + for (int rowIndex = 0; rowIndex < builder.rows.size(); rowIndex++) { + Map row = builder.rows.get(rowIndex); + if (current == null || !current.allocRow()) { + current = new CursorWindow(false); + current.setStartPosition(rowIndex); + current.setNumColumns(builder.columns.length); + windows.add(current); + if (!current.allocRow()) { + windows.remove(current); + return windows.toArray(new CursorWindow[0]); + } + } + for (int columnIndex = 0; columnIndex < builder.columns.length; columnIndex++) { + Object val = row.get(builder.columns[columnIndex]); + if (val == null) { + current.putNull(rowIndex, columnIndex); + } else if (val instanceof String) { + current.putString((String) val, rowIndex, columnIndex); + } else if (val instanceof Long) { + current.putLong((Long) val, rowIndex, columnIndex); + } else if (val instanceof Integer) { + current.putLong((Integer) val, rowIndex, columnIndex); + } else if (val instanceof Boolean) { + if ((Boolean) val) + current.putLong(1, rowIndex, columnIndex); + } else if (val instanceof byte[]) { + current.putBlob((byte[]) val, rowIndex, columnIndex); + } else if (val instanceof Double) { + current.putDouble((Double) val, rowIndex, columnIndex); + } else if (val instanceof Float) { + current.putDouble((Float) val, rowIndex, columnIndex); + } else { + throw new IllegalArgumentException("Unsupported object for column " + columnIndex + ": " + val); + } + } + } + } catch (RuntimeException e) { + for (CursorWindow window : windows) { + window.close(); + } + throw e; + } + return windows.toArray(new CursorWindow[0]); + } + + private static CursorWindow[] createCursorWindows(Cursor cursor) { + if (cursor.getColumnCount() == 0) return new CursorWindow[0]; + List windows = new ArrayList<>(); + CursorWindow current = null; + int rowIndex = 0; + while (cursor.moveToNext()) { + if (current == null || !current.allocRow()) { + current = new CursorWindow(false); + current.setStartPosition(rowIndex); + current.setNumColumns(cursor.getColumnCount()); + windows.add(current); + if (!current.allocRow()) { + windows.remove(current); + return windows.toArray(new CursorWindow[0]); + } + } + for (int i = 0; i < cursor.getColumnCount(); i++) { + switch (getCursorType(cursor, i)) { + case FIELD_TYPE_NULL: + current.putNull(rowIndex, i); + break; + case FIELD_TYPE_BLOB: + current.putBlob(cursor.getBlob(i), rowIndex, i); + break; + case FIELD_TYPE_FLOAT: + current.putDouble(cursor.getDouble(i), rowIndex, i); + break; + case FIELD_TYPE_INTEGER: + current.putLong(cursor.getLong(i), rowIndex, i); + break; + case FIELD_TYPE_STRING: + current.putString(cursor.getString(i), rowIndex, i); + break; + } + } + rowIndex++; + } + cursor.close(); + return windows.toArray(new CursorWindow[0]); + } + + /** + * Gets the number of rows in the data holder. + * + * @return the number of rows in the data holder. + */ + public int getCount() { + int c = 0; + if (windows != null) { + for (CursorWindow window : windows) { + c += window.getNumRows(); + } + } + return c; + } + + /** + * Retrieves the string value for a given column at the provided position. + * This will throw an {@link IllegalArgumentException} if the column does not exist, the + * position is invalid, or the data holder has been closed. + * + * @param column The column to retrieve. + * @param row The row to retrieve the data from. + * @param windowIndex Index of the cursor window to extract the data from. + * @return The string value in that column. + */ + public String getString(String column, int row, int windowIndex) { + return windows[windowIndex].getString(row, columnIndices.get(column)); + } + + @NonNull + @Override + public String toString() { + return "DataHolder{" + + "columns=" + Arrays.toString(columns) + + ", windows=" + Arrays.toString(windows) + + ", statusCode=" + statusCode + + ", metadata=" + metadata + + '}'; + } + + public void validateContents() { + columnIndices = new HashMap<>(); + for (int i = 0; i < columns.length; i++) { + columnIndices.put(columns[i], i); + } + } + + /** + * Helper class to build {@link DataHolder} instances containing arbitrary data. + *

+ * Note that the constructor is private; use DataHolder.builder() to create instances of this class. + */ + public static class Builder { + private final String[] columns; + private final ArrayList> rows; + + private Builder(String[] columns) { + this.columns = columns; + this.rows = new ArrayList<>(); + } + + /** + * Instantiate an {@link DataHolder} from this {@link DataHolder.Builder} with the given status code and metadata. + * + * @param statusCode The status code of this {@link DataHolder}. + * @param metadata The metadata associated with this {@link DataHolder} (may be null). + * @return {@link DataHolder} representation of this object. + */ + public DataHolder build(int statusCode, Bundle metadata) { + return new DataHolder(columns, createCursorWindows(this), statusCode, metadata); + } + + /** + * Instantiate an {@link DataHolder} from this {@link DataHolder.Builder} with the given status code and null metadata. + * + * @param statusCode The status code of this {@link DataHolder}. + * @return {@link DataHolder} representation of this object. + */ + public DataHolder build(int statusCode) { + return build(statusCode, null); + } + + /** + * @return The number of rows that the resulting DataHolder will contain. + */ + public int getCount() { + return rows.size(); + } + + } + + public static final Creator CREATOR = new AutoCreator(DataHolder.class) { + @Override + public DataHolder createFromParcel(Parcel parcel) { + DataHolder res = super.createFromParcel(parcel); + res.validateContents(); + return res; + } + }; +} diff --git a/play-services-base-api/src/main/java/com/google/android/gms/common/images/WebImage.java b/play-services-base-api/src/main/java/com/google/android/gms/common/images/WebImage.java new file mode 100644 index 00000000..4b5e4ad7 --- /dev/null +++ b/play-services-base-api/src/main/java/com/google/android/gms/common/images/WebImage.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013-2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.images; + +import android.net.Uri; + +import androidx.annotation.NonNull; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +import java.util.Locale; + +public class WebImage extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(WebImage.class); + + public WebImage () { + this.uri = null; + } + + public WebImage (Uri uri) { + this.uri = uri; + } + + @SafeParceled(1) + private final int versionCode = 1; + + @SafeParceled(2) + private final Uri uri; + + @SafeParceled(3) + private final int width = 0; + + @SafeParceled(4) + private final int height = 0; + + public Uri getUrl() { + return uri; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + @NonNull + public String toString() { + assert uri != null; + return String.format(Locale.getDefault(), "Image %dx%d %s", width, height, uri); + } +} diff --git a/play-services-base-core/build.gradle b/play-services-base-core/build.gradle new file mode 100644 index 00000000..5a5dfa30 --- /dev/null +++ b/play-services-base-core/build.gradle @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +dependencies { + api project(':play-services-basement') + api "androidx.lifecycle:lifecycle-service:$lifecycleVersion" + + implementation "androidx.annotation:annotation:$annotationVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" +} + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + compileOptions { + sourceCompatibility = 17 + targetCompatibility = 17 + } + namespace 'org.microg.gms.base.core' +} \ No newline at end of file diff --git a/play-services-base-core/src/main/AndroidManifest.xml b/play-services-base-core/src/main/AndroidManifest.xml new file mode 100644 index 00000000..6585028c --- /dev/null +++ b/play-services-base-core/src/main/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/play-services-base-core/src/main/java/org/microg/gms/AbstractGmsServiceBroker.java b/play-services-base-core/src/main/java/org/microg/gms/AbstractGmsServiceBroker.java new file mode 100644 index 00000000..9ff67ff8 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/AbstractGmsServiceBroker.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.RemoteException; +import android.util.Log; + +import com.google.android.gms.common.internal.GetServiceRequest; +import com.google.android.gms.common.internal.IGmsCallbacks; +import com.google.android.gms.common.internal.IGmsServiceBroker; +import com.google.android.gms.common.internal.ValidateAccountRequest; + +import org.microg.gms.common.GmsService; + +import java.util.EnumSet; + +public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub { + private static final String TAG = "GmsServiceBroker"; + private final EnumSet supportedServices; + + public AbstractGmsServiceBroker(EnumSet supportedServices) { + this.supportedServices = supportedServices; + } + + @Deprecated + @Override + public void getPeopleService(IGmsCallbacks callback, int versionCode, String packageName, + Bundle params) throws RemoteException { + callGetService(GmsService.PEOPLE, callback, versionCode, packageName, params); + } + + @Deprecated + @Override + public void getGoogleIdentityService(IGmsCallbacks callback, int versionCode, + String packageName, Bundle params) throws RemoteException { + callGetService(GmsService.ACCOUNT, callback, versionCode, packageName, params); + } + + @Deprecated + @Override + public void getAddressService(IGmsCallbacks callback, int versionCode, String packageName) + throws RemoteException { + callGetService(callback, versionCode, packageName); + } + + private void callGetService(IGmsCallbacks callback, int gmsVersion, + String packageName) throws RemoteException { + callGetService(GmsService.ADDRESS, callback, gmsVersion, packageName, null); + } + + private void callGetService(GmsService service, IGmsCallbacks callback, int gmsVersion, String packageName, Bundle extras) throws RemoteException { + GetServiceRequest request = new GetServiceRequest(service.SERVICE_ID); + request.gmsVersion = gmsVersion; + request.packageName = packageName; + request.extras = extras; + request.account = null; + request.scopes = null; + getService(callback, request); + } + + @Override + public void getService(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException { + GmsService gmsService = GmsService.byServiceId(request.serviceId); + if ((supportedServices.contains(gmsService)) || supportedServices.contains(GmsService.ANY)) { + handleServiceRequest(callback, request, gmsService); + } else { + Log.d(TAG, "Service not supported: " + request); + throw new IllegalArgumentException("Service not supported: " + request.serviceId); + } + } + + public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException; + + @Override + public void validateAccount(IGmsCallbacks callback, ValidateAccountRequest request) { + throw new IllegalArgumentException("ValidateAccountRequest not supported"); + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + if (super.onTransact(code, data, reply, flags)) return true; + Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags); + return false; + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/BaseService.java b/play-services-base-core/src/main/java/org/microg/gms/BaseService.java new file mode 100644 index 00000000..38545ff0 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/BaseService.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms; + +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleService; + +import com.google.android.gms.common.internal.GetServiceRequest; +import com.google.android.gms.common.internal.IGmsCallbacks; +import com.google.android.gms.common.internal.IGmsServiceBroker; + +import org.microg.gms.common.GmsService; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.EnumSet; + +public abstract class BaseService extends LifecycleService { + private final IGmsServiceBroker broker; + private final EnumSet services; + protected final String TAG; + + public BaseService(String tag, GmsService supportedService, GmsService... supportedServices) { + this.TAG = tag; + services = EnumSet.of(supportedService); + services.addAll(Arrays.asList(supportedServices)); + broker = new AbstractGmsServiceBroker(services) { + @Override + public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException { + try { + request.extras.keySet(); // call to unparcel() + } catch (Exception e) { + // Sometimes we need to define the correct ClassLoader before unparcel(). Ignore those. + } + Log.d(TAG, "bound by: " + request); + BaseService.this.handleServiceRequest(callback, request, service); + } + }; + } + + @Override + public IBinder onBind(@NonNull Intent intent) { + super.onBind(intent); + Log.d(TAG, "onBind: " + intent); + return broker.asBinder(); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + writer.println(TAG + " providing services " + services.toString()); + } + + public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException; +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/DummyService.java b/play-services-base-core/src/main/java/org/microg/gms/DummyService.java new file mode 100644 index 00000000..3fb2749a --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/DummyService.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms; + +import android.os.RemoteException; + +import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.common.internal.GetServiceRequest; +import com.google.android.gms.common.internal.IGmsCallbacks; + +import org.microg.gms.common.GmsService; + +public class DummyService extends BaseService { + public DummyService() { + super("GmsDummySvc", GmsService.ANY); + } + + @Override + public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException { + callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null); + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/DeviceConfiguration.java b/play-services-base-core/src/main/java/org/microg/gms/common/DeviceConfiguration.java new file mode 100644 index 00000000..d4ab5940 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/DeviceConfiguration.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.opengl.GLES10; +import android.os.Build; +import android.util.DisplayMetrics; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; + +public class DeviceConfiguration { + public List availableFeatures; + public int densityDpi; + public int glEsVersion; + public List glExtensions; + public boolean hasFiveWayNavigation; + public boolean hasHardKeyboard; + public int heightPixels; + public int keyboardType; + public List locales; + public List nativePlatforms; + public int navigation; + public int screenLayout; + public List sharedLibraries; + public int touchScreen; + public int widthPixels; + + public DeviceConfiguration(Context context) { + ConfigurationInfo configurationInfo = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getDeviceConfigurationInfo(); + touchScreen = configurationInfo.reqTouchScreen; + keyboardType = configurationInfo.reqKeyboardType; + navigation = configurationInfo.reqNavigation; + Configuration configuration = context.getResources().getConfiguration(); + screenLayout = configuration.screenLayout; + hasHardKeyboard = (configurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD) > 0; + hasFiveWayNavigation = (configurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV) > 0; + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + densityDpi = displayMetrics.densityDpi; + glEsVersion = configurationInfo.reqGlEsVersion; + PackageManager packageManager = context.getPackageManager(); + String[] systemSharedLibraryNames = packageManager.getSystemSharedLibraryNames(); + sharedLibraries = new ArrayList<>(); + if (systemSharedLibraryNames != null) sharedLibraries.addAll(Arrays.asList(systemSharedLibraryNames)); + for (String s : new String[]{"com.google.android.maps", "com.google.android.media.effects", "com.google.widevine.software.drm"}) { + if (!sharedLibraries.contains(s)) { + sharedLibraries.add(s); + } + } + Collections.sort(sharedLibraries); + availableFeatures = new ArrayList<>(); + if (packageManager.getSystemAvailableFeatures() != null) { + for (FeatureInfo featureInfo : packageManager.getSystemAvailableFeatures()) { + if (featureInfo != null && featureInfo.name != null) availableFeatures.add(featureInfo.name); + } + } + Collections.sort(availableFeatures); + this.nativePlatforms = getNativePlatforms(); + widthPixels = displayMetrics.widthPixels; + heightPixels = displayMetrics.heightPixels; + locales = new ArrayList<>(Arrays.asList(context.getAssets().getLocales())); + for (int i = 0; i < locales.size(); i++) { + locales.set(i, locales.get(i).replace("-", "_")); + } + Collections.sort(locales); + Set glExtensions = new HashSet<>(); + addEglExtensions(glExtensions); + this.glExtensions = new ArrayList<>(glExtensions); + Collections.sort(this.glExtensions); + } + + @SuppressWarnings({"deprecation", "InlinedApi"}) + private static List getNativePlatforms() { + List nativePlatforms; + return Arrays.asList(Build.SUPPORTED_ABIS); + } + + private static void addEglExtensions(Set glExtensions) { + EGL10 egl10 = (EGL10) EGLContext.getEGL(); + if (egl10 != null) { + EGLDisplay display = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + egl10.eglInitialize(display, new int[2]); + int[] cf = new int[1]; + if (egl10.eglGetConfigs(display, null, 0, cf)) { + EGLConfig[] configs = new EGLConfig[cf[0]]; + if (egl10.eglGetConfigs(display, configs, cf[0], cf)) { + int[] a1 = + new int[]{EGL10.EGL_WIDTH, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_HEIGHT, EGL10.EGL_PBUFFER_BIT, + EGL10.EGL_NONE}; + int[] a2 = new int[]{12440, EGL10.EGL_PIXMAP_BIT, EGL10.EGL_NONE}; + int[] a3 = new int[1]; + for (int i = 0; i < cf[0]; i++) { + egl10.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_CAVEAT, a3); + if (a3[0] != EGL10.EGL_SLOW_CONFIG) { + egl10.eglGetConfigAttrib(display, configs[i], EGL10.EGL_SURFACE_TYPE, a3); + if ((1 & a3[0]) != 0) { + egl10.eglGetConfigAttrib(display, configs[i], EGL10.EGL_RENDERABLE_TYPE, a3); + if ((1 & a3[0]) != 0) { + addExtensionsForConfig(egl10, display, configs[i], a1, null, glExtensions); + } + if ((4 & a3[0]) != 0) { + addExtensionsForConfig(egl10, display, configs[i], a1, a2, glExtensions); + } + } + } + } + } + } + egl10.eglTerminate(display); + } + } + + private static void addExtensionsForConfig(EGL10 egl10, EGLDisplay egldisplay, EGLConfig eglconfig, int[] ai, + int[] ai1, Set set) { + EGLContext eglcontext = egl10.eglCreateContext(egldisplay, eglconfig, EGL10.EGL_NO_CONTEXT, ai1); + if (eglcontext != EGL10.EGL_NO_CONTEXT) { + javax.microedition.khronos.egl.EGLSurface eglsurface = + egl10.eglCreatePbufferSurface(egldisplay, eglconfig, ai); + if (eglsurface != EGL10.EGL_NO_SURFACE) { + egl10.eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext); + String s = GLES10.glGetString(7939); + if (s != null && !s.isEmpty()) { + String[] as = s.split(" "); + set.addAll(Arrays.asList(as).subList(0, as.length)); + } + egl10.eglMakeCurrent(egldisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + egl10.eglDestroySurface(egldisplay, eglsurface); + } + egl10.eglDestroyContext(egldisplay, eglcontext); + } + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/DeviceIdentifier.java b/play-services-base-core/src/main/java/org/microg/gms/common/DeviceIdentifier.java new file mode 100644 index 00000000..ab5d22cb --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/DeviceIdentifier.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import java.util.Random; + +public class DeviceIdentifier { + public String wifiMac = randomMacAddress(); // TODO: static + public String meid = randomMeid(); + public String esn; + + + private static String randomMacAddress() { + StringBuilder mac = new StringBuilder("b407f9"); + Random rand = new Random(); + for (int i = 0; i < 6; i++) { + mac.append(Integer.toString(rand.nextInt(16), 16)); + } + return mac.toString(); + } + + private static String randomMeid() { + // http://en.wikipedia.org/wiki/International_Mobile_Equipment_Identity + // We start with a known base, and generate random MEID + String meid; + Random rand = new Random(); + StringBuilder meidBuilder = new StringBuilder("35503104"); + for (int i = 0; i < 6; i++) { + meidBuilder.append(rand.nextInt(10)); + } + meid = meidBuilder.toString(); + + // Luhn algorithm (check digit) + int sum = 0; + for (int i = 0; i < meid.length(); i++) { + int c = Integer.parseInt(String.valueOf(meid.charAt(i))); + if ((meid.length() - i - 1) % 2 == 0) { + c *= 2; + c = c % 10 + c / 10; + } + + sum += c; + } + final int check = (100 - sum) % 10; + meid += Integer.toString(check); + + return meid; + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java b/play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java new file mode 100644 index 00000000..c7a2ff52 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java @@ -0,0 +1,120 @@ +package org.microg.gms.common; + +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.PowerManager; +import android.provider.Settings; +import android.util.Log; + +import androidx.core.app.NotificationCompat; + +import org.microg.gms.base.core.R; + +public class ForegroundServiceContext extends ContextWrapper { + private static final String TAG = "ForegroundService"; + public static final String EXTRA_FOREGROUND = "foreground"; + + public ForegroundServiceContext(Context base) { + super(base); + } + + @Override + public ComponentName startService(Intent service) { + if (!isIgnoringBatteryOptimizations()) { + Log.d(TAG, "Starting in foreground mode."); + service.putExtra(EXTRA_FOREGROUND, true); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return super.startForegroundService(service); + } else { + return super.startService(service); + } + } + return super.startService(service); + } + + private boolean isIgnoringBatteryOptimizations() { + PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); + return powerManager.isIgnoringBatteryOptimizations(getPackageName()); + } + + private static String getServiceName(Service service) { + String serviceName = null; + try { + ForegroundServiceInfo annotation = service.getClass().getAnnotation(ForegroundServiceInfo.class); + if (annotation != null) { + if (annotation.res() != 0) { + try { + serviceName = service.getString(annotation.res()); + } catch (Exception ignored) { + } + } + if (serviceName == null) { + serviceName = annotation.value(); + } + } + } catch (Exception ignored) { + } + if (serviceName == null) { + serviceName = service.getClass().getSimpleName(); + } + return serviceName; + } + + public static void completeForegroundService(Service service, Intent intent, String tag) { + if (intent != null && intent.getBooleanExtra(EXTRA_FOREGROUND, false)) { + String serviceName = getServiceName(service); + Log.d(tag, "Started " + serviceName + " in foreground mode."); + try { + Notification notification = buildForegroundNotification(service, serviceName); + service.startForeground(serviceName.hashCode(), notification); + Log.d(tag, "Notification: " + notification); + } catch (Exception e) { + Log.w(tag, e); + } + } + } + + @SuppressLint("BatteryLife") + private static Notification buildForegroundNotification(Context context, String serviceName) { + Intent notificationIntent = new Intent(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + notificationIntent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + notificationIntent.setData(Uri.parse("package:" + context.getPackageName())); + } else { + notificationIntent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + PendingIntent notificationPendingIntent = PendingIntent.getActivity(context, + 0, + notificationIntent, + 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel Channel = new NotificationChannel("foreground-service", + context.getResources().getString(R.string.notification_service_name), + NotificationManager.IMPORTANCE_LOW); + Channel.setShowBadge(false); + Channel.setVibrationPattern(new long[]{0}); + Channel.setLockscreenVisibility(0); + context.getSystemService(NotificationManager.class).createNotificationChannel(Channel); + } + return new NotificationCompat.Builder(context, "foreground-service") + .setOngoing(true) + .setContentIntent(notificationPendingIntent) + .setSmallIcon(R.drawable.ic_foreground_notification) + .setContentTitle(context.getResources().getString(R.string.small_notification_service_title)) + .setStyle(new NotificationCompat.BigTextStyle() + .setBigContentTitle(context.getResources().getString(R.string.big_notification_service_title)) + .bigText(context.getResources().getString(R.string.notification_service_content))) + .build(); + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceInfo.java b/play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceInfo.java new file mode 100644 index 00000000..8a8102a6 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceInfo.java @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ForegroundServiceInfo { + String value(); + int res() default 0; +} \ No newline at end of file diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/HttpFormClient.java b/play-services-base-core/src/main/java/org/microg/gms/common/HttpFormClient.java new file mode 100644 index 00000000..a941ad3b --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/HttpFormClient.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import android.net.Uri; +import android.util.Log; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; + +public class HttpFormClient { + private static final String TAG = "GmsHttpFormClient"; + + public static T request(String url, Request request, Class tClass) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("POST"); + connection.setDoInput(true); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder content = new StringBuilder(); + request.prepare(); + for (Field field : request.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + Object objVal = field.get(request); + if (field.isAnnotationPresent(RequestContentDynamic.class)) { + Map contentParams = (Map) objVal; + for (Map.Entry param : contentParams.entrySet()) { + appendParam(content, param.getKey(), param.getValue()); + } + continue; + } + String value = objVal != null ? String.valueOf(objVal) : null; + Boolean boolVal = null; + if (field.getType().equals(boolean.class)) { + boolVal = field.getBoolean(request); + } + if (field.isAnnotationPresent(RequestHeader.class)) { + RequestHeader annotation = field.getAnnotation(RequestHeader.class); + value = valueFromBoolVal(value, boolVal, annotation.truePresent(), annotation.falsePresent()); + if (value != null || annotation.nullPresent()) { + for (String key : annotation.value()) { + connection.setRequestProperty(key, String.valueOf(value)); + } + } + } + if (field.isAnnotationPresent(RequestContent.class)) { + RequestContent annotation = field.getAnnotation(RequestContent.class); + value = valueFromBoolVal(value, boolVal, annotation.truePresent(), annotation.falsePresent()); + if (value != null || annotation.nullPresent()) { + for (String key : annotation.value()) { + appendParam(content, key, value); + } + } + } + } catch (Exception ignored) { + } + } + + Log.d(TAG, "-- Request --\n" + content); + OutputStream os = connection.getOutputStream(); + os.write(content.toString().getBytes()); + os.close(); + + if (connection.getResponseCode() != 200) { + String error = connection.getResponseMessage(); + try { + error = new String(Utils.readStreamToEnd(connection.getErrorStream())); + } catch (IOException e) { + // Ignore + } + throw new IOException(error); + } + + String result = new String(Utils.readStreamToEnd(connection.getInputStream())); + Log.d(TAG, "-- Response --\n" + result); + return parseResponse(tClass, connection, result); + } + + private static String valueFromBoolVal(String value, Boolean boolVal, boolean truePresent, boolean falsePresent) { + if (boolVal != null) { + if (boolVal && truePresent) { + return "1"; + } else if (!boolVal && falsePresent) { + return "0"; + } else { + return null; + } + } else { + return value; + } + } + + private static void appendParam(StringBuilder content, String key, String value) { + if (content.length() > 0) + content.append("&"); + content.append(Uri.encode(key)).append("=").append(Uri.encode(String.valueOf(value))); + } + + private static T parseResponse(Class tClass, HttpURLConnection connection, String result) throws IOException { + Map> headerFields = connection.getHeaderFields(); + T response; + try { + response = tClass.getConstructor().newInstance(); + } catch (Exception e) { + return null; + } + String[] entries = result.split("\n"); + for (String s : entries) { + String[] keyValuePair = s.split("=", 2); + String key = keyValuePair[0].trim(); + String value = keyValuePair[1].trim(); + boolean matched = false; + try { + for (Field field : tClass.getDeclaredFields()) { + if (field.isAnnotationPresent(ResponseField.class) && + key.equals(field.getAnnotation(ResponseField.class).value())) { + matched = true; + if (field.getType().equals(String.class)) { + field.set(response, value); + } else if (field.getType().equals(boolean.class)) { + field.setBoolean(response, value.equals("1")); + } else if (field.getType().equals(long.class)) { + field.setLong(response, Long.parseLong(value)); + } else if (field.getType().equals(int.class)) { + field.setInt(response, Integer.parseInt(value)); + } + } + } + } catch (Exception e) { + Log.w(TAG, e); + } + if (!matched) { + Log.w(TAG, "Response line '" + s + "' not processed"); + } + } + for (Field field : tClass.getDeclaredFields()) { + if (field.isAnnotationPresent(ResponseHeader.class)) { + List strings = headerFields.get(field.getAnnotation(ResponseHeader.class).value()); + if (strings == null || strings.size() != 1) continue; + String value = strings.get(0); + try { + if (field.getType().equals(String.class)) { + field.set(response, value); + } else if (field.getType().equals(boolean.class)) { + field.setBoolean(response, value.equals("1")); + } else if (field.getType().equals(long.class)) { + field.setLong(response, Long.parseLong(value)); + } else if (field.getType().equals(int.class)) { + field.setInt(response, Integer.parseInt(value)); + } + } catch (Exception e) { + Log.w(TAG, e); + } + } + if (field.isAnnotationPresent(ResponseStatusCode.class) && field.getType() == int.class) { + try { + field.setInt(response, connection.getResponseCode()); + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } + } + if (field.isAnnotationPresent(ResponseStatusText.class) && field.getType() == String.class) { + try { + field.set(response, connection.getResponseMessage()); + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } + } + } + return response; + } + + public static void requestAsync(final String url, final Request request, final Class tClass, + final Callback callback) { + new Thread(new Runnable() { + @Override + public void run() { + try { + callback.onResponse(request(url, request, tClass)); + } catch (Exception e) { + callback.onException(e); + } + } + }).start(); + } + + public static abstract class Request { + protected void prepare() { + } + } + + public interface Callback { + void onResponse(T response); + + void onException(Exception exception); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RequestHeader { + public String[] value(); + + public boolean truePresent() default true; + + public boolean falsePresent() default false; + + public boolean nullPresent() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RequestContent { + public String[] value(); + + public boolean truePresent() default true; + + public boolean falsePresent() default false; + + public boolean nullPresent() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RequestContentDynamic { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface ResponseField { + public String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface ResponseHeader { + public String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface ResponseStatusCode { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface ResponseStatusText { + } +} \ No newline at end of file diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java b/play-services-base-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java new file mode 100644 index 00000000..66f44776 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import android.os.IInterface; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +public class MultiListenerProxy implements InvocationHandler { + private static final String TAG = "GmsMultiListener"; + + public static T get(Class tClass, final Collection listeners) { + return get(tClass, new CollectionListenerPool<>(listeners)); + } + + public static T get(Class tClass, final ListenerPool listenerPool) { + return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new MultiListenerProxy<>(listenerPool)); + } + + private final ListenerPool listeners; + + private MultiListenerProxy(ListenerPool listeners) { + this.listeners = listeners; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + for (T listener : new HashSet<>(listeners)) { + try { + method.invoke(listener, args); + } catch (Exception e) { + Log.w(TAG, e); + listeners.remove(listener); + } + } + return null; + } + + public static abstract class ListenerPool implements Collection { + @Override + public boolean addAll(Collection collection) { + return false; + } + + @Override + public boolean add(T object) { + return false; + } + + @Override + public boolean containsAll(Collection collection) { + for (Object o : collection) { + if (!contains(o)) return false; + } + return true; + } + + @Override + public boolean removeAll(Collection collection) { + boolean x = true; + for (Object o : collection) { + if (!remove(o)) x = false; + } + return x; + } + + @Override + public boolean retainAll(Collection collection) { + return false; + } + + @NonNull + @Override + public Object[] toArray() { + throw new IllegalArgumentException(); + } + + @NonNull + @Override + public T1[] toArray(T1[] array) { + throw new IllegalArgumentException(); + } + } + + private static class CollectionListenerPool extends ListenerPool { + + private final Collection listeners; + + public CollectionListenerPool(Collection listeners) { + this.listeners = listeners; + } + + @Override + public void clear() { + listeners.clear(); + } + + @Override + public boolean contains(Object object) { + return listeners.contains(object); + } + + @Override + public boolean isEmpty() { + return listeners.isEmpty(); + } + + @NonNull + @Override + public Iterator iterator() { + return listeners.iterator(); + } + + @Override + public boolean remove(Object object) { + return listeners.remove(object); + } + + @Override + public int size() { + return listeners.size(); + } + } + + public static class MultiCollectionListenerPool extends ListenerPool { + private final Collection> multiCol; + + public MultiCollectionListenerPool(Collection> multiCol) { + this.multiCol = multiCol; + } + + @Override + public void clear() { + for (Collection ts : multiCol) { + ts.clear(); + } + } + + @Override + public boolean contains(Object object) { + for (Collection ts : multiCol) { + if (ts.contains(object)) return true; + } + return false; + } + + @Override + public boolean isEmpty() { + for (Collection ts : multiCol) { + if (!ts.isEmpty()) return false; + } + return true; + } + + @NonNull + @Override + public Iterator iterator() { + final Iterator> interMed = multiCol.iterator(); + return new Iterator() { + private Iterator med; + + @Override + public boolean hasNext() { + while ((med == null || !med.hasNext()) && interMed.hasNext()) { + med = interMed.next().iterator(); + } + return med != null && med.hasNext(); + } + + @Override + public T next() { + while (med == null || !med.hasNext()) { + med = interMed.next().iterator(); + } + return med.next(); + } + + @Override + public void remove() { + med.remove(); + } + }; + } + + @Override + public boolean remove(Object object) { + for (Collection ts : multiCol) { + if (ts.remove(object)) return true; + } + return false; + } + + @Override + public int size() { + int sum = 0; + for (Collection ts : multiCol) { + sum += ts.size(); + } + return sum; + } + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/NonCancelToken.java b/play-services-base-core/src/main/java/org/microg/gms/common/NonCancelToken.java new file mode 100644 index 00000000..29cdba34 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/NonCancelToken.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import android.os.RemoteException; + +import com.google.android.gms.common.internal.ICancelToken; + +public class NonCancelToken extends ICancelToken.Stub { + @Override + public void cancel() throws RemoteException { + + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-base-core/src/main/java/org/microg/gms/common/PackageUtils.java new file mode 100644 index 00000000..787d2e11 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import android.app.ActivityManager; +import android.app.Application; +import android.app.PendingIntent; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.os.Binder; +import android.util.Log; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Method; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static android.os.Build.VERSION.SDK_INT; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1; +import static org.microg.gms.common.Constants.GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1; + +public class PackageUtils { + + private static final String GOOGLE_PLATFORM_KEY = GMS_PACKAGE_SIGNATURE_SHA1; + private static final String GOOGLE_PLATFORM_KEY_2 = GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1; + private static final String GOOGLE_APP_KEY = "24bb24c05e47e0aefa68a58a766179d9b613a600"; + private static final String[] GOOGLE_PRIMARY_KEYS = {GOOGLE_PLATFORM_KEY, GOOGLE_PLATFORM_KEY_2, GOOGLE_APP_KEY}; + + private static final Map KNOWN_GOOGLE_PACKAGES; + + static { + KNOWN_GOOGLE_PACKAGES = new HashMap<>(); + KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.youtube.music", "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"); + KNOWN_GOOGLE_PACKAGES.put("com.google.android.youtube", "24bb24c05e47e0aefa68a58a766179d9b613a600"); + } + + public static boolean isGooglePackage(Context context, String packageName) { + String signatureDigest = firstSignatureDigest(context, packageName); + return isGooglePackage(packageName, signatureDigest); + } + + public static boolean isGooglePackage(PackageManager packageManager, String packageName) { + String signatureDigest = firstSignatureDigest(packageManager, packageName); + return isGooglePackage(packageName, signatureDigest); + } + + public static boolean isGooglePackage(String packageName, byte[] bytes) { + return isGooglePackage(packageName, sha1sum(bytes)); + } + + public static boolean isGooglePackage(String packageName, String signatureDigest) { + if (signatureDigest == null) return false; + if (Arrays.asList(GOOGLE_PRIMARY_KEYS).contains(signatureDigest)) return true; + if (!KNOWN_GOOGLE_PACKAGES.containsKey(packageName)) return false; + return KNOWN_GOOGLE_PACKAGES.get(packageName).equals(signatureDigest); + } + + public static void assertExtendedAccess(Context context) { + if (!callerHasExtendedAccess(context)) + throw new SecurityException("Access denied, missing EXTENDED_ACCESS permission"); + } + + public static boolean callerHasExtendedAccess(Context context) { + String[] packagesForUid = context.getPackageManager().getPackagesForUid(Binder.getCallingUid()); + if (packagesForUid != null && packagesForUid.length != 0) { + for (String packageName : packagesForUid) { + packageName = PackageSpoofUtils.spoofPackageName(context.getPackageManager(), packageName); + if (isGooglePackage(context, packageName) || GMS_PACKAGE_NAME.equals(packageName)) + return true; + } + } + return context.checkCallingPermission("org.mgoogle.gms.EXTENDED_ACCESS") == PackageManager.PERMISSION_GRANTED; + } + + public static void checkPackageUid(Context context, String packageName, int callingUid) { + getAndCheckPackage(context, packageName, callingUid, 0); + } + + @Nullable + public static String firstSignatureDigest(Context context, String packageName) { + return firstSignatureDigest(context.getPackageManager(), packageName); + } + + @Nullable + public static String firstSignatureDigest(PackageManager packageManager, String packageName) { + if (packageName.equals("com.google.android.apps.youtube.music") || packageName.contains("youtube.music")) { + return "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"; + } + if (packageName.equals("com.google.android.apps.youtube.unplugged") || packageName.contains("youtube.unplugged")) { + return "3a82b5ee26bc46bf68113d920e610cd090198d4a"; + } + if (packageName.equals("com.google.android.youtube.tv") || packageName.contains("youtube.tv")) { + return "61226bdb57cc32c8a2a9ef71f7bc9548e95dcc0b"; + } + if (packageName.equals("com.google.android.youtube") || packageName.contains("youtube")) { + return "24bb24c05e47e0aefa68a58a766179d9b613a600"; + } + final PackageInfo info; + try { + info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + if (info != null && info.signatures != null && info.signatures.length > 0) { + for (Signature sig : info.signatures) { + String digest = sha1sum(sig.toByteArray()); + if (digest != null) { + // spoof or use real one + return PackageSpoofUtils.spoofStringSignature(packageManager, packageName, digest); + } + } + } + return null; + } + + @Nullable + public static byte[] firstSignatureDigestBytes(Context context, String packageName) { + return firstSignatureDigestBytes(context.getPackageManager(), packageName); + } + + @Nullable + public static byte[] firstSignatureDigestBytes(PackageManager packageManager, String packageName) { + final PackageInfo info; + try { + info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + if (info != null && info.signatures != null && info.signatures.length > 0) { + for (Signature sig : info.signatures) { + byte[] digest = sha1bytes(sig.toByteArray()); + if (digest != null) { + // spoof or use real one + return PackageSpoofUtils.spoofBytesSignature(packageManager, packageName, digest); + } + } + } + return null; + } + + @Nullable + public static String getCallingPackage(Context context) { + int callingUid = Binder.getCallingUid(), callingPid = Binder.getCallingPid(); + String packageName = packageFromProcessId(context, callingPid); + if (packageName == null) { + packageName = firstPackageFromUserId(context, callingUid); + } + + // spoof or use real one + return PackageSpoofUtils.spoofPackageName(context.getPackageManager(), packageName); + } + + public static byte[] sha1bytes(byte[] bytes) { + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA1"); + } catch (final NoSuchAlgorithmException e) { + return null; + } + if (md != null) { + return md.digest(bytes); + } + return null; + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, String suggestedPackageName) { + return getAndCheckCallingPackage(context, suggestedPackageName, 0); + } + + @Nullable + public static String getAndCheckCallingPackageOrExtendedAccess(Context context, String suggestedPackageName) { + try { + return getAndCheckCallingPackage(context, suggestedPackageName, 0); + } catch (Exception e) { + if (callerHasExtendedAccess(context)) { + return suggestedPackageName; + } + throw e; + } + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, int suggestedCallerUid) { + return getAndCheckCallingPackage(context, null, suggestedCallerUid); + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, String suggestedPackageName, int suggestedCallerUid) { + return getAndCheckCallingPackage(context, suggestedPackageName, suggestedCallerUid, 0); + } + + @Nullable + public static String getAndCheckCallingPackage(Context context, String suggestedPackageName, int suggestedCallerUid, int suggestedCallerPid) { + int callingUid = Binder.getCallingUid(), callingPid = Binder.getCallingPid(); + if (suggestedCallerUid > 0 && suggestedCallerUid != callingUid) { + throw new SecurityException("suggested UID [" + suggestedCallerUid + "] and real calling UID [" + callingUid + "] mismatch!"); + } + if (suggestedCallerPid > 0 && suggestedCallerPid != callingPid) { + throw new SecurityException("suggested PID [" + suggestedCallerPid + "] and real calling PID [" + callingPid + "] mismatch!"); + } + return getAndCheckPackage(context, suggestedPackageName, callingUid, Binder.getCallingPid()); + } + + @Nullable + public static String getAndCheckPackage(Context context, String suggestedPackageName, int callingUid) { + return getAndCheckPackage(context, suggestedPackageName, callingUid, 0); + } + + @Nullable + public static String getAndCheckPackage(Context context, String suggestedPackageName, int callingUid, int callingPid) { + String packageName = packageFromProcessId(context, callingPid); + if (packageName == null) { + String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid); + if (packagesForUid != null && packagesForUid.length != 0) { + if (packagesForUid.length == 1) { + packageName = packagesForUid[0]; + } else if (Arrays.asList(packagesForUid).contains(suggestedPackageName)) { + packageName = suggestedPackageName; + } else { + packageName = packagesForUid[0]; + } + } + } + if (packageName != null && suggestedPackageName != null && !packageName.equals(suggestedPackageName)) { + throw new SecurityException("UID [" + callingUid + "] is not related to packageName [" + suggestedPackageName + "] (seems to be " + packageName + ")"); + } + + // spoof or use real one + return PackageSpoofUtils.spoofPackageName(context.getPackageManager(), packageName); + } + + @Nullable + @Deprecated + public static String packageFromProcessId(Context context, int pid) { + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + if (manager == null) return null; + if (pid <= 0) return null; + List runningAppProcesses = manager.getRunningAppProcesses(); + if (runningAppProcesses != null) { + for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcesses) { + if (processInfo.pid == pid && processInfo.pkgList.length == 1) { + return processInfo.pkgList[0]; + } + } + } + return null; + } + + @Nullable + public static String firstPackageFromUserId(Context context, int uid) { + String[] packagesForUid = context.getPackageManager().getPackagesForUid(uid); + if (packagesForUid != null && packagesForUid.length != 0) { + return packagesForUid[0]; + } + return null; + } + + @SuppressWarnings("deprecation") + public static String packageFromPendingIntent(PendingIntent pi) { + if (pi == null) return null; + return pi.getCreatorPackage(); + } + + public static String getProcessName() { + if (android.os.Build.VERSION.SDK_INT >= 28) + return Application.getProcessName(); + try { + Class activityThread = Class.forName("android.app.ActivityThread"); + String methodName = "currentProcessName"; + Method getProcessName = activityThread.getDeclaredMethod(methodName); + return (String) getProcessName.invoke(null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean isPersistentProcess() { + String processName = getProcessName(); + if (processName == null) { + Log.w("GmsPackageUtils", "Can't determine process name of current process"); + return false; + } + return processName.endsWith(":persistent"); + } + + public static boolean isMainProcess(Context context) { + String processName = getProcessName(); + if (processName == null) { + Log.w("GmsPackageUtils", "Can't determine process name of current process"); + return false; + } + return processName.equals(context.getPackageName()); + } + + public static void warnIfNotPersistentProcess(Class clazz) { + if (!isPersistentProcess()) { + Log.w("GmsPackageUtils", clazz.getSimpleName() + " initialized outside persistent process", new RuntimeException()); + } + } + + public static void warnIfNotMainProcess(Context context, Class clazz) { + if (!isMainProcess(context)) { + Log.w("GmsPackageUtils", clazz.getSimpleName() + " initialized outside main process", new RuntimeException()); + } + } + + public static String sha1sum(byte[] bytes) { + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA1"); + } catch (final NoSuchAlgorithmException e) { + return null; + } + if (md != null) { + bytes = md.digest(bytes); + if (bytes != null) { + StringBuilder sb = new StringBuilder(2 * bytes.length); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + } + return null; + } + + public static int versionCode(Context context, String packageName) { + try { + return context.getPackageManager().getPackageInfo(packageName, 0).versionCode; + } catch (PackageManager.NameNotFoundException e) { + return -1; + } + } + + public static String versionName(Context context, String packageName) { + try { + return context.getPackageManager().getPackageInfo(packageName, 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + public static int targetSdkVersion(Context context, String packageName) { + try { + return context.getPackageManager().getApplicationInfo(packageName, 0).targetSdkVersion; + } catch (PackageManager.NameNotFoundException e) { + return -1; + } + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/PhoneInfo.java b/play-services-base-core/src/main/java/org/microg/gms/common/PhoneInfo.java new file mode 100644 index 00000000..1fbc4bf9 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/PhoneInfo.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import java.util.Random; + +public class PhoneInfo { + public String cellOperator = "26207"; + public String roaming = "mobile-notroaming"; + public String simOperator = "26207"; + public String imsi = randomImsi(); + + private String randomImsi() { + Random random = new Random(); + StringBuilder sb = new StringBuilder(simOperator); + while (sb.length() < 15) { + sb.append(random.nextInt(10)); + } + return sb.toString(); + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java b/play-services-base-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java new file mode 100644 index 00000000..bc310c1c --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013-2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ResolveInfo; +import android.os.IBinder; +import android.os.IInterface; +import android.util.Log; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; + +public class RemoteListenerProxy implements ServiceConnection, InvocationHandler { + private static final String TAG = "GmsRemoteListener"; + private final Context context; + private final Intent searchIntent; + private final String bindAction; + private IBinder remote; + private boolean connecting; + private final List waiting = new ArrayList<>(); + private final Class tClass; + + public static T get(Context context, Intent intent, Class tClass, String bindAction) { + return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, + new RemoteListenerProxy<>(context, intent, tClass, bindAction)); + } + + private RemoteListenerProxy(Context context, Intent intent, Class tClass, String bindAction) { + this.context = context; + this.searchIntent = intent; + this.tClass = tClass; + this.bindAction = bindAction; + } + + private boolean connect() { + synchronized (this) { + if (!connecting) { + try { + ResolveInfo resolveInfo = context.getPackageManager().resolveService(searchIntent, 0); + if (resolveInfo != null) { + Intent intent = new Intent(bindAction); + intent.setPackage(resolveInfo.serviceInfo.packageName); + intent.setClassName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name); + connecting = context.bindService(intent, this, Context.BIND_AUTO_CREATE); + if (!connecting) Log.d(TAG, "Could not connect to: " + intent); + return connecting; + } + return false; + } catch (Exception e) { + Log.w(TAG, e); + } + } + return true; + } + } + + private void runOncePossible(Runnable runnable) { + synchronized (this) { + if (remote == null) { + waiting.add(runnable); + } else { + runnable.run(); + } + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (this) { + remote = service; + if (!waiting.isEmpty()) { + try { + for (Runnable runnable : waiting) { + runnable.run(); + } + } catch (Exception e) { + } + waiting.clear(); + try { + context.unbindService(RemoteListenerProxy.this); + } catch (Exception e) { + } + connecting = false; + remote = null; + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized (this) { + remote = null; + } + } + + @Override + public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { + if (method.getDeclaringClass().equals(tClass)) { + runOncePossible(() -> { + try { + Object asInterface = Class.forName(tClass.getName() + "$Stub").getMethod("asInterface", IBinder.class).invoke(null, remote); + method.invoke(asInterface, args); + } catch (Exception e) { + Log.w(TAG, e); + } + }); + connect(); + return null; + } else if (method.getDeclaringClass().equals(Object.class)) { + return method.invoke(this, args); + } + return null; + } +} diff --git a/play-services-base-core/src/main/java/org/microg/gms/common/Utils.java b/play-services-base-core/src/main/java/org/microg/gms/common/Utils.java new file mode 100644 index 00000000..1aa984a2 --- /dev/null +++ b/play-services-base-core/src/main/java/org/microg/gms/common/Utils.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.content.Context; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +public class Utils { + + public static Locale getLocale(Context context) { + return Locale.getDefault(); // TODO + } + + public static DeviceIdentifier getDeviceIdentifier(Context context) { + return new DeviceIdentifier(); + } + + public static PhoneInfo getPhoneInfo(Context context) { + return new PhoneInfo(); + } + + public static boolean hasSelfPermissionOrNotify(Context context, String permission) { + if (context.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) { + Log.w("GmsUtils", "Lacking permission to " + permission + " for pid:" + android.os.Process.myPid() + " uid:" + android.os.Process.myUid()); + try { + //TODO: Toast.makeText(context, context.getString(R.string.lacking_permission_toast, permission), Toast.LENGTH_SHORT).show(); + } catch (RuntimeException e) { + } + return false; + } + return true; + } + + public static byte[] readStreamToEnd(final InputStream is) throws IOException { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + if (is != null) { + final byte[] buff = new byte[1024]; + int read; + do { + bos.write(buff, 0, (read = is.read(buff)) < 0 ? 0 : read); + } while (read >= 0); + is.close(); + } + return bos.toByteArray(); + } +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/common/PackageSpoofUtils.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/common/PackageSpoofUtils.kt new file mode 100644 index 00000000..94ec088d --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/common/PackageSpoofUtils.kt @@ -0,0 +1,155 @@ +package org.microg.gms.common + +import android.content.pm.PackageManager +import android.os.Bundle +import android.util.Log + +/** + * utilities to spoof package information + */ +internal object PackageSpoofUtils { + private const val TAG = "SpoofUtils" + private const val META_SPOOF_PACKAGE_NAME = + "com.mgoogle.android.gms.SPOOFED_PACKAGE_NAME" + private const val META_SPOOF_PACKAGE_SIGNATURE = + "com.mgoogle.android.gms.SPOOFED_PACKAGE_SIGNATURE" + + private val spoofedPackageNameCache = HashMap() + private val spoofedPackageSignatureCache = HashMap() + + /** + * spoof the package name of a package, if a spoofed name is set + * + * @param packageManager manager used to get package information + * @param realPackageName the real package name + * @return either the spoofed or the real package name, depending on if the package requested spoofing + */ + @JvmStatic + fun spoofPackageName( + packageManager: PackageManager, + realPackageName: String? + ): String? { + if (realPackageName.isNullOrEmpty()) return realPackageName + + val spoofedPackageName = getSpoofedPackageName(packageManager, realPackageName) + return if (!spoofedPackageName.isNullOrEmpty()) { + Log.i(TAG, "package name of $realPackageName spoofed to $spoofedPackageName") + spoofedPackageName + } else realPackageName + } + + /** + * spoof the signature of a package, if a spoofed name is set + * + * @param packageManager manager used to get package information + * @param packageName name of the package to check + * @param realSignature the real package signature + * @return either the spoofed or the real signature, depending on if the package requested spoofing + */ + @JvmStatic + @JvmName("spoofStringSignature") + fun spoofSignature( + packageManager: PackageManager, + packageName: String, + realSignature: String? + ): String? { + val spoofedSignature = getSpoofedSignature(packageManager, packageName) + return if (!spoofedSignature.isNullOrEmpty()) { + Log.i(TAG, "package signature of $packageName spoofed to $spoofedSignature") + spoofedSignature + } else realSignature + } + + /** + * spoof the signature of a package, if a spoofed name is set + * + * @param packageManager manager used to get package information + * @param packageName name of the package to check + * @param realSignature the real package signature + * @return either the spoofed or the real signature, depending on if the package requested spoofing + */ + @JvmStatic + @JvmName("spoofBytesSignature") + fun spoofSignature( + packageManager: PackageManager, + packageName: String, + realSignature: ByteArray? + ): ByteArray? { + val spoofedSignatureString = getSpoofedSignature(packageManager, packageName) + return if (!spoofedSignatureString.isNullOrEmpty()) { + Log.i(TAG, "package signature of $packageName spoofed to $spoofedSignatureString") + + // convert hex string to bytes + spoofedSignatureString.chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() + } else realSignature + } + + /** + * get the spoofed package name for a package + * + * @param packageManager manager used to get package information + * @param packageName name of the package to get spoofed name of + * @return spoofed name string, or null if none set + */ + private fun getSpoofedPackageName( + packageManager: PackageManager, + packageName: String + ): String? { + return spoofedPackageNameCache[packageName] ?: run { + // fetch value + val meta = getPackageMetadata(packageManager, packageName) + val spoofedPackageName = meta?.getString(META_SPOOF_PACKAGE_NAME) + if (spoofedPackageName != null) { + spoofedPackageNameCache[packageName] = spoofedPackageName + } + + spoofedPackageName + } + } + + /** + * get the spoofed package signature for a package + * + * @param packageManager manager used to get package information + * @param packageName name of the package to get spoofed signature of + * @return spoofed signature string, or null if none set + */ + private fun getSpoofedSignature( + packageManager: PackageManager, + packageName: String + ): String? { + return spoofedPackageSignatureCache[packageName] ?: run { + // fetch value + val meta = getPackageMetadata(packageManager, packageName) + val spoofedSignature = meta?.getString(META_SPOOF_PACKAGE_SIGNATURE) + if (spoofedSignature != null) { + spoofedPackageSignatureCache[packageName] = spoofedSignature + } + + spoofedSignature + } + } + + /** + * get package metadata + * + * @param packageManager manager used to get package information + * @param packageName name of the package to get metadata of + * @return package metadata bundle + */ + private fun getPackageMetadata(packageManager: PackageManager, packageName: String): Bundle? { + return try { + // PackageManager.getPackageInfo() has been deprecated in targetSdkVersion 30+ + // To solve this, add the QUERY_ALL_PACKAGES permission to AndroidManifest.xml + packageManager + .getPackageInfo(packageName, PackageManager.GET_META_DATA) + ?.applicationInfo + ?.metaData + } catch (e: PackageManager.NameNotFoundException) { + Log.e(TAG, "failed to get application metadata for $packageName", e) + null + } + } +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/profile/Build.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/profile/Build.kt new file mode 100644 index 00000000..c0398c2b --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/profile/Build.kt @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2021 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.profile + +object Build { + @JvmField + var BOARD: String? = null + + @JvmField + var BOOTLOADER: String? = null + + @JvmField + var BRAND: String? = null + + @JvmField + var CPU_ABI: String? = null + + @JvmField + var CPU_ABI2: String? = null + + @JvmField + var SUPPORTED_ABIS: Array = emptyArray() + + @JvmField + var DEVICE: String? = null + + @JvmField + var DISPLAY: String? = null + + @JvmField + var FINGERPRINT: String? = null + + @JvmField + var HARDWARE: String? = null + + @JvmField + var HOST: String? = null + + @JvmField + var ID: String? = null + + @JvmField + var MANUFACTURER: String? = null + + @JvmField + var MODEL: String? = null + + @JvmField + var PRODUCT: String? = null + + @JvmField + var RADIO: String? = null + + @JvmField + var SERIAL: String? = null + + @JvmField + var TAGS: String? = null + + @JvmField + var TIME: Long = 0L + + @JvmField + var TYPE: String? = null + + @JvmField + var USER: String? = null + + object VERSION { + @JvmField + var CODENAME: String? = null + + @JvmField + var INCREMENTAL: String? = null + + @JvmField + var RELEASE: String? = null + + @JvmField + var SDK: String? = null + + @JvmField + var SDK_INT: Int = 0 + + @JvmField + var SECURITY_PATCH: String? = null + } + + fun generateWebViewUserAgentString(original: String): String { + if (!original.startsWith("Mozilla/5.0 (")) return original + val closeParen: Int = original.indexOf(')') + + return "Mozilla/5.0 (Linux; Android ${VERSION.RELEASE}; $MODEL Build/$ID; wv)${original.substring(closeParen + 1)}" + } +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileManager.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileManager.kt new file mode 100644 index 00000000..0cbc69b8 --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileManager.kt @@ -0,0 +1,341 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.profile + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.XmlResourceParser +import android.util.Log +import org.microg.gms.utils.FileXmlResourceParser +import org.microg.mgms.settings.SettingsContract +import org.microg.mgms.settings.SettingsContract.Profile +import org.xmlpull.v1.XmlPullParser +import java.io.File +import java.util.* +import kotlin.random.Random + +@Suppress("Warnings") +object ProfileManager { + private const val TAG = "ProfileManager" + const val PROFILE_REAL = "real" + const val PROFILE_AUTO = "auto" + const val PROFILE_NATIVE = "native" + const val PROFILE_USER = "user" + const val PROFILE_SYSTEM = "system" + + private var activeProfile: String? = null + + private fun getUserProfileFile(context: Context): File = File(context.filesDir, "device_profile.xml") + private fun getSystemProfileFile(): File = File("/system/etc/microg_device_profile.xml") + private fun getProfileResId(context: Context, profile: String) = context.resources.getIdentifier("${context.packageName}:xml/profile_$profile".lowercase(Locale.US), null, null) + + fun getConfiguredProfile(context: Context): String = Profile.getContentUri(context)?.let { + SettingsContract.getSettings(context, + it, arrayOf(Profile.PROFILE)) { it.getString(0) } + } + ?: PROFILE_AUTO + + fun getAutoProfile(context: Context): String { + if (hasProfile(context, PROFILE_SYSTEM) && isAutoProfile(context, PROFILE_SYSTEM)) return PROFILE_SYSTEM + val profile = "${android.os.Build.PRODUCT}_${android.os.Build.VERSION.SDK_INT}" + if (hasProfile(context, profile) && isAutoProfile(context, profile)) return profile + return PROFILE_NATIVE + } + + fun hasProfile(context: Context, profile: String): Boolean = when (profile) { + PROFILE_AUTO -> hasProfile(context, getAutoProfile(context)) + PROFILE_NATIVE, PROFILE_REAL -> true + PROFILE_USER -> getUserProfileFile(context).exists() + PROFILE_SYSTEM -> getSystemProfileFile().exists() + else -> getProfileResId(context, profile) != 0 + } + + private fun getProfileXml(context: Context, profile: String): XmlResourceParser? = kotlin.runCatching { + when (profile) { + PROFILE_AUTO -> getProfileXml(context, getAutoProfile(context)) + PROFILE_NATIVE, PROFILE_REAL -> null + PROFILE_USER -> FileXmlResourceParser(getUserProfileFile(context)) + PROFILE_SYSTEM -> FileXmlResourceParser(getSystemProfileFile()) + else -> { + val profileResId = getProfileResId(context, profile) + if (profileResId == 0) return@runCatching null + context.resources.getXml(profileResId) + } + } + }.getOrNull() + + fun isAutoProfile(context: Context, profile: String): Boolean = kotlin.runCatching { + when (profile) { + PROFILE_AUTO -> false + PROFILE_REAL -> false + PROFILE_NATIVE -> true + else -> getProfileXml(context, profile)?.use { + var next = it.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (it.name) { + "profile" -> { + return@use it.getAttributeBooleanValue(null, "auto", false) + } + } + } + next = it.next() + } + } == true + } + }.getOrDefault(false) + + private fun getProfileData(context: Context, profile: String, realData: Map): Map { + try { + if (profile in listOf(PROFILE_REAL, PROFILE_NATIVE)) return realData + val profileResId = getProfileResId(context, profile) + if (profileResId == 0) return realData + val resultData = mutableMapOf() + resultData.putAll(realData) + getProfileXml(context, profile)?.use { + var next = it.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (it.name) { + "data" -> { + val key = it.getAttributeValue(null, "key") + val value = it.getAttributeValue(null, "value") + resultData[key] = value + Log.d(TAG, "Overwrite from profile: $key = $value") + } + } + } + next = it.next() + } + } + return resultData + } catch (e: Exception) { + Log.w(TAG, e) + return realData + } + } + + private fun getProfile(context: Context) = getConfiguredProfile(context).let { if (it != PROFILE_AUTO) it else getAutoProfile(context) } + private fun getSerialFromSettings(context: Context): String? = + Profile.getContentUri(context)?.let { + SettingsContract.getSettings(context, + it, arrayOf(Profile.SERIAL)) { it.getString(0) } + } + private fun saveSerial(context: Context, serial: String) = + Profile.getContentUri(context) + ?.let { SettingsContract.setSettings(context, it) { put(Profile.SERIAL, serial) } } + + private fun randomSerial(template: String, prefixLength: Int = (template.length / 2).coerceAtMost(6)): String { + val serial = StringBuilder() + template.forEachIndexed { index, c -> + serial.append(when { + index < prefixLength -> c + c.isDigit() -> '0' + Random.nextInt(10) + c.isLowerCase() && c <= 'f' -> 'a' + Random.nextInt(6) + c.isLowerCase() -> 'a' + Random.nextInt(26) + c.isUpperCase() && c <= 'F' -> 'A' + Random.nextInt(6) + c.isUpperCase() -> 'A' + Random.nextInt(26) + else -> c + }) + } + return serial.toString() + } + + @SuppressLint("MissingPermission") + private fun getProfileSerialTemplate(context: Context, profile: String): String { + // Native + if (profile in listOf(PROFILE_REAL, PROFILE_NATIVE)) { + var candidate = try { + if (android.os.Build.VERSION.SDK_INT >= 26) { + android.os.Build.getSerial() + } else { + android.os.Build.SERIAL + } + } catch (e: Exception) { + android.os.Build.SERIAL + } + if (candidate != android.os.Build.UNKNOWN) return candidate + } + + // From profile + try { + getProfileXml(context, profile)?.use { + var next = it.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (it.name) { + "serial" -> return it.getAttributeValue(null, "template") + } + } + next = it.next() + } + } + } catch (e: Exception) { + Log.w(TAG, e) + } + + // Fallback + return randomSerial("008741A0B2C4D6E8") + } + + @SuppressLint("MissingPermission") + fun getSerial(context: Context, profile: String = getProfile(context), local: Boolean = false): String { + if (!local) getSerialFromSettings(context)?.let { return it } + val serialTemplate = getProfileSerialTemplate(context, profile) + val serial = when { + profile == PROFILE_REAL && serialTemplate != android.os.Build.UNKNOWN -> serialTemplate + else -> randomSerial(serialTemplate) + } + if (!local) saveSerial(context, serial) + return serial + } + + private fun getRealData(): Map = mutableMapOf( + "Build.BOARD" to android.os.Build.BOARD, + "Build.BOOTLOADER" to android.os.Build.BOOTLOADER, + "Build.BRAND" to android.os.Build.BRAND, + "Build.CPU_ABI" to android.os.Build.CPU_ABI, + "Build.CPU_ABI2" to android.os.Build.CPU_ABI2, + "Build.DEVICE" to android.os.Build.DEVICE, + "Build.DISPLAY" to android.os.Build.DISPLAY, + "Build.FINGERPRINT" to android.os.Build.FINGERPRINT, + "Build.HARDWARE" to android.os.Build.HARDWARE, + "Build.HOST" to android.os.Build.HOST, + "Build.ID" to android.os.Build.ID, + "Build.MANUFACTURER" to android.os.Build.MANUFACTURER, + "Build.MODEL" to android.os.Build.MODEL, + "Build.PRODUCT" to android.os.Build.PRODUCT, + "Build.RADIO" to android.os.Build.RADIO, + "Build.SERIAL" to android.os.Build.SERIAL, + "Build.TAGS" to android.os.Build.TAGS, + "Build.TIME" to android.os.Build.TIME.toString(), + "Build.TYPE" to android.os.Build.TYPE, + "Build.USER" to android.os.Build.USER, + "Build.VERSION.CODENAME" to android.os.Build.VERSION.CODENAME, + "Build.VERSION.INCREMENTAL" to android.os.Build.VERSION.INCREMENTAL, + "Build.VERSION.RELEASE" to android.os.Build.VERSION.RELEASE, + "Build.VERSION.SDK" to android.os.Build.VERSION.SDK, + "Build.VERSION.SDK_INT" to android.os.Build.VERSION.SDK_INT.toString() + ).apply { + if (android.os.Build.VERSION.SDK_INT >= 21) { + put("Build.SUPPORTED_ABIS", android.os.Build.SUPPORTED_ABIS.joinToString(",")) + } + if (android.os.Build.VERSION.SDK_INT >= 23) { + put("Build.VERSION.SECURITY_PATCH", android.os.Build.VERSION.SECURITY_PATCH) + } + } + + private fun applyProfileData(profileData: Map) { + fun applyStringField(key: String, valueSetter: (String) -> Unit) = profileData[key]?.let { valueSetter(it) } + fun applyIntField(key: String, valueSetter: (Int) -> Unit) = profileData[key]?.toIntOrNull()?.let { valueSetter(it) } + fun applyLongField(key: String, valueSetter: (Long) -> Unit) = profileData[key]?.toLongOrNull()?.let { valueSetter(it) } + + applyStringField("Build.BOARD") { Build.BOARD = it } + applyStringField("Build.BOOTLOADER") { Build.BOOTLOADER = it } + applyStringField("Build.BRAND") { Build.BRAND = it } + applyStringField("Build.CPU_ABI") { Build.CPU_ABI = it } + applyStringField("Build.CPU_ABI2") { Build.CPU_ABI2 = it } + applyStringField("Build.DEVICE") { Build.DEVICE = it } + applyStringField("Build.DISPLAY") { Build.DISPLAY = it } + applyStringField("Build.FINGERPRINT") { Build.FINGERPRINT = it } + applyStringField("Build.HARDWARE") { Build.HARDWARE = it } + applyStringField("Build.HOST") { Build.HOST = it } + applyStringField("Build.ID") { Build.ID = it } + applyStringField("Build.MANUFACTURER") { Build.MANUFACTURER = it } + applyStringField("Build.MODEL") { Build.MODEL = it } + applyStringField("Build.PRODUCT") { Build.PRODUCT = it } + applyStringField("Build.RADIO") { Build.RADIO = it } + applyStringField("Build.SERIAL") { Build.SERIAL = it } + applyStringField("Build.TAGS") { Build.TAGS = it } + applyLongField("Build.TIME") { Build.TIME = it } + applyStringField("Build.TYPE") { Build.TYPE = it } + applyStringField("Build.USER") { Build.USER = it } + applyStringField("Build.VERSION.CODENAME") { Build.VERSION.CODENAME = it } + applyStringField("Build.VERSION.INCREMENTAL") { Build.VERSION.INCREMENTAL = it } + applyStringField("Build.VERSION.RELEASE") { Build.VERSION.RELEASE = it } + applyStringField("Build.VERSION.SDK") { Build.VERSION.SDK = it } + applyIntField("Build.VERSION.SDK_INT") { Build.VERSION.SDK_INT = it } + if (android.os.Build.VERSION.SDK_INT >= 21) { + Build.SUPPORTED_ABIS = profileData["Build.SUPPORTED_ABIS"]?.split(",")?.toTypedArray() ?: emptyArray() + } else { + Build.SUPPORTED_ABIS = emptyArray() + } + if (android.os.Build.VERSION.SDK_INT >= 23) { + Build.VERSION.SECURITY_PATCH = profileData["Build.VERSION.SECURITY_PATCH"] + } else { + Build.VERSION.SECURITY_PATCH = null + } + } + + private fun applyProfile(context: Context, profile: String, serial: String = getSerial(context, profile)) { + val profileData = getProfileData(context, profile, getRealData()) + if (Log.isLoggable(TAG, Log.VERBOSE)) { + for ((key, value) in profileData) { + Log.v(TAG, "") + } + } + applyProfileData(profileData) + Build.SERIAL = serial + Log.d(TAG, "Using Serial ${Build.SERIAL}") + activeProfile = profile + } + + fun getProfileName(context: Context, profile: String): String? = getProfileName { getProfileXml(context, profile) } + + private fun getProfileName(parserCreator: () -> XmlResourceParser?): String? = parserCreator()?.use { + var next = it.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (it.name) { + "profile" -> { + return@use it.getAttributeValue(null, "name") + } + } + } + next = it.next() + } + null + } + + fun setProfile(context: Context, profile: String?) { + val changed = getProfile(context) != profile + val newProfile = profile ?: PROFILE_AUTO + val newSerial = if (changed) getSerial(context, newProfile, true) else getSerial(context) + Profile.getContentUri(context)?.let { + SettingsContract.setSettings(context, it) { + put(Profile.PROFILE, newProfile) + if (changed) put(Profile.SERIAL, newSerial) + } + } + if (changed && activeProfile != null) applyProfile(context, newProfile, newSerial) + } + + fun importUserProfile(context: Context, file: File): Boolean { + val profileName = getProfileName { FileXmlResourceParser(file) } ?: return false + try { + Log.d(TAG, "Importing user profile '$profileName'") + file.copyTo(getUserProfileFile(context)) + if (activeProfile == PROFILE_USER) applyProfile(context, PROFILE_USER) + return true + } catch (e: Exception) { + Log.w(TAG, e) + return false + } + } + + @JvmStatic + fun ensureInitialized(context: Context) { + synchronized(this) { + try { + val profile = getProfile(context) + if (activeProfile == profile) return + applyProfile(context, profile) + } catch (e: Exception) { + Log.w(TAG, e) + } + } + } +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/utils/BinderUtils.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/utils/BinderUtils.kt new file mode 100644 index 00000000..79e8ff8f --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/utils/BinderUtils.kt @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.utils + +import android.os.Binder +import android.os.IBinder +import android.os.Parcel +import android.util.Log + +private const val TAG = "BinderUtils" + +fun IBinder.warnOnTransactionIssues(code: Int, reply: Parcel?, flags: Int, base: () -> Boolean): Boolean { + if (base.invoke()) { + if ((flags and Binder.FLAG_ONEWAY) > 0 && (reply?.dataSize() ?: 0) > 0) { + Log.w(TAG, "Method $code in $interfaceDescriptor is oneway, but returned data") + } + return true + } + Log.w(TAG, "Unknown method $code in $interfaceDescriptor, skipping") + return (flags and Binder.FLAG_ONEWAY) > 0 // Don't return false on oneway transaction to suppress warning +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/utils/FileXmlResourceParser.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/utils/FileXmlResourceParser.kt new file mode 100644 index 00000000..689d0f46 --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/utils/FileXmlResourceParser.kt @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.utils + +import android.content.res.XmlResourceParser +import android.util.Xml +import org.xmlpull.v1.XmlPullParser +import java.io.Closeable +import java.io.File +import java.io.FileReader +import java.io.Reader + +class FileXmlResourceParser(private val reader: Reader, private val parser: XmlPullParser = Xml.newPullParser()) : + XmlResourceParser, + XmlPullParser by parser, + Closeable by reader { + constructor(file: File) : this(FileReader(file)) + + init { + parser.setInput(reader) + } + + override fun getAttributeNameResource(index: Int): Int { + return 0 + } + + override fun getAttributeListValue( + namespace: String?, attribute: String?, + options: Array?, defaultValue: Int + ): Int { + val s = getAttributeValue(namespace, attribute) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeBooleanValue( + namespace: String?, attribute: String?, + defaultValue: Boolean + ): Boolean { + + val s = getAttributeValue(namespace, attribute) + return s?.toBooleanStrictOrNull() ?: defaultValue + } + + override fun getAttributeResourceValue( + namespace: String?, attribute: String?, + defaultValue: Int + ): Int { + val s = getAttributeValue(namespace, attribute) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeIntValue( + namespace: String?, attribute: String?, + defaultValue: Int + ): Int { + val s = getAttributeValue(namespace, attribute) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeUnsignedIntValue( + namespace: String?, attribute: String?, + defaultValue: Int + ): Int { + val s = getAttributeValue(namespace, attribute) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeFloatValue( + namespace: String?, attribute: String?, + defaultValue: Float + ): Float { + val s = getAttributeValue(namespace, attribute) + return s?.toFloat() ?: defaultValue + } + + override fun getAttributeListValue( + index: Int, + options: Array?, defaultValue: Int + ): Int { + val s = getAttributeValue(index) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeBooleanValue(index: Int, defaultValue: Boolean): Boolean { + val s = getAttributeValue(index) + return s?.toBooleanStrictOrNull() ?: defaultValue + } + + override fun getAttributeResourceValue(index: Int, defaultValue: Int): Int { + val s = getAttributeValue(index) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeIntValue(index: Int, defaultValue: Int): Int { + val s = getAttributeValue(index) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeUnsignedIntValue(index: Int, defaultValue: Int): Int { + val s = getAttributeValue(index) + return s?.toInt() ?: defaultValue + } + + override fun getAttributeFloatValue(index: Int, defaultValue: Float): Float { + val s = getAttributeValue(index) + return s?.toFloat() ?: defaultValue + } + + override fun getIdAttribute(): String? { + return getAttributeValue(null, "id") + } + + override fun getClassAttribute(): String? { + return getAttributeValue(null, "class") + } + + override fun getIdAttributeResourceValue(defaultValue: Int): Int { + return getAttributeResourceValue(null, "id", defaultValue) + } + + override fun getStyleAttribute(): Int { + return getAttributeResourceValue(null, "style", 0) + } +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsContract.kt b/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsContract.kt new file mode 100644 index 00000000..4f930962 --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsContract.kt @@ -0,0 +1,125 @@ +package org.microg.mgms.settings + +import android.content.ContentValues +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.os.Binder + +object SettingsContract { + fun getAuthority(context: Context) = "${context.packageName}.microg.settings" + fun getAuthorityUri(context: Context): Uri = Uri.parse("content://${getAuthority(context)}") + + object CheckIn { + private const val id = "check-in" + fun getContentUri(context: Context): Uri? = Uri.withAppendedPath(getAuthorityUri(context), id) + + const val ENABLED = "checkin_enable_service" + const val ANDROID_ID = "androidId" + const val DIGEST = "digest" + const val LAST_CHECK_IN = "lastCheckin" + const val SECURITY_TOKEN = "securityToken" + const val VERSION_INFO = "versionInfo" + const val DEVICE_DATA_VERSION_INFO = "deviceDataVersionInfo" + + const val BRAND_SPOOF = "brandSpoof" + + const val HIDE_LAUNCHER_ICON = "hideLauncherIcon" + + val PROJECTION = arrayOf( + ENABLED, + ANDROID_ID, + DIGEST, + LAST_CHECK_IN, + SECURITY_TOKEN, + VERSION_INFO, + DEVICE_DATA_VERSION_INFO, + BRAND_SPOOF, + HIDE_LAUNCHER_ICON, + ) + const val PREFERENCES_NAME = "checkin" + const val INITIAL_DIGEST = "1-929a0dca0eee55513280171a8585da7dcd3700f8" + } + + object Gcm { + private const val id = "gcm" + fun getContentUri(context: Context): Uri? = Uri.withAppendedPath(getAuthorityUri(context), id) + + const val FULL_LOG = "gcm_full_log" + const val LAST_PERSISTENT_ID = "gcm_last_persistent_id" + const val ENABLE_GCM = "gcm_enable_mcs_service" + + const val NETWORK_MOBILE = "gcm_network_mobile" + const val NETWORK_WIFI = "gcm_network_wifi" + const val NETWORK_ROAMING = "gcm_network_roaming" + const val NETWORK_OTHER = "gcm_network_other" + + const val LEARNT_MOBILE = "gcm_learnt_mobile" + const val LEARNT_WIFI = "gcm_learnt_wifi" + const val LEARNT_OTHER = "gcm_learnt_other" + + val PROJECTION = arrayOf( + FULL_LOG, + LAST_PERSISTENT_ID, + ENABLE_GCM, + NETWORK_MOBILE, + NETWORK_WIFI, + NETWORK_ROAMING, + NETWORK_OTHER, + LEARNT_MOBILE, + LEARNT_WIFI, + LEARNT_OTHER, + ) + } + + object Auth { + private const val id = "auth" + fun getContentUri(context: Context): Uri? = Uri.withAppendedPath(getAuthorityUri(context), id) + + const val TRUST_GOOGLE = "auth_manager_trust_google" + const val VISIBLE = "auth_manager_visible" + + val PROJECTION = arrayOf( + TRUST_GOOGLE, + VISIBLE, + ) + } + + object Profile { + private const val id = "profile" + fun getContentUri(context: Context): Uri? = Uri.withAppendedPath(getAuthorityUri(context), id) + + const val PROFILE = "device_profile" + const val SERIAL = "device_profile_serial" + + val PROJECTION = arrayOf( + PROFILE, + SERIAL + ) + } + + private fun withoutCallingIdentity(f: () -> T): T { + val identity = Binder.clearCallingIdentity() + try { + return f.invoke() + } finally { + Binder.restoreCallingIdentity(identity) + } + } + + @JvmStatic + fun getSettings(context: Context, uri: Uri, projection: Array?, f: (Cursor) -> T): T = withoutCallingIdentity { + context.contentResolver.query(uri, projection, null, null, null).use { c -> + require(c != null) { "Cursor for query $uri ${projection?.toList()} was null" } + if (!c.moveToFirst()) error("Cursor for query $uri ${projection?.toList()} was empty") + f.invoke(c) + } + } + + @JvmStatic + fun setSettings(context: Context, uri: Uri, v: ContentValues.() -> Unit) = withoutCallingIdentity { + val values = ContentValues().apply { v.invoke(this) } + val affected = context.contentResolver.update(uri, values, null, null) + require(affected == 1) { "Update for $uri with $values affected 0 rows"} + } +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsProvider.kt b/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsProvider.kt new file mode 100644 index 00000000..ba7e2e25 --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsProvider.kt @@ -0,0 +1,280 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +@file:Suppress("DEPRECATION") + +package org.microg.mgms.settings + +import android.annotation.SuppressLint +import android.content.ContentProvider +import android.content.ContentValues +import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.content.SharedPreferences +import android.database.Cursor +import android.database.MatrixCursor +import android.net.Uri +import android.preference.PreferenceManager +import org.microg.gms.common.PackageUtils.warnIfNotMainProcess +import org.microg.mgms.settings.SettingsContract.Auth +import org.microg.mgms.settings.SettingsContract.CheckIn +import org.microg.mgms.settings.SettingsContract.Gcm +import org.microg.mgms.settings.SettingsContract.Profile +import org.microg.mgms.settings.SettingsContract.getAuthority +import java.io.File + +/** + * All settings access should go through this [ContentProvider], + * because it provides safe access from different processes which normal [SharedPreferences] don't. + */ +@SuppressLint("DiscouragedPrivateApi") +class SettingsProvider : ContentProvider() { + companion object { + const val INTERVAL = 1 * 60 * 1000 // 1 minute + } + + private val preferences: SharedPreferences by lazy { + PreferenceManager.getDefaultSharedPreferences(context) + } + private val checkInPrefs by lazy { + context!!.getSharedPreferences(CheckIn.PREFERENCES_NAME, MODE_PRIVATE) + } + private val systemDefaultPreferences: SharedPreferences? by lazy { + try { + Context::class.java.getDeclaredMethod( + "getSharedPreferences", + File::class.java, + Int::class.javaPrimitiveType + ).invoke(context, File("/system/etc/microg.xml"), MODE_PRIVATE) as SharedPreferences + } catch (ignored: Exception) { + null + } + } + + override fun onCreate(): Boolean { + return true + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ): Cursor? = when (uri) { + CheckIn.getContentUri(context!!) -> queryCheckIn(projection ?: CheckIn.PROJECTION) + Gcm.getContentUri(context!!) -> queryGcm(projection ?: Gcm.PROJECTION) + Auth.getContentUri(context!!) -> queryAuth(projection ?: Auth.PROJECTION) + Profile.getContentUri(context!!) -> queryProfile(projection ?: Profile.PROJECTION) + else -> null + } + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array? + ): Int { + warnIfNotMainProcess(context, this.javaClass) + if (values == null) return 0 + when (uri) { + CheckIn.getContentUri(context!!) -> updateCheckIn(values) + Gcm.getContentUri(context!!) -> updateGcm(values) + Auth.getContentUri(context!!) -> updateAuth(values) + Profile.getContentUri(context!!) -> updateProfile(values) + else -> return 0 + } + return 1 + } + + private fun queryCheckIn(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + CheckIn.ENABLED -> getSettingsBoolean(key, true) + CheckIn.ANDROID_ID -> checkInPrefs.getLong(key, 0) + CheckIn.DIGEST -> checkInPrefs.getString(key, CheckIn.INITIAL_DIGEST) + ?: CheckIn.INITIAL_DIGEST + CheckIn.LAST_CHECK_IN -> checkInPrefs.getLong(key, 0) + CheckIn.SECURITY_TOKEN -> checkInPrefs.getLong(key, 0) + CheckIn.VERSION_INFO -> checkInPrefs.getString(key, "") ?: "" + CheckIn.DEVICE_DATA_VERSION_INFO -> checkInPrefs.getString(key, "") ?: "" + CheckIn.BRAND_SPOOF -> getSettingsBoolean(key, false) + CheckIn.HIDE_LAUNCHER_ICON -> getSettingsBoolean(key, false) + else -> throw IllegalArgumentException() + } + } + + private fun updateCheckIn(values: ContentValues) { + if (values.size() == 0) return + if (values.size() == 1 && values.containsKey(CheckIn.ENABLED)) { + // special case: only changing enabled state + updateCheckInEnabled(values.getAsBoolean(CheckIn.ENABLED)) + return + } + val editor = checkInPrefs.edit() + values.valueSet().forEach { (key, value) -> + if (key == CheckIn.ENABLED) { + // special case: not saved in checkInPrefs + updateCheckInEnabled(value as Boolean) + } + if (key == CheckIn.BRAND_SPOOF) { + // special case: not saved in checkInPrefs + updateSpoofingEnabled(value as Boolean) + } + if (key == CheckIn.HIDE_LAUNCHER_ICON) { + // special case: not saved in checkInPrefs + updateHideLauncherIcon(value as Boolean) + } + when (key) { + CheckIn.ANDROID_ID -> editor.putLong(key, value as Long) + CheckIn.DIGEST -> editor.putString(key, value as String?) + CheckIn.LAST_CHECK_IN -> editor.putLong(key, value as Long) + CheckIn.SECURITY_TOKEN -> editor.putLong(key, value as Long) + CheckIn.VERSION_INFO -> editor.putString(key, value as String?) + CheckIn.DEVICE_DATA_VERSION_INFO -> editor.putString(key, value as String?) + } + } + editor.apply() + } + + private fun updateCheckInEnabled(enabled: Boolean) { + preferences.edit() + .putBoolean(CheckIn.ENABLED, enabled) + .apply() + } + + private fun updateSpoofingEnabled(enabled: Boolean) { + preferences.edit() + .putBoolean(CheckIn.BRAND_SPOOF, enabled) + .apply() + } + + private fun updateHideLauncherIcon(enabled: Boolean) { + preferences.edit() + .putBoolean(CheckIn.HIDE_LAUNCHER_ICON, enabled) + .apply() + } + + private fun queryGcm(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Gcm.ENABLE_GCM -> getSettingsBoolean(key, true) + Gcm.FULL_LOG -> getSettingsBoolean(key, true) + + Gcm.LAST_PERSISTENT_ID -> preferences.getString(key, "") ?: "" + + Gcm.NETWORK_MOBILE -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + Gcm.NETWORK_WIFI -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + Gcm.NETWORK_ROAMING -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + Gcm.NETWORK_OTHER -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + + Gcm.LEARNT_MOBILE -> preferences.getInt(key, INTERVAL) + Gcm.LEARNT_WIFI -> preferences.getInt(key, INTERVAL) + Gcm.LEARNT_OTHER -> preferences.getInt(key, INTERVAL) + + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateGcm(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Gcm.ENABLE_GCM -> editor.putBoolean(key, value as Boolean) + Gcm.FULL_LOG -> editor.putBoolean(key, value as Boolean) + + Gcm.LAST_PERSISTENT_ID -> editor.putString(key, value as String?) + + Gcm.NETWORK_MOBILE -> editor.putString(key, (value as Int).toString()) + Gcm.NETWORK_WIFI -> editor.putString(key, (value as Int).toString()) + Gcm.NETWORK_ROAMING -> editor.putString(key, (value as Int).toString()) + Gcm.NETWORK_OTHER -> editor.putString(key, (value as Int).toString()) + + Gcm.LEARNT_MOBILE -> editor.putInt(key, value as Int) + Gcm.LEARNT_WIFI -> editor.putInt(key, value as Int) + Gcm.LEARNT_OTHER -> editor.putInt(key, value as Int) + + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryAuth(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Auth.TRUST_GOOGLE -> getSettingsBoolean(key, true) + Auth.VISIBLE -> getSettingsBoolean(key, false) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateAuth(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Auth.TRUST_GOOGLE -> editor.putBoolean(key, value as Boolean) + Auth.VISIBLE -> editor.putBoolean(key, value as Boolean) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryProfile(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Profile.PROFILE -> getSettingsString(key, "auto") + Profile.SERIAL -> getSettingsString(key) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateProfile(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Profile.PROFILE -> editor.putString(key, value as String?) + Profile.SERIAL -> editor.putString(key, value as String?) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun MatrixCursor.addRow( + p: Array, + valueGetter: (String) -> Any? + ): MatrixCursor { + val row = newRow() + for (key in p) row.add(valueGetter.invoke(key)) + return this + } + + override fun getType(uri: Uri): String { + return "vnd.android.cursor.item/vnd.${getAuthority(context!!)}.${uri.path}" + } + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + throw UnsupportedOperationException() + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + throw UnsupportedOperationException() + } + + /** + * Returns the current setting of the given [key] + * using the default value from [systemDefaultPreferences] or [def] if not available. + * @return the current setting as [Int], because [ContentProvider] does not support [Boolean]. + */ + private fun getSettingsBoolean(key: String, def: Boolean): Int { + return listOf(preferences, systemDefaultPreferences).getBooleanAsInt(key, def) + } + + private fun getSettingsString(key: String, def: String? = null): String? = listOf(preferences, systemDefaultPreferences).getString(key, def) + + private fun List.getString(key: String, def: String?): String? = foldRight(def) { preferences, defValue -> preferences?.getString(key, defValue) ?: defValue } + private fun List.getBoolean(key: String, def: Boolean): Boolean = foldRight(def) { preferences, defValue -> preferences?.getBoolean(key, defValue) ?: defValue } + private fun List.getBooleanAsInt(key: String, def: Boolean): Int = if (getBoolean(key, def)) 1 else 0 +} diff --git a/play-services-base-core/src/main/res/drawable/ic_foreground_notification.xml b/play-services-base-core/src/main/res/drawable/ic_foreground_notification.xml new file mode 100644 index 00000000..ace07f8a --- /dev/null +++ b/play-services-base-core/src/main/res/drawable/ic_foreground_notification.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/play-services-base-core/src/main/res/values-ar/strings.xml b/play-services-base-core/src/main/res/values-ar/strings.xml new file mode 100644 index 00000000..e6947e00 --- /dev/null +++ b/play-services-base-core/src/main/res/values-ar/strings.xml @@ -0,0 +1,11 @@ + + + + تحذير توفير البطارية + توسيع الإشعارات + الحفاظ على microG يعمل في الخلفية + انقر هنا لتعطيل تحسين البطارية لـ vanced microG ، ثم أعد تشغيل الجهاز لإزالة هذا الإشعار + diff --git a/play-services-base-core/src/main/res/values-az/strings.xml b/play-services-base-core/src/main/res/values-az/strings.xml new file mode 100644 index 00000000..c24e9758 --- /dev/null +++ b/play-services-base-core/src/main/res/values-az/strings.xml @@ -0,0 +1,11 @@ + + + + Batareya qənaət xəbərdarlığı + Bildirişi genişləndir + MicroG-ni arxa planda saxla + MicroG üçün batareyanın optimallaşdırılmasını deaktiv etmək üçün bura klikləyin, sonra bu bildirişi silmək üçün cihazı yenidən başladın + diff --git a/play-services-base-core/src/main/res/values-be/strings.xml b/play-services-base-core/src/main/res/values-be/strings.xml new file mode 100644 index 00000000..d887a045 --- /dev/null +++ b/play-services-base-core/src/main/res/values-be/strings.xml @@ -0,0 +1,11 @@ + + + + Папярэджанне аб працы ў рэжыме эканоміі энергіі + Разгарнуць апавяшчэнне + Трымаць microG у фоне + Націсніце тут, каб адключыць аптымізацыю батарэі для Vanced microG, а потым перазапусціце прыладу, каб гэта паведамленне знікла + diff --git a/play-services-base-core/src/main/res/values-bn/strings.xml b/play-services-base-core/src/main/res/values-bn/strings.xml new file mode 100644 index 00000000..34114e10 --- /dev/null +++ b/play-services-base-core/src/main/res/values-bn/strings.xml @@ -0,0 +1,11 @@ + + + + ব্যাটারি সংরক্ষণ সতর্কতা + বিজ্ঞপ্তি প্রসারিত করুন + microG ব্যাকগ্রাউন্ডে রাখুন + vanced microG কে ব্যাটারি অপটিমাইজেশন থেকে সরাতে এখানে ট্যাপ করুন, এরপর এই বিজ্ঞপ্তি সরাতে ফোন রিস্টার্ট করুন + diff --git a/play-services-base-core/src/main/res/values-de/strings.xml b/play-services-base-core/src/main/res/values-de/strings.xml new file mode 100644 index 00000000..0ff5c5e0 --- /dev/null +++ b/play-services-base-core/src/main/res/values-de/strings.xml @@ -0,0 +1,10 @@ + + + + Battery Saving Warning + Expand notification + Keep microG in background + diff --git a/play-services-base-core/src/main/res/values-es-rES/strings.xml b/play-services-base-core/src/main/res/values-es-rES/strings.xml new file mode 100644 index 00000000..3ecced44 --- /dev/null +++ b/play-services-base-core/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,11 @@ + + + + Advertencia de ahorro de batería + Expandir notificación + Mantener microG en segundo plano + Toque aquí para desactivar la optimización de la batería para vanced microG, luego reinicie el dispositivo para eliminar esta notificación + diff --git a/play-services-base-core/src/main/res/values-fr/strings.xml b/play-services-base-core/src/main/res/values-fr/strings.xml new file mode 100644 index 00000000..09ccaafa --- /dev/null +++ b/play-services-base-core/src/main/res/values-fr/strings.xml @@ -0,0 +1,11 @@ + + + + Avertissement d\'économie de batterie + Élargir la notification + Garder microG en arrière-plan + Appuyez ici pour désactiver l\'optimisation de la batterie pour la microG vanced, puis redémarrez l\'appareil pour supprimer cette notification + diff --git a/play-services-base-core/src/main/res/values-he/strings.xml b/play-services-base-core/src/main/res/values-he/strings.xml new file mode 100644 index 00000000..ea887fa9 --- /dev/null +++ b/play-services-base-core/src/main/res/values-he/strings.xml @@ -0,0 +1,11 @@ + + + + אזהרת חיסכון בסוללה + הרחב את ההודעה + השאר את microG ברקע + הקש כאן כדי להשבית את אופטימיזציית הסוללה עבור vanced microG, ולאחר מכן הפעל מחדש את המכשיר כדי להסיר את ההתראה הזו + diff --git a/play-services-base-core/src/main/res/values-hi/strings.xml b/play-services-base-core/src/main/res/values-hi/strings.xml new file mode 100644 index 00000000..91551946 --- /dev/null +++ b/play-services-base-core/src/main/res/values-hi/strings.xml @@ -0,0 +1,11 @@ + + + + बैटरी बचाने की चेतावनी + अधिसूचना का विस्तार करें + माइक्रो जी को बैकग्राउंड में रखें + वैन्ड माइक्रोजी के लिए बैटरी अनुकूलन को अक्षम करने के लिए यहां टैप करें, फिर इस अधिसूचना को हटाने के लिए डिवाइस को पुनरारंभ करें + diff --git a/play-services-base-core/src/main/res/values-hu/strings.xml b/play-services-base-core/src/main/res/values-hu/strings.xml new file mode 100644 index 00000000..8a6badd3 --- /dev/null +++ b/play-services-base-core/src/main/res/values-hu/strings.xml @@ -0,0 +1,11 @@ + + + + Akkumulátorkímélő figyelmeztetés + Értesítés bővítése + Tartsa a microG-t a háttérben + Koppintson ide az akkumulátor-optimalizálás letiltásához a vanced microG számára, majd indítsa újra a készüléket az értesítés eltávolításához + diff --git a/play-services-base-core/src/main/res/values-id/strings.xml b/play-services-base-core/src/main/res/values-id/strings.xml new file mode 100644 index 00000000..11599ce3 --- /dev/null +++ b/play-services-base-core/src/main/res/values-id/strings.xml @@ -0,0 +1,11 @@ + + + + Peringatan daya baterai + Perluas notifikasi + Izinkan microG berjalan di latar belakang + Tekan disini untuk mematikan optimasi baterai untuk vanced microG, lalu mulai ulang perangkat untuk menghapus notifikasi ini + diff --git a/play-services-base-core/src/main/res/values-it/strings.xml b/play-services-base-core/src/main/res/values-it/strings.xml new file mode 100644 index 00000000..420ece24 --- /dev/null +++ b/play-services-base-core/src/main/res/values-it/strings.xml @@ -0,0 +1,11 @@ + + + + Avviso Risparmio Energetico + Espandi la notifica + Mantieni i microG in secondo piano + Tocca qui per disattivare l\'ottimizzazione della batteria per microG, poi riavvia il dispositivo per rimuovere questa notifica. + diff --git a/play-services-base-core/src/main/res/values-ja/strings.xml b/play-services-base-core/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..97f279c1 --- /dev/null +++ b/play-services-base-core/src/main/res/values-ja/strings.xml @@ -0,0 +1,11 @@ + + + + 電池の最適化 の警告 + 通知を展開する + Vanced microGをバックグラウンドで保持 + ここをタップして、Vanced microGの[電池の最適化]を無効にし、デバイスを再起動して通知を削除してください。 + diff --git a/play-services-base-core/src/main/res/values-ko/strings.xml b/play-services-base-core/src/main/res/values-ko/strings.xml new file mode 100644 index 00000000..20515386 --- /dev/null +++ b/play-services-base-core/src/main/res/values-ko/strings.xml @@ -0,0 +1,11 @@ + + + + 배터리 절약 경고 + 알림 펼치기 + microG를 백그라운드에서 유지 + 여기를 탭하여 vanced microG의 배터리 최적화를 비활성화한 다음 기기를 다시 시작하여 이 알림을 제거하세요 + diff --git a/play-services-base-core/src/main/res/values-lt/strings.xml b/play-services-base-core/src/main/res/values-lt/strings.xml new file mode 100644 index 00000000..02dfd797 --- /dev/null +++ b/play-services-base-core/src/main/res/values-lt/strings.xml @@ -0,0 +1,11 @@ + + + + Baterijos taupymo įspėjimas + Išskleisti pranešimą + Laikyti microG fone + Bakstelėkite čia, kad išjungtumėte microG akumuliatoriaus optimizavimą, tada iš naujo paleiskite įrenginį, kad pašalintumėte šį pranešimą + diff --git a/play-services-base-core/src/main/res/values-pt-rBR/strings.xml b/play-services-base-core/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 00000000..c0440e52 --- /dev/null +++ b/play-services-base-core/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,11 @@ + + + + Aviso de carregamento da bateria + Expandir notificações + Manter o microG em segundo plano + Toque aqui para desativar a otimização de bateria para microG. Tancado e reinicie o dispositivo para remover esta notificação + diff --git a/play-services-base-core/src/main/res/values-ru/strings.xml b/play-services-base-core/src/main/res/values-ru/strings.xml new file mode 100644 index 00000000..169bde62 --- /dev/null +++ b/play-services-base-core/src/main/res/values-ru/strings.xml @@ -0,0 +1,11 @@ + + + + Предупреждение о работе в режиме энергосбережения + Развернуть уведомление + Держать microG в фоне + Нажмите здесь, чтобы отключить оптимизацию батареи для vanced microG, а затем перезапустите устройство, чтобы это уведомление пропало + diff --git a/play-services-base-core/src/main/res/values-th/strings.xml b/play-services-base-core/src/main/res/values-th/strings.xml new file mode 100644 index 00000000..6b918bea --- /dev/null +++ b/play-services-base-core/src/main/res/values-th/strings.xml @@ -0,0 +1,11 @@ + + + + คำเตือนการประหยัดแบตเตอรี่ + ขยายการแจ้งเตือน + ให้ microG ทำงานเบื้องหลัง + แตะที่นี่เพื่อปิดใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่สำหรับ vanced microG จากนั้นรีสตาร์ทอุปกรณ์เพื่อลบการแจ้งเตือนนี้  + diff --git a/play-services-base-core/src/main/res/values-tr/strings.xml b/play-services-base-core/src/main/res/values-tr/strings.xml new file mode 100644 index 00000000..7ab0c17f --- /dev/null +++ b/play-services-base-core/src/main/res/values-tr/strings.xml @@ -0,0 +1,11 @@ + + + + Pil tasarrufu uyarısı + Bildirimleri genişlet + MicroG arka planda çalışsın + Vanced microG için batarya optimizasyonunu devre dışı bırakmak için buraya tıklayın, daha sonra bu bildirimi silmek için cihazı yeniden başlatın + diff --git a/play-services-base-core/src/main/res/values-uk/strings.xml b/play-services-base-core/src/main/res/values-uk/strings.xml new file mode 100644 index 00000000..4d49f0eb --- /dev/null +++ b/play-services-base-core/src/main/res/values-uk/strings.xml @@ -0,0 +1,11 @@ + + + + Попередження про економію батареї + Розгорнути сповіщення + Тримайте microG у фоновому режимі + Торкніться тут, щоб вимкнути оптимізацію акумулятора для vanced microG, а потім перезапустіть пристрій, щоб видалити це сповіщення + diff --git a/play-services-base-core/src/main/res/values-vi/strings.xml b/play-services-base-core/src/main/res/values-vi/strings.xml new file mode 100644 index 00000000..6df24ce4 --- /dev/null +++ b/play-services-base-core/src/main/res/values-vi/strings.xml @@ -0,0 +1,11 @@ + + + + Cảnh báo tối ưu hóa pin + Mở rộng thông báo + Giữ Vanced microG chạy trong nền + Nhấn vào đây để tắt tối ưu hóa pin cho Vanced microG, sau đó khởi động lại thiết bị hoặc buộc dừng ứng dụng để xóa thông báo này + diff --git a/play-services-base-core/src/main/res/values-zh-rCN/strings.xml b/play-services-base-core/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 00000000..3afca6ef --- /dev/null +++ b/play-services-base-core/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,11 @@ + + + + 省电警告 + 展开通知 + 保持 microG 后台服务 + 点此关闭 vanced microG 的电池最适化,然后重启手机以移除该通知 + diff --git a/play-services-base-core/src/main/res/values-zh-rTW/strings.xml b/play-services-base-core/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 00000000..4e03cda1 --- /dev/null +++ b/play-services-base-core/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,12 @@ + + + + 電池最佳化警示 + 展開完整通知 + 讓microG在背景執行 + 點擊此通知來關閉Vanced microG的電池最佳化 +關閉後重新啟動手機以移除此通知 + diff --git a/play-services-base-core/src/main/res/values/strings.xml b/play-services-base-core/src/main/res/values/strings.xml new file mode 100644 index 00000000..55d77535 --- /dev/null +++ b/play-services-base-core/src/main/res/values/strings.xml @@ -0,0 +1,12 @@ + + + + + Battery Saving Warning + Expand notification + Keep Vanced microG in background + Tap here to disable battery optimization for Vanced microG, then restart the device to remove this notification + diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle new file mode 100644 index 00000000..7c7057d4 --- /dev/null +++ b/play-services-base/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + namespace 'org.microg.gms.base' +} + +dependencies { + api project(':play-services-base-api') + + // Dependencies from play-services-base:18.1.0 + api "androidx.collection:collection:$collectionVersion" + api "androidx.core:core:$coreVersion" + api "androidx.fragment:fragment:$fragmentVersion" + api project(':play-services-basement') + api project(':play-services-tasks') +} \ No newline at end of file diff --git a/play-services-base/gradle.properties b/play-services-base/gradle.properties new file mode 100644 index 00000000..6949ecb1 --- /dev/null +++ b/play-services-base/gradle.properties @@ -0,0 +1,33 @@ +# +# Copyright 2013-2016 microG Project Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +POM_NAME=Play Services Library Base +POM_DESCRIPTION=Base classes used by all Play Services Library modules + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W \ No newline at end of file diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c31f1c05 --- /dev/null +++ b/play-services-base/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java new file mode 100644 index 00000000..61ee1ec0 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common; + +import android.app.Dialog; + +import org.microg.gms.common.PublicApi; + +@PublicApi +public class GoogleApiAvailability { + + private GoogleApiAvailability() { + } + + private static final class InstanceHolder { + static final GoogleApiAvailability instance = new GoogleApiAvailability(); + } + + /** + * Returns the singleton instance of GoogleApiAvailability. + */ + public static GoogleApiAvailability getInstance() { + return InstanceHolder.instance; + } + + + /** + * Returns a dialog to address the provided errorCode. The returned dialog displays a localized + * message about the error and upon user confirmation (by tapping on dialog) will direct them + * to the Play Store if Google Play services is out of date or missing, or to system settings + * if Google Play services is disabled on the device. + * + */ + public Dialog getErrorDialog() { + // TODO + return null; + } + +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java new file mode 100644 index 00000000..0941e410 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common; + +import android.os.Bundle; + +@Deprecated +public interface GooglePlayServicesClient { + void connect(); + + void disconnect(); + + boolean isConnected(); + + @Deprecated + interface OnConnectionFailedListener { + + void onConnectionFailed(ConnectionResult result); + } + + @Deprecated + interface ConnectionCallbacks { + + void onConnected(Bundle connectionHint); + + void onDisconnected(); + } +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java new file mode 100644 index 00000000..d5399280 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common; + +import android.app.Dialog; +import android.app.PendingIntent; +import android.util.Log; + +import org.microg.gms.common.Constants; +import org.microg.gms.common.PublicApi; + +/** + * Utility class for verifying that the Google Play services APK is available and up-to-date on + * this device. The same checks are performed if one uses {@link AdvertisingIdClient} or + * {@link GoogleAuthUtil} to connect to the service. + *

+ * TODO: methods :) + */ +@PublicApi +public class GooglePlayServicesUtil { + private static final String TAG = "GooglePlayServicesUtil"; + + /** + * Package name for Google Play services. + */ + @Deprecated + public static final String GOOGLE_PLAY_SERVICES_PACKAGE = Constants.GMS_PACKAGE_NAME; + + /** + * Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode). + */ + @Deprecated + public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.GMS_VERSION_CODE; + + /** + * Returns a dialog to address the provided errorCode. The returned dialog displays a localized + * message about the error and upon user confirmation (by tapping on dialog) will direct them + * to the Play Store if Google Play services is out of date or missing, or to system settings + * if Google Play services is disabled on the device. + * + */ + @Deprecated + public static Dialog getErrorDialog() { + return GoogleApiAvailability.getInstance().getErrorDialog(); + } + + /** + * Returns a PendingIntent to address the provided errorCode. It will direct them to one of the + * following places to either the Play Store if Google Play services is out of date or missing, + * or system settings if Google Play services is disabled on the device. + * + */ + @Deprecated + public static PendingIntent getErrorPendingIntent() { + return null; // TODO + } + + /** + * Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable()}. + */ + @Deprecated + public static String getErrorString() { + return null; // TODO + } + + /** + * Returns the open source software license information for the Google Play services + * application, or null if Google Play services is not available on this device. + */ + @Deprecated + public static String getOpenSourceSoftwareLicenseInfo() { + return null; // TODO + } + + /** + * Verifies that Google Play services is installed and enabled on this device, and that the + * version installed on this device is no older than the one required by this client. + * + * @return status code indicating whether there was an error. Can be one of following in + * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_VERSION_UPDATE_REQUIRED, + * SERVICE_DISABLED, SERVICE_INVALID + */ + @Deprecated + public static int isGooglePlayServicesAvailable() { + Log.d(TAG, "As we can't know right now if the later desired feature is available, " + + "we just pretend it to be."); + return ConnectionResult.SUCCESS; + } + + @Deprecated + public static boolean isGoogleSignedUid() { + return false; // TODO + } + + /** + * Determines whether an error is user-recoverable. If true, proceed by calling + * {@link #getErrorDialog()} and showing the dialog. + * + * @return true if the error is recoverable with {@link #getErrorDialog()} + */ + @Deprecated + public static boolean isUserRecoverableError() { + return false; // TODO + } + + @Deprecated + public static boolean showErrorDialogFragment() { + return false; // TODO + } + + /** + * Displays a notification relevant to the provided error code. This method is similar to + * {@link #getErrorDialog()}, but is provided for background + * tasks that cannot or shouldn't display dialogs. + * + */ + @Deprecated + public static void showErrorNotification() { + // TODO + } +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java b/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java new file mode 100644 index 00000000..fd400c45 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import org.microg.safeparcel.AutoSafeParcelable; + +@Deprecated +public class AccountInfo extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(AccountInfo.class); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java new file mode 100644 index 00000000..6b7f15e3 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.common.api.ApiClientBuilder; + +/** + * Describes a section of the Google Play Services API that should be made available. Instances of + * this should be passed into {@link GoogleApiClient.Builder#addApi(Api)} to enable the appropriate + * parts of Google Play Services. + *

+ * Google APIs are partitioned into sections which allow your application to configure only the + * services it requires. Each Google API provides an API object which can be passed to + * {@link GoogleApiClient.Builder#addApi(Api)} in order to configure and enable that functionality + * in your {@link GoogleApiClient} instance. + *

+ * See {@link GoogleApiClient.Builder} for usage examples. + */ +@PublicApi +public final class Api { + private final ApiClientBuilder builder; + + @PublicApi(exclude = true) + public Api(ApiClientBuilder builder) { + this.builder = builder; + } + + @PublicApi(exclude = true) + public ApiClientBuilder getBuilder() { + return builder; + } + + /** + * Base interface for API options. These are used to configure specific parameters for + * individual API surfaces. The default implementation has no parameters. + */ + @PublicApi + public interface ApiOptions { + /** + * Base interface for {@link ApiOptions} in {@link Api}s that have options. + */ + @PublicApi + interface HasOptions extends ApiOptions { + } + + /** + * Base interface for {@link ApiOptions} that are not required, don't exist. + */ + @PublicApi + interface NotRequiredOptions extends ApiOptions { + } + + } + +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java new file mode 100644 index 00000000..f0adb967 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.api; + +import android.content.Context; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.common.api.GoogleApiManager; + +@PublicApi +public abstract class GoogleApi implements HasApiKey { + @PublicApi(exclude = true) + public Api api; + + @PublicApi(exclude = true) + protected GoogleApi(Context context, Api api) { + this.api = api; + GoogleApiManager.getInstance(); + } + + @PublicApi(exclude = true) + public O getOptions() { + return null; + } +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java new file mode 100644 index 00000000..8a6b0690 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java @@ -0,0 +1,6 @@ +package com.google.android.gms.common.api; + +import android.app.Activity; + +public class GoogleApiActivity extends Activity { +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java new file mode 100644 index 00000000..0e20b6d5 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.os.Looper; + +import com.google.android.gms.common.ConnectionResult; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.common.api.ApiClientSettings; +import org.microg.gms.common.api.GoogleApiClientImpl; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * The main entry point for Google Play services integration. + *

+ * GoogleApiClient is used with a variety of static methods. Some of these methods require that + * GoogleApiClient be connected, some will queue up calls before GoogleApiClient is connected; + * check the specific API documentation to determine whether you need to be connected. + *

+ * Before any operation is executed, the GoogleApiClient must be connected using the + * {@link #connect()} method. The client is not considered connected until the + * {@link ConnectionCallbacks#onConnected(Bundle)} callback has been called. + *

+ * When your app is done using this client, call {@link #disconnect()}, even if the async result + * from {@link #connect()} has not yet been delivered. + *

+ * You should instantiate a client object in your Activity's {@link Activity#onCreate(Bundle)} + * method and then call {@link #connect()} in {@link Activity#onStart()} and {@link #disconnect()} + * in {@link Activity#onStop()}, regardless of the state. + */ +@PublicApi +@Deprecated +public interface GoogleApiClient { + + /** + * Connects the client to Google Play services. This method returns immediately, and connects + * to the service in the background. If the connection is successful, + * {@link ConnectionCallbacks#onConnected(Bundle)} is called and enqueued items are executed. + * On a failure, {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} is + * called. + */ + void connect(); + + /** + * Closes the connection to Google Play services. No calls can be made using this client after + * calling this method. Any method calls that haven't executed yet will be canceled. That is + * {@link ResultCallback#onResult(Result)} won't be called, if connection to the service hasn't + * been established yet all calls already made will be canceled. + * + * @see #connect() + */ + void disconnect(); + + /** + * Checks if the client is currently connected to the service, so that requests to other + * methods will succeed. Applications should guard client actions caused by the user with a + * call to this method. + * + * @return {@code true} if the client is connected to the service. + */ + boolean isConnected(); + + /** + * Checks if the client is attempting to connect to the service. + * + * @return {@code true} if the client is attempting to connect to the service. + */ + boolean isConnecting(); + + /** + * Returns {@code true} if the specified listener is currently registered to receive connection + * events. + * + * @param listener The listener to check for. + * @return {@code true} if the specified listener is currently registered to receive connection + * events. + * @see #registerConnectionCallbacks(ConnectionCallbacks) + * @see #unregisterConnectionCallbacks(ConnectionCallbacks) + */ + boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener); + + /** + * Returns {@code true} if the specified listener is currently registered to receive connection + * failed events. + * + * @param listener The listener to check for. + * @return {@code true} if the specified listener is currently registered to receive connection + * failed events. + * @see #registerConnectionFailedListener(OnConnectionFailedListener) + * @see #unregisterConnectionFailedListener(OnConnectionFailedListener) + */ + boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener); + + /** + * Registers a listener to receive connection events from this {@link GoogleApiClient}. If the + * service is already connected, the listener's {@link ConnectionCallbacks#onConnected(Bundle)} + * method will be called immediately. Applications should balance calls to this method with + * calls to {@link #unregisterConnectionCallbacks(ConnectionCallbacks)} to avoid leaking + * resources. + *

+ * If the specified listener is already registered to receive connection events, this method + * will not add a duplicate entry for the same listener, but will still call the listener's + * {@link ConnectionCallbacks#onConnected(Bundle)} method if currently connected. + *

+ * Note that the order of messages received here may not be stable, so clients should not rely + * on the order that multiple listeners receive events in. + * + * @param listener the listener where the results of the asynchronous {@link #connect()} call + * are delivered. + */ + void registerConnectionCallbacks(ConnectionCallbacks listener); + + /** + * Registers a listener to receive connection failed events from this {@link GoogleApiClient}. + * Unlike {@link #registerConnectionCallbacks(ConnectionCallbacks)}, if the service is not + * already connected, the listener's + * {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} method will not be + * called immediately. Applications should balance calls to this method with calls to + * {@link #unregisterConnectionFailedListener(OnConnectionFailedListener)} to avoid leaking + * resources. + *

+ * If the specified listener is already registered to receive connection failed events, this + * method will not add a duplicate entry for the same listener. + *

+ * Note that the order of messages received here may not be stable, so clients should not rely + * on the order that multiple listeners receive events in. + * + * @param listener the listener where the results of the asynchronous {@link #connect()} call + * are delivered. + */ + void registerConnectionFailedListener(OnConnectionFailedListener listener); + + /** + * Removes a connection listener from this {@link GoogleApiClient}. Note that removing a + * listener does not generate any callbacks. + *

+ * If the specified listener is not currently registered to receive connection events, this + * method will have no effect. + * + * @param listener the listener to unregister. + */ + void unregisterConnectionCallbacks(ConnectionCallbacks listener); + + @PublicApi + class Builder { + private final Context context; + private final Map apis = new HashMap<>(); + private final Set connectionCallbacks = new HashSet<>(); + private final Set connectionFailedListeners = new HashSet<>(); + private final Looper looper; + + public Builder(Context context) { + this.context = context; + this.looper = context.getMainLooper(); + } + + /** + * Specify which Apis are requested by your app. See {@link Api} for more information. + * + * @param api The Api requested by your app. + * @see Api + */ + public Builder addApi(Api api) { + apis.put(api, null); + return this; + } + + /** + * Builds a new {@link GoogleApiClient} object for communicating with the Google APIs. + * + * @return The {@link GoogleApiClient} object. + */ + public GoogleApiClient build() { + int clientId = -1; + return new GoogleApiClientImpl(context, looper, getClientSettings(), apis, connectionCallbacks, connectionFailedListeners); + } + + private ApiClientSettings getClientSettings() { + return null; + } + + } + + /** + * Provides callbacks that are called when the client is connected or disconnected from the + * service. Most applications implement {@link #onConnected(Bundle)} to start making requests. + */ + @PublicApi + @Deprecated + interface ConnectionCallbacks extends org.microg.gms.common.api.ConnectionCallbacks { + } + + @PublicApi + @Deprecated + interface OnConnectionFailedListener extends org.microg.gms.common.api.OnConnectionFailedListener { + } +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/HasApiKey.java b/play-services-base/src/main/java/com/google/android/gms/common/api/HasApiKey.java new file mode 100644 index 00000000..642a636d --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/HasApiKey.java @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.api; + +public interface HasApiKey { +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java new file mode 100644 index 00000000..e5f92160 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +public interface PendingResult { +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/internal/ApiKey.java b/play-services-base/src/main/java/com/google/android/gms/common/api/internal/ApiKey.java new file mode 100644 index 00000000..b679b2b9 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/internal/ApiKey.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.api.internal; + +public class ApiKey { + + public ApiKey() { + } +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java b/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java new file mode 100644 index 00000000..907aee2d --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.data; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.api.Releasable; + +import org.microg.gms.common.PublicApi; + +import java.util.Iterator; + +/** + * TODO + */ +@PublicApi +public abstract class DataBuffer implements Releasable, Iterable { + + @PublicApi(exclude = true) + public DataBuffer() { + } + + /** + * @deprecated use {@link #release()} instead + */ + @Deprecated + public final void close() { + release(); + } + + /** + * @deprecated {@link #release()} is idempotent, and so is safe to call multiple times + */ + @Deprecated + public boolean isClosed() { + return false; + } + + @NonNull + @Override + public Iterator iterator() { + return null; + } + + /** + * Releases resources used by the buffer. This method is idempotent. + */ + @Override + public void release() { + + } + +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java new file mode 100644 index 00000000..cea4229b --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common.api; + +import android.os.Looper; + +import com.google.android.gms.common.api.PendingResult; + +public class AbstractPendingResult implements PendingResult { + + public AbstractPendingResult(Looper looper) { + new ResultCallbackHandler<>(looper); + } + +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java new file mode 100644 index 00000000..602e150e --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common.api; + +public interface ApiClient { + void connect(); + + void disconnect(); + + boolean isConnected(); + + boolean isConnecting(); +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java new file mode 100644 index 00000000..b5aa386f --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common.api; + +import android.content.Context; +import android.os.Looper; + +import com.google.android.gms.common.api.Api; + +public interface ApiClientBuilder { + ApiClient build(O options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener); +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java new file mode 100644 index 00000000..d1e314a7 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +public class ApiClientSettings { +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ConnectionCallbacks.java b/play-services-base/src/main/java/org/microg/gms/common/api/ConnectionCallbacks.java new file mode 100644 index 00000000..738a43d3 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ConnectionCallbacks.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import android.os.Bundle; + +import com.google.android.gms.common.api.GoogleApiClient; + +public interface ConnectionCallbacks { + + /** + * After calling {@link #connect()}, this method will be invoked asynchronously when the + * connect request has successfully completed. After this callback, the application can + * make requests on other methods provided by the client and expect that no user + * intervention is required to call methods that use account and scopes provided to the + * client constructor. + *

+ * Note that the contents of the {@code connectionHint} Bundle are defined by the specific + * services. Please see the documentation of the specific implementation of + * {@link GoogleApiClient} you are using for more information. + * + * @param connectionHint Bundle of data provided to clients by Google Play services. May + * be null if no content is provided by the service. + */ + void onConnected(Bundle connectionHint); + + /** + * Called when the client is temporarily in a disconnected state. This can happen if there + * is a problem with the remote service (e.g. a crash or resource problem causes it to be + * killed by the system). When called, all requests have been canceled and no outstanding + * listeners will be executed. GoogleApiClient will automatically attempt to restore the + * connection. Applications should disable UI components that require the service, and wait + * for a call to {@link #onConnected(Bundle)} to re-enable them. + * + * @param cause The reason for the disconnection. Defined by constants {@code CAUSE_*}. + */ + void onConnectionSuspended(int cause); +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java new file mode 100644 index 00000000..371a6555 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common.api; + +import android.content.Context; +import android.os.Bundle; +import android.os.Looper; +import android.util.Log; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class GoogleApiClientImpl implements GoogleApiClient { + private static final String TAG = "GmsApiClientImpl"; + + private final Map apiConnections = new HashMap<>(); + private final Set connectionCallbacks = new HashSet<>(); + private final Set connectionFailedListeners = new HashSet<>(); + private boolean shouldDisconnect = false; + + public GoogleApiClientImpl(Context context, Looper looper, ApiClientSettings clientSettings, + Map apis, + Set connectionCallbacks, + Set connectionFailedListeners) { + this.connectionCallbacks.addAll(connectionCallbacks); + this.connectionFailedListeners.addAll(connectionFailedListeners); + + for (Api api : apis.keySet()) { + ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { + @Override + public void onConnected(Bundle connectionHint) { + Log.d(TAG, "ConnectionCallbacks : onConnected()"); + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnected(connectionHint); + } + } + + @Override + public void onConnectionSuspended(int cause) { + Log.d(TAG, "ConnectionCallbacks : onConnectionSuspended()"); + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnectionSuspended(cause); + } + } + }; + OnConnectionFailedListener baseConnectionFailedListener = result -> { + Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()"); + for (OnConnectionFailedListener listener : connectionFailedListeners) { + listener.onConnectionFailed(result); + } + }; + apiConnections.put(api, api.getBuilder().build(apis.get(api), context, looper, clientSettings, baseConnectionCallbacks, baseConnectionFailedListener)); + } + } + + @Override + public synchronized void connect() { + Log.d(TAG, "connect()"); + if (isConnected() || isConnecting()) { + if (shouldDisconnect) { + shouldDisconnect = false; + return; + } + Log.d(TAG, "Already connected/connecting, nothing to do"); + return; + } + for (ApiClient connection : apiConnections.values()) { + if (!connection.isConnected()) { + connection.connect(); + } + } + } + + @Override + public synchronized void disconnect() { + Log.d(TAG, "disconnect()"); + for (ApiClient connection : apiConnections.values()) { + if (connection.isConnected()) { + connection.disconnect(); + } + } + } + + @Override + public synchronized boolean isConnected() { + for (ApiClient connection : apiConnections.values()) { + if (!connection.isConnected()) return false; + } + return true; + } + + @Override + public synchronized boolean isConnecting() { + for (ApiClient connection : apiConnections.values()) { + if (connection.isConnecting()) return true; + } + return false; + } + + @Override + public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) { + return connectionCallbacks.contains(listener); + } + + @Override + public boolean isConnectionFailedListenerRegistered( + OnConnectionFailedListener listener) { + return connectionFailedListeners.contains(listener); + } + + @Override + public void registerConnectionCallbacks(ConnectionCallbacks listener) { + connectionCallbacks.add(listener); + } + + @Override + public void registerConnectionFailedListener(OnConnectionFailedListener listener) { + connectionFailedListeners.add(listener); + } + + @Override + public void unregisterConnectionCallbacks(ConnectionCallbacks listener) { + connectionCallbacks.remove(listener); + } + +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java new file mode 100644 index 00000000..a5fa2545 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import android.annotation.SuppressLint; + +public class GoogleApiManager { + @SuppressLint("StaticFieldLeak") + private static GoogleApiManager instance; + + private GoogleApiManager() { + } + + public synchronized static void getInstance() { + if (instance == null) instance = new GoogleApiManager(); + } + +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/OnConnectionFailedListener.java b/play-services-base/src/main/java/org/microg/gms/common/api/OnConnectionFailedListener.java new file mode 100644 index 00000000..cd994235 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/OnConnectionFailedListener.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import android.app.Activity; + +import com.google.android.gms.common.ConnectionResult; + +public interface OnConnectionFailedListener { + /** + * Called when there was an error connecting the client to the service. + * + * @param result A {@link ConnectionResult} that can be used for resolving the error, and + * deciding what sort of error occurred. To resolve the error, the resolution + * must be started from an activity with a non-negative {@code requestCode} + * passed to {@link ConnectionResult#startResolutionForResult(Activity, int)}. + * Applications should implement {@link Activity#onActivityResult} in their + * Activity to call {@link #connect()} again if the user has resolved the + * issue (resultCode is {@link Activity#RESULT_OK}). + */ + void onConnectionFailed(ConnectionResult result); +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java new file mode 100644 index 00000000..a737a076 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common.api; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.ResultCallback; + +class ResultCallbackHandler extends Handler { + private static final String TAG = "GmsResultCbackHandler"; + public static final int CALLBACK_ON_COMPLETE = 1; + public static final int CALLBACK_ON_TIMEOUT = 2; + + public ResultCallbackHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CALLBACK_ON_COMPLETE: + OnCompleteObject o = (OnCompleteObject) msg.obj; + Log.d(TAG, "handleMessage() : onResult(" + o.result + ")"); + o.callback.onResult(o.result); + break; + case CALLBACK_ON_TIMEOUT: + // TODO + break; + } + } + + public static class OnCompleteObject { + public ResultCallback callback; + public R result; + + public OnCompleteObject(ResultCallback callback, R result) { + this.callback = callback; + this.result = result; + } + } +} diff --git a/play-services-base/src/main/res/values/version.xml b/play-services-base/src/main/res/values/version.xml new file mode 100644 index 00000000..318bb1c2 --- /dev/null +++ b/play-services-base/src/main/res/values/version.xml @@ -0,0 +1,20 @@ + + + + + 6599436 + diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle new file mode 100644 index 00000000..8971fb98 --- /dev/null +++ b/play-services-basement/build.gradle @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +dependencies { + api "org.microg:safe-parcel:1.7.0" + + // Dependencies from play-services-basement:18.0.0 + api "androidx.collection:collection:$collectionVersion" + api "androidx.core:core-ktx:$coreVersion" + api "androidx.fragment:fragment-ktx:$fragmentVersion" +} + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + aidlPackageWhiteList "com/google/android/gms/common/api/Status.aidl" + aidlPackageWhiteList "com/google/android/gms/common/internal/ICancelToken.aidl" + aidlPackageWhiteList "com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl" + aidlPackageWhiteList "com/google/android/gms/dynamic/IObjectWrapper.aidl" + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + buildConfigField "int", "VERSION_CODE", "$appVersionCode" + } + + buildFeatures { + viewBinding = true + aidl true + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + compileOptions { + sourceCompatibility = 17 + targetCompatibility = 17 + } + namespace 'org.microg.gms.basement' +} \ No newline at end of file diff --git a/play-services-basement/gradle.properties b/play-services-basement/gradle.properties new file mode 100644 index 00000000..f7f3ecc1 --- /dev/null +++ b/play-services-basement/gradle.properties @@ -0,0 +1,33 @@ +# +# Copyright 2013-2016 microG Project Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +POM_NAME=Play Services Basement +POM_DESCRIPTION=Interfaces and objects used all over Play Services API, Library and Core + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsApi + +POM_SCM_URL=https://github.com/microg/android_external_GmsApi +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/api/Status.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/api/Status.aidl new file mode 100644 index 00000000..701f99a9 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/api/Status.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.api; + +parcelable Status; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl new file mode 100644 index 00000000..791dbe90 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.internal; + +parcelable GetServiceRequest; diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl new file mode 100644 index 00000000..aebd4794 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl @@ -0,0 +1,5 @@ +package com.google.android.gms.common.internal; + +interface ICancelToken { + void cancel(); +} diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl new file mode 100644 index 00000000..b0210281 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.common.internal; + +import android.os.Bundle; + +interface IGmsCallbacks { + void onPostInitComplete(int statusCode, IBinder binder, in Bundle params); +} diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl new file mode 100644 index 00000000..8ff5c840 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl @@ -0,0 +1,15 @@ +package com.google.android.gms.common.internal; + +import android.os.Bundle; + +import com.google.android.gms.common.internal.IGmsCallbacks; +import com.google.android.gms.common.internal.GetServiceRequest; +import com.google.android.gms.common.internal.ValidateAccountRequest; + +interface IGmsServiceBroker { + void getPeopleService(IGmsCallbacks callback, int code, String str, in Bundle params) = 4; + void getGoogleIdentityService(IGmsCallbacks callback, int code, String str, in Bundle params) = 16; + void getAddressService(IGmsCallbacks callback, int code, String str) = 23; + void getService(IGmsCallbacks callback, in GetServiceRequest request) = 45; + void validateAccount(IGmsCallbacks callback, in ValidateAccountRequest request) = 46; +} diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl new file mode 100644 index 00000000..cef6ad7e --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.internal; + +parcelable ValidateAccountRequest; diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl new file mode 100644 index 00000000..35e1474a --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.dynamic; + +/** + * The concrete class implementing IObjectWrapper must have exactly one declared private field + * for the wrapped object. Preferably, this is an instance of the ObjectWrapper class. + */ +interface IObjectWrapper { +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java b/play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java new file mode 100644 index 00000000..23ebfb81 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.IntentSender; +import android.text.TextUtils; + +import java.util.Arrays; + +/** + * Contains all possible error codes for when a client fails to connect to Google Play services. + * These error codes are used by {@link GoogleApiClient.OnConnectionFailedListener}. + */ +public class ConnectionResult { + /** + * The connection was successful. + */ + public static final int SUCCESS = 0; + /** + * Google Play services is missing on this device. The calling activity should pass this error + * code to {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to get a localized + * error dialog that will resolve the error when shown. + */ + public static final int SERVICE_MISSING = 1; + /** + * The installed version of Google Play services is out of date. The calling activity should + * pass this error code to {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to + * get a localized error dialog that will resolve the error when shown. + */ + public static final int SERVICE_VERSION_UPDATE_REQUIRED = 2; + /** + * The installed version of Google Play services has been disabled on this device. The calling + * activity should pass this error code to + * {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to get a localized error + * dialog that will resolve the error when shown. + */ + public static final int SERVICE_DISABLED = 3; + /** + * The client attempted to connect to the service but the user is not signed in. The client may + * choose to continue without using the API or it may call + * {@link #startResolutionForResult(Activity, int)} to prompt the user to sign in. After the + * sign in activity returns with {@link Activity#RESULT_OK} further attempts to connect should + * succeed. + */ + public static final int SIGN_IN_REQUIRED = 4; + /** + * The client attempted to connect to the service with an invalid account name specified. + */ + public static final int INVALID_ACCOUNT = 5; + /** + * Completing the connection requires some form of resolution. A resolution will be available + * to be started with {@link #startResolutionForResult(Activity, int)}. If the result returned + * is {@link Activity#RESULT_OK}, then further attempts to connect should either complete or + * continue on to the next issue that needs to be resolved. + */ + public static final int RESOLUTION_REQUIRED = 6; + /** + * A network error occurred. Retrying should resolve the problem. + */ + public static final int NETWORK_ERROR = 7; + /** + * An internal error occurred. Retrying should resolve the problem. + */ + public static final int INTERNAL_ERROR = 8; + /** + * The version of the Google Play services installed on this device is not authentic. + */ + public static final int SERVICE_INVALID = 9; + /** + * The application is misconfigured. This error is not recoverable and will be treated as + * fatal. The developer should look at the logs after this to determine more actionable + * information. + */ + public static final int DEVELOPER_ERROR = 10; + /** + * The application is not licensed to the user. This error is not recoverable and will be + * treated as fatal. + */ + public static final int LICENSE_CHECK_FAILED = 11; + /** + * The client canceled the connection by calling {@link GoogleApiClient#disconnect()}. + * Only returned by {@link GoogleApiClient#blockingConnect()}. + */ + public static final int CANCELED = 13; + /** + * The timeout was exceeded while waiting for the connection to complete. Only returned by + * {@link GoogleApiClient#blockingConnect()}. + */ + public static final int TIMEOUT = 14; + /** + * An interrupt occurred while waiting for the connection complete. Only returned by + * {@link GoogleApiClient#blockingConnect()}. + */ + public static final int INTERRUPTED = 15; + /** + * One of the API components you attempted to connect to is not available. The API will not + * work on this device, and updating Google Play services will not likely solve the problem. + * Using the API on the device should be avoided. + */ + public static final int API_UNAVAILABLE = 16; + + /** + * Service doesn't have one or more required permissions. + */ + public static final int SERVICE_MISSING_PERMISSION = 19; + + /** + * The Drive API requires external storage (such as an SD card), but no external storage is + * mounted. This error is recoverable if the user installs external storage (if none is + * present) and ensures that it is mounted (which may involve disabling USB storage mode, + * formatting the storage, or other initialization as required by the device). + *

+ * This error should never be returned on a device with emulated external storage. On devices + * with emulated external storage, the emulated "external storage" is always present regardless + * of whether the device also has removable storage. + */ + @Deprecated + public static final int DRIVE_EXTERNAL_STORAGE_REQUIRED = 1500; + + private final int statusCode; + private final PendingIntent pendingIntent; + private final String message; + + /** + * Creates a connection result. + * + * @param statusCode The status code. + */ + public ConnectionResult(int statusCode) { + this(statusCode, null); + } + + /** + * Creates a connection result. + * + * @param statusCode The status code. + * @param pendingIntent A pending intent that will resolve the issue when started, or null. + */ + public ConnectionResult(int statusCode, PendingIntent pendingIntent) { + this(statusCode, pendingIntent, getStatusString(statusCode)); + } + + /** + * Creates a connection result. + * + * @param statusCode The status code. + * @param pendingIntent A pending intent that will resolve the issue when started, or null. + * @param message An additional error message for the connection result, or null. + */ + public ConnectionResult(int statusCode, PendingIntent pendingIntent, String message) { + this.statusCode = statusCode; + this.pendingIntent = pendingIntent; + this.message = message; + } + + static String getStatusString(int statusCode) { + switch (statusCode) { + case -1: + return "UNKNOWN"; + case 0: + return "SUCCESS"; + case 1: + return "SERVICE_MISSING"; + case 2: + return "SERVICE_VERSION_UPDATE_REQUIRED"; + case 3: + return "SERVICE_DISABLED"; + case 4: + return "SIGN_IN_REQUIRED"; + case 5: + return "INVALID_ACCOUNT"; + case 6: + return "RESOLUTION_REQUIRED"; + case 7: + return "NETWORK_ERROR"; + case 8: + return "INTERNAL_ERROR"; + case 9: + return "SERVICE_INVALID"; + case 10: + return "DEVELOPER_ERROR"; + case 11: + return "LICENSE_CHECK_FAILED"; + case 13: + return "CANCELED"; + case 14: + return "TIMEOUT"; + case 15: + return "INTERRUPTED"; + case 16: + return "API_UNAVAILABLE"; + case 17: + return "SIGN_IN_FAILED"; + case 18: + return "SERVICE_UPDATING"; + case 19: + return "SERVICE_MISSING_PERMISSION"; + case 20: + return "RESTRICTED_PROFILE"; + case 21: + return "API_VERSION_UPDATE_REQUIRED"; + case 42: + return "UPDATE_ANDROID_WEAR"; + case 99: + return "UNFINISHED"; + case 1500: + return "DRIVE_EXTERNAL_STORAGE_REQUIRED"; + default: + return "UNKNOWN_ERROR_CODE(" + statusCode + ")"; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (!(o instanceof ConnectionResult)) { + return false; + } else { + ConnectionResult r = (ConnectionResult)o; + return statusCode == r.statusCode && pendingIntent == null ? r.pendingIntent == null : pendingIntent.equals(r.pendingIntent) && TextUtils.equals(message, r.message); + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{statusCode, pendingIntent, message}); + } + + /** + * Returns {@code true} if calling {@link #startResolutionForResult(Activity, int)} will start + * any intents requiring user interaction. + * + * @return {@code true} if there is a resolution that can be started. + */ + public boolean hasResolution() { + return statusCode != 0 && pendingIntent != null; + } + + /** + * Resolves an error by starting any intents requiring user interaction. See + * {@link #SIGN_IN_REQUIRED}, and {@link #RESOLUTION_REQUIRED}. + * + * @param activity An Activity context to use to resolve the issue. The activity's + * {@link Activity#onActivityResult} method will be invoked after the user + * is done. If the resultCode is {@link Activity#RESULT_OK}, the application + * should try to connect again. + * @param requestCode The request code to pass to {@link Activity#onActivityResult}. + * @throws IntentSender.SendIntentException If the resolution intent has been canceled or is no + * longer able to execute the request. + */ + public void startResolutionForResult(Activity activity, int requestCode) throws + IntentSender.SendIntentException { + if (hasResolution()) { + activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0); + } + } +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/Feature.java b/play-services-basement/src/main/java/com/google/android/gms/common/Feature.java new file mode 100644 index 00000000..0a5b0813 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/Feature.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class Feature extends AutoSafeParcelable { + @Field(1) + private String name; + @Field(2) + private int oldVersion; + @Field(3) + private long version = -1; + + private Feature() { + } + + public Feature(String name, long version) { + this.name = name; + this.version = version; + } + + public String getName() { + return name; + } + + public long getVersion() { + if (version == -1) return oldVersion; + return version; + } + + public static final Creator CREATOR = new AutoSafeParcelable.AutoCreator<>(Feature.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java new file mode 100644 index 00000000..984c6fce --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import org.microg.gms.common.PublicApi; + +@PublicApi +public class CommonStatusCodes { + public static final int SUCCESS = 0; + @Deprecated + public static final int SERVICE_MISSING = 1; + @Deprecated + public static final int SERVICE_VERSION_UPDATE_REQUIRED = 2; + @Deprecated + public static final int SERVICE_DISABLED = 3; + public static final int SIGN_IN_REQUIRED = 4; + public static final int RESOLUTION_REQUIRED = 6; + public static final int INTERNAL_ERROR = 8; + public static final int ERROR = 13; + public static final int INTERRUPTED = 14; + public static final int CANCELED = 16; + +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java new file mode 100644 index 00000000..cbbbd4c7 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +/** + * Represents a resource, or a holder of resources, which may be released once they are no longer needed. + */ +public interface Releasable { + void release(); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Result.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Result.java new file mode 100644 index 00000000..6c4f6921 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Result.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import org.microg.gms.common.PublicApi; + +/** + * Represents the final result of invoking an API method in Google Play Services. + */ +@PublicApi +public interface Result { + Status getStatus(); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java new file mode 100644 index 00000000..f6c46b06 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +/** + * An interface for receiving a {@link Result} from a {@link PendingResult} as an asynchronous + * callback. + */ +public interface ResultCallback { + /** + * Called when the {@link Result} is ready. It is the responsibility of each callback to + * release any resources associated with the result. Some result types may implement + * {@link Releasable}, in which case {@link Releasable#release()} should be used to free the + * associated resources. + * + * @param result The result from the API call. May not be null. + */ + void onResult(R result); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Scope.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Scope.java new file mode 100644 index 00000000..a90803e7 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Scope.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +/** + * Describes an OAuth 2.0 scope to request. This has security implications for the user, and + * requesting additional scopes will result in authorization dialogs. + */ +@PublicApi +public class Scope extends AutoSafeParcelable { + @SafeParceled(1) + private final int versionCode = 1; + @SafeParceled(2) + private final String scopeUri; + + private Scope() { + scopeUri = null; + } + + /** + * Creates a new scope with the given URI. + */ + public Scope(String scopeUri) { + this.scopeUri = scopeUri; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Scope)) return false; + assert scopeUri != null; + return scopeUri.equals(((Scope) o).scopeUri); + } + + @Override + public int hashCode() { + assert scopeUri != null; + return scopeUri.hashCode(); + } + + @NonNull + @Override + public String toString() { + assert scopeUri != null; + return scopeUri; + } + + public static final Creator CREATOR = new AutoCreator<>(Scope.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java new file mode 100644 index 00000000..433ef043 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.api; + +import android.app.PendingIntent; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +/** + * Represents the results of work. + */ +@PublicApi +public final class Status extends AutoSafeParcelable implements Result { + @PublicApi(exclude = true) + public static final Status INTERNAL_ERROR = new Status(CommonStatusCodes.INTERNAL_ERROR, "Internal error"); + @PublicApi(exclude = true) + public static final Status CANCELED = new Status(CommonStatusCodes.CANCELED, "Cancelled"); + @PublicApi(exclude = true) + public static final Status SUCCESS = new Status(CommonStatusCodes.SUCCESS, "Success"); + + @SafeParceled(1000) + private final int versionCode = 1; + + @SafeParceled(1) + private final int statusCode; + + private Status() { + statusCode = 0; + } + + /** + * Creates a representation of the status resulting from a GoogleApiClient operation. + * + * @param statusCode The status code. + */ + public Status(int statusCode) { + this(statusCode, null); + } + + /** + * Creates a representation of the status resulting from a GoogleApiClient operation. + * + * @param statusCode The status code. + * @param statusMessage The message associated with this status, or null. + */ + public Status(int statusCode, String statusMessage) { + this(statusCode, statusMessage, null); + } + + /** + * Creates a representation of the status resulting from a GoogleApiClient operation. + * + * @param statusCode The status code. + * @param statusMessage The message associated with this status, or null. + * @param resolution A pending intent that will resolve the issue when started, or null. + */ + public Status(int statusCode, String statusMessage, PendingIntent resolution) { + this.statusCode = statusCode; + } + + /** + * Returns the status of this result. Use {@link #isSuccess()} to determine whether the call + * was successful, and {@link #getStatusCode()} to determine what the error cause was. + *

+ * Certain errors are due to failures that can be resolved by launching a particular intent. + * The resolution intent is available via {@link #getResolution()}. + */ + @Override + public Status getStatus() { + return this; + } + + /** + * Returns true if the operation was canceled. + */ + public boolean isCanceled() { + return statusCode == CommonStatusCodes.CANCELED; + } + + public static final Creator CREATOR = new AutoCreator<>(Status.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java new file mode 100644 index 00000000..47ec3949 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +public class BinderWrapper implements Parcelable { + public IBinder binder; + + public BinderWrapper(IBinder binder) { + this.binder = binder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(binder); + } + + public static final Creator CREATOR = new Creator() { + @Override + public BinderWrapper createFromParcel(Parcel source) { + return new BinderWrapper(source.readStrongBinder()); + } + + @Override + public BinderWrapper[] newArray(int size) { + return new BinderWrapper[size]; + } + }; +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java new file mode 100644 index 00000000..a203a99f --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import android.accounts.Account; +import android.os.Bundle; +import android.os.IBinder; + +import com.google.android.gms.common.Feature; +import com.google.android.gms.common.api.Scope; + +import org.microg.gms.common.Constants; +import org.microg.gms.common.GmsService; +import org.microg.safeparcel.AutoSafeParcelable; + +import java.util.Arrays; + +public class GetServiceRequest extends AutoSafeParcelable { + @Field(1) + private final int versionCode = 4; + @Field(2) + public final int serviceId; + @Field(3) + public int gmsVersion; + @Field(4) + public String packageName; + @Field(5) + public IBinder accountAccessor; + @Field(6) + public Scope[] scopes; + @Field(7) + public Bundle extras; + @Field(8) + public Account account; + @Field(9) + private long field9; + @Field(10) + public Feature[] defaultFeatures; + @Field(11) + public Feature[] apiFeatures; + @Field(12) + private boolean field12; + @Field(13) + private int field13; + @Field(14) + private boolean field14; + @Field(15) + private String field15; + + private GetServiceRequest() { + serviceId = -1; + gmsVersion = Constants.GMS_VERSION_CODE; + } + + public GetServiceRequest(int serviceId) { + this.serviceId = serviceId; + this.gmsVersion = Constants.GMS_VERSION_CODE; + this.field12 = true; + } + + @Override + public String toString() { + return "GetServiceRequest{" + + "serviceId=" + GmsService.nameFromServiceId(serviceId) + + ", gmsVersion=" + gmsVersion + + ", packageName='" + packageName + '\'' + + (scopes == null || scopes.length == 0 ? "" : (", scopes=" + Arrays.toString(scopes))) + + (extras == null ? "" : (", extras=" + extras)) + + (account == null ? "" : (", account=" + account)) + + '}'; + } + + public static Creator CREATOR = new AutoCreator(GetServiceRequest.class); +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java new file mode 100644 index 00000000..75e59d95 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ValidateAccountRequest extends AutoSafeParcelable { + public static Creator CREATOR = new AutoCreator<>(ValidateAccountRequest.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java b/play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java new file mode 100644 index 00000000..9158c941 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.dynamic; + +import android.os.IBinder; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Field; + +public class ObjectWrapper extends IObjectWrapper.Stub { + private final T t; + + public ObjectWrapper(T t) { + this.t = t; + } + + @Nullable + public static Object unwrap(IObjectWrapper obj) { + if (obj == null) { + return null; + } + if (obj instanceof ObjectWrapper) { + return ((ObjectWrapper) obj).t; + } + IBinder binder = obj.asBinder(); + Field[] fields = binder.getClass().getDeclaredFields(); + + if (fields.length < 1) { + throw new IllegalArgumentException("No fields were found"); + } + + // Ignore synthetic field(s) from JaCoCo or elsewhere + // https://www.jacoco.org/jacoco/trunk/doc/faq.html + + @Nullable + Field field = null; + + for (Field currentField : fields) { + if (currentField.isSynthetic()) { + continue; + } + + if (field == null) { + field = currentField; + } else { + throw new IllegalArgumentException("Too many non-synthetic fields were found"); + } + } + + if (field == null) { + throw new IllegalArgumentException("No non-synthetic fields were found"); + } + + if (!field.isAccessible()) { + field.setAccessible(true); + try { + return field.get(binder); + } catch (NullPointerException localNullPointerException) { + throw new IllegalArgumentException("Binder object is null.", + localNullPointerException); + } catch (IllegalArgumentException localIllegalArgumentException) { + throw new IllegalArgumentException("remoteBinder is the wrong class.", + localIllegalArgumentException); + } catch (IllegalAccessException localIllegalAccessException) { + throw new IllegalArgumentException("Could not access the field in remoteBinder.", + localIllegalAccessException); + } + } else { + throw new IllegalArgumentException(); + } + } + + @Nullable + public static T unwrapTyped(IObjectWrapper obj, Class clazz) { + try { + return clazz.cast(unwrap(obj)); + } catch (ClassCastException e) { + return null; + } + } + + public static ObjectWrapper wrap(T t) { + return new ObjectWrapper<>(t); + } +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/org/microg/gms/auth/AuthConstants.java b/play-services-basement/src/main/java/org/microg/gms/auth/AuthConstants.java new file mode 100644 index 00000000..774f5756 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/auth/AuthConstants.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.auth; + +public class AuthConstants { + public static final String SCOPE_GET_ACCOUNT_ID = "^^_account_id_^^"; + public static final String PROVIDER_METHOD_GET_ACCOUNTS = "get_accounts"; + public static final String PROVIDER_METHOD_CLEAR_PASSWORD = "clear_password"; + public static final String PROVIDER_EXTRA_CLEAR_PASSWORD = "clear_password"; + public static final String PROVIDER_EXTRA_ACCOUNTS = "accounts"; + public static final String DEFAULT_ACCOUNT_TYPE = "com.mgoogle"; +} diff --git a/play-services-basement/src/main/java/org/microg/gms/common/Constants.java b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java new file mode 100644 index 00000000..077bdbde --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013-2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import org.microg.gms.basement.BuildConfig; + +public class Constants { + public static final int GMS_VERSION_CODE = (BuildConfig.VERSION_CODE / 1000) * 1000; + public static final String GMS_PACKAGE_NAME = "com.google.android.gms"; + public static final String GMS_PACKAGE_SIGNATURE_SHA1 = "38918a453d07199354f8b19af05ec6562ced5788"; + public static final String GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1 = "bd32424203e0fb25f36b57e5aa356f9bdd1da998"; + + @Deprecated + public static final int MAX_REFERENCE_VERSION = GMS_VERSION_CODE; +} diff --git a/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java new file mode 100644 index 00000000..739bc259 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +public enum GmsService { + UNKNOWN(-2), + ANY(-1), + PEOPLE(5, "com.google.android.gms.people.service.START"), + ACCOUNT(9, "com.google.android.gms.accounts.ACCOUNT_SERVICE"), + ADDRESS(12, "com.google.android.gms.identity.service.BIND"), + AUTH_PROXY(16, "com.google.android.gms.auth.service.START"), + INDEX(21, "com.google.android.gms.icing.INDEX_SERVICE"), + COMMON(39, "com.google.android.gms.common.service.START"), + SIGN_IN(44, "com.google.android.gms.signin.service.START"), + CREDENTIALS(68, "com.google.android.gms.auth.api.credentials.service.START"), + IDENTITY_SIGN_IN(212, "com.google.android.gms.auth.api.identity.service.signin.START"), + ; + + public final int SERVICE_ID; + public final String ACTION; + public final String[] SECONDARY_ACTIONS; + + GmsService(int serviceId, String... actions) { + this.SERVICE_ID = serviceId; + this.ACTION = actions.length > 0 ? actions[0] : null; + this.SECONDARY_ACTIONS = actions; + } + + public static GmsService byServiceId(int serviceId) { + for (GmsService service : values()) { + if (service.SERVICE_ID == serviceId) return service; + } + return UNKNOWN; + } + + public static String nameFromServiceId(int serviceId) { + return byServiceId(serviceId).toString(serviceId); + } + + public String toString(int serviceId) { + if (this != UNKNOWN) return toString(); + return "UNKNOWN(" + serviceId + ")"; + } +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java b/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java new file mode 100644 index 00000000..92663979 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An class, method or field is named public, if it can be used with the original play services + * client library. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface PublicApi { + /** + * @return the first version that contains the given class, method or field + */ + String since() default "0"; + + /** + * @return the last version that contains the given class, method or field + */ + String until() default "latest"; + + /** + * Used on a method or field to exclude it from the public api if the corresponding class was + * marked as public api. + * + * @return true if the method or field is not part of the public api + */ + boolean exclude() default false; +} diff --git a/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java b/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java new file mode 100644 index 00000000..d96fe90a --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.gms.gcm; + +public final class GcmConstants { + public static final String ACTION_C2DM_RECEIVE = "com.mgoogle.android.c2dm.intent.RECEIVE"; + public static final String ACTION_C2DM_REGISTER = "com.mgoogle.android.c2dm.intent.REGISTER"; + public static final String ACTION_C2DM_REGISTRATION = "com.mgoogle.android.c2dm.intent.REGISTRATION"; + public static final String ACTION_C2DM_UNREGISTER = "com.mgoogle.android.c2dm.intent.UNREGISTER"; + + public static final String EXTRA_APP = "app"; + public static final String EXTRA_APP_OVERRIDE = "org.microg.gms.gcm.APP_OVERRIDE"; + public static final String EXTRA_APP_VERSION_NAME = "app_ver_name"; + public static final String EXTRA_COLLAPSE_KEY = "collapse_key"; + public static final String EXTRA_DELETE = "delete"; + public static final String EXTRA_ERROR = "error"; + public static final String EXTRA_FROM = "from"; + public static final String EXTRA_KID = "kid"; + public static final String EXTRA_MESSENGER = "google.messenger"; + public static final String EXTRA_MESSAGE_ID = "google.message_id"; + public static final String EXTRA_REGISTRATION_ID = "registration_id"; + public static final String EXTRA_RETRY_AFTER = "Retry-After"; + public static final String EXTRA_SENDER = "sender"; + public static final String EXTRA_SEND_TO = "google.to"; + public static final String EXTRA_SEND_FROM = "google.from"; + public static final String EXTRA_TTL = "google.ttl"; + public static final String EXTRA_UNREGISTERED = "unregistered"; + + public static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE"; +} diff --git a/play-services-basement/src/main/kotlin/com/google/android/gms/dynamic/ObjectWrapper.kt b/play-services-basement/src/main/kotlin/com/google/android/gms/dynamic/ObjectWrapper.kt new file mode 100644 index 00000000..27996b8f --- /dev/null +++ b/play-services-basement/src/main/kotlin/com/google/android/gms/dynamic/ObjectWrapper.kt @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.dynamic + diff --git a/play-services-core-proto/build.gradle b/play-services-core-proto/build.gradle new file mode 100644 index 00000000..6edfcb4b --- /dev/null +++ b/play-services-core-proto/build.gradle @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.squareup.wire' +apply plugin: 'kotlin' + +dependencies { + implementation "com.squareup.wire:wire-runtime:$wireVersion" +} + +wire { + kotlin { + javaInterop = true + } +} + +sourceSets { + main.java.srcDirs += "$buildDir/generated/source/wire" +} + +compileKotlin { + kotlinOptions.jvmTarget = 17 +} + +compileTestKotlin { + kotlinOptions.jvmTarget = 17 +} \ No newline at end of file diff --git a/play-services-core-proto/src/main/proto/appcert.proto b/play-services-core-proto/src/main/proto/appcert.proto new file mode 100644 index 00000000..ee02bf55 --- /dev/null +++ b/play-services-core-proto/src/main/proto/appcert.proto @@ -0,0 +1,32 @@ +option java_package = "org.microg.gms.auth.appcert"; +option java_outer_classname = "AppCertProto"; + +message DeviceKeyRequest { + optional uint64 androidId = 2; + optional uint64 sessionId = 3; + message VersionInfo { + optional fixed32 sdkVersion = 1; + optional fixed32 gmsVersion = 2; + } + optional VersionInfo versionInfo = 4; + optional string token = 6; +} + +message DeviceKey { + optional uint64 keyId = 1; + optional uint64 deviceId = 3; + optional bytes macSecret = 4; + optional bytes keyCert = 5; +} + +message SpatulaHeaderProto { + message PackageInfo { + optional string packageName = 1; + optional string packageCertificateHash = 3; + } + optional PackageInfo packageInfo = 1; + optional bytes hmac = 2; + optional uint64 deviceId = 3; + optional uint64 keyId = 4; + optional bytes keyCert = 5; +} diff --git a/play-services-core-proto/src/main/proto/auth.proto b/play-services-core-proto/src/main/proto/auth.proto new file mode 100644 index 00000000..8a6997a7 --- /dev/null +++ b/play-services-core-proto/src/main/proto/auth.proto @@ -0,0 +1,17 @@ +option java_package = "org.microg.gms.auth"; + +option java_outer_classname = "AuthProto"; + +message ConsentData { + message AppDetails { + optional string title = 1; + optional string email = 3; + } + optional AppDetails app = 1; + message ScopeDetails { + optional string title = 1; + optional string description = 2; + optional string id = 6; + } + repeated ScopeDetails scopes = 2; +} \ No newline at end of file diff --git a/play-services-core-proto/src/main/proto/checkin.proto b/play-services-core-proto/src/main/proto/checkin.proto new file mode 100644 index 00000000..5b1e8141 --- /dev/null +++ b/play-services-core-proto/src/main/proto/checkin.proto @@ -0,0 +1,270 @@ +option java_package = "org.microg.gms.checkin"; + +option java_outer_classname = "CheckinProto"; + +// Sample data, if provided, is fished from a Nexus 7 (2013) / flo running Android 5.0 +message CheckinRequest { + // unused + optional string imei = 1; + + // Gservices["android_id"] or 0 on first-checkin + optional int64 androidId = 2; + + // Gservices["digest"] or "" + optional string digest = 3; + + required Checkin checkin = 4; + message Checkin { + // empty Build on pre-checkin + required Build build = 1; + message Build { + // Build.FINGERPRINT + // eg. google/razor/flo:5.0.1/LRX22C/1602158:user/release-keys + optional string fingerprint = 1; + + // Build.HARDWARE + // eg. flo + optional string hardware = 2; + + // Build.BRAND + // eg. google + optional string brand = 3; + + // Build.getRadioVersion() + optional string radio = 4; + + // Build.BOOTLOADER + // eg. FLO-04.04 + optional string bootloader = 5; + + // GoogleSettingsContract.Partner["client_id"] + // eg. android-google + optional string clientId = 6; + + // Build.TIME / 1000L + // eg. 1416533192 + optional int64 time = 7; + + // PackageInfo.versionCode + // eg. 6188736 + optional int32 packageVersionCode = 8; + + // Build.DEVICE + // eg. flo + optional string device = 9; + + // Build.VERSION.SDK_INT + // eg. 21 + optional int32 sdkVersion = 10; + + // Build.MODEL + // eg. Nexus 7 + optional string model = 11; + + // Build.MANUFACTURER + // eg. asus + optional string manufacturer = 12; + + // Build.PRODUCT + // eg. razor + optional string product = 13; + + // fileExists("/system/recovery-from-boot.p") + // eg. false + optional bool otaInstalled = 14; + } + + // last checkin ms or 0 if first checkin + // eg. 0 + optional int64 lastCheckinMs = 2; + + // eg. ("event_log_start",~,1424612602652) on first checkin + repeated Event event = 3; + message Event { + optional string tag = 1; + optional string value = 2; + optional int64 timeMs = 3; + } + + // unknown, n/a on first checkin + repeated Statistic stat = 4; + message Statistic { + required string tag = 1; + optional int32 count = 2; + optional float sum = 3; + } + + // unused + repeated string requestedGroup = 5; + + // TelephonyManager.getNetworkOperator != null|empty + optional string cellOperator = 6; + + // TelephonyManager.getSimOperator != null|empty + optional string simOperator = 7; + + // "WIFI::" | ("mobile" | "notmobile" | "unknown") + "-" + ("roaming" | "notroaming" | "unknown") + optional string roaming = 8; + + // UserHandle.myUserId + // eg. 0 + optional int32 userNumber = 9; + } + + // unused + optional string desiredBuild = 5; + + // Locale.toString + optional string locale = 6; + + // GoogleSettingsContract.Partner["logging_id2"] (choosen randomly on first checkin) + // eg. 12561488293572742346 + optional int64 loggingId = 7; + + // unused + optional string marketCheckin = 8; + + // NetworkInfo.getExtraInfo, WifiInfo.getMacAddress (12 hex-digits) + // eg. d850e6abcdef + repeated string macAddress = 9; + + // TelephonyManager.getDeviceId (14 hex-digits), not set on tablets + optional string meid = 10; + + // "[]" followed by "", empty string on first checkin + repeated string accountCookie = 11; + + // TimeZone.getId + // eg. GMT + optional string timeZone = 12; + + // security token as given on first checkin, not set on first checkin + optional fixed64 securityToken = 13; + + // use 3 + optional int32 version = 14; + + // SHA-1 of each in /system/etc/security/otacerts.zip or "--IOException--" or "--no-output--" + // eg. dKXTm1QH9QShGQwBM/4rg6/lCNQ= + repeated string otaCert = 15; + + // Build.SERIAL != "unknown" + // eg. 07d90b18 + optional string serial = 16; + + // TelephonyManager.getDeviceId (8 hex-digits), not set on tablets + optional string esn = 17; + + optional DeviceConfig deviceConfiguration = 18; + message DeviceConfig { + // ConfigurationInfo.reqTouchScreen + // eg. 3 + optional int32 touchScreen = 1; + + // ConfigurationInfo.reqKeyboardType + // eg. 1 + optional int32 keyboardType = 2; + + // ConfigurationInfo.reqNavigation + // eg. 1 + optional int32 navigation = 3; + // ConfigurationInfo.screenLayout + // eg. 3 + optional int32 screenLayout = 4; + + // ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD + // eg. 0 + optional bool hasHardKeyboard = 5; + + // ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV + // eg. 0 + optional bool hasFiveWayNavigation = 6; + + // DisplayMetrics.densityDpi + // eg. 320 + optional int32 densityDpi = 7; + + // ConfigurationInfo.reqGlEsVersion + // eg. 196608 + optional int32 glEsVersion = 8; + + // PackageManager.getSystemSharedLibraryNames + // eg. "android.test.runner", "com.android.future.usb.accessory", + // "com.android.media.remotedisplay", "com.android.mediadrm.signer", "com.google.android.maps", + // "com.google.android.media.effects", "com.google.widevine.software.drm", "javax.obex" + repeated string sharedLibrary = 9; + + // PackageManager.getSystemAvailableFeatures + // eg. android.hardware.[...] + repeated string availableFeature = 10; + + // Build.CPU_ABI and Build.CPU_ABI2 != "unknown" + // eg. "armeabi-v7a", "armeabi" + repeated string nativePlatform = 11; + + // DisplayMetrics.widthPixels + // eg. 1200 + optional int32 widthPixels = 12; + + // DisplayMetrics.heightPixels + // eg. 1824 + optional int32 heightPixels = 13; + + // Context.getAssets.getLocales + // eg. [...], "en-US", [...] + repeated string locale = 14; + + // GLES10.glGetString(GLES10.GL_EXTENSIONS) + // eg. "GL_AMD_compressed_ATC_texture", [...] + repeated string glExtension = 15; + + // unused + optional int32 deviceClass = 16; + // unused + optional int32 maxApkDownloadSizeMb = 17; + } + + // "ethernet" or "wifi" + repeated string macAddressType = 19; + + // unknown, use 0 on pre- and first-checkin, and 1 for later checkins + // also present on pre-checkin + required int32 fragment = 20; + + // unknown + optional string userName = 21; + + // UserManager.getUserSerialNumber + // eg. 0 + optional int32 userSerialNumber = 22; +} + +message CheckinResponse { + optional bool statsOk = 1; + repeated Intent intent = 2; + message Intent { + optional string action = 1; + optional string dataUri = 2; + optional string mimeType = 3; + optional string javaClass = 4; + repeated Extra extra = 5; + message Extra { + optional string name = 6; + optional string value = 7; + } + } + optional int64 timeMs = 3; + optional string digest = 4; + repeated GservicesSetting setting = 5; + message GservicesSetting { + optional bytes name = 1; + optional bytes value = 2; + } + optional bool marketOk = 6; + optional fixed64 androidId = 7; + optional fixed64 securityToken = 8; + optional bool settingsDiff = 9; + repeated string deleteSetting = 10; + optional string versionInfo = 11; + optional string deviceDataVersionInfo = 12; +} diff --git a/play-services-core-proto/src/main/proto/mcs.proto b/play-services-core-proto/src/main/proto/mcs.proto new file mode 100644 index 00000000..590f7138 --- /dev/null +++ b/play-services-core-proto/src/main/proto/mcs.proto @@ -0,0 +1,270 @@ +// Derived from mcs.proto in chromium source code. Original license text below. + +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// MCS protocol for communication between Chrome client and Mobile Connection +// Server . + +option java_package = "org.microg.gms.gcm.mcs"; + +/* + Common fields/comments: + + stream_id: no longer sent by server, each side keeps a counter + last_stream_id_received: sent only if a packet was received since last time + a last_stream was sent + status: new bitmask including the 'idle' as bit 0. + + */ + +/** + TAG: 0 + */ +message HeartbeatPing { + optional int32 stream_id = 1; + optional int32 last_stream_id_received = 2; + optional int64 status = 3; +} + +/** + TAG: 1 + */ +message HeartbeatAck { + optional int32 stream_id = 1; + optional int32 last_stream_id_received = 2; + optional int64 status = 3; +} + +message ErrorInfo { + required int32 code = 1; + optional string message = 2; + optional string type = 3; + optional Extension extension = 4; +} + +// MobileSettings class. +// "u:f", "u:b", "u:s" - multi user devices reporting foreground, background +// and stopped users. +// hbping: heatbeat ping interval +// rmq2v: include explicit stream IDs + +message Setting { + required string name = 1; + required string value = 2; +} + +message HeartbeatStat { + required string ip = 1; + required bool timeout = 2; + required int32 interval_ms = 3; +} + +message HeartbeatConfig { + optional bool upload_stat = 1; + optional string ip = 2; + optional int32 interval_ms = 3; +} + +/** + TAG: 2 + */ +message LoginRequest { + enum AuthService { + ANDROID_ID = 2; + } + required string id = 1; // Must be present ( proto required ), may be empty + // string. + // mcs.android.com. + required string domain = 2; + // Decimal android ID + required string user = 3; + + required string resource = 4; + + // Secret + required string auth_token = 5; + + // Format is: android-HEX_DEVICE_ID + // The user is the decimal value. + optional string device_id = 6; + + // RMQ1 - no longer used + optional int64 last_rmq_id = 7; + + repeated Setting setting = 8; + optional int32 compress = 9; + repeated string received_persistent_id = 10; + + // Replaced by "rmq2v" setting + optional bool include_stream_ids = 11; + + optional bool adaptive_heartbeat = 12; + optional HeartbeatStat heartbeat_stat = 13; + // Must be true. + optional bool use_rmq2 = 14; + optional int64 account_id = 15; + + // ANDROID_ID = 2 + optional AuthService auth_service = 16; + + optional int32 network_type = 17; + optional int64 status = 18; +} + +/** + * TAG: 3 + */ +message LoginResponse { + required string id = 1; + // Not used. + optional string jid = 2; + // Null if login was ok. + optional ErrorInfo error = 3; + repeated Setting setting = 4; + optional int32 stream_id = 5; + // Should be "1" + optional int32 last_stream_id_received = 6; + optional HeartbeatConfig heartbeat_config = 7; + // used by the client to synchronize with the server timestamp. + optional int64 server_timestamp = 8; +} + +message StreamErrorStanza { + required string type = 1; + optional string text = 2; +} + +/** + * TAG: 4 + */ +message Close { +} + +message Extension { + // 12: SelectiveAck + // 13: StreamAck + required int32 id = 1; + required bytes data = 2; +} + +/** + * TAG: 7 + * IqRequest must contain a single extension. IqResponse may contain 0 or 1 + * extensions. + */ +message IqStanza { + enum IqType { + GET = 0; + SET = 1; + RESULT = 2; + IQ_ERROR = 3; + } + + optional int64 rmq_id = 1; + required IqType type = 2; + required string id = 3; + optional string from = 4; + optional string to = 5; + optional ErrorInfo error = 6; + + // Only field used in the 38+ protocol (besides common last_stream_id_received, status, rmq_id) + optional Extension extension = 7; + + optional string persistent_id = 8; + optional int32 stream_id = 9; + optional int32 last_stream_id_received = 10; + optional int64 account_id = 11; + optional int64 status = 12; +} + +message AppData { + required string key = 1; + required string value = 2; +} + +/** + * TAG: 8 + */ +message DataMessageStanza { + // Not used. + optional int64 rmq_id = 1; + + // This is the message ID, set by client, DMP.9 (message_id) + optional string id = 2; + + // Project ID of the sender, DMP.1 + required string from = 3; + + // Part of DMRequest - also the key in DataMessageProto. + optional string to = 4; + + // Package name. DMP.2 + required string category = 5; + + // The collapsed key, DMP.3 + optional string token = 6; + + // User data + GOOGLE. prefixed special entries, DMP.4 + repeated AppData app_data = 7; + + // Not used. + optional bool from_trusted_server = 8; + + // Part of the ACK protocol, returned in DataMessageResponse on server side. + // It's part of the key of DMP. + optional string persistent_id = 9; + + // In-stream ack. Increments on each message sent - a bit redundant + // Not used in DMP/DMR. + optional int32 stream_id = 10; + optional int32 last_stream_id_received = 11; + + // Not used. + optional string permission = 12; + + // Sent by the device shortly after registration. + optional string reg_id = 13; + + // Not used. + optional string pkg_signature = 14; + // Not used. + optional string client_id = 15; + + // serial number of the target user, DMP.8 + // It is the 'serial number' according to user manager. + optional int64 device_user_id = 16; + + // Time to live, in seconds. + optional int32 ttl = 17; + // Timestamp ( according to client ) when message was sent by app, in seconds + optional int64 sent = 18; + + // How long has the message been queued before the flush, in seconds. + // This is needed to account for the time difference between server and + // client: server should adjust 'sent' based on his 'receive' time. + optional int32 queued = 19; + + optional int64 status = 20; + + optional bytes raw_data = 21; + + optional int32 delay = 22; +} + +/** + Included in IQ with ID 13, sent from client or server after 10 unconfirmed + messages. + */ +message StreamAck { + // No last_streamid_received required. This is included within an IqStanza, + // which includes the last_stream_id_received. +} + +/** + Included in IQ sent after LoginResponse from server with ID 12. +*/ +message SelectiveAck { + repeated string id = 1; +} diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle new file mode 100644 index 00000000..2449e81f --- /dev/null +++ b/play-services-core/build.gradle @@ -0,0 +1,107 @@ +/* + * Copyright 2013-2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' + +dependencies { + implementation "com.squareup.wire:wire-runtime:$wireVersion" + implementation "de.hdodenhof:circleimageview:$circleimageviewVersion" + implementation "androidx.webkit:webkit:$webkitVersion" + + implementation project(':play-services-base-core') + implementation project(':play-services-core-proto') + implementation project(':play-services-core:microg-ui-tools') // deprecated + implementation project(':play-services-api') + implementation project(':play-services-tasks') + + // AndroidX UI + implementation "androidx.appcompat:appcompat:$appcompatVersion" + implementation "androidx.preference:preference-ktx:$preferenceVersion" + + implementation 'org.microg.gms:conscrypt-gmscore:2.5.2' + implementation "com.google.android.material:material:$materialVersion" + + // Navigation + implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" + implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" + + implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion" + implementation "com.android.volley:volley:$volleyVersion" + + api project(':play-services-basement') +} + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + defaultConfig { + versionName version + versionCode appVersionCode + resConfigs 'xxhdpi' + + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + + ndk { + abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + } + + } + + buildFeatures { + dataBinding = true + viewBinding = true + } + + sourceSets { + main { + java.srcDirs += 'src/main/kotlin' + } + } + + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + packagingOptions { + jniLibs { + useLegacyPackaging false + } + resources { + excludes += ['META-INF/ASL2.0'] + } + } + lint { + disable 'MissingTranslation', 'BatteryLife', 'ImpliedQuantity', 'MissingQuantity', 'InvalidWakeLockTag', 'Range' + } + namespace 'com.mgoogle.android.gms' +} + +if (file('user.gradle').exists()) { + apply from: 'user.gradle' +} \ No newline at end of file diff --git a/play-services-core/microg-ui-tools/build.gradle b/play-services-core/microg-ui-tools/build.gradle new file mode 100644 index 00000000..d9a5c2bb --- /dev/null +++ b/play-services-core/microg-ui-tools/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2016 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 11 + targetCompatibility = 11 + } + lint { + disable 'MissingTranslation' + } + namespace 'org.microg.tools.ui' + +} + +dependencies { + implementation "androidx.appcompat:appcompat:$appcompatVersion" + implementation "androidx.preference:preference:$preferenceVersion" + implementation "com.google.android.material:material:$materialVersion" +} + diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java new file mode 100644 index 00000000..b77ea4ce --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public abstract class AbstractAboutFragment extends Fragment { + + protected abstract void collectLibraries(List libraries); + + public static Drawable getIcon(Context context) { + try { + PackageManager pm = context.getPackageManager(); + return pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadIcon(pm); + } catch (PackageManager.NameNotFoundException e) { + // Never happens, self package always exists! + throw new RuntimeException(e); + } + } + + public static String getAppName(Context context) { + try { + PackageManager pm = context.getPackageManager(); + CharSequence label = pm.getPackageInfo(context.getPackageName(), 0).applicationInfo.loadLabel(pm); + if (TextUtils.isEmpty(label)) return context.getPackageName(); + return label.toString().trim(); + } catch (PackageManager.NameNotFoundException e) { + // Never happens, self package always exists! + throw new RuntimeException(e); + } + } + + protected String getAppName() { + return getAppName(requireContext()); + } + + public static String getLibVersion(String packageName) { + try { + String versionName = (String) Class.forName(packageName + ".BuildConfig").getField("VERSION_NAME").get(null); + if (TextUtils.isEmpty(versionName)) return ""; + return versionName.trim(); + } catch (Exception e) { + return ""; + } + } + + public static String getSelfVersion(Context context) { + return getLibVersion(context.getPackageName()); + } + + protected String getSelfVersion() { + return getSelfVersion(requireContext()); + } + + protected String getSummary() { + return null; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View aboutRoot = inflater.inflate(R.layout.about_root, container, false); + ((ImageView) aboutRoot.findViewById(android.R.id.icon)).setImageDrawable(getIcon(requireContext())); + ((TextView) aboutRoot.findViewById(android.R.id.title)).setText(getAppName()); + ((TextView) aboutRoot.findViewById(R.id.about_version)).setText(getString(R.string.about_version_str, getSelfVersion())); + String summary = getSummary(); + if (summary != null) { + ((TextView) aboutRoot.findViewById(android.R.id.summary)).setText(summary); + aboutRoot.findViewById(android.R.id.summary).setVisibility(View.VISIBLE); + } + + List libraries = new ArrayList<>(); + collectLibraries(libraries); + Collections.sort(libraries); + ((ListView) aboutRoot.findViewById(android.R.id.list)).setAdapter(new LibraryAdapter(getContext(), libraries.toArray(new Library[0]))); + + return aboutRoot; + } + + private class LibraryAdapter extends ArrayAdapter { + + public LibraryAdapter(Context context, Library[] libraries) { + super(context, android.R.layout.simple_list_item_2, android.R.id.text1, libraries); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View v = super.getView(position, convertView, parent); + ((TextView) v.findViewById(android.R.id.text1)).setText(getString(R.string.about_name_version_str, getItem(position).name, getLibVersion(getItem(position).packageName))); + ((TextView) v.findViewById(android.R.id.text2)).setText(getItem(position).copyright != null ? getItem(position).copyright : getString(R.string.about_default_license)); + return v; + } + } + + protected static class Library implements Comparable { + private final String packageName; + private final String name; + private final String copyright; + + public Library(String packageName, String name, String copyright) { + this.packageName = packageName; + this.name = name; + this.copyright = copyright; + } + + @NonNull + @Override + public String toString() { + return name + ", " + copyright; + } + + @Override + public int compareTo(Library another) { + return name.toLowerCase(Locale.US).compareTo(another.name.toLowerCase(Locale.US)); + } + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractDashboardActivity.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractDashboardActivity.java new file mode 100644 index 00000000..85bc123f --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractDashboardActivity.java @@ -0,0 +1,86 @@ +package org.microg.tools.ui; + +import android.os.Bundle; +import android.view.ViewGroup; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractDashboardActivity extends AppCompatActivity { + protected int preferencesResource = 0; + + private final List conditions = new ArrayList<>(); + private ViewGroup conditionContainer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dashboard_activity); + conditionContainer = (ViewGroup) findViewById(R.id.condition_container); + + getSupportFragmentManager().beginTransaction() + .replace(R.id.content_wrapper, getFragment()) + .commit(); + } + + @Override + protected void onResume() { + super.onResume(); + forceConditionReevaluation(); + } + + private synchronized void resetConditionViews() { + conditionContainer.removeAllViews(); + for (Condition condition : conditions) { + if (condition.isEvaluated()) { + if (condition.isActive(this)) { + addConditionToView(condition); + } + } else { + evaluateConditionAsync(condition); + } + } + } + + private void evaluateConditionAsync(final Condition condition) { + if (condition.willBeEvaluating()) { + new Thread(() -> { + if (condition.isActive(AbstractDashboardActivity.this)) { + runOnUiThread(() -> { + if (conditions.contains(condition) && condition.isEvaluated()) { + addConditionToView(condition); + } + }); + } + }).start(); + } + } + + protected void forceConditionReevaluation() { + for (Condition condition : conditions) { + condition.resetEvaluated(); + } + resetConditionViews(); + } + + private synchronized void addConditionToView(Condition condition) { + for (int i = 0; i < conditionContainer.getChildCount(); i++) { + if (conditionContainer.getChildAt(i).getTag() == condition) return; + } + conditionContainer.addView(condition.createView(this, conditionContainer)); + } + + protected Fragment getFragment() { + if (preferencesResource == 0) { + throw new IllegalStateException("Neither preferencesResource given, nor overriden getFragment()"); + } + ResourceSettingsFragment fragment = new ResourceSettingsFragment(); + Bundle b = new Bundle(); + b.putInt(ResourceSettingsFragment.EXTRA_PREFERENCE_RESOURCE, preferencesResource); + fragment.setArguments(b); + return fragment; + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java new file mode 100644 index 00000000..db1da7b2 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java @@ -0,0 +1,57 @@ +package org.microg.tools.ui; + +import android.os.Bundle; +import android.view.MenuItem; +import android.view.ViewGroup; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + +import java.util.Objects; + +public abstract class AbstractSettingsActivity extends AppCompatActivity { + protected boolean showHomeAsUp = false; + protected int preferencesResource = 0; + protected int customBarLayout = 0; + protected SwitchBar switchBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.settings_activity); + if (showHomeAsUp) { + Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); + } + + switchBar = (SwitchBar) findViewById(R.id.switch_bar); + + ViewGroup customBarContainer = (ViewGroup) findViewById(R.id.custom_bar); + if (customBarLayout != 0) { + customBarContainer.addView(getLayoutInflater().inflate(customBarLayout, customBarContainer, false)); + } + + getSupportFragmentManager().beginTransaction() + .replace(R.id.content_wrapper, getFragment()) + .commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + protected Fragment getFragment() { + if (preferencesResource == 0) { + throw new IllegalStateException("Neither preferencesResource given, nor overriden getFragment()"); + } + ResourceSettingsFragment fragment = new ResourceSettingsFragment(); + Bundle b = new Bundle(); + b.putInt(ResourceSettingsFragment.EXTRA_PREFERENCE_RESOURCE, preferencesResource); + fragment.setArguments(b); + return fragment; + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java new file mode 100644 index 00000000..78ac70b0 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + +public abstract class AbstractSettingsFragment extends PreferenceFragmentCompat { + private static final String TAG = AbstractSettingsFragment.class.getSimpleName(); + + private static final String DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG"; + + @Override + public void onDisplayPreferenceDialog(@NonNull Preference preference) { + if (preference instanceof DialogPreference) { + DialogFragment f = DialogPreference.DialogPreferenceCompatDialogFragment.newInstance(preference.getKey()); + f.setTargetFragment(this, 0); + assert getFragmentManager() != null; + f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); + } else { + super.onDisplayPreferenceDialog(preference); + } + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/Condition.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/Condition.java new file mode 100644 index 00000000..c94b3bd6 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/Condition.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.DrawableRes; +import androidx.annotation.PluralsRes; +import androidx.annotation.StringRes; +import androidx.core.content.res.ResourcesCompat; + +public class Condition { + @DrawableRes + private final int iconRes; + private final Drawable icon; + + @StringRes + private final int titleRes; + @PluralsRes + private final int titlePluralsRes; + private final CharSequence title; + + @StringRes + private final int summaryRes; + @PluralsRes + private final int summaryPluralsRes; + private final CharSequence summary; + + @StringRes + private final int firstActionTextRes; + @PluralsRes + private final int firstActionPluralsRes; + private final CharSequence firstActionText; + private final View.OnClickListener firstActionListener; + + @StringRes + private final int secondActionTextRes; + @PluralsRes + private final int secondActionPluralsRes; + private final CharSequence secondActionText; + private final View.OnClickListener secondActionListener; + + private final Evaluation evaluation; + + private boolean evaluated = false; + private boolean evaluating = false; + private int evaluatedPlurals = -1; + private boolean active; + + Condition(Builder builder) { + icon = builder.icon; + title = builder.title; + summary = builder.summary; + firstActionText = builder.firstActionText; + firstActionListener = builder.firstActionListener; + secondActionText = builder.secondActionText; + secondActionListener = builder.secondActionListener; + summaryRes = builder.summaryRes; + iconRes = builder.iconRes; + firstActionTextRes = builder.firstActionTextRes; + secondActionTextRes = builder.secondActionTextRes; + titleRes = builder.titleRes; + evaluation = builder.evaluation; + titlePluralsRes = builder.titlePluralsRes; + summaryPluralsRes = builder.summaryPluralsRes; + firstActionPluralsRes = builder.firstActionPluralsRes; + secondActionPluralsRes = builder.secondActionPluralsRes; + } + + View createView(final Context context, ViewGroup container) { + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.condition_card, container, false); + Drawable icon = getIcon(context); + if (icon != null) + ((ImageView) view.findViewById(android.R.id.icon)).setImageDrawable(icon); + ((TextView) view.findViewById(android.R.id.title)).setText(getTitle(context)); + ((TextView) view.findViewById(android.R.id.summary)).setText(getSummary(context)); + Button first = (Button) view.findViewById(R.id.first_action); + first.setText(getFirstActionText(context)); + first.setOnClickListener(getFirstActionListener()); + CharSequence secondActionText = getSecondActionText(context); + if (secondActionText != null) { + Button second = (Button) view.findViewById(R.id.second_action); + second.setText(secondActionText); + second.setOnClickListener(getSecondActionListener()); + second.setVisibility(View.VISIBLE); + } + final View detailGroup = view.findViewById(R.id.detail_group); + final ImageView expandIndicator = (ImageView) view.findViewById(R.id.expand_indicator); + View.OnClickListener expandListener = v -> { + if (detailGroup.getVisibility() == View.VISIBLE) { + expandIndicator.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_expand_more, context.getTheme())); + detailGroup.setVisibility(View.GONE); + } else { + expandIndicator.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_expand_less, context.getTheme())); + detailGroup.setVisibility(View.VISIBLE); + } + }; + view.findViewById(R.id.collapsed_group).setOnClickListener(expandListener); + expandIndicator.setOnClickListener(expandListener); + view.setTag(this); + return view; + } + + public Drawable getIcon(Context context) { + if (iconRes != 0) { + return ResourcesCompat.getDrawable(context.getResources(), iconRes, context.getTheme()); + } + return icon; + } + + public CharSequence getTitle(Context context) { + if (titleRes != 0) { + return context.getString(titleRes); + } + if (titlePluralsRes != 0) { + return context.getResources().getQuantityString(titlePluralsRes, evaluatedPlurals); + } + return title; + } + + public CharSequence getSummary(Context context) { + if (summaryRes != 0) { + return context.getString(summaryRes); + } + if (summaryPluralsRes != 0) { + return context.getResources().getQuantityString(summaryPluralsRes, evaluatedPlurals); + } + return summary; + } + + public View.OnClickListener getFirstActionListener() { + return firstActionListener; + } + + public CharSequence getFirstActionText(Context context) { + if (firstActionTextRes != 0) { + return context.getString(firstActionTextRes); + } + if (firstActionPluralsRes != 0) { + return context.getResources().getQuantityString(firstActionPluralsRes, evaluatedPlurals); + } + return firstActionText; + } + + public View.OnClickListener getSecondActionListener() { + return secondActionListener; + } + + public CharSequence getSecondActionText(Context context) { + if (secondActionTextRes != 0) { + return context.getString(secondActionTextRes); + } + if (secondActionPluralsRes != 0) { + return context.getResources().getQuantityString(secondActionPluralsRes, evaluatedPlurals); + } + return secondActionText; + } + + public synchronized boolean willBeEvaluating() { + if (!evaluating && !evaluated && evaluation != null) { + return evaluating = true; + } else { + return false; + } + } + + public boolean isEvaluated() { + return evaluated || evaluation == null; + } + + public synchronized void evaluate(Context context) { + active = evaluation == null || evaluation.isActive(context); + assert evaluation != null; + evaluatedPlurals = evaluation.getPluralsCount(); + evaluated = true; + evaluating = false; + } + + public boolean isActive(Context context) { + if (!evaluated && evaluation != null) evaluate(context); + return active; + } + + public void resetEvaluated() { + this.evaluated = false; + } + + public static abstract class Evaluation { + public abstract boolean isActive(Context context); + + public int getPluralsCount() { + return 1; + } + } + + public static class Builder { + + @DrawableRes + private int iconRes; + private Drawable icon; + @StringRes + private int titleRes; + @PluralsRes + private final int titlePluralsRes; + private CharSequence title; + @StringRes + private int summaryRes; + @PluralsRes + private final int summaryPluralsRes; + private CharSequence summary; + @StringRes + private final int firstActionTextRes; + @PluralsRes + private final int firstActionPluralsRes; + private final CharSequence firstActionText; + private final View.OnClickListener firstActionListener; + @StringRes + private final int secondActionTextRes; + @PluralsRes + private final int secondActionPluralsRes; + private final CharSequence secondActionText; + private final View.OnClickListener secondActionListener; + private final Evaluation evaluation; + + + public Builder(int titlePluralsRes, int summaryPluralsRes, int firstActionTextRes, int firstActionPluralsRes, CharSequence firstActionText, View.OnClickListener firstActionListener, int secondActionTextRes, int secondActionPluralsRes, CharSequence secondActionText, View.OnClickListener secondActionListener, Evaluation evaluation) { + this.titlePluralsRes = titlePluralsRes; + this.summaryPluralsRes = summaryPluralsRes; + this.firstActionTextRes = firstActionTextRes; + this.firstActionPluralsRes = firstActionPluralsRes; + this.firstActionText = firstActionText; + this.firstActionListener = firstActionListener; + this.secondActionTextRes = secondActionTextRes; + this.secondActionPluralsRes = secondActionPluralsRes; + this.secondActionText = secondActionText; + this.secondActionListener = secondActionListener; + this.evaluation = evaluation; + } + + public Builder icon(Drawable val) { + icon = val; + return this; + } + + public Builder icon(@DrawableRes int val) { + iconRes = val; + return this; + } + + public Builder title(CharSequence val) { + title = val; + return this; + } + + public Builder title(@StringRes int val) { + titleRes = val; + return this; + } + + public Builder summary(CharSequence val) { + summary = val; + return this; + } + + public Builder summary(@StringRes int val) { + summaryRes = val; + return this; + } + + public Condition build() { + return new Condition(this); + } + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java new file mode 100644 index 00000000..b5335089 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.preference.Preference; +import androidx.preference.PreferenceDialogFragmentCompat; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceViewHolder; + +public class DialogPreference extends androidx.preference.DialogPreference implements PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback { + + private static final String DIALOG_FRAGMENT_TAG = + "android.support.v7.preference.PreferenceFragment.DIALOG"; + + public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DialogPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DialogPreference(Context context) { + super(context); + } + + protected View onCreateDialogView() { + return null; + } + + /** + * Called when the dialog is dismissed and should be used to save data to + * the {@link SharedPreferences}. + * + */ + protected void onDialogClosed() { + } + + @Override + public boolean onPreferenceDisplayDialog(@NonNull PreferenceFragmentCompat caller, @NonNull Preference pref) { + DialogPreferenceCompatDialogFragment fragment = new DialogPreferenceCompatDialogFragment(); + fragment.setTargetFragment(caller, 0); + assert caller.getFragmentManager() != null; + fragment.show(caller.getFragmentManager(), DIALOG_FRAGMENT_TAG); + return true; + } + + @Override + public void onBindViewHolder(@NonNull PreferenceViewHolder view) { + super.onBindViewHolder(view); + + ViewGroup.LayoutParams layoutParams = view.findViewById(R.id.icon_frame).getLayoutParams(); + if (layoutParams instanceof LinearLayout.LayoutParams) { + if (((LinearLayout.LayoutParams) layoutParams).leftMargin < 0) { + ((LinearLayout.LayoutParams) layoutParams).leftMargin = 0; + } + } + } + + public static class DialogPreferenceCompatDialogFragment extends PreferenceDialogFragmentCompat { + + @Override + protected View onCreateDialogView(@NonNull Context context) { + if (getPreference() instanceof DialogPreference) { + View view = ((DialogPreference) getPreference()).onCreateDialogView(); + if (view != null) return view; + } + return super.onCreateDialogView(context); + } + + @Override + public void onDialogClosed(boolean positiveResult) { + if (getPreference() instanceof DialogPreference) { + ((DialogPreference) getPreference()).onDialogClosed(); + } + } + + public static DialogFragment newInstance(String key) { + final DialogPreferenceCompatDialogFragment fragment = new DialogPreferenceCompatDialogFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + return fragment; + } + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java new file mode 100644 index 00000000..78abc781 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.os.Bundle; + +import androidx.annotation.Nullable; + +public class ResourceSettingsFragment extends AbstractSettingsFragment { + + public static final String EXTRA_PREFERENCE_RESOURCE = "preferencesResource"; + + protected int preferencesResource; + + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { + Bundle b = getArguments(); + if (b != null) { + preferencesResource = b.getInt(EXTRA_PREFERENCE_RESOURCE, preferencesResource); + } + if (preferencesResource != 0) { + addPreferencesFromResource(preferencesResource); + } + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java new file mode 100644 index 00000000..0a30f683 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2014-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.style.TextAppearanceSpan; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.google.android.material.switchmaterial.SwitchMaterial; + +import java.util.ArrayList; + +public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener, + View.OnClickListener { + + public interface OnSwitchChangeListener { + /** + * Called when the checked state of the Switch has changed. + * + * @param switchView The Switch view whose state has changed. + * @param isChecked The new checked state of switchView. + */ + void onSwitchChanged(SwitchMaterial switchView, boolean isChecked); + } + + private final TextAppearanceSpan mSummarySpan; + + private final ToggleSwitch mSwitch; + private final TextView mTextView; + private String mLabel; + private String mSummary; + + private final ArrayList mSwitchChangeListeners = + new ArrayList<>(); + + public SwitchBar(Context context) { + this(context, null); + } + + public SwitchBar(Context context, AttributeSet attrs) { + super(context, attrs); + + LayoutInflater.from(context).inflate(R.layout.switch_bar, this); + + mTextView = (TextView) findViewById(R.id.switch_text); + mTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + mLabel = getResources().getString(R.string.abc_capital_off); + mSummarySpan = new TextAppearanceSpan(context, androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_Switch); + updateText(); + + mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget); + // Prevent onSaveInstanceState() to be called as we are managing the state of the Switch + // on our own + mSwitch.setSaveEnabled(false); + mSwitch.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + + addOnSwitchChangeListener((switchView, isChecked) -> setTextViewLabel(isChecked)); + + setOnClickListener(this); + + // Default is hide + setVisibility(View.GONE); + } + + public void setTextViewLabel(boolean isChecked) { + mLabel = getResources() + .getString(isChecked ? R.string.abc_capital_on : R.string.abc_capital_off); + updateText(); + } + + public void setSummary(String summary) { + mSummary = summary; + updateText(); + } + + private void updateText() { + if (TextUtils.isEmpty(mSummary)) { + mTextView.setText(mLabel); + return; + } + final SpannableStringBuilder ssb = new SpannableStringBuilder(mLabel).append('\n'); + final int start = ssb.length(); + ssb.append(mSummary); + ssb.setSpan(mSummarySpan, start, ssb.length(), 0); + mTextView.setText(ssb); + } + + public void setChecked(boolean checked) { + setTextViewLabel(checked); + mSwitch.setChecked(checked); + } + + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mTextView.setEnabled(enabled); + mSwitch.setEnabled(enabled); + } + + public boolean isShowing() { + return (getVisibility() == View.VISIBLE); + } + + @Override + public void onClick(View v) { + final boolean isChecked = !mSwitch.isChecked(); + setChecked(isChecked); + } + + public void propagateChecked(boolean isChecked) { + final int count = mSwitchChangeListeners.size(); + for (int n = 0; n < count; n++) { + mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked); + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + propagateChecked(isChecked); + } + + public void addOnSwitchChangeListener(OnSwitchChangeListener listener) { + if (mSwitchChangeListeners.contains(listener)) { + throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener"); + } + mSwitchChangeListeners.add(listener); + } + + static class SavedState extends BaseSavedState { + boolean checked; + boolean visible; + + SavedState(Parcelable superState) { + super(superState); + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + checked = (Boolean) in.readValue(Boolean.class.getClassLoader()); + visible = (Boolean) in.readValue(Boolean.class.getClassLoader()); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeValue(checked); + out.writeValue(visible); + } + + @NonNull + @Override + public String toString() { + return "SwitchBar.SavedState{" + + Integer.toHexString(System.identityHashCode(this)) + + " checked=" + checked + + " visible=" + visible + "}"; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + SavedState ss = new SavedState(superState); + ss.checked = mSwitch.isChecked(); + ss.visible = isShowing(); + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + + super.onRestoreInstanceState(ss.getSuperState()); + + mSwitch.setCheckedInternal(ss.checked); + setTextViewLabel(ss.checked); + setVisibility(ss.visible ? View.VISIBLE : View.GONE); + mSwitch.setOnCheckedChangeListener(ss.visible ? this : null); + + requestLayout(); + } +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBarResourceSettingsFragment.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBarResourceSettingsFragment.java new file mode 100644 index 00000000..f590aa25 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBarResourceSettingsFragment.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.os.Bundle; + +import com.google.android.material.switchmaterial.SwitchMaterial; + +public abstract class SwitchBarResourceSettingsFragment extends ResourceSettingsFragment implements SwitchBar.OnSwitchChangeListener { + private final SwitchMaterial switchMaterial; + private boolean listenerSetup = false; + + protected SwitchBarResourceSettingsFragment(SwitchMaterial switchMaterial) { + this.switchMaterial = switchMaterial; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + } + + @Override + public void onResume() { + super.onResume(); + if (!listenerSetup) { + listenerSetup = true; + } + } + + @Override + public void onPause() { + if (listenerSetup) { + listenerSetup = false; + } + super.onPause(); + } + + @Override + public void onSwitchChanged(SwitchMaterial switchView, boolean isChecked) { + if (switchView == switchMaterial) { + onSwitchBarChanged(isChecked); + } + } + + public abstract void onSwitchBarChanged(boolean isChecked); +} diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java new file mode 100644 index 00000000..3a3a7bb3 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2014-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 org.microg.tools.ui; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; + +import com.google.android.material.switchmaterial.SwitchMaterial; + +@SuppressLint("NewApi") +public class ToggleSwitch extends SwitchMaterial { + + private ToggleSwitch.OnBeforeCheckedChangeListener mOnBeforeListener; + + public interface OnBeforeCheckedChangeListener { + boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked); + } + + public ToggleSwitch(Context context) { + super(context); + } + + public ToggleSwitch(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ToggleSwitch(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setChecked(boolean checked) { + if (mOnBeforeListener != null + && mOnBeforeListener.onBeforeCheckedChanged(this, checked)) { + return; + } + super.setChecked(checked); + } + + public void setCheckedInternal(boolean checked) { + super.setChecked(checked); + } +} diff --git a/play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_less.xml b/play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_less.xml new file mode 100644 index 00000000..62ff063e --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_less.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_more.xml b/play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_more.xml new file mode 100644 index 00000000..871f6575 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_more.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/play-services-core/microg-ui-tools/src/main/res/layout-v21/preference_material.xml b/play-services-core/microg-ui-tools/src/main/res/layout-v21/preference_material.xml new file mode 100644 index 00000000..3f7906ae --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/res/layout-v21/preference_material.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/play-services-core/microg-ui-tools/src/main/res/layout/about_root.xml b/play-services-core/microg-ui-tools/src/main/res/layout/about_root.xml new file mode 100644 index 00000000..3c0a274b --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/res/layout/about_root.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + diff --git a/play-services-core/microg-ui-tools/src/main/res/layout/condition_card.xml b/play-services-core/microg-ui-tools/src/main/res/layout/condition_card.xml new file mode 100644 index 00000000..d42bf4a6 --- /dev/null +++ b/play-services-core/microg-ui-tools/src/main/res/layout/condition_card.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +