diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml deleted file mode 100644 index 1913b6f2..00000000 --- a/.github/workflows/debug.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Debug APK Builder - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: JDK 8 Setup - uses: actions/setup-java@v1 - with: - java-version: 11.0.8 - - - name: Install NDK - run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;21.0.6113669" - - - name: Grant rights - run: chmod +x ./gradlew - - - name: Build project - run: ./gradlew build - - - name: Build APK - run: ./gradlew assembleDebug - - - name: Upload to Artifacts - uses: actions/upload-artifact@v2 - with: - name: 'MicroG' - path: play-services-core/build/outputs/apk/debug/play-services-core-debug.apk \ No newline at end of file diff --git a/README.md b/README.md index b0b2112a..29f47891 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,14 @@ -# Vanced MicroG +# Vanced+ MicroG -![Build Status](https://github.com/TeamVanced/VancedMicroG/workflows/Debug%20APK%20Builder/badge.svg) -[![Github All Releases](https://img.shields.io/github/downloads/inotia00/VancedMicroG/total.svg)](https://github.com/inotia00/VancedMicroG/releases) [![Github All Releases](https://img.shields.io/github/release/inotia00/VancedMicroG.svg)](https://github.com/inotia00/VancedMicroG/releases) +This fork tweaks MicroG to be usable by applications that require Google authentication such as [YouTube Vanced+](https://gitlab.com/cuynu/vancedx). -microG GmsCore is a FLOSS (Free/Libre Open Source Software) framework to allow applications designed for Google Play Services to run on systems, where Play Services is not available. - -This fork tweaks MicroG to be usable by applications that require Google authentication such as Vanced. +## Warning! +Vanced+ microG are currently **DOES NOT WORK** with ReVanced/RVX as they now uses their own ReVanced microG with specific package name! However, there are workaround : [Issues #15](https://gitlab.com/cuynu/VancedxMicroG/-/issues/15#note_1837372638) ## Notable changes - No longer a system app -- Package name changed from `com.google.android.gms` to `com.mgoogle.android.gms` to support installation alongside the official MicroG +- Package name changed from `com.google.android.gms` to `com.mgoogle.android.gms` to support installation alongside the official MicroG or device with Google Play Services pre-installed - Removed unnecessary features: - Ads - Analytics @@ -34,4 +32,4 @@ This fork tweaks MicroG to be usable by applications that require Google authent ## Credits - Source Code for MicroG 0.2.25.224113 was provided by [@OxrxL](https://github.com/OxrxL) -- [@shadow578](https://github.com/shadow578)'s commit used to apply ReVanced's `SPOOFED_PACKAGE_SIGNATURE` \ No newline at end of file +- [@shadow578](https://github.com/shadow578)'s commit used to apply `SPOOFED_PACKAGE_SIGNATURE` diff --git a/build.gradle b/build.gradle index accd8069..bcf0bcbe 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,16 @@ */ buildscript { - ext.kotlinVersion = '1.7.20' + ext.applicationNamespace = "com.google.android.gms" + ext.basePackageName = "com.mgoogle" + + ext.safeParcelVersion = '1.7.1' + + ext.kotlinVersion = '1.7.21' ext.coroutineVersion = '1.6.4' ext.annotationVersion = '1.5.0' - ext.appcompatVersion = '1.5.1' + ext.appcompatVersion = '1.6.1' ext.coreVersion = '1.9.0' ext.collectionVersion = '1.3.0-dev01' ext.fragmentVersion = '1.5.5' @@ -20,14 +25,14 @@ buildscript { ext.circleimageviewVersion = '3.1.0' ext.volleyVersion = '1.2.1' - ext.wireVersion = '4.4.3' + ext.wireVersion = '4.8.0' - ext.androidBuildGradleVersion = '7.3.1' - ext.androidBuildVersionTools = '33.0.1' + ext.androidBuildGradleVersion = '8.0.2' + ext.androidBuildVersionTools = '33.0.2' ext.androidMinSdk = 23 - ext.androidTargetSdk = 29 + ext.androidTargetSdk = 30 ext.androidCompileSdk = 33 repositories { @@ -42,22 +47,13 @@ buildscript { } } -def execResult(...args) { - def stdout = new ByteArrayOutputStream() - exec { - commandLine args - standardOutput = stdout - } - return stdout.toString() -} -def gmsVersion = "22.49.13" +def gmsVersion = "25.09.34" def gmsVersionCode = Integer.parseInt(gmsVersion.replaceAll('\\.', '')) -def ourVersionBase = "0.2.26" -def ourVersionMinor = 52 +def ourVersionBase = "0.3.0" -def ourVersionCode = gmsVersionCode * 1000 + ourVersionMinor +def ourVersionCode = gmsVersionCode * 1000 def ourVersionName = "$ourVersionBase.$gmsVersionCode" allprojects { @@ -74,4 +70,13 @@ subprojects { mavenCentral() google() } + afterEvaluate { + // Temporary hack for Android Studio + if (project.plugins.hasPlugin('com.android.base')) { + dependencies { + compileOnly "org.microg:safe-parcel:$safeParcelVersion" + testCompileOnly "org.microg:safe-parcel:$safeParcelVersion" + } + } + } } \ No newline at end of file diff --git a/fake-signature/build.gradle b/fake-signature/build.gradle new file mode 100644 index 00000000..e04d9893 --- /dev/null +++ b/fake-signature/build.gradle @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' + +android { + namespace "org.microg.signature.fake" + + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +dependencies { +} diff --git a/fake-signature/src/main/AndroidManifest.xml b/fake-signature/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1b3ae15d --- /dev/null +++ b/fake-signature/src/main/AndroidManifest.xml @@ -0,0 +1,1159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fake-signature/src/main/res/values/arrays.xml b/fake-signature/src/main/res/values/arrays.xml new file mode 100644 index 00000000..315cce58 --- /dev/null +++ b/fake-signature/src/main/res/values/arrays.xml @@ -0,0 +1,15 @@ + + + + + com.google.android.gms + com.android.vending + com.spotify.music + + + com.truecaller + + \ No newline at end of file diff --git a/play-services-core/src/main/res/values/signature.xml b/fake-signature/src/main/res/values/signature.xml old mode 100755 new mode 100644 similarity index 56% rename from play-services-core/src/main/res/values/signature.xml rename to fake-signature/src/main/res/values/signature.xml index df53f65c..282e0dc1 --- a/play-services-core/src/main/res/values/signature.xml +++ b/fake-signature/src/main/res/values/signature.xml @@ -1,21 +1,14 @@ - - + 308204433082032ba003020102020900c2e08746644a308d300d06092a864886f70d01010405003074310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c6520496e632e3110300e060355040b1307416e64726f69643110300e06035504031307416e64726f6964301e170d3038303832313233313333345a170d3336303130373233313333345a3074310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c6520496e632e3110300e060355040b1307416e64726f69643110300e06035504031307416e64726f696430820120300d06092a864886f70d01010105000382010d00308201080282010100ab562e00d83ba208ae0a966f124e29da11f2ab56d08f58e2cca91303e9b754d372f640a71b1dcb130967624e4656a7776a92193db2e5bfb724a91e77188b0e6a47a43b33d9609b77183145ccdf7b2e586674c9e1565b1f4c6a5955bff251a63dabf9c55c27222252e875e4f8154a645f897168c0b1bfc612eabf785769bb34aa7984dc7e2ea2764cae8307d8c17154d7ee5f64a51a44a602c249054157dc02cd5f5c0e55fbef8519fbe327f0b1511692c5a06f19d18385f5c4dbc2d6b93f68cc2979c70e18ab93866b3bd5db8999552a0e3b4c99df58fb918bedc182ba35e003c1b4b10dd244a8ee24fffd333872ab5221985edab0fc0d0b145b6aa192858e79020103a381d93081d6301d0603551d0e04160414c77d8cc2211756259a7fd382df6be398e4d786a53081a60603551d2304819e30819b8014c77d8cc2211756259a7fd382df6be398e4d786a5a178a4763074310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e205669657731143012060355040a130b476f6f676c6520496e632e3110300e060355040b1307416e64726f69643110300e06035504031307416e64726f6964820900c2e08746644a308d300c0603551d13040530030101ff300d06092a864886f70d010104050003820101006dd252ceef85302c360aaace939bcff2cca904bb5d7a1661f8ae46b2994204d0ff4a68c7ed1a531ec4595a623ce60763b167297a7ae35712c407f208f0cb109429124d7b106219c084ca3eb3f9ad5fb871ef92269a8be28bf16d44c8d9a08e6cb2f005bb3fe2cb96447e868e731076ad45b33f6009ea19c161e62641aa99271dfd5228c5c587875ddb7f452758d661f6cc0cccb7352e424cc4365c523532f7325137593c4ae341f4db41edda0d0b1071a7c440f0fe9ea01cb627ca674369d084bd2fd911ff06cdbf2cfa10dc0f893ae35762919048c7efc64c7144178342f70581c9de573af55b390dd7fdb9418631895d5f759f30112687ff621410c069308a + + 308202ed308201d5a003020102020426ffa009300d06092a864886f70d01010b05003027310b300906035504061302444531183016060355040a130f4e4f47415050532050726f6a656374301e170d3132313030363132303533325a170d3337303933303132303533325a3027310b300906035504061302444531183016060355040a130f4e4f47415050532050726f6a65637430820122300d06092a864886f70d01010105000382010f003082010a02820101009a8d2a5336b0eaaad89ce447828c7753b157459b79e3215dc962ca48f58c2cd7650df67d2dd7bda0880c682791f32b35c504e43e77b43c3e4e541f86e35a8293a54fb46e6b16af54d3a4eda458f1a7c8bc1b7479861ca7043337180e40079d9cdccb7e051ada9b6c88c9ec635541e2ebf0842521c3024c826f6fd6db6fd117c74e859d5af4db04448965ab5469b71ce719939a06ef30580f50febf96c474a7d265bb63f86a822ff7b643de6b76e966a18553c2858416cf3309dd24278374bdd82b4404ef6f7f122cec93859351fc6e5ea947e3ceb9d67374fe970e593e5cd05c905e1d24f5a5484f4aadef766e498adf64f7cf04bddd602ae8137b6eea40722d0203010001a321301f301d0603551d0e04160414110b7aa9ebc840b20399f69a431f4dba6ac42a64300d06092a864886f70d01010b0500038201010007c32ad893349cf86952fb5a49cfdc9b13f5e3c800aece77b2e7e0e9c83e34052f140f357ec7e6f4b432dc1ed542218a14835acd2df2deea7efd3fd5e8f1c34e1fb39ec6a427c6e6f4178b609b369040ac1f8844b789f3694dc640de06e44b247afed11637173f36f5886170fafd74954049858c6096308fc93c1bc4dd5685fa7a1f982a422f2a3b36baa8c9500474cf2af91c39cbec1bc898d10194d368aa5e91f1137ec115087c31962d8f76cd120d28c249cf76f4c70f5baa08c70a7234ce4123be080cee789477401965cfe537b924ef36747e8caca62dfefdd1a6288dcb1c4fd2aaa6131a7ad254e9742022cfd597d2ca5c660ce9e41ff537e5a4041e37 + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 976747da..126b7d28 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,5 @@ android.useAndroidX=true -android.disableAutomaticComponentCreation=true org.gradle.jvmargs=-Xmx2048m -XX:+UseParallelGC +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c053..d64cd491 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d38c612c..0e1ae8ce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Mon Dec 22 22:27:19 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 60cf9e8c..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -1,6 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: Apache-2.0 # # Copyright © 2015-2021 the original authors. # @@ -56,7 +55,7 @@ # 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 +# https://github.com/gradle/gradle/blob/HEAD/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/. @@ -81,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 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"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -128,26 +125,35 @@ if [ -n "$JAVA_HOME" ] ; then 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. + if ! command -v java >/dev/null 2>&1 + then + 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 fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -192,11 +198,15 @@ if "$cygwin" || "$msys" ; then 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. + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -204,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. @@ -230,4 +246,4 @@ eval "set -- $( tr '\n' ' ' )" '"$@"' -exec "$JAVACMD" "$@" \ No newline at end of file +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 5093609d..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -54,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -64,21 +65,6 @@ 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 @@ -86,17 +72,19 @@ 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% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 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 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/play-services-api/build.gradle b/play-services-api/build.gradle index e254f48d..20d8bea5 100644 --- a/play-services-api/build.gradle +++ b/play-services-api/build.gradle @@ -26,9 +26,13 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } + buildFeatures { + aidl true + } + namespace 'org.microg.gms.api' } dependencies { diff --git a/play-services-api/src/main/AndroidManifest.xml b/play-services-api/src/main/AndroidManifest.xml index d4a4d07a..359a9b71 100644 --- a/play-services-api/src/main/AndroidManifest.xml +++ b/play-services-api/src/main/AndroidManifest.xml @@ -15,4 +15,4 @@ ~ limitations under the License. --> - + 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 index ae33a334..d6afdf80 100644 --- 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 @@ -7,7 +7,7 @@ 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; +import com.google.android.gms.auth.HasCapabilitiesRequest; interface IAuthManagerService { Bundle getToken(String accountName, String scope, in Bundle extras) = 0; @@ -18,6 +18,6 @@ interface IAuthManagerService { Bundle getAccounts(in Bundle extras) = 5; Bundle removeAccount(in Account account) = 6; Bundle requestGoogleAccountsAccess(String packageName) = 7; - int hasCapabilities(in HasCababilitiesRequest request) = 8; + int hasCapabilities(in HasCapabilitiesRequest 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/HasCababilitiesRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCapabilitiesRequest.aidl similarity index 52% rename from play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl rename to play-services-api/src/main/aidl/com/google/android/gms/auth/HasCapabilitiesRequest.aidl index c0c080cd..b6cb6b9d 100644 --- 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/HasCapabilitiesRequest.aidl @@ -1,3 +1,3 @@ package com.google.android.gms.auth; -parcelable HasCababilitiesRequest; +parcelable HasCapabilitiesRequest; 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 deleted file mode 100644 index ae02e068..00000000 --- a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl +++ /dev/null @@ -1,3 +0,0 @@ -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/IGoogleCertificatesApi.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl deleted file mode 100644 index da539f32..00000000 --- a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl +++ /dev/null @@ -1,12 +0,0 @@ -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/java/com/google/android/gms/auth/HasCababilitiesRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/HasCapabilitiesRequest.java similarity index 63% rename from play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java rename to play-services-api/src/main/java/com/google/android/gms/auth/HasCapabilitiesRequest.java index 27b94732..e35ca6d7 100644 --- 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/HasCapabilitiesRequest.java @@ -9,10 +9,10 @@ import android.accounts.Account; import org.microg.safeparcel.AutoSafeParcelable; -public class HasCababilitiesRequest extends AutoSafeParcelable { +public class HasCapabilitiesRequest extends AutoSafeParcelable { @Field(1) public Account account; @Field(2) public String[] capabilities; - public static final Creator CREATOR = new AutoCreator<>(HasCababilitiesRequest.class); + public static final Creator CREATOR = new AutoCreator<>(HasCapabilitiesRequest.class); } diff --git a/play-services-base-api/build.gradle b/play-services-base-api/build.gradle index b806da4c..23b6b07c 100644 --- a/play-services-base-api/build.gradle +++ b/play-services-base-api/build.gradle @@ -34,7 +34,11 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } + buildFeatures { + aidl true + } + namespace 'org.microg.gms.base.api' } \ No newline at end of file diff --git a/play-services-base-api/src/main/AndroidManifest.xml b/play-services-base-api/src/main/AndroidManifest.xml index 7c8eeef3..055d07d5 100644 --- a/play-services-base-api/src/main/AndroidManifest.xml +++ b/play-services-base-api/src/main/AndroidManifest.xml @@ -3,4 +3,4 @@ ~ SPDX-FileCopyrightText: 2020, microG Project Team ~ SPDX-License-Identifier: Apache-2.0 --> - + 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 index f7b8e46a..4af2bcf1 100644 --- 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 @@ -18,8 +18,12 @@ package com.google.android.gms.common.data; import android.annotation.SuppressLint; import android.content.ContentValues; +import android.database.AbstractWindowedCursor; +import android.database.CharArrayBuffer; import android.database.Cursor; import android.database.CursorWindow; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -41,6 +45,9 @@ import java.util.Map; */ @PublicApi(until = "1") public class DataHolder extends AutoSafeParcelable implements Closeable { + @SafeParceled(1000) + private final int versionCode = 1; + @SafeParceled(1) private final String[] columns; @@ -62,6 +69,13 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { protected static final int FIELD_TYPE_STRING = 3; protected static final int FIELD_TYPE_BLOB = 4; + private DataHolder() { + columns = null; + windows = null; + statusCode = 0; + metadata = null; + } + /** * Creates a data holder with the specified data. * @@ -78,6 +92,17 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { 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(AbstractWindowedCursor cursor, int statusCode, Bundle metadata) { + this(cursor.getColumnNames(), createCursorWindows(cursor), statusCode, metadata); + } + /** * Creates a data holder wrapping the provided cursor, with provided status code and metadata. * @@ -110,7 +135,23 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { return builder(columns, null); } - @SuppressWarnings("deprecation") + /** + * @param statusCode The status code of this {@link DataHolder}. + * @param metadata The metadata associated with this {@link DataHolder} (may be null). + * @return An empty {@link DataHolder} object with the given status and metadata. + */ + public static DataHolder empty(int statusCode, Bundle metadata) { + return new DataHolder(new String[0], new CursorWindow[0], statusCode, metadata); + } + + /** + * @param statusCode The status code of this {@link DataHolder}. + * @return An empty {@link DataHolder} object with the given status and null metadata. + */ + public static DataHolder empty(int statusCode) { + return empty(statusCode, null); + } + @SuppressLint({"NewApi", "ObsoleteSdkInt"}) static int getCursorType(Cursor cursor, int i) { return cursor.getType(i); @@ -131,6 +172,20 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { } } + /** + * Copies the String content in the given column at the provided position into a {@link CharArrayBuffer}. + * 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. + * @param dataOut The {@link CharArrayBuffer} to copy into. + */ + public void copyToBuffer(String column, int row, int windowIndex, CharArrayBuffer dataOut) { + throw new RuntimeException("Not yet available"); + } + @SuppressWarnings("deprecation") private static CursorWindow[] createCursorWindows(Builder builder) { if (builder.columns.length == 0) return new CursorWindow[0]; @@ -223,6 +278,34 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { return windows.toArray(new CursorWindow[0]); } + /** + * Retrieves the boolean 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 boolean value in that column. + */ + public boolean getBoolean(String column, int row, int windowIndex) { + return windows[windowIndex].getLong(row, columnIndices.get(column)) == 1; + } + + /** + * Retrieves the byte array 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 byte array value in that column. + */ + public byte[] getByteArray(String column, int row, int windowIndex) { + return windows[windowIndex].getBlob(row, columnIndices.get(column)); + } + /** * Gets the number of rows in the data holder. * @@ -238,6 +321,38 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { return c; } + /** + * Retrieves the integer 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 integer value in that column. + */ + public int getInteger(String column, int row, int windowIndex) { + return windows[windowIndex].getInt(row, columnIndices.get(column)); + } + + /** + * Retrieves the long 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 long value in that column. + */ + public long getLong(String column, int row, int windowIndex) { + return windows[windowIndex].getLong(row, columnIndices.get(column)); + } + + public int getStatusCode() { + return statusCode; + } + /** * 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 @@ -252,6 +367,42 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { return windows[windowIndex].getString(row, columnIndices.get(column)); } + /** + * Returns whether the given column at the provided position contains null. + * 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 Whether the column value is null at this position. + */ + public boolean isNull(String column, int row, int windowIndex) { + return windows[windowIndex].isNull(row, columnIndices.get(column)); + } + + public boolean isClosed() { + synchronized (this) { + return closed; + } + } + + /** + * Retrieves the column data at the provided position as a URI if possible, checking for null values. + * 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 column data as a URI, or null if not present. + */ + public Uri parseUri(String column, int row, int windowIndex) { + String string = getString(column, row, windowIndex); + if (string != null) return Uri.parse(string); + return null; + } + @Override public String toString() { return "DataHolder{" + @@ -315,6 +466,18 @@ public class DataHolder extends AutoSafeParcelable implements Closeable { return rows.size(); } + /** + * Sort the rows in this builder based on the standard data type comparisons for the value in the provided column. + * Calling this multiple times with the same column will not change the sort order of the builder. + * Note that any data which is added after this call will not be sorted. + * + * @param sortColumn The column to sort the rows in this builder by. + * @return {@link DataHolder.Builder} to continue construction. + */ + public Builder sort(String sortColumn) { + throw new RuntimeException("Not yet implemented"); + } + /** * Add a new row of data to the {@link DataHolder} this {@link DataHolder.Builder} will create. Note that the data must contain an entry for all columns *

diff --git a/play-services-base-core/build.gradle b/play-services-base-core/build.gradle index a0849ff8..34288f21 100644 --- a/play-services-base-core/build.gradle +++ b/play-services-base-core/build.gradle @@ -21,9 +21,10 @@ android { buildToolsVersion androidBuildVersionTools defaultConfig { - versionName version minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk + + buildConfigField "String", "BASE_PACKAGE_NAME", "\"$basePackageName\"" } sourceSets { @@ -31,7 +32,8 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + 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 index de583c4d..369707e7 100644 --- a/play-services-base-core/src/main/AndroidManifest.xml +++ b/play-services-base-core/src/main/AndroidManifest.xml @@ -3,11 +3,26 @@ ~ SPDX-FileCopyrightText: 2020, microG Project Team ~ SPDX-License-Identifier: Apache-2.0 --> - + + + + + + android:exported="true" + android:readPermission="${applicationId}.permission.READ_SETTINGS" + android:writePermission="${applicationId}.permission.WRITE_SETTINGS" /> + + 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 index 1cd39310..a0ff2b99 100644 --- 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 @@ -18,7 +18,6 @@ package org.microg.gms; import android.accounts.Account; import android.os.Bundle; -import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.util.Log; @@ -29,6 +28,7 @@ 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.base.core.BuildConfig; import org.microg.gms.common.GmsService; import java.util.EnumSet; @@ -77,7 +77,7 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub { request.gmsVersion = gmsVersion; request.packageName = packageName; request.extras = extras; - request.account = accountName == null ? null : new Account(accountName, "com.mgoogle"); + request.account = accountName == null ? null : new Account(accountName, BuildConfig.BASE_PACKAGE_NAME); request.scopes = scopes == null ? null : scopesFromStringArray(scopes); getService(callback, request); } 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 index d4ab5940..55bffb3c 100644 --- 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 @@ -16,6 +16,8 @@ package org.microg.gms.common; +import android.annotation.SuppressLint; + import android.app.ActivityManager; import android.content.Context; import android.content.pm.ConfigurationInfo; @@ -26,6 +28,8 @@ import android.opengl.GLES10; import android.os.Build; import android.util.DisplayMetrics; +import org.microg.gms.profile.ProfileManager; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -56,6 +60,7 @@ public class DeviceConfiguration { public int widthPixels; public DeviceConfiguration(Context context) { + ProfileManager.ensureInitialized(context); ConfigurationInfo configurationInfo = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getDeviceConfigurationInfo(); touchScreen = configurationInfo.reqTouchScreen; keyboardType = configurationInfo.reqKeyboardType; @@ -78,29 +83,35 @@ public class DeviceConfiguration { } 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); - } + packageManager.getSystemAvailableFeatures(); + 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); + locales = getLocales(context); Set glExtensions = new HashSet<>(); addEglExtensions(glExtensions); this.glExtensions = new ArrayList<>(glExtensions); Collections.sort(this.glExtensions); } - @SuppressWarnings({"deprecation", "InlinedApi"}) + @SuppressLint("GetLocales") + private static List getLocales(Context context) { + List locales = new ArrayList(); + locales.addAll(Arrays.asList(context.getAssets().getLocales())); + for (int i = 0; i < locales.size(); i++) { + locales.set(i, locales.get(i).replace("-", "_")); + } + Collections.sort(locales); + return locales; + } + + @SuppressWarnings({"InlinedApi"}) private static List getNativePlatforms() { - List nativePlatforms; return Arrays.asList(Build.SUPPORTED_ABIS); } 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 index c7a2ff52..417ad6f7 100644 --- 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 @@ -1,5 +1,7 @@ package org.microg.gms.common; +import static android.os.Build.VERSION.SDK_INT; + import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationChannel; @@ -11,7 +13,6 @@ 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; @@ -33,7 +34,7 @@ public class ForegroundServiceContext extends ContextWrapper { 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) { + if (SDK_INT >= 26) { return super.startForegroundService(service); } else { return super.startService(service); @@ -75,7 +76,7 @@ public class ForegroundServiceContext extends ContextWrapper { String serviceName = getServiceName(service); Log.d(tag, "Started " + serviceName + " in foreground mode."); try { - Notification notification = buildForegroundNotification(service, serviceName); + Notification notification = buildForegroundNotification(service); service.startForeground(serviceName.hashCode(), notification); Log.d(tag, "Notification: " + notification); } catch (Exception e) { @@ -85,9 +86,9 @@ public class ForegroundServiceContext extends ContextWrapper { } @SuppressLint("BatteryLife") - private static Notification buildForegroundNotification(Context context, String serviceName) { + private static Notification buildForegroundNotification(Context context) { Intent notificationIntent = new Intent(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SDK_INT >= 31) { notificationIntent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); notificationIntent.setData(Uri.parse("package:" + context.getPackageName())); } else { @@ -97,8 +98,8 @@ public class ForegroundServiceContext extends ContextWrapper { PendingIntent notificationPendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, - 0); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + PendingIntent.FLAG_IMMUTABLE); + if (SDK_INT >= 26) { NotificationChannel Channel = new NotificationChannel("foreground-service", context.getResources().getString(R.string.notification_service_name), NotificationManager.IMPORTANCE_LOW); 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 index 50d112d0..03fd8b93 100644 --- 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 @@ -16,6 +16,10 @@ package org.microg.gms.common; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1; + +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Application; import android.app.PendingIntent; @@ -28,6 +32,8 @@ import android.util.Log; import androidx.annotation.Nullable; +import org.microg.gms.base.core.BuildConfig; + import java.lang.reflect.Method; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -35,28 +41,25 @@ 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; +import java.util.Objects; 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 String GOOGLE_LEGACY_KEY = "58e1c4133f7441ec3d2c270270a14802da47ba0e"; // Seems to be no longer used. + private static final String[] GOOGLE_PRIMARY_KEYS = {GOOGLE_PLATFORM_KEY, "24bb24c05e47e0aefa68a58a766179d9b613a600", "afb0fed5eeaebdd86f56a97742f4b6b33ef59875", "61226bdb57cc32c8a2a9ef71f7bc9548e95dcc0b", "3a82b5ee26bc46bf68113d920e610cd090198d4a"}; - private static final Map KNOWN_GOOGLE_PACKAGES; + public static final Map KNOWN_GOOGLE_PACKAGES; static { KNOWN_GOOGLE_PACKAGES = new HashMap<>(); + KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.youtube", "24bb24c05e47e0aefa68a58a766179d9b613a600"); KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.youtube.music", "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"); } public static boolean isGooglePackage(Context context, String packageName) { String signatureDigest = firstSignatureDigest(context, packageName); + packageName = PackageSpoofUtils.spoofPackageName(context.getPackageManager(), packageName); return isGooglePackage(packageName, signatureDigest); } @@ -73,7 +76,7 @@ public class PackageUtils { 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); + return Objects.equals(KNOWN_GOOGLE_PACKAGES.get(packageName), signatureDigest); } public static void assertExtendedAccess(Context context) { @@ -83,14 +86,14 @@ public class PackageUtils { public static boolean callerHasExtendedAccess(Context context) { String[] packagesForUid = context.getPackageManager().getPackagesForUid(Binder.getCallingUid()); - if (packagesForUid != null && packagesForUid.length != 0) { + if (packagesForUid != null) { 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; + return context.checkCallingPermission(BuildConfig.BASE_PACKAGE_NAME + ".gms.EXTENDED_ACCESS") == PackageManager.PERMISSION_GRANTED; } public static void checkPackageUid(Context context, String packageName, int callingUid) { @@ -120,9 +123,9 @@ public class PackageUtils { try { info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); } catch (PackageManager.NameNotFoundException e) { - return null; + return KNOWN_GOOGLE_PACKAGES.get(packageName); } - if (info != null && info.signatures != null && info.signatures.length > 0) { + if (info != null && info.signatures != null) { for (Signature sig : info.signatures) { String digest = sha1sum(sig.toByteArray()); if (digest != null) { @@ -147,7 +150,7 @@ public class PackageUtils { } catch (PackageManager.NameNotFoundException e) { return null; } - if (info != null && info.signatures != null && info.signatures.length > 0) { + if (info != null && info.signatures != null) { for (Signature sig : info.signatures) { byte[] digest = sha1bytes(sig.toByteArray()); if (digest != null) { @@ -178,10 +181,7 @@ public class PackageUtils { } catch (final NoSuchAlgorithmException e) { return null; } - if (md != null) { - return md.digest(bytes); - } - return null; + return md.digest(bytes); } @Nullable @@ -220,7 +220,7 @@ public class PackageUtils { if (suggestedCallerPid > 0 && suggestedCallerPid != callingPid) { throw new SecurityException("suggested PID [" + suggestedCallerPid + "] and real calling PID [" + callingPid + "] mismatch!"); } - return getAndCheckPackage(context, suggestedPackageName, callingUid, Binder.getCallingPid()); + return getAndCheckPackage(context, suggestedPackageName, callingUid, callingPid); } @Nullable @@ -252,7 +252,6 @@ public class PackageUtils { } @Nullable - @Deprecated public static String packageFromProcessId(Context context, int pid) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); if (manager == null) return null; @@ -277,7 +276,6 @@ public class PackageUtils { return null; } - @SuppressWarnings("deprecation") public static String packageFromPendingIntent(PendingIntent pi) { if (pi == null) return null; return pi.getCreatorPackage(); @@ -287,8 +285,10 @@ public class PackageUtils { if (android.os.Build.VERSION.SDK_INT >= 28) return Application.getProcessName(); try { + @SuppressLint("PrivateApi") Class activityThread = Class.forName("android.app.ActivityThread"); String methodName = "currentProcessName"; + @SuppressLint("DiscouragedPrivateApi") Method getProcessName = activityThread.getDeclaredMethod(methodName); return (String) getProcessName.invoke(null); } catch (Exception e) { @@ -333,17 +333,12 @@ public class PackageUtils { } 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(); - } + bytes = md.digest(bytes); + StringBuilder sb = new StringBuilder(2 * bytes.length); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); } - return null; + return sb.toString(); } public static int versionCode(Context context, String packageName) { 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 index 94ec088d..7aec1077 100644 --- 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 @@ -4,15 +4,17 @@ import android.content.pm.PackageManager import android.os.Bundle import android.util.Log +import org.microg.gms.base.core.BuildConfig + /** * 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" + "${BuildConfig.BASE_PACKAGE_NAME}.android.gms.SPOOFED_PACKAGE_NAME" private const val META_SPOOF_PACKAGE_SIGNATURE = - "com.mgoogle.android.gms.SPOOFED_PACKAGE_SIGNATURE" + "${BuildConfig.BASE_PACKAGE_NAME}.android.gms.SPOOFED_PACKAGE_SIGNATURE" private val spoofedPackageNameCache = HashMap() private val spoofedPackageSignatureCache = HashMap() @@ -139,6 +141,7 @@ internal object PackageSpoofUtils { * @param packageName name of the package to get metadata of * @return package metadata bundle */ + @Suppress("DEPRECATION") private fun getPackageMetadata(packageManager: PackageManager, packageName: String): Bundle? { return try { // PackageManager.getPackageInfo() has been deprecated in targetSdkVersion 30+ 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 index a52059a9..725e0d6a 100644 --- 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 @@ -24,7 +24,6 @@ object Build { var CPU_ABI2: String? = null @JvmField - @TargetApi(21) var SUPPORTED_ABIS: Array = emptyArray() @JvmField @@ -90,6 +89,9 @@ object Build { @JvmField var SECURITY_PATCH: String? = null + + @JvmField + var DEVICE_INITIAL_SDK_INT: Int = 0 } fun generateWebViewUserAgentString(original: String): String { 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 index ed756017..76286eab 100644 --- 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 @@ -10,8 +10,8 @@ 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.microg.gms.settings.SettingsContract +import org.microg.gms.settings.SettingsContract.Profile import org.xmlpull.v1.XmlPullParser import java.io.File import java.util.* @@ -68,43 +68,60 @@ object ProfileManager { 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) + else -> { + val parser = getProfileXml(context, profile) + if (parser != null) { + try { + var next = parser.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (parser.name) { + "profile" -> { + return@runCatching parser.getAttributeBooleanValue(null, "auto", false) + } + } } + next = parser.next() } + } finally { + parser.close() } - next = it.next() + false + } else { + false } - } == true + } } }.getOrDefault(false) + fun getActiveProfileData(context: Context): Map = + getProfileData(context, getProfile(context), getRealData()) + 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 + if (profile != PROFILE_USER && getProfileResId(context, profile) == 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") + val parser = getProfileXml(context, profile) + if (parser != null) { + try { + var next = parser.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (parser.name) { + "data" -> { + val key = parser.getAttributeValue(null, "key") + val value = parser.getAttributeValue(null, "value") + resultData[key] = value + Log.d(TAG, "Overwrite from profile: $key = $value") + } } } + next = parser.next() } - next = it.next() + } finally { + parser.close() } } return resultData @@ -152,15 +169,20 @@ object ProfileManager { // 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") + val parser = getProfileXml(context, profile) + if (parser != null) { + try { + var next = parser.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (parser.name) { + "serial" -> return parser.getAttributeValue(null, "template") + } } + next = parser.next() } - next = it.next() + } finally { + parser.close() } } } catch (e: Exception) { @@ -183,6 +205,7 @@ object ProfileManager { return serial } + @SuppressLint("BlockedPrivateApi") private fun getRealData(): Map = mutableMapOf( "Build.BOARD" to android.os.Build.BOARD, "Build.BOOTLOADER" to android.os.Build.BOOTLOADER, @@ -216,9 +239,15 @@ object ProfileManager { if (android.os.Build.VERSION.SDK_INT >= 23) { put("Build.VERSION.SECURITY_PATCH", android.os.Build.VERSION.SECURITY_PATCH) } + try { + val field = android.os.Build.VERSION::class.java.getDeclaredField("DEVICE_INITIAL_SDK_INT") + field.isAccessible = true + put("Build.VERSION.DEVICE_INITIAL_SDK_INT", field.getInt(null).toString()) + } catch (ignored: Exception) { + } } - private fun applyProfileData(profileData: Map) { + 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) } @@ -248,6 +277,7 @@ object ProfileManager { 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 } + applyIntField("Build.VERSION.DEVICE_INITIAL_SDK_INT") { Build.VERSION.DEVICE_INITIAL_SDK_INT = it } if (android.os.Build.VERSION.SDK_INT >= 21) { Build.SUPPORTED_ABIS = profileData["Build.SUPPORTED_ABIS"]?.split(",")?.toTypedArray() ?: emptyArray() } else { @@ -275,19 +305,26 @@ object ProfileManager { 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") + private fun getProfileName(parserCreator: () -> XmlResourceParser?): String? { + val parser = parserCreator() + if (parser != null) { + try { + var next = parser.next() + while (next != XmlPullParser.END_DOCUMENT) { + when (next) { + XmlPullParser.START_TAG -> when (parser.name) { + "profile" -> { + return parser.getAttributeValue(null, "name") + } + } } + next = parser.next() } + } finally { + parser.close() } - next = it.next() } - null + return null } fun setProfile(context: Context, profile: String?) { @@ -305,7 +342,7 @@ object ProfileManager { val profileName = getProfileName { FileXmlResourceParser(file) } ?: return false try { Log.d(TAG, "Importing user profile '$profileName'") - file.copyTo(getUserProfileFile(context)) + file.copyTo(getUserProfileFile(context), overwrite = true) if (activeProfile == PROFILE_USER) applyProfile(context, PROFILE_USER) return true } catch (e: Exception) { diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileProvider.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileProvider.kt new file mode 100644 index 00000000..53e4149f --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileProvider.kt @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2023 e Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.profile + +import android.content.ContentProvider +import android.content.ContentValues +import android.database.Cursor +import android.database.MatrixCursor +import android.net.Uri +import org.microg.gms.settings.SettingsContract + +class ProfileProvider : ContentProvider() { + + val COLUMN_ID = "profile_id" + val COLUMN_VALUE = "profile_value" + + override fun onCreate(): Boolean { + ProfileManager.ensureInitialized(context!!) + return true + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ): Cursor = + MatrixCursor(arrayOf(COLUMN_ID, COLUMN_VALUE)).apply { + ProfileManager.getActiveProfileData(context!!).entries + .forEach { + addRow(arrayOf(it.key, it.value)) + } + } + + override fun getType(uri: Uri): String { + return "vnd.android.cursor.item/vnd.${SettingsContract.getAuthority(context!!)}.${uri.path}" + } + + override fun insert(uri: Uri, values: ContentValues?): Nothing = throw UnsupportedOperationException() + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Nothing = + throw UnsupportedOperationException() + + override fun update( + uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array? + ): Nothing = throw UnsupportedOperationException() + +} diff --git a/play-services-base-core/src/main/kotlin/org/microg/gms/settings/MetaDataPreferences.kt b/play-services-base-core/src/main/kotlin/org/microg/gms/settings/MetaDataPreferences.kt new file mode 100644 index 00000000..1e21f6d9 --- /dev/null +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/settings/MetaDataPreferences.kt @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.settings + +import android.content.Context +import android.content.SharedPreferences +import android.content.pm.PackageManager.GET_META_DATA +import android.os.Bundle + +class MetaDataPreferences(private val context: Context, private val prefix: String = "") : SharedPreferences { + private val metaData by lazy { + runCatching { context.packageManager.getApplicationInfo(context.packageName, GET_META_DATA) }.getOrNull()?.metaData ?: Bundle.EMPTY + } + + override fun getAll(): Map = metaData.keySet().filter { it.startsWith(prefix) }.associate { it.substring(prefix.length) to metaData.get(it) } + + override fun getString(key: String, defValue: String?): String? = metaData.getString(prefix + key, defValue) + + override fun getStringSet(key: String, defValues: Set?): Set? = metaData.getStringArray(prefix + key)?.toSet() ?: defValues + + override fun getInt(key: String?, defValue: Int): Int = metaData.getInt(prefix + key, defValue) + + override fun getLong(key: String?, defValue: Long): Long = metaData.getLong(prefix + key, defValue) + + override fun getFloat(key: String?, defValue: Float): Float = metaData.getFloat(prefix + key, defValue) + + override fun getBoolean(key: String?, defValue: Boolean): Boolean = metaData.getBoolean(prefix + key, defValue) + + override fun contains(key: String?): Boolean = metaData.containsKey(prefix + key) + + override fun edit(): SharedPreferences.Editor { + throw UnsupportedOperationException() + } + + override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener?) { + throw UnsupportedOperationException() + } + + override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener?) { + throw UnsupportedOperationException() + } +} \ No newline at end of file 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/gms/settings/SettingsContract.kt similarity index 97% rename from play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsContract.kt rename to play-services-base-core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt index 841b47a0..8851d1a1 100644 --- a/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsContract.kt +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt @@ -1,4 +1,4 @@ -package org.microg.mgms.settings +package org.microg.gms.settings import android.content.ContentValues import android.content.Context @@ -22,7 +22,6 @@ object SettingsContract { 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" @@ -81,10 +80,12 @@ object SettingsContract { const val TRUST_GOOGLE = "auth_manager_trust_google" const val VISIBLE = "auth_manager_visible" + const val INCLUDE_ANDROID_ID = "auth_include_android_id" val PROJECTION = arrayOf( TRUST_GOOGLE, VISIBLE, + INCLUDE_ANDROID_ID, ) } 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/gms/settings/SettingsProvider.kt similarity index 90% rename from play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsProvider.kt rename to play-services-base-core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt index f1829b2c..39dc00e1 100644 --- a/play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsProvider.kt +++ b/play-services-base-core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt @@ -4,7 +4,7 @@ */ @file:Suppress("DEPRECATION") -package org.microg.mgms.settings +package org.microg.gms.settings import android.content.ContentProvider import android.content.ContentValues @@ -16,21 +16,20 @@ 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 org.microg.gms.settings.SettingsContract.Auth +import org.microg.gms.settings.SettingsContract.CheckIn +import org.microg.gms.settings.SettingsContract.Gcm +import org.microg.gms.settings.SettingsContract.Profile +import org.microg.gms.settings.SettingsContract.getAuthority import java.io.File +private const val SETTINGS_PREFIX = "org.microg.gms.settings." + /** * All settings access should go through this [ContentProvider], * because it provides safe access from different processes which normal [SharedPreferences] don't. */ class SettingsProvider : ContentProvider() { - companion object { - const val INTERVAL = 1 * 60 * 1000 // 1 minute - } private val preferences: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) @@ -50,6 +49,10 @@ class SettingsProvider : ContentProvider() { } } + private val metaDataPreferences: SharedPreferences by lazy { + MetaDataPreferences(context!!, SETTINGS_PREFIX) + } + override fun onCreate(): Boolean { return true } @@ -97,7 +100,7 @@ class SettingsProvider : ContentProvider() { 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) + CheckIn.HIDE_LAUNCHER_ICON -> getSettingsBoolean(key, true) else -> throw IllegalArgumentException() } } @@ -165,9 +168,9 @@ class SettingsProvider : ContentProvider() { 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, Companion.INTERVAL) - Gcm.LEARNT_WIFI -> preferences.getInt(key, Companion.INTERVAL) - Gcm.LEARNT_OTHER -> preferences.getInt(key, Companion.INTERVAL) + Gcm.LEARNT_MOBILE -> preferences.getInt(key, 300000) + Gcm.LEARNT_WIFI -> preferences.getInt(key, 300000) + Gcm.LEARNT_OTHER -> preferences.getInt(key, 300000) else -> throw IllegalArgumentException("Unknown key: $key") } @@ -202,6 +205,7 @@ class SettingsProvider : ContentProvider() { when (key) { Auth.TRUST_GOOGLE -> getSettingsBoolean(key, true) Auth.VISIBLE -> getSettingsBoolean(key, false) + Auth.INCLUDE_ANDROID_ID -> getSettingsBoolean(key, true) else -> throw IllegalArgumentException("Unknown key: $key") } } @@ -213,6 +217,7 @@ class SettingsProvider : ContentProvider() { when (key) { Auth.TRUST_GOOGLE -> editor.putBoolean(key, value as Boolean) Auth.VISIBLE -> editor.putBoolean(key, value as Boolean) + Auth.INCLUDE_ANDROID_ID -> editor.putBoolean(key, value as Boolean) else -> throw IllegalArgumentException("Unknown key: $key") } } @@ -267,12 +272,12 @@ class SettingsProvider : ContentProvider() { * @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) + return listOf(preferences, systemDefaultPreferences, metaDataPreferences).getBooleanAsInt(key, def) } - private fun getSettingsString(key: String, def: String? = null): String? = listOf(preferences, systemDefaultPreferences).getString(key, def) - private fun getSettingsInt(key: String, def: Int): Int = listOf(preferences, systemDefaultPreferences).getInt(key, def) - private fun getSettingsLong(key: String, def: Long): Long = listOf(preferences, systemDefaultPreferences).getLong(key, def) + private fun getSettingsString(key: String, def: String? = null): String? = listOf(preferences, systemDefaultPreferences, metaDataPreferences).getString(key, def) + private fun getSettingsInt(key: String, def: Int): Int = listOf(preferences, systemDefaultPreferences, metaDataPreferences).getInt(key, def) + private fun getSettingsLong(key: String, def: Long): Long = listOf(preferences, systemDefaultPreferences, metaDataPreferences).getLong(key, def) private fun List.getString(key: String, def: String?): String? = foldRight(def) { preferences, defValue -> preferences?.getString(key, defValue) ?: defValue } private fun List.getInt(key: String, def: Int): Int = foldRight(def) { preferences, defValue -> preferences?.getInt(key, defValue) ?: defValue } 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 index 79e8ff8f..226bb431 100644 --- 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 @@ -12,13 +12,13 @@ import android.util.Log private const val TAG = "BinderUtils" -fun IBinder.warnOnTransactionIssues(code: Int, reply: Parcel?, flags: Int, base: () -> Boolean): Boolean { +fun IBinder.warnOnTransactionIssues(code: Int, reply: Parcel?, flags: Int, tag: String = TAG, 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") + Log.w(tag, "Method $code in $interfaceDescriptor is oneway, but returned data") } return true } - Log.w(TAG, "Unknown method $code in $interfaceDescriptor, skipping") + 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/res/layout/preference_app_heading.xml b/play-services-base-core/src/main/res/layout/preference_app_heading.xml new file mode 100644 index 00000000..cb7e4edf --- /dev/null +++ b/play-services-base-core/src/main/res/layout/preference_app_heading.xml @@ -0,0 +1,43 @@ + + + + + + + \ No newline at end of file diff --git a/play-services-base-core/src/main/res/layout/preference_switch_bar.xml b/play-services-base-core/src/main/res/layout/preference_switch_bar.xml new file mode 100644 index 00000000..9818305b --- /dev/null +++ b/play-services-base-core/src/main/res/layout/preference_switch_bar.xml @@ -0,0 +1,42 @@ + + + + + + + + \ No newline at end of file diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index 8857d222..e1cff12a 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -26,9 +26,10 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } + namespace 'org.microg.gms.base' } dependencies { diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index 3ab02d97..c31f1c05 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -15,8 +15,7 @@ ~ limitations under the License. --> - + CREATOR = new AutoCreator<>(AccountInfo.class); + 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 index 065fff8a..fd91a375 100644 --- 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 @@ -20,6 +20,7 @@ import android.accounts.Account; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.ApiClientBuilder; @@ -100,4 +101,14 @@ public final class Api { } } + @Hide + public interface Client { + void connect(); + + void disconnect(); + + boolean isConnected(); + + boolean isConnecting(); + } } 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 index 54e2c63a..4a33737c 100644 --- 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 @@ -11,16 +11,18 @@ import com.google.android.gms.common.api.internal.ApiKey; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.TaskCompletionSource; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.GoogleApiManager; import org.microg.gms.common.api.PendingGoogleApiCall; @PublicApi public abstract class GoogleApi implements HasApiKey { - private final GoogleApiManager manager; - @PublicApi(exclude = true) + private GoogleApiManager manager; + @Hide public Api api; + @Hide + public O options; @PublicApi(exclude = true) protected GoogleApi(Context context, Api api) { @@ -29,7 +31,14 @@ public abstract class GoogleApi implements HasApiKey Task scheduleTask(PendingGoogleApiCall apiCall) { + protected GoogleApi(Context context, Api api, O options) { + this.api = api; + this.manager = GoogleApiManager.getInstance(context); + this.options = options; + } + + @PublicApi(exclude = true) + protected Task scheduleTask(PendingGoogleApiCall apiCall) { TaskCompletionSource completionSource = new TaskCompletionSource<>(); manager.scheduleTask(this, apiCall, completionSource); return completionSource.getTask(); @@ -43,6 +52,6 @@ public abstract class GoogleApi implements HasApiKey apis = new HashMap<>(); - private final Set connectionCallbacks = new HashSet<>(); - private final Set connectionFailedListeners = new HashSet<>(); - private final Set scopes = new HashSet<>(); + private final Map apis = new HashMap(); + private final Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); + private final Set scopes = new HashSet(); private String accountName; private int clientId = -1; private FragmentActivity fragmentActivity; @@ -364,7 +364,12 @@ public interface GoogleApiClient { } private ApiClientSettings getClientSettings() { - return null; + ApiClientSettings clientSettings = new ApiClientSettings(); + clientSettings.accountName = accountName; + clientSettings.scopes = new HashSet<>(scopes); + clientSettings.gravityForPopups = gravityForPopups; + clientSettings.viewForPopups = viewForPopups; + return clientSettings; } public Builder enableAutoManage(FragmentActivity fragmentActivity, int cliendId, @@ -444,6 +449,8 @@ public interface GoogleApiClient { * A suspension cause informing you that a peer device connection was lost. */ int CAUSE_NETWORK_LOST = 2; + + void onConnected(Bundle connectionHint); } /** 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 index e3881366..ec162920 100644 --- 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 @@ -40,20 +40,20 @@ public interface PendingResult { * Blocks until the task is completed. This is not allowed on the UI thread. The returned * result object can have an additional failure mode of INTERRUPTED. */ - R await(); + public R await(); /** * Blocks until the task is completed or has timed out waiting for the result. This is not * allowed on the UI thread. The returned result object can have an additional failure mode * of either INTERRUPTED or TIMEOUT. */ - R await(long time, TimeUnit unit); + public R await(long time, TimeUnit unit); - void cancel(); + public void cancel(); - boolean isCanceled(); + public boolean isCanceled(); - void setResultCallback(ResultCallback callback, long time, TimeUnit unit); + public void setResultCallback(ResultCallback callback, long time, TimeUnit unit); - void setResultCallback(ResultCallback callback); + public void setResultCallback(ResultCallback callback); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java b/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java index 132a6148..c527866f 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java @@ -16,9 +16,9 @@ package org.microg.gms.common; -import org.microg.gms.common.api.ApiClient; +import com.google.android.gms.common.api.Api; -public class DummyApiClient implements ApiClient { +public class DummyApiClient implements Api.Client { private boolean connected = false; @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java index 8b816fd8..e5b71717 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java @@ -27,16 +27,17 @@ import android.os.RemoteException; import android.util.Log; import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.common.internal.ConnectionInfo; 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.api.ApiClient; import org.microg.gms.common.api.ConnectionCallbacks; import org.microg.gms.common.api.OnConnectionFailedListener; -public abstract class GmsClient implements ApiClient { +public abstract class GmsClient implements Api.Client { private static final String TAG = "GmsClient"; private final Context context; @@ -47,6 +48,9 @@ public abstract class GmsClient implements ApiClient { private I serviceInterface; private final String actionString; + protected boolean requireMicrog; + protected String packageName; + protected int serviceId = -1; protected Account account = null; protected Bundle extras = new Bundle(); @@ -56,6 +60,8 @@ public abstract class GmsClient implements ApiClient { this.callbacks = callbacks; this.connectionFailedListener = connectionFailedListener; this.actionString = actionString; + this.requireMicrog = false; + this.packageName = context.getPackageName(); } protected void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks) throws RemoteException { @@ -63,8 +69,7 @@ public abstract class GmsClient implements ApiClient { throw new IllegalStateException("Service ID not set in constructor and onConnectedToBroker not implemented"); } GetServiceRequest request = new GetServiceRequest(serviceId); - request.extras = new Bundle(); - request.packageName = context.getPackageName(); + request.packageName = packageName; request.account = account; request.extras = extras; broker.getService(callbacks, request); @@ -83,7 +88,7 @@ public abstract class GmsClient implements ApiClient { MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection); } serviceConnection = new GmsServiceConnection(); - if (!MultiConnectionKeeper.getInstance(context).bind(actionString, serviceConnection)) { + if (!MultiConnectionKeeper.getInstance(context).bind(actionString, serviceConnection, requireMicrog)) { state = ConnectionState.ERROR; handleConnectionFailed(); } @@ -184,5 +189,16 @@ public abstract class GmsClient implements ApiClient { callbacks.onConnected(params); } } + + @Override + public void onAccountValidationComplete(int statusCode, Bundle params) throws RemoteException { + Log.d(TAG, "GmsCallbacks : onAccountValidationComplete"); + } + + @Override + public void onPostInitCompleteWithConnectionInfo(int statusCode, IBinder binder, ConnectionInfo info) throws RemoteException { + onPostInitComplete(statusCode, binder, info == null ? null : info.params); + } } + } diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java index 9a56566c..b77c6d2c 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java @@ -27,10 +27,9 @@ import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.Result; import org.microg.gms.common.api.AbstractPendingResult; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.GoogleApiClientImpl; -public class GmsConnector { +public class GmsConnector { private static final String TAG = "GmsConnector"; private final GoogleApiClientImpl apiClient; @@ -43,8 +42,8 @@ public class GmsConnector { this.callback = callback; } - public static PendingResult call(GoogleApiClient client, Api api, GmsConnector.Callback callback) { - return new GmsConnector<>(client, api, callback).connect(); + public static PendingResult call(GoogleApiClient client, Api api, GmsConnector.Callback callback) { + return new GmsConnector(client, api, callback).connect(); } public AbstractPendingResult connect() { @@ -52,7 +51,7 @@ public class GmsConnector { apiClient.incrementUsageCounter(); apiClient.getApiConnection(api); Looper looper = apiClient.getLooper(); - final AbstractPendingResult result = new AbstractPendingResult<>(looper); + final AbstractPendingResult result = new AbstractPendingResult(looper); Message msg = new Message(); msg.obj = result; new Handler(looper).sendMessage(msg); @@ -78,9 +77,12 @@ public class GmsConnector { final AbstractPendingResult result = (AbstractPendingResult) msg.obj; try { C connection = (C) apiClient.getApiConnection(api); - callback.onClientAvailable(connection, realResult -> { - result.deliverResult(realResult); - apiClient.decrementUsageCounter(); + callback.onClientAvailable(connection, new GmsConnector.Callback.ResultProvider() { + @Override + public void onResultAvailable(R realResult) { + result.deliverResult(realResult); + apiClient.decrementUsageCounter(); + } }); } catch (RemoteException ignored) { diff --git a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java index 913291f4..616224bf 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java +++ b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java @@ -16,13 +16,15 @@ package org.microg.gms.common; -import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; - import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; +import android.content.pm.ResolveInfo; import android.os.IBinder; import android.util.Log; @@ -31,14 +33,15 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; + public class MultiConnectionKeeper { private static final String TAG = "GmsMultiConKeeper"; - @SuppressLint("StaticFieldLeak") private static MultiConnectionKeeper INSTANCE; private final Context context; - private final Map connections = new HashMap<>(); + private final Map connections = new HashMap(); public MultiConnectionKeeper(Context context) { this.context = context; @@ -46,13 +49,17 @@ public class MultiConnectionKeeper { public synchronized static MultiConnectionKeeper getInstance(Context context) { if (INSTANCE == null) - INSTANCE = new MultiConnectionKeeper(context); + INSTANCE = new MultiConnectionKeeper(context.getApplicationContext()); return INSTANCE; } public synchronized boolean bind(String action, ServiceConnection connection) { - Log.d(TAG, "bind(" + action + ", " + connection + ")"); + return bind(action, connection, false); + } + + public synchronized boolean bind(String action, ServiceConnection connection, boolean requireMicrog) { Connection con = connections.get(action); + Log.d(TAG, "bind(" + action + ", " + connection + ", " + requireMicrog + ") has=" + (con != null)); if (con != null) { if (!con.forwardsConnection(connection)) { con.addConnectionForward(connection); @@ -60,11 +67,12 @@ public class MultiConnectionKeeper { con.bind(); } } else { - con = new Connection(action); + con = new Connection(action, requireMicrog); con.addConnectionForward(connection); con.bind(); connections.put(action, con); } + Log.d(TAG, "bind() : bound=" + con.isBound()); return con.isBound(); } @@ -73,21 +81,26 @@ public class MultiConnectionKeeper { Connection con = connections.get(action); if (con != null) { con.removeConnectionForward(connection); - if (!con.hasForwards() && con.isBound()) { - con.unbind(); - connections.remove(action); + if (con.isBound()) { + if (!con.hasForwards()) { + con.unbind(); + connections.remove(action); + } else { + Log.d(TAG, "Not unbinding for " + connection + ": has pending other bindings on action " + action); + } } } } public class Connection { private final String actionString; - private final Set connectionForwards = new HashSet<>(); + private final boolean requireMicrog; + private final Set connectionForwards = new HashSet(); private boolean bound = false; private boolean connected = false; private IBinder binder; private ComponentName component; - private final ServiceConnection serviceConnection = new ServiceConnection() { + private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + @@ -115,7 +128,12 @@ public class MultiConnectionKeeper { }; public Connection(String actionString) { + this(actionString, false); + } + + public Connection(String actionString, boolean requireMicrog) { this.actionString = actionString; + this.requireMicrog = requireMicrog; } @SuppressLint("InlinedApi") @@ -124,29 +142,53 @@ public class MultiConnectionKeeper { Intent gmsIntent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); Intent selfIntent = new Intent(actionString).setPackage(context.getPackageName()); Intent intent; - if (context.getPackageManager().resolveService(gmsIntent, 0) == null) { + ResolveInfo resolveInfo; + if ((resolveInfo = context.getPackageManager().resolveService(gmsIntent, 0)) == null) { Log.w(TAG, "No GMS service found for " + actionString); if (context.getPackageManager().resolveService(selfIntent, 0) != null) { - Log.d(TAG, "Found service for "+actionString+" in self package, using it instead"); + Log.d(TAG, "Found service for " + actionString + " in self package, using it instead"); intent = selfIntent; } else { return; } + } else if (requireMicrog && !isMicrog(resolveInfo)) { + Log.w(TAG, "GMS service found for " + actionString + " but looks not like microG"); + if (context.getPackageManager().resolveService(selfIntent, 0) != null) { + Log.d(TAG, "Found service for " + actionString + " in self package, using it instead"); + intent = selfIntent; + } else { + intent = gmsIntent; + } } else { intent = gmsIntent; } - int flags = Context.BIND_AUTO_CREATE; - flags |= Context.BIND_ADJUST_WITH_ACTIVITY; + int flags = Context.BIND_AUTO_CREATE | Context.BIND_DEBUG_UNBIND | Context.BIND_ADJUST_WITH_ACTIVITY; bound = context.bindService(intent, serviceConnection, flags); + Log.d(TAG, "Connection(" + actionString + ") : bind() : bindService=" + bound); if (!bound) { context.unbindService(serviceConnection); } } + public boolean isMicrog(ResolveInfo resolveInfo) { + if (resolveInfo == null || resolveInfo.serviceInfo == null) return false; + if (resolveInfo.serviceInfo.name.startsWith("org.microg.")) return true; + try { + PermissionInfo info = context.getPackageManager().getPermissionInfo("org.microg.gms.EXTENDED_ACCESS", 0); + return info.packageName.equals(resolveInfo.serviceInfo.packageName); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + public boolean isBound() { return bound; } + public IBinder getBinder() { + return binder; + } + public void unbind() { Log.d(TAG, "Connection(" + actionString + ") : unbind()"); try { 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 index 560f3681..15aac9e3 100644 --- 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 @@ -34,7 +34,7 @@ public class AbstractPendingResult implements PendingResult private ResultCallback resultCallback; public AbstractPendingResult(Looper looper) { - handler = new ResultCallbackHandler<>(looper); + handler = new ResultCallbackHandler(looper); } private R getResult() { 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 deleted file mode 100644 index 602e150e..00000000 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 index b5aa386f..18336fa5 100644 --- 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 @@ -22,5 +22,5 @@ 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); + Api.Client 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 index d1e314a7..af67e799 100644 --- 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 @@ -5,5 +5,15 @@ package org.microg.gms.common.api; +import android.view.View; + +import java.util.Set; + public class ApiClientSettings { + public String accountName; + public String packageName; + public Integer sessionId; + public Set scopes; + public int gravityForPopups; + public View viewForPopups; } 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 index e59f68f6..2dffa374 100644 --- 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 @@ -19,10 +19,11 @@ package org.microg.gms.common.api; import android.content.Context; import android.os.Bundle; import android.os.Looper; -import android.util.Log; import androidx.fragment.app.FragmentActivity; +import android.util.Log; + import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApiClient; @@ -41,10 +42,10 @@ public class GoogleApiClientImpl implements GoogleApiClient { private final Context context; private final Looper looper; private final ApiClientSettings clientSettings; - private final Map apis = new HashMap<>(); - private final Map apiConnections = new HashMap<>(); - private final Set connectionCallbacks = new HashSet<>(); - private final Set connectionFailedListeners = new HashSet<>(); + private final Map apis = new HashMap(); + private final Map apiConnections = new HashMap(); + private final Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); private final int clientId; private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { @Override @@ -63,12 +64,16 @@ public class GoogleApiClientImpl implements GoogleApiClient { } } }; - private final OnConnectionFailedListener baseConnectionFailedListener = result -> { - Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()"); - for (OnConnectionFailedListener listener : connectionFailedListeners) { - listener.onConnectionFailed(result); - } - }; + private final OnConnectionFailedListener baseConnectionFailedListener = new + OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult result) { + Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()"); + for (OnConnectionFailedListener listener : connectionFailedListeners) { + listener.onConnectionFailed(result); + } + } + }; private int usageCounter = 0; private boolean shouldDisconnect = false; @@ -84,6 +89,10 @@ public class GoogleApiClientImpl implements GoogleApiClient { this.connectionFailedListeners.addAll(connectionFailedListeners); this.clientId = clientId; + if (this.clientSettings.sessionId == null) { + this.clientSettings.sessionId = hashCode(); + } + for (Api api : apis.keySet()) { apiConnections.put(api, api.getBuilder().build(apis.get(api), context, looper, clientSettings, baseConnectionCallbacks, baseConnectionFailedListener)); } @@ -102,7 +111,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { return looper; } - public ApiClient getApiConnection(Api api) { + public Api.Client getApiConnection(Api api) { return apiConnections.get(api); } @@ -132,7 +141,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { Log.d(TAG, "Already connected/connecting, nothing to do"); return; } - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (!connection.isConnected()) { connection.connect(); } @@ -145,7 +154,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { shouldDisconnect = true; } else { Log.d(TAG, "disconnect()"); - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (connection.isConnected()) { connection.disconnect(); } @@ -155,7 +164,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public synchronized boolean isConnected() { - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (!connection.isConnected()) return false; } return true; @@ -163,7 +172,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public synchronized boolean isConnecting() { - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (connection.isConnecting()) return true; } return false; 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 index 4e05aa75..5cf7933e 100644 --- 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 @@ -11,19 +11,19 @@ import android.os.Bundle; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApi; +import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.TaskCompletionSource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; public class GoogleApiManager { private static GoogleApiManager instance; - private final Context context; - private final Map clientMap = new HashMap<>(); - private final Map>> waitingApiCallMap = new HashMap<>(); + private Context context; + private Map clientMap = new HashMap<>(); + private Map>> waitingApiCallMap = new HashMap<>(); private GoogleApiManager(Context context) { this.context = context; @@ -34,26 +34,30 @@ public class GoogleApiManager { return instance; } - private synchronized A clientForApi(GoogleApi api) { + private synchronized A clientForApi(GoogleApi api) { ApiInstance apiInstance = new ApiInstance(api); if (clientMap.containsKey(apiInstance)) { return (A) clientMap.get(apiInstance); } else { - ApiClient client = api.api.getBuilder().build(api.getOptions(), context, context.getMainLooper(), null, new ConnectionCallback(apiInstance), new ConnectionFailedListener(apiInstance)); + Api.Client client = api.api.getBuilder().build(api.getOptions(), context, context.getMainLooper(), null, new ConnectionCallback(apiInstance), new ConnectionFailedListener(apiInstance)); clientMap.put(apiInstance, client); waitingApiCallMap.put(apiInstance, new ArrayList<>()); return (A) client; } } - public synchronized void scheduleTask(GoogleApi api, PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { + public synchronized void scheduleTask(GoogleApi api, PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { A client = clientForApi(api); boolean connecting = client.isConnecting(); boolean connected = client.isConnected(); if (connected) { - apiCall.execute(client, completionSource); + try { + apiCall.execute(client, completionSource); + } catch (Exception e) { + completionSource.setException(e); + } } else { - waitingApiCallMap.get(new ApiInstance(api)).add(new WaitingApiCall<>((PendingGoogleApiCall) apiCall, completionSource)); + waitingApiCallMap.get(new ApiInstance(api)).add(new WaitingApiCall((PendingGoogleApiCall) apiCall, completionSource)); if (!connecting) { client.connect(); } @@ -63,7 +67,11 @@ public class GoogleApiManager { private synchronized void onInstanceConnected(ApiInstance apiInstance, Bundle connectionHint) { List> waitingApiCalls = waitingApiCallMap.get(apiInstance); for (WaitingApiCall waitingApiCall : waitingApiCalls) { - waitingApiCall.execute(clientMap.get(apiInstance)); + try { + waitingApiCall.execute(clientMap.get(apiInstance)); + } catch (Exception e) { + waitingApiCall.failed(e); + } } waitingApiCalls.clear(); } @@ -81,7 +89,7 @@ public class GoogleApiManager { } private class ConnectionCallback implements ConnectionCallbacks { - private final ApiInstance apiInstance; + private ApiInstance apiInstance; public ConnectionCallback(ApiInstance apiInstance) { this.apiInstance = apiInstance; @@ -99,7 +107,7 @@ public class GoogleApiManager { } private class ConnectionFailedListener implements OnConnectionFailedListener { - private final ApiInstance apiInstance; + private ApiInstance apiInstance; public ConnectionFailedListener(ApiInstance apiInstance) { this.apiInstance = apiInstance; @@ -112,15 +120,15 @@ public class GoogleApiManager { } private static class WaitingApiCall { - private final PendingGoogleApiCall apiCall; - private final TaskCompletionSource completionSource; + private PendingGoogleApiCall apiCall; + private TaskCompletionSource completionSource; - public WaitingApiCall(PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { + public WaitingApiCall(PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { this.apiCall = apiCall; this.completionSource = completionSource; } - public void execute(ApiClient client) { + public void execute(Api.Client client) throws Exception { apiCall.execute(client, completionSource); } @@ -135,8 +143,8 @@ public class GoogleApiManager { WaitingApiCall that = (WaitingApiCall) o; - if (!Objects.equals(apiCall, that.apiCall)) return false; - return Objects.equals(completionSource, that.completionSource); + if (apiCall != null ? !apiCall.equals(that.apiCall) : that.apiCall != null) return false; + return completionSource != null ? completionSource.equals(that.completionSource) : that.completionSource == null; } @Override @@ -148,8 +156,8 @@ public class GoogleApiManager { } private static class ApiInstance { - private final Class apiClass; - private final Api.ApiOptions apiOptions; + private Class apiClass; + private Api.ApiOptions apiOptions; public ApiInstance(Class apiClass, Api.ApiOptions apiOptions) { this.apiClass = apiClass; @@ -167,8 +175,8 @@ public class GoogleApiManager { ApiInstance that = (ApiInstance) o; - if (!Objects.equals(apiClass, that.apiClass)) return false; - return Objects.equals(apiOptions, that.apiOptions); + if (apiClass != null ? !apiClass.equals(that.apiClass) : that.apiClass != null) return false; + return apiOptions != null ? apiOptions.equals(that.apiOptions) : that.apiOptions == null; } @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java index 8c92abd7..578d02b9 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java @@ -5,8 +5,9 @@ package org.microg.gms.common.api; +import com.google.android.gms.common.api.Api; import com.google.android.gms.tasks.TaskCompletionSource; -public interface PendingGoogleApiCall { - void execute(A client, TaskCompletionSource completionSource); +public interface PendingGoogleApiCall { + void execute(A client, TaskCompletionSource completionSource) throws Exception; } 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 index 1ddb4d47..c19c849c 100644 --- 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 @@ -50,7 +50,7 @@ class ResultCallbackHandler extends Handler { public void sendResultCallback(ResultCallback callback, R result) { Message message = new Message(); message.what = CALLBACK_ON_COMPLETE; - message.obj = new OnCompleteObject<>(callback, result); + message.obj = new OnCompleteObject(callback, result); sendMessage(message); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/InstantGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java similarity index 77% rename from play-services-base/src/main/java/org/microg/gms/common/api/InstantGoogleApiCall.java rename to play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java index 5e813d8b..f836b9d4 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/InstantGoogleApiCall.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java @@ -5,9 +5,10 @@ package org.microg.gms.common.api; +import com.google.android.gms.common.api.Api; import com.google.android.gms.tasks.TaskCompletionSource; -public interface InstantGoogleApiCall extends PendingGoogleApiCall { +public interface ReturningGoogleApiCall extends PendingGoogleApiCall { R execute(A client) throws Exception; @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java new file mode 100644 index 00000000..ee8cbf66 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.tasks.TaskCompletionSource; + +public interface VoidReturningGoogleApiCall extends PendingGoogleApiCall{ + void execute(A client) throws Exception; + + @Override + default void execute(A client, TaskCompletionSource completionSource) { + try { + execute(client); + completionSource.setResult(null); + } catch (Exception e) { + completionSource.setException(e); + } + } +} diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle index 9499e1c3..9884e3c9 100644 --- a/play-services-basement/build.gradle +++ b/play-services-basement/build.gradle @@ -18,7 +18,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' dependencies { - api "org.microg:safe-parcel:1.7.0" + api "org.microg:safe-parcel:$safeParcelVersion" // Dependencies from play-services-basement:18.0.0 api "androidx.collection:collection:$collectionVersion" @@ -39,10 +39,12 @@ android { minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk buildConfigField "int", "VERSION_CODE", "$appVersionCode" + buildConfigField "String", "BASE_PACKAGE_NAME", "\"$basePackageName\"" } buildFeatures { viewBinding = true + aidl true } sourceSets { @@ -50,7 +52,8 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } + namespace 'org.microg.gms.basement' } \ No newline at end of file diff --git a/play-services-basement/src/main/AndroidManifest.xml b/play-services-basement/src/main/AndroidManifest.xml index bd436773..359a9b71 100644 --- a/play-services-basement/src/main/AndroidManifest.xml +++ b/play-services-basement/src/main/AndroidManifest.xml @@ -15,4 +15,4 @@ ~ limitations under the License. --> - + diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl new file mode 100644 index 00000000..e46e6d5d --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable ConnectionResult; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl new file mode 100644 index 00000000..fab15aa1 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable GoogleCertificatesLookupQuery; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl new file mode 100644 index 00000000..e9b4bff3 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable GoogleCertificatesLookupResponse; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl new file mode 100644 index 00000000..e6aedfef --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable GoogleCertificatesQuery; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl new file mode 100644 index 00000000..b393f11d --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal; +parcelable ConnectionInfo; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl similarity index 100% rename from play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl 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 index b0210281..8fe63347 100644 --- 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 @@ -1,7 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + package com.google.android.gms.common.internal; import android.os.Bundle; +import com.google.android.gms.common.internal.ConnectionInfo; interface IGmsCallbacks { void onPostInitComplete(int statusCode, IBinder binder, in Bundle params); + void onAccountValidationComplete(int statusCode, in Bundle params); + void onPostInitCompleteWithConnectionInfo(int statusCode, IBinder binder, in ConnectionInfo info); } diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl new file mode 100644 index 00000000..6d54ad26 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl @@ -0,0 +1,18 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.common.GoogleCertificatesLookupQuery; +import com.google.android.gms.common.GoogleCertificatesLookupResponse; +import com.google.android.gms.common.GoogleCertificatesQuery; +import com.google.android.gms.dynamic.IObjectWrapper; + +interface IGoogleCertificatesApi { + IObjectWrapper getGoogleCertificates() = 0; + IObjectWrapper getGoogleReleaseCertificates() = 1; + boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData) = 2; + boolean isGoogleSigned(String packageName, IObjectWrapper certData) = 3; + boolean isGoogleOrPlatformSigned(in GoogleCertificatesQuery query, IObjectWrapper packageManager) = 4; + GoogleCertificatesLookupResponse isPackageGoogleOrPlatformSigned(in GoogleCertificatesLookupQuery query) = 5; + boolean isPackageGoogleOrPlatformSignedAvailable() = 6; + GoogleCertificatesLookupResponse queryPackageSigned(in GoogleCertificatesLookupQuery query) = 7; + boolean isFineGrainedPackageVerificationAvailable() = 8; +} \ 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-basement/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl similarity index 100% rename from play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java new file mode 100644 index 00000000..ab8da97a --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common; + +import android.content.Context; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; + +@SafeParcelable.Class +public class GoogleCertificatesLookupQuery extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getCallingPackage") + String callingPackage; + @Field(2) + boolean allowTestKeys; + @Field(3) + boolean ignoreTestKeysOverride; + @Field(4) + IObjectWrapper contextWrapper; + private Context context; + @Field(5) + boolean isChimeraPackage; + @Field(6) + boolean includeHashesInErrorMessage; + + public String getCallingPackage() { + return callingPackage; + } + + public Context getContext() { + if (context == null && contextWrapper != null) { + context = ObjectWrapper.unwrapTyped(contextWrapper, Context.class); + } + return context; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GoogleCertificatesLookupQuery.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java new file mode 100644 index 00000000..90e0ee3a --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common; + +import android.os.Parcel; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class GoogleCertificatesLookupResponse extends AbstractSafeParcelable { + @Field(1) + public final boolean result; + @Field(2) + public final String errorMessage; + @Field(3) + public final int statusValue; + @Field(4) + public final int firstPartyStatusValue; + + @Constructor + public GoogleCertificatesLookupResponse(@Param(1) boolean result, @Param(2) String errorMessage, @Param(3) int statusValue, @Param(4) int firstPartyStatusValue) { + this.result = result; + this.errorMessage = errorMessage; + this.statusValue = statusValue; + this.firstPartyStatusValue = firstPartyStatusValue; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + CREATOR.writeToParcel(this, out, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GoogleCertificatesLookupResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesQuery.java similarity index 57% rename from play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java rename to play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesQuery.java index c8240279..dce40a90 100644 --- a/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java +++ b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesQuery.java @@ -1,41 +1,38 @@ /* - * 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. + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 */ -package com.google.android.gms.common.internal; +package com.google.android.gms.common; import android.os.IBinder; +import android.os.Parcel; import android.os.RemoteException; - +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.CertData; +import com.google.android.gms.common.internal.ICertData; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper; +import org.microg.gms.common.Hide; -import org.microg.safeparcel.AutoSafeParcelable; -import org.microg.safeparcel.SafeParceled; - -public class GoogleCertificatesQuery extends AutoSafeParcelable { - @SafeParceled(1) - private String packageName; - @SafeParceled(2) - private IBinder certDataBinder; +@Hide +@SafeParcelable.Class +public class GoogleCertificatesQuery extends AbstractSafeParcelable { + @Field(value = 1, getterName = "getCallingPackage") + String callingPackage; + @Field(2) + IBinder certDataBinder; private CertData certData; - @SafeParceled(3) - private boolean allowNonRelease; + @Field(3) + boolean allowTestKeys; + @Field(4) + boolean ignoreTestKeysOverride; - public String getPackageName() { - return packageName; + public String getCallingPackage() { + return callingPackage; } public CertData getCertData() { @@ -71,5 +68,10 @@ public class GoogleCertificatesQuery extends AutoSafeParcelable { return certData; } - public static final Creator CREATOR = new AutoCreator(GoogleCertificatesQuery.class); + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GoogleCertificatesQuery.class); } diff --git a/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/CertData.java similarity index 100% rename from play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java rename to play-services-basement/src/main/java/com/google/android/gms/common/internal/CertData.java diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java new file mode 100644 index 00000000..19326d08 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal; + +import android.os.Bundle; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.Feature; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class ConnectionInfo extends AbstractSafeParcelable { + @Field(1) + public Bundle params; + @Field(2) + public Feature[] features; + @Field(3) + public int unknown3; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ConnectionInfo.class); +} 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 index a203a99f..9df8735c 100644 --- 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 @@ -31,7 +31,7 @@ import java.util.Arrays; public class GetServiceRequest extends AutoSafeParcelable { @Field(1) - private final int versionCode = 4; + private final int versionCode = 6; @Field(2) public final int serviceId; @Field(3) @@ -47,6 +47,7 @@ public class GetServiceRequest extends AutoSafeParcelable { @Field(8) public Account account; @Field(9) + @Deprecated private long field9; @Field(10) public Feature[] defaultFeatures; @@ -59,7 +60,7 @@ public class GetServiceRequest extends AutoSafeParcelable { @Field(14) private boolean field14; @Field(15) - private String field15; + private String attributionTag; private GetServiceRequest() { serviceId = -1; 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 index 75e59d95..1cf0ecc1 100644 --- 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 @@ -16,8 +16,18 @@ package com.google.android.gms.common.internal; -import org.microg.safeparcel.AutoSafeParcelable; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; -public class ValidateAccountRequest extends AutoSafeParcelable { - public static Creator CREATOR = new AutoCreator<>(ValidateAccountRequest.class); +@SafeParcelable.Class +public class ValidateAccountRequest extends AbstractSafeParcelable { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static SafeParcelableCreatorAndWriter CREATOR = findCreator(ValidateAccountRequest.class); } diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java new file mode 100644 index 00000000..ec7d84d5 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +public abstract class AbstractSafeParcelable implements SafeParcelable { + + @SuppressWarnings("unchecked") + public static SafeParcelableCreatorAndWriter findCreator(java.lang.Class tClass) { + String creatorClassName = tClass.getName() + "$000Creator"; + try { + return (SafeParcelableCreatorAndWriter) java.lang.Class.forName(creatorClassName).newInstance(); + } catch (Exception e) { + throw new RuntimeException("No Creator found for " + tClass.getName(), e); + } + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java new file mode 100644 index 00000000..226f50e8 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java @@ -0,0 +1,328 @@ +/* + * SPDX-FileCopyrightText: 2015, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.HashMap; + +@SuppressWarnings("MagicNumber") +public final class SafeParcelReader { + + private SafeParcelReader() { + } + + @Deprecated + public static int halfOf(int i) { + return i & 0xFFFF; + } + + public static int getFieldId(int header) { + return header & 0xFFFF; + } + + @Deprecated + public static int readSingleInt(Parcel parcel) { + return parcel.readInt(); + } + + public static int readHeader(Parcel parcel) { + return parcel.readInt(); + } + + private static int readSize(Parcel parcel, int header) { + if ((header & 0xFFFF0000) != 0xFFFF0000) + return header >> 16 & 0xFFFF; + return parcel.readInt(); + } + + private static void readExpectedSize(Parcel parcel, int header, int expectedSize) { + int i = readSize(parcel, header); + if (i != expectedSize) + throw new ReadException("Expected size " + expectedSize + " got " + i + " (0x" + Integer.toHexString(i) + ")", parcel); + } + + @Deprecated + public static int readStart(Parcel parcel) { + return readObjectHeader(parcel); + } + + public static int readObjectHeader(Parcel parcel) { + int header = readHeader(parcel); + int size = readSize(parcel, header); + int start = parcel.dataPosition(); + if (getFieldId(header) != SafeParcelable.SAFE_PARCEL_OBJECT_MAGIC) + throw new ReadException("Expected object header. Got 0x" + Integer.toHexString(header), parcel); + int end = start + size; + if ((end < start) || (end > parcel.dataSize())) + throw new ReadException("Size read is invalid start=" + start + " end=" + end, parcel); + return end; + } + + public static int readInt(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return parcel.readInt(); + } + + public static byte readByte(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return (byte) parcel.readInt(); + } + + public static short readShort(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return (short) parcel.readInt(); + } + + public static boolean readBool(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return parcel.readInt() != 0; + } + + public static long readLong(Parcel parcel, int header) { + readExpectedSize(parcel, header, 8); + return parcel.readLong(); + } + + public static float readFloat(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return parcel.readFloat(); + } + + public static double readDouble(Parcel parcel, int header) { + readExpectedSize(parcel, header, 8); + return parcel.readDouble(); + } + + public static String readString(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + String string = parcel.readString(); + parcel.setDataPosition(start + size); + return string; + } + + public static IBinder readBinder(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + IBinder binder = parcel.readStrongBinder(); + parcel.setDataPosition(start + size); + return binder; + } + + public static T readParcelable(Parcel parcel, int header, Parcelable.Creator creator) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + T t = creator.createFromParcel(parcel); + parcel.setDataPosition(start + size); + return t; + } + + public static ArrayList readList(Parcel parcel, int header, ClassLoader classLoader) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + ArrayList list = parcel.readArrayList(classLoader); + parcel.setDataPosition(start + size); + return list; + } + + public static HashMap readMap(Parcel parcel, int header, ClassLoader classLoader) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + HashMap map = parcel.readHashMap(classLoader); + parcel.setDataPosition(start + size); + return map; + } + + public static ArrayList readParcelableList(Parcel parcel, int header, Parcelable.Creator creator) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + ArrayList list = parcel.createTypedArrayList(creator); + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readStringList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + ArrayList list = parcel.createStringArrayList(); + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readIntegerList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readInt()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readLongList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readLong()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readFloatList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readFloat()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readDoubleList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readDouble()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readBooleanList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readInt() != 0); + } + parcel.setDataPosition(start + size); + return list; + } + + public static T[] readParcelableArray(Parcel parcel, int header, Parcelable.Creator creator) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + T[] arr = parcel.createTypedArray(creator); + parcel.setDataPosition(start + size); + return arr; + } + + public static String[] readStringArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + String[] arr = parcel.createStringArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static byte[] readByteArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + byte[] arr = parcel.createByteArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static byte[][] readByteArrayArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + byte[][] arr = new byte[length][]; + for (int i = 0; i < length; i++) { + arr[i] = parcel.createByteArray(); + } + parcel.setDataPosition(start + size); + return arr; + } + + public static float[] readFloatArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + float[] arr = parcel.createFloatArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static int[] readIntArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int[] arr = parcel.createIntArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static Bundle readBundle(Parcel parcel, int header, ClassLoader classLoader) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + Bundle bundle = parcel.readBundle(classLoader); + parcel.setDataPosition(start + size); + return bundle; + } + + public static void skip(Parcel parcel, int header) { + int size = readSize(parcel, header); + parcel.setDataPosition(parcel.dataPosition() + size); + } + + public static class ReadException extends RuntimeException { + public ReadException(String message, Parcel parcel) { + super(message); + } + } +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java new file mode 100644 index 00000000..b2f96116 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java @@ -0,0 +1,384 @@ +/* + * SPDX-FileCopyrightText: 2015, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; +import java.util.Map; + +@SuppressWarnings("MagicNumber") +public final class SafeParcelWriter { + + private SafeParcelWriter() { + } + + private static void writeHeader(Parcel parcel, int fieldId, int size) { + if (size >= 0xFFFF) { + parcel.writeInt(0xFFFF0000 | fieldId); + parcel.writeInt(size); + } else { + parcel.writeInt(size << 16 | fieldId); + } + } + + @Deprecated + public static int writeStart(Parcel parcel) { + return writeObjectHeader(parcel); + } + + public static int writeObjectHeader(Parcel parcel) { + writeHeader(parcel, SafeParcelable.SAFE_PARCEL_OBJECT_MAGIC, 0xFFFF); + return parcel.dataPosition(); + } + + private static int writeObjectHeader(Parcel parcel, int fieldId) { + writeHeader(parcel, fieldId, 0xFFFF); + return parcel.dataPosition(); + } + + @Deprecated + public static void writeEnd(Parcel parcel, int start) { + finishObjectHeader(parcel, start); + } + + public static void finishObjectHeader(Parcel parcel, int start) { + int end = parcel.dataPosition(); + int length = end - start; + parcel.setDataPosition(start - 4); + parcel.writeInt(length); + parcel.setDataPosition(end); + } + + public static void write(Parcel parcel, int fieldId, Boolean val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val ? 1 : 0); + } + + public static void write(Parcel parcel, int fieldId, Byte val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val); + } + + public static void write(Parcel parcel, int fieldId, Short val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val); + } + + public static void write(Parcel parcel, int fieldId, Integer val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val); + } + + public static void write(Parcel parcel, int fieldId, Long val) { + if (val == null) return; + writeHeader(parcel, fieldId, 8); + parcel.writeLong(val); + } + + public static void write(Parcel parcel, int fieldId, Float val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeFloat(val); + } + + public static void write(Parcel parcel, int fieldId, Double val) { + if (val == null) return; + writeHeader(parcel, fieldId, 8); + parcel.writeDouble(val); + } + + public static void write(Parcel parcel, int fieldId, String val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeString(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Parcelable val, int flags, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + val.writeToParcel(parcel, flags); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Bundle val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeBundle(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, byte[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeByteArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, byte[][] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.length); + for (byte[] arr : val) { + parcel.writeByteArray(arr); + } + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, float[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeFloatArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, int[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeIntArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, String[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeStringArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void writeStringList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeStringList(val); + finishObjectHeader(parcel, start); + } + } + + public static void writeIntegerList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Integer i : val) { + parcel.writeInt(i); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeLongList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Long l : val) { + parcel.writeLong(l); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeFloatList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Float f : val) { + parcel.writeFloat(f); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeDoubleList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Double d : val) { + parcel.writeDouble(d); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeBooleanList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Boolean b : val) { + parcel.writeInt(b ? 1 : 0); + } + finishObjectHeader(parcel, start); + } + } + + private static void writeArrayPart(Parcel parcel, T val, int flags) { + int before = parcel.dataPosition(); + parcel.writeInt(1); + int start = parcel.dataPosition(); + val.writeToParcel(parcel, flags); + int end = parcel.dataPosition(); + parcel.setDataPosition(before); + parcel.writeInt(end - start); + parcel.setDataPosition(end); + } + + public static void write(Parcel parcel, int fieldId, T[] val, int flags, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.length); + for (T t : val) { + if (t == null) { + parcel.writeInt(0); + } else { + writeArrayPart(parcel, t, flags); + } + } + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, List val, int flags, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (T t : val) { + if (t == null) { + parcel.writeInt(0); + } else { + writeArrayPart(parcel, t, flags); + } + } + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Parcel val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.appendFrom(val, 0, val.dataSize()); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeList(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Map val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeMap(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, IBinder val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeStrongBinder(val); + finishObjectHeader(parcel, start); + } + } + +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java new file mode 100644 index 00000000..0d8d1cc0 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Parcelable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public interface SafeParcelable extends Parcelable { + int SAFE_PARCEL_OBJECT_MAGIC = 0x4F45; + + @Target(ElementType.TYPE) + @interface Class { + } + + @Target(ElementType.CONSTRUCTOR) + @interface Constructor { + + } + + @Target(ElementType.PARAMETER) + @interface Param { + int value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @interface Field { + int value(); + + boolean mayNull() default false; + + java.lang.Class subClass() default SafeParcelable.class; + + boolean useValueParcel() default false; + + boolean useDirectList() default false; + + long versionCode() default -1; + + String defaultValue() default ""; + + String type() default ""; + + String getterName() default ""; + + String getter() default ""; + } +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java new file mode 100644 index 00000000..6afaa9c6 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Parcel; +import android.os.Parcelable; + +public interface SafeParcelableCreatorAndWriter extends Parcelable.Creator { + void writeToParcel(T object, Parcel parcel, int flags); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java new file mode 100644 index 00000000..66b8b4d5 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SafeParcelableSerializer { + public static T deserializeFromBytes(byte[] bytes, Parcelable.Creator tCreator) { + if (bytes == null) return null; + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + T parcelable = tCreator.createFromParcel(parcel); + parcel.recycle(); + return parcelable; + } + + public static byte[] serializeToBytes(T parcelable) { + if (parcelable == null) return null; + Parcel parcel = Parcel.obtain(); + parcelable.writeToParcel(parcel, 0); + byte[] bytes = parcel.marshall(); + parcel.recycle(); + return bytes; + } +} 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 index 649af71b..4bc96034 100644 --- 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 @@ -18,6 +18,7 @@ package com.google.android.gms.dynamic; import android.os.IBinder; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.lang.reflect.Field; @@ -34,37 +35,13 @@ public class ObjectWrapper extends IObjectWrapper.Stub { if (obj == null) { return null; } + if (obj instanceof ObjectWrapper) { - return ((ObjectWrapper) obj).t; + 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"); - } + Field field = getField(binder); if (!field.isAccessible()) { field.setAccessible(true); @@ -85,6 +62,35 @@ public class ObjectWrapper extends IObjectWrapper.Stub { } } + @NonNull + private static Field getField(IBinder binder) { + Field[] fields = binder.getClass().getDeclaredFields(); + + if (fields.length < 1) { + throw new IllegalArgumentException("No fields were found"); + } + + @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"); + } + return field; + } + @Nullable public static T unwrapTyped(IObjectWrapper obj, Class clazz) { try { @@ -95,6 +101,6 @@ public class ObjectWrapper extends IObjectWrapper.Stub { } public static ObjectWrapper wrap(T t) { - return new ObjectWrapper(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 index 52c1dc72..6cac0d45 100644 --- 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 @@ -16,6 +16,8 @@ package org.microg.gms.auth; +import org.microg.gms.basement.BuildConfig; + public class AuthConstants { public static final String DEFAULT_ACCOUNT = "<>"; public static final String SCOPE_GET_ACCOUNT_ID = "^^_account_id_^^"; @@ -23,5 +25,5 @@ public class AuthConstants { 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"; + public static final String DEFAULT_ACCOUNT_TYPE = BuildConfig.BASE_PACKAGE_NAME; } 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 index 217ea437..a905f2bc 100644 --- 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 @@ -21,10 +21,8 @@ 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 GSF_PACKAGE_NAME = "com.mgoogle.android.gsf"; + public static final String GSF_PACKAGE_NAME = BuildConfig.BASE_PACKAGE_NAME + ".android.gsf"; 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 index c9b11ad1..efdbb1d2 100644 --- 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 @@ -19,15 +19,156 @@ package org.microg.gms.common; public enum GmsService { UNKNOWN(-2), ANY(-1), + GAMES(1, "com.google.android.gms.games.service.START", "com.google.android.gms.games.internal.connect.service.START"), + PLUS(2, "com.google.android.gms.plus.service.START", "com.google.android.gms.plus.service.internal.START"), + PANORAMA(3, "com.google.android.gms.panorama.service.START"), + WALLET(4, "com.google.android.gms.wallet.service.BIND"), PEOPLE(5, "com.google.android.gms.people.service.START"), + LOCATION(6), + APPSTATE(7, "com.google.android.gms.appstate.service.START"), + ADREQUEST(8, "com.google.android.gms.ads.service.START"), ACCOUNT(9, "com.google.android.gms.accounts.ACCOUNT_SERVICE"), + CAST(10, "com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE"), + DRIVE(11, "com.google.android.gms.drive.ApiService.START"), ADDRESS(12, "com.google.android.gms.identity.service.BIND"), + CAR(13, "com.google.android.gms.car.service.START"), + WEARABLE(14, "com.google.android.gms.wearable.BIND"), AUTH_PROXY(16, "com.google.android.gms.auth.service.START"), + FITNESS(17, "com.google.android.gms.fitness.GoogleFitnessService.START"), + REMINDERS(18, "com.google.android.gms.reminders.service.START"), + LIGHTWEIGHT_INDEX(19, "com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE"), + DEVICE_CONNECTION(20, "com.google.android.gms.deviceconnection.service.START"), INDEX(21, "com.google.android.gms.icing.INDEX_SERVICE"), + LOCATION_REPORTING(22, "com.google.android.gms.location.reporting.service.START", "com.google.android.location.reporting.service.START"), + LOCATION_MANAGER(23, "com.google.android.location.internal.GoogleLocationManagerService.START"), + PLAY_LOG(24, "com.google.android.gms.playlog.service.START"), + DROIDGUARD(25, "com.google.android.gms.droidguard.service.START"), + LOCKBOX(26, "com.google.android.gms.lockbox.service.START"), + CAST_MIRRORING(27, "com.google.android.gms.cast_mirroring.service.START"), + NETWORK_QUALITY(28, "com.google.android.gms.mdm.services.START"), + FEEDBACK(29, "com.google.android.gms.feedback.internal.IFeedbackService"), + SEARCH_ADMINISTRATION(30), + PHOTO_AUTO_BACKUP(31, "com.google.android.gms.photos.autobackup.service.START"), + SEARCH_QUERIES(32), + SEARCH_GLOBAL(33), + UDC(35, "com.google.android.gms.udc.service.START"), + SEARCH_CORPORA(36), + DEVICE_MANAGER(37, "com.google.android.gms.mdm.services.DeviceManagerApiService.START"), + PSEUDONYMOUS_ID(38, "com.google.android.gms.pseudonymous.service.START"), COMMON(39, "com.google.android.gms.common.service.START"), + CLEARCUT_LOGGER(40, "com.google.android.gms.clearcut.service.START"), + USAGE_REPORTING(41, "com.google.android.gms.usagereporting.service.START"), + KIDS(42, "com.google.android.gms.kids.service.START"), + DOWNLOAD(43, "com.google.android.gms.common.download.START"), SIGN_IN(44, "com.google.android.gms.signin.service.START"), + SAFETY_NET_CLIENT(45, "com.google.android.gms.safetynet.service.START"), + GSERVICES(46, "com.google.android.gms.ads.gservice.START"), + CONTEXT_MANAGER(47, "com.google.android.contextmanager.service.ContextManagerService.START"), + AUDIO_MODEM(48, "com.google.android.gms.audiomodem.service.AudioModemService.START"), + NEARBY_SHARING(49, "com.google.android.gms.nearby.sharing.service.NearbySharingService.START"), + LIGHTWEIGHT_NETWORK_QUALITY(50, "com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START"), + PHENOTYPE(51, "com.google.android.gms.phenotype.service.START"), + VOICE_UNLOCK(52, "com.google.android.gms.speech.service.START"), + NEARBY_CONNECTIONS(54, "com.google.android.gms.nearby.connection.service.START"), + FITNESS_SENSORS(55, "com.google.android.gms.fitness.SensorsApi"), + FITNESS_RECORDING(56, "com.google.android.gms.fitness.RecordingApi"), + FITNESS_HISTORY(57, "com.google.android.gms.fitness.HistoryApi"), + FITNESS_SESSIONS(58, "com.google.android.gms.fitness.SessionsApi"), + FITNESS_BLE(59, "com.google.android.gms.fitness.BleApi"), + FITNESS_CONFIG(60, "com.google.android.gms.fitness.ConfigApi"), + FITNESS_INTERNAL(61, "com.google.android.gms.fitness.InternalApi"), + NEARBY_MESSAGES(62, "com.google.android.gms.nearby.messages.service.NearbyMessagesService.START"), + HELP(63, "com.google.android.gms.googlehelp.service.GoogleHelpService.START"), + CONFIG(64, "com.google.android.gms.config.START"), + GEODATA(65, "com.google.android.gms.location.places.GeoDataApi"), + SEARCH_IME(66), + PLACE_DETECTION(67, "com.google.android.gms.location.places.PlaceDetectionApi"), CREDENTIALS(68, "com.google.android.gms.auth.api.credentials.service.START"), + NEARBY_BOOTSTRAP(69, "com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START"), + PLUS_INTERNAL(70), + SOURCE_DEVICE(75, "com.google.android.gms.smartdevice.d2d.SourceDeviceService.START"), + TARGET_DEVICE(76, "com.google.android.gms.smartdevice.d2d.TargetDeviceService.START"), + APP_INVITE(77, "com.google.android.gms.appinvite.service.START"), + TAP_AND_PAY(79, "com.google.android.gms.tapandpay.service.BIND"), + CHROME_SYNC(80, "com.google.android.gms.chromesync.service.START"), + ACCOUNTS(81, "com.google.android.gms.smartdevice.setup.accounts.AccountsService.START"), + CAST_REMOTE_DISPLAY(83, "com.google.android.gms.cast.remote_display.service.START"), + TRUST_AGENT(85, "com.google.android.gms.trustagent.StateApi.START"), + AUTH_SIGN_IN(91, "com.google.android.gms.auth.api.signin.service.START"), + MEASUREMENT(93, "com.google.android.gms.measurement.START"), + FREIGHTER(98, "com.google.android.gms.freighter.service.START"), + GUNS(110, "com.google.android.gms.notifications.service.START"), + BLE(111, "com.google.android.gms.beacon.internal.IBleService.START"), + FIREBASE_AUTH(112, "com.google.firebase.auth.api.gms.service.START"), + APP_INDEXING(113), + GASS(116, "com.google.android.gms.gass.START"), + WORK_ACCOUNT(120), + INSTANT_APPS(121, "com.google.android.gms.instantapps.START"), + CAST_FIRSTPATY(122, "com.google.android.gms.cast.firstparty.START"), + AD_CACHE(123, "com.google.android.gms.ads.service.CACHE"), + SMS_RETRIEVER(126, "com.google.android.gms.auth.api.phone.service.SmsRetrieverApiService.START"), + CRYPT_AUTH(129, "com.google.android.gms.auth.cryptauth.cryptauthservice.START"), + DYNAMIC_LINKS(131, "com.google.firebase.dynamiclinks.service.START"), + FONTS(132, "com.google.android.gms.fonts.service.START"), + ROMANESCO(135, "com.google.android.gms.romanesco.service.START"), + TRAINER(139, "com.google.android.gms.learning.trainer.START"), + FIDO2_REGULAR(148, "com.google.android.gms.fido.fido2.regular.START"), + FIDO2_PRIVILEGED(149, "com.google.android.gms.fido.fido2.privileged.START"), + DATA_DOWNLOAD(152, "com.google.android.mdd.service.START"), + ACCOUNT_DATA(153, "com.google.android.gms.auth.account.data.service.START"), + CONSTELLATION(155, "com.google.android.gms.constellation.service.START"), + AUDIT(154, "com.google.android.gms.audit.service.START"), + SYSTEM_UPDATE(157, "com.google.android.gms.update.START_API_SERVICE"), + MOBSTORE(160, "com.google.android.mobstore.service.START"), + USER_LOCATION(163, "com.google.android.gms.userlocation.service.START"), + AD_HTTP(166, "com.google.android.gms.ads.service.HTTP"), + LANGUAGE_PROFILE(167, "com.google.android.gms.languageprofile.service.START"), + MDNS(168, "com.google.android.gms.mdns.service.START"), + FIDO2_ZEROPARTY(180, "com.google.android.gms.fido.fido2.zeroparty.START"), + G1_RESTORE(181, "com.google.android.gms.backup.G1_RESTORE"), + G1_BACKUP(182, "com.google.android.gms.backup.G1_BACKUP"), + OSS_LICENSES(185, "com.google.android.gms.oss.licenses.service.START"), + PAYSE(188, "com.google.android.gms.payse.service.BIND"), + RCS(189, "com.google.android.gms.rcs.START"), + CARRIER_AUTH(191, "com.google.android.gms.carrierauth.service.START"), + SYSTEM_UPDATE_SINGLE_UESR(192, "com.google.android.gms.update.START_SINGLE_USER_API_SERVICE"), + APP_USAGE(193, "com.google.android.gms.appusage.service.START"), + NEARBY_SHARING_2(194, "com.google.android.gms.nearby.sharing.START_SERVICE"), + AD_CONSENT_LOOKUP(195, "com.google.android.gms.ads.service.CONSENT_LOOKUP"), + CREDENTIAL_MANAGER(196, "com.google.android.gms.credential.manager.service.firstparty.START"), + PHONE_INTERNAL(197, "com.google.android.gms.auth.api.phone.service.InternalService.START"), + PAY(198, "com.google.android.gms.pay.service.BIND"), + ASTERISM(199, "com.google.android.gms.asterism.service.START"), + MODULE_RESTORE(201, "com.google.android.gms.backup.GMS_MODULE_RESTORE"), + FACS_CACHE(202, "com.google.android.gms.facs.cache.service.START"), + RECAPTCHA(205, "com.google.android.gms.recaptcha.service.START"), + CONTACT_SYNC(208, "com.google.android.gms.people.contactssync.service.START"), IDENTITY_SIGN_IN(212, "com.google.android.gms.auth.api.identity.service.signin.START"), + CREDENTIAL_STORE(214, "com.google.android.gms.fido.credentialstore.internal_service.START"), + MDI_SYNC(215, "com.google.android.gms.mdisync.service.START"), + EVENT_ATTESTATION(216, "com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION"), + SCHEDULER(218, "com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE"), + AUTHORIZATION(219, "com.google.android.gms.auth.api.identity.service.authorization.START"), + FACS_SYNC(220, "com.google.android.gms.facs.internal.service.START"), + AUTH_CONFIG_SYNC(221, "com.google.android.gms.auth.config.service.START"), + CREDENTIAL_SAVING(223, "com.google.android.gms.auth.api.identity.service.credentialsaving.START"), + GOOGLE_AUTH(224, "com.google.android.gms.auth.account.authapi.START"), + ENTERPRISE_LOADER(225, "com.google.android.gms.enterprise.loader.service.START"), + THUNDERBIRD(226, "com.google.android.gms.thunderbird.service.START"), + NEARBY_EXPOSURE(236, "com.google.android.gms.nearby.exposurenotification.START"), + GMS_COMPLIANCE(257, "com.google.android.gms.gmscompliance.service.START"), + FIDO_SOURCE_DEVICE(262, "com.google.android.gms.fido.sourcedevice.service.START"), + FAST_PAIR(265, "com.google.android.gms.nearby.fastpair.START"), + MATCHSTICK_LIGHTER(268, "com.google.android.gms.matchstick.lighter.service.START"), + FIDO_TARGET_DEVICE_INTERNAL(269, "com.google.android.gms.fido.targetdevice.internal_service.START"), + TELEMETRY(270, "com.google.android.gms.common.telemetry.service.START"), + SECOND_DEVICE_AUTH(275, "com.google.android.gms.setup.auth.SecondDeviceAuth.START"), + LOCATION_SHARING_REPORTER(277, "com.google.android.gms.locationsharingreporter.service.START"), + OCR(279, "com.google.android.gms.ocr.service.START"), + POTOKENS(285, "com.google.android.gms.potokens.service.START"), + OCR_INTERNAL(281, "com.google.android.gms.ocr.service.internal.START"), + IN_APP_REACH(315, "com.google.android.gms.inappreach.service.START"), + APP_ERRORS(334, "com.google.android.gms.apperrors.service.START_APP_ERROR"), ; public int SERVICE_ID; @@ -69,4 +210,4 @@ public enum GmsService { 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/Hide.java b/play-services-basement/src/main/java/org/microg/gms/common/Hide.java new file mode 100644 index 00000000..e15414b5 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/Hide.java @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2023 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; + +/** + * Hide the class, method or field from the public API. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface Hide { +} 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 index 92663979..a7fb9b44 100644 --- 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 @@ -43,6 +43,8 @@ public @interface PublicApi { * marked as public api. * * @return true if the method or field is not part of the public api + * @deprecated use {@link Hide} instead */ + @Deprecated 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 index d96fe90a..00117d4e 100644 --- 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 @@ -16,29 +16,80 @@ package org.microg.gms.gcm; +import org.microg.gms.basement.BuildConfig; + 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 ACTION_C2DM_RECEIVE = BuildConfig.BASE_PACKAGE_NAME + ".android.c2dm.intent.RECEIVE"; + public static final String ACTION_C2DM_REGISTER = BuildConfig.BASE_PACKAGE_NAME + ".android.c2dm.intent.REGISTER"; + public static final String ACTION_C2DM_REGISTRATION = BuildConfig.BASE_PACKAGE_NAME + ".android.c2dm.intent.REGISTRATION"; + public static final String ACTION_C2DM_UNREGISTER = BuildConfig.BASE_PACKAGE_NAME + ".android.c2dm.intent.UNREGISTER"; + public static final String ACTION_GCM_SEND = BuildConfig.BASE_PACKAGE_NAME + ".android.gcm.intent.SEND"; + public static final String ACTION_NOTIFICATION_OPEN = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.gcm.NOTIFICATION_OPEN"; + public static final String ACTION_NOTIFICATION_DISMISS = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.gcm.NOTIFICATION_DISMISS"; + public static final String ACTION_SCHEDULE = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.gcm.ACTION_SCHEDULE"; + public static final String ACTION_TASK_READY = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.gcm.ACTION_TASK_READY"; + public static final String ACTION_TASK_INITIALZE = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.gcm.SERVICE_ACTION_INITIALIZE"; + public static final String ACTION_INSTANCE_ID = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.iid.InstanceID"; 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_ID = "appid"; + public static final String EXTRA_APP_VERSION_CODE = "app_ver"; public static final String EXTRA_APP_VERSION_NAME = "app_ver_name"; + public static final String EXTRA_CLIENT_VERSION = "cliv"; + public static final String EXTRA_COMPONENT = "component"; public static final String EXTRA_COLLAPSE_KEY = "collapse_key"; + public static final String EXTRA_DELAY = "google.delay"; 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_GSF_INTENT = "GSF"; + public static final String EXTRA_GMS_VERSION = "gmsv"; + public static final String EXTRA_IS_MESSENGER2 = "messenger2"; public static final String EXTRA_KID = "kid"; public static final String EXTRA_MESSENGER = "google.messenger"; + public static final String EXTRA_MESSAGE_TYPE = "message_type"; public static final String EXTRA_MESSAGE_ID = "google.message_id"; + public static final String EXTRA_OS_VERSION = "osv"; + public static final String EXTRA_PENDING_INTENT = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.gcm.PENDING_INTENT"; + public static final String EXTRA_PUBLIC_KEY = "pub2"; + public static final String EXTRA_RAWDATA = "rawData"; + public static final String EXTRA_RAWDATA_BASE64 = "gcm.rawData64"; public static final String EXTRA_REGISTRATION_ID = "registration_id"; public static final String EXTRA_RETRY_AFTER = "Retry-After"; + public static final String EXTRA_SCHEDULER_ACTION = "scheduler_action"; + public static final String EXTRA_SCOPE = "scope"; public static final String EXTRA_SENDER = "sender"; + public static final String EXTRA_SENDER_LEGACY = "legacy.sender"; public static final String EXTRA_SEND_TO = "google.to"; public static final String EXTRA_SEND_FROM = "google.from"; + public static final String EXTRA_SIGNATURE = "sig"; + public static final String EXTRA_SUBSCIPTION = "subscription"; + public static final String EXTRA_SUBTYPE = "subtype"; + public static final String EXTRA_USE_GSF = "useGsf"; + public static final String EXTRA_TAG = "tag"; + public static final String EXTRA_TOPIC = "gcm.topic"; public static final String EXTRA_TTL = "google.ttl"; public static final String EXTRA_UNREGISTERED = "unregistered"; + public static final String MESSAGE_TYPE_GCM = "gcm"; + public static final String MESSAGE_TYPE_DELETED_MESSAGE = "deleted_message"; + public static final String MESSAGE_TYPE_SEND_ERROR = "send_error"; + public static final String MESSAGE_TYPE_SEND_EVENT = "send_event"; + + public static final String SCHEDULER_ACTION_CANCEL = "CANCEL_TASK"; + public static final String SCHEDULER_ACTION_CANCEL_ALL = "CANCEL_ALL"; + public static final String SCHEDULER_ACTION_SCHEDULE = "SCHEDULE_TASK"; + + public static final String PERMISSION_GTALK = BuildConfig.BASE_PACKAGE_NAME + ".android.gtalkservice.permission.GTALK_SERVICE"; + public static final String PERMISSION_NETWORK_TASK = BuildConfig.BASE_PACKAGE_NAME + ".android.gms.permission.BIND_NETWORK_TASK_SERVICE"; + public static final String PERMISSION_RECEIVE = BuildConfig.BASE_PACKAGE_NAME + ".android.c2dm.permission.RECEIVE"; + public static final String PERMISSION_SEND = BuildConfig.BASE_PACKAGE_NAME + ".android.c2dm.permission.SEND"; + public static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE"; + + public static final String INSTANCE_ID_SCOPE_GCM = "GCM"; + + public static final String GCMID_INSTANCE_ID = "google.com/iid"; + public static final String GCMID_REFRESH = "gcm.googleapis.com/refresh"; } diff --git a/play-services-core-proto/build.gradle b/play-services-core-proto/build.gradle index 2cfb351e..6edfcb4b 100644 --- a/play-services-core-proto/build.gradle +++ b/play-services-core-proto/build.gradle @@ -21,9 +21,9 @@ sourceSets { } compileKotlin { - kotlinOptions.jvmTarget = 1.8 + kotlinOptions.jvmTarget = 17 } compileTestKotlin { - kotlinOptions.jvmTarget = 1.8 + kotlinOptions.jvmTarget = 17 } \ No newline at end of file diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index a1232546..22e0b42c 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -23,6 +23,8 @@ dependencies { implementation "de.hdodenhof:circleimageview:$circleimageviewVersion" implementation "androidx.webkit:webkit:$webkitVersion" + implementation project(':fake-signature') + implementation project(':play-services-base-core') implementation project(':play-services-core-proto') implementation project(':play-services-core:microg-ui-tools') // deprecated @@ -47,21 +49,41 @@ dependencies { } android { + namespace "${basePackageName}.android.gms" + compileSdkVersion androidCompileSdk buildToolsVersion androidBuildVersionTools defaultConfig { versionName version versionCode appVersionCode - resConfigs 'xxhdpi' + resourceConfigurations += ['xxhdpi'] minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk + buildConfigField "String", "BASE_PACKAGE_NAME", "\"$basePackageName\"" + + multiDexEnabled true + multiDexKeepProguard file('multidex-keep.pro') + ndk { abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } + buildConfigField "String", "APPLICATION_NAMESPACE", "\"${android.namespace}\"" + buildConfigField "String", "BASE_PACKAGE_NAME", "\"$basePackageName\"" + manifestPlaceholders = [ + basePackageName: "$basePackageName" + ] + + // provide package name to xml resources + resValue "string", "var_authenticator_account_type", "$basePackageName" + resValue "string", "var_contact_syncadapter_account_type", "$basePackageName" + resValue "string", "var_pref_auth_action_security_settings", "${basePackageName}.android.gms.accountsettings.SECURITY_SETTINGS" + resValue "string", "var_pref_auth_action_privacy_settings", "${basePackageName}.android.gms.accountsettings.PRIVACY_SETTINGS" + resValue "string", "var_pref_auth_action_preferences_settings", "${basePackageName}.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS" + resValue "string", "var_pref_start_gms_package", "${basePackageName}.android.gms" } buildFeatures { @@ -76,7 +98,7 @@ android { } lintOptions { - disable 'MissingTranslation', 'BatteryLife', 'ImpliedQuantity', 'MissingQuantity', 'InvalidWakeLockTag', 'Range' + disable 'MissingTranslation', 'GetLocales', 'BatteryLife', 'ImpliedQuantity', 'MissingQuantity', 'InvalidWakeLockTag', 'Range' } buildTypes { @@ -87,14 +109,14 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } packagingOptions { exclude 'META-INF/ASL2.0' jniLibs { - useLegacyPackaging false + useLegacyPackaging true } } } diff --git a/play-services-core/microg-ui-tools/build.gradle b/play-services-core/microg-ui-tools/build.gradle index d0dbd48f..260aa6a5 100644 --- a/play-services-core/microg-ui-tools/build.gradle +++ b/play-services-core/microg-ui-tools/build.gradle @@ -23,17 +23,20 @@ android { defaultConfig { minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk + + buildConfigField "String", "GMS_APPLICATION_NAMESPACE", "\"$applicationNamespace\"" } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } lintOptions { // TODO: Remove MissingTranslation once we have stable strings and proper translations. - disable 'MissingTranslation' + disable 'MissingTranslation', 'GetLocales' } + namespace 'org.microg.tools.ui' } dependencies { diff --git a/play-services-core/microg-ui-tools/src/main/AndroidManifest.xml b/play-services-core/microg-ui-tools/src/main/AndroidManifest.xml index 004f299c..359a9b71 100644 --- a/play-services-core/microg-ui-tools/src/main/AndroidManifest.xml +++ b/play-services-core/microg-ui-tools/src/main/AndroidManifest.xml @@ -15,4 +15,4 @@ ~ limitations under the License. --> - + 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 index a771a9ea..2b0542a5 100644 --- 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 @@ -3,7 +3,7 @@ package org.microg.tools.ui; import android.os.Bundle; import android.view.MenuItem; import android.view.ViewGroup; - +import androidx.appcompat.widget.Toolbar; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; @@ -19,6 +19,7 @@ public abstract class AbstractSettingsActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.settings_activity); + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); if (showHomeAsUp) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } 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 index 0a30f683..b9144222 100644 --- 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 @@ -116,12 +116,39 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC mSwitch.setChecked(checked); } + public void setCheckedInternal(boolean checked) { + setTextViewLabel(checked); + mSwitch.setCheckedInternal(checked); + } + + public boolean isChecked() { + return mSwitch.isChecked(); + } + public void setEnabled(boolean enabled) { super.setEnabled(enabled); mTextView.setEnabled(enabled); mSwitch.setEnabled(enabled); } + public final ToggleSwitch getSwitch() { + return mSwitch; + } + + public void show() { + if (!isShowing()) { + setVisibility(View.VISIBLE); + mSwitch.setOnCheckedChangeListener(this); + } + } + + public void hide() { + if (isShowing()) { + setVisibility(View.GONE); + mSwitch.setOnCheckedChangeListener(null); + } + } + public boolean isShowing() { return (getVisibility() == View.VISIBLE); } @@ -151,6 +178,13 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC mSwitchChangeListeners.add(listener); } + public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) { + if (!mSwitchChangeListeners.contains(listener)) { + throw new IllegalStateException("Cannot remove OnSwitchChangeListener"); + } + mSwitchChangeListeners.remove(listener); + } + static class SavedState extends BaseSavedState { boolean checked; boolean visible; 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 index c6cadb2f..f590aa25 100644 --- 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 @@ -21,30 +21,27 @@ import android.os.Bundle; import com.google.android.material.switchmaterial.SwitchMaterial; public abstract class SwitchBarResourceSettingsFragment extends ResourceSettingsFragment implements SwitchBar.OnSwitchChangeListener { - protected SwitchBar switchBar; - private SwitchMaterial switchMaterial; + private final SwitchMaterial switchMaterial; private boolean listenerSetup = false; + protected SwitchBarResourceSettingsFragment(SwitchMaterial switchMaterial) { + this.switchMaterial = switchMaterial; + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - -// switchBar = activity.getSwitchBar(); -// switchBar.show(); -// switchMaterial = switchBar.getSwitch(); } @Override public void onDestroyView() { super.onDestroyView(); -// switchBar.hide(); } @Override public void onResume() { super.onResume(); if (!listenerSetup) { -// switchBar.addOnSwitchChangeListener(this); listenerSetup = true; } } @@ -52,7 +49,6 @@ public abstract class SwitchBarResourceSettingsFragment extends ResourceSettings @Override public void onPause() { if (listenerSetup) { -// switchBar.removeOnSwitchChangeListener(this); listenerSetup = false; } super.onPause(); diff --git a/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml b/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml index a664de26..e5f71828 100644 --- a/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml +++ b/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml @@ -19,6 +19,14 @@ android:layout_height="match_parent" android:orientation="vertical"> + + + diff --git a/play-services-core/multidex-keep.pro b/play-services-core/multidex-keep.pro new file mode 100644 index 00000000..0cd8699e --- /dev/null +++ b/play-services-core/multidex-keep.pro @@ -0,0 +1,9 @@ +# Make sure maps is in the primary dex file +-keep class com.google.android.gms.maps.** { *; } +-keep class org.microg.gms.maps.** { *; } +-keep class com.mapbox.** { *; } +-keep class org.oscim.** { *; } + +# Keep Conscrypt in the primary dex file otherwise it will error out on legacy Android versions +-keep class com.google.android.gms.common.security.ProviderInstallerImpl { *; } +-keep class com.google.android.gms.org.conscrypt.** { *; } diff --git a/play-services-core/proguard-rules.pro b/play-services-core/proguard-rules.pro index 9b421ddd..923a7661 100644 --- a/play-services-core/proguard-rules.pro +++ b/play-services-core/proguard-rules.pro @@ -21,10 +21,13 @@ # Keep dynamically loaded GMS classes -keep public class com.google.android.gms.common.security.ProviderInstallerImpl { public *; } +-keep public class com.google.android.gms.dynamic.IObjectWrapper { public *; } +-keep public class com.google.android.gms.dynamite.descriptors.** { public *; } # Keep AutoSafeParcelables -keep public class * extends org.microg.safeparcel.AutoSafeParcelable { @org.microg.safeparcel.SafeParceled *; + (...); } # Keep form data diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 0cbed7c6..5a42409c 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -15,42 +15,48 @@ ~ limitations under the License. --> + xmlns:tools="http://schemas.android.com/tools"> + - + @@ -59,15 +65,17 @@ + - - - + + + - + @@ -86,42 +94,41 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:multiArch="true" - android:theme="@style/AppTheme" - android:networkSecurityConfig="@xml/network_security_config"> + android:theme="@style/Theme.App" + android:networkSecurityConfig="@xml/network_security_config" + tools:targetApi="r"> - - - + + android:exported="true" /> - + - + @@ -132,15 +139,16 @@ - + + @@ -148,10 +156,11 @@ - - + + @@ -159,9 +168,10 @@ - + @@ -171,6 +181,7 @@ @@ -183,6 +194,7 @@ @@ -206,6 +218,7 @@ @@ -218,12 +231,13 @@ - + - + + android:process=":ui" + android:theme="@style/Theme.LoginBlue.Base"> @@ -248,7 +263,7 @@ @@ -270,11 +285,12 @@ @@ -282,19 +298,22 @@ - + - + - + @@ -302,7 +321,8 @@ @@ -333,10 +353,11 @@ @@ -354,6 +375,15 @@ android:resource="@string/gms_settings_summary" /> + + + + + android:taskAffinity="org.microg.gms.settings" /> + + + - + + + + + + + + + - + - + diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java b/play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java new file mode 100644 index 00000000..3605e8ac --- /dev/null +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.chimera; + +import android.annotation.SuppressLint; +import android.util.Log; +import androidx.annotation.Keep; + +import android.content.Context; + +@Keep +public class DynamiteModuleInitializer { + private static final String TAG = "DynamiteModule"; + + public static void initializeModuleV1(Context context) { + initializeModuleV2(context, "com.google.android.gms".equals(context.getPackageName())); + } + + public static void initializeModuleV2(Context context, boolean withGmsPackage) { + Log.d(TAG, "initializeModuleV2 context: " + context + ", withGmsPackage: " + withGmsPackage); + } +} diff --git a/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java b/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java deleted file mode 100644 index 7ab30bc3..00000000 --- a/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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; - -import android.content.pm.PackageManager; -import android.os.RemoteException; -import android.util.Log; - -import androidx.annotation.Keep; - -import com.google.android.gms.common.internal.GoogleCertificatesQuery; -import com.google.android.gms.common.internal.IGoogleCertificatesApi; -import com.google.android.gms.dynamic.IObjectWrapper; -import com.google.android.gms.dynamic.ObjectWrapper; - -import org.microg.gms.common.PackageUtils; - -@Keep -public class GoogleCertificatesImpl extends IGoogleCertificatesApi.Stub { - private static final String TAG = "GmsCertImpl"; - - @Override - public IObjectWrapper getGoogleCertficates() throws RemoteException { - Log.d(TAG, "unimplemented Method: getGoogleCertficates"); - return null; - } - - @Override - public IObjectWrapper getGoogleReleaseCertificates() throws RemoteException { - Log.d(TAG, "unimplemented Method: getGoogleReleaseCertificates"); - return null; - } - - @Override - public boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData) throws RemoteException { - return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, byte[].class)); - } - - @Override - public boolean isGoogleSigned(String packageName, IObjectWrapper certData) throws RemoteException { - return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, byte[].class)); - } - - @Override - public boolean isGoogleOrPlatformSigned(GoogleCertificatesQuery query, IObjectWrapper packageManager) throws RemoteException { - PackageManager pm = ObjectWrapper.unwrapTyped(packageManager, PackageManager.class); - if (query == null || query.getPackageName() == null) { - return false; - } else if (query.getCertData() == null) { - if (pm == null) return false; - return PackageUtils.isGooglePackage(pm, query.getPackageName()); - } else { - return PackageUtils.isGooglePackage(query.getPackageName(), query.getCertData().getBytes()); - } - } -} diff --git a/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java b/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java index e25391e5..0270e626 100644 --- a/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java +++ b/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java @@ -5,11 +5,12 @@ package com.google.android.gms.common.security; +import static android.os.Build.VERSION.SDK_INT; import static com.google.android.gms.security.ProviderInstaller.PROVIDER_NAME; +import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.os.Build; import android.os.Process; import android.util.Log; @@ -53,13 +54,14 @@ public class ProviderInstallerImpl { String packageName = PackageUtils.packageFromProcessId(context, Process.myPid()); if (packageName != null && packageName.contains(".")) return packageName; try { + @SuppressLint("DiscouragedPrivateApi") Method getBasePackageName = Context.class.getDeclaredMethod("getBasePackageName"); packageName = (String) getBasePackageName.invoke(context); if (packageName != null) return packageName; - } catch (Exception e) { + } catch (Exception ignored) { } - if (Build.VERSION.SDK_INT >= 29) { + if (SDK_INT >= 29) { return context.getOpPackageName(); } Context applicationContext = context.getApplicationContext(); diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java index 9c96646b..9274f439 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java @@ -17,6 +17,7 @@ package org.microg.gms.auth; import static android.accounts.AccountManager.VISIBILITY_VISIBLE; +import static android.os.Build.VERSION.SDK_INT; import android.Manifest; import android.accounts.Account; @@ -27,7 +28,6 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.util.Log; @@ -36,6 +36,7 @@ import androidx.annotation.Nullable; import org.microg.gms.common.PackageUtils; import java.util.Arrays; +import java.util.Objects; import static org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT_TYPE; import static org.microg.gms.auth.AuthConstants.PROVIDER_EXTRA_ACCOUNTS; @@ -54,11 +55,10 @@ public class AccountContentProvider extends ContentProvider { @Nullable @Override public Bundle call(String method, String arg, Bundle extras) { - String suggestedPackageName; - suggestedPackageName = getCallingPackage(); + String suggestedPackageName = getCallingPackage(); String packageName = PackageUtils.getAndCheckCallingPackage(getContext(), suggestedPackageName); Log.d(TAG, "Call " + method + " from " + packageName + " with arg " + arg); - if (!PackageUtils.callerHasExtendedAccess(getContext())) { + if (!PackageUtils.callerHasExtendedAccess(Objects.requireNonNull(getContext()))) { String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid()); if (packagesForUid != null && packagesForUid.length != 0) Log.w(TAG, "Not granting extended access to " + Arrays.toString(packagesForUid) @@ -72,10 +72,10 @@ public class AccountContentProvider extends ContentProvider { if (arg != null && (arg.equals(DEFAULT_ACCOUNT_TYPE) || arg.startsWith(DEFAULT_ACCOUNT_TYPE + "."))) { AccountManager am = AccountManager.get(getContext()); accounts = am.getAccountsByTypeForPackage(arg, packageName); - if (accounts == null || accounts.length == 0) { + if (accounts.length == 0) { accounts = am.getAccountsByType(arg); } - if (Build.VERSION.SDK_INT >= 26 && accounts != null && arg.equals(DEFAULT_ACCOUNT_TYPE)) { + if (SDK_INT >= 26 && arg.equals(DEFAULT_ACCOUNT_TYPE)) { for (Account account : accounts) { if (am.getAccountVisibility(account, packageName) == AccountManager.VISIBILITY_UNDEFINED) { Log.d(TAG, "Make account " + account + " visible to " + packageName); diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java index 73adeabf..57b6a516 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java @@ -18,24 +18,25 @@ package org.microg.gms.auth; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; +import static android.os.Build.VERSION.SDK_INT; import static org.microg.gms.auth.AuthPrefs.isTrustGooglePermitted; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.PackageManager; -import android.os.Build; import android.util.Log; +import org.microg.gms.base.core.BuildConfig; import org.microg.gms.common.PackageUtils; -import org.microg.mgms.settings.SettingsContract; +import org.microg.gms.settings.SettingsContract; import java.io.IOException; public class AuthManager { private static final String TAG = "GmsAuthManager"; - public static final String PERMISSION_TREE_BASE = "com.mgoogle.android.googleapps.permission.GOOGLE_AUTH."; + public static final String PERMISSION_TREE_BASE = BuildConfig.BASE_PACKAGE_NAME + ".android.googleapps.permission.GOOGLE_AUTH."; public static final String PREF_AUTH_VISIBLE = SettingsContract.Auth.VISIBLE; public static final int ONE_HOUR_IN_SECONDS = 60 * 60; @@ -104,7 +105,7 @@ public class AuthManager { public void setPermitted(boolean value) { setUserData(buildPermKey(), value ? "1" : "0"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && value && packageName != null) { + if (SDK_INT >= 26 && value && packageName != null) { // Make account persistently visible as we already granted access accountManager.setAccountVisibility(getAccount(), packageName, AccountManager.VISIBILITY_VISIBLE); } @@ -169,7 +170,7 @@ public class AuthManager { public void setAuthToken(String service, String auth) { getAccountManager().setAuthToken(getAccount(), buildTokenKey(service), auth); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && packageName != null && auth != null) { + if (SDK_INT >= 26 && packageName != null && auth != null) { // Make account persistently visible as we already granted access accountManager.setAccountVisibility(getAccount(), packageName, AccountManager.VISIBILITY_VISIBLE); } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java index 622ff4a0..7f57a370 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java @@ -18,8 +18,6 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.annotation.SuppressLint; import android.app.NotificationManager; import android.app.PendingIntent; @@ -40,7 +38,7 @@ import com.google.android.gms.auth.AccountChangeEventsRequest; import com.google.android.gms.auth.AccountChangeEventsResponse; import com.google.android.gms.auth.GetHubTokenInternalResponse; import com.google.android.gms.auth.GetHubTokenRequest; -import com.google.android.gms.auth.HasCababilitiesRequest; +import com.google.android.gms.auth.HasCapabilitiesRequest; import com.google.android.gms.auth.TokenData; import com.google.android.gms.common.api.Scope; @@ -52,11 +50,8 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; -import static android.accounts.AccountManager.KEY_ACCOUNTS; -import static android.accounts.AccountManager.KEY_ACCOUNT_NAME; -import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE; -import static android.accounts.AccountManager.KEY_AUTHTOKEN; -import static android.accounts.AccountManager.KEY_CALLER_PID; +import static android.accounts.AccountManager.*; +import static android.os.Build.VERSION.SDK_INT; public class AuthManagerServiceImpl extends IAuthManagerService.Stub { private static final String TAG = "GmsAuthManagerSvc"; @@ -72,6 +67,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { public static final String KEY_REQUEST_VISIBLE_ACTIVITIES = "request_visible_actions"; public static final String KEY_SUPPRESS_PROGRESS_SCREEN = "suppressProgressScreen"; public static final String KEY_SYNC_EXTRAS = "sync_extras"; + public static final String KEY_DELEGATION_TYPE = "delegation_type"; + public static final String KEY_DELEGATEE_USER_ID = "delegatee_user_id"; public static final String KEY_ERROR = "Error"; public static final String KEY_USER_RECOVERY_INTENT = "userRecoveryIntent"; @@ -118,7 +115,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); - Log.d(TAG, "getToken: account:" + account.name + " scope:" + scope + " extras:" + extras + ", notify: " + notify); + if (!AuthConstants.SCOPE_GET_ACCOUNT_ID.equals(scope)) + Log.d(TAG, "getToken: account:" + account.name + " scope:" + scope + " extras:" + extras + ", notify: " + notify); /* * TODO: This scope seems to be invalid (according to https://developers.google.com/oauthplayground/), @@ -126,6 +124,7 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { */ scope = scope.replace("https://www.googleapis.com/auth/identity.plus.page.impersonation ", ""); + assert packageName != null; AuthManager authManager = new AuthManager(context, account.name, packageName, scope); Bundle result = new Bundle(); result.putString(KEY_ACCOUNT_NAME, account.name); @@ -180,12 +179,22 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { @Override public Bundle requestGoogleAccountsAccess(String packageName) throws RemoteException { - Log.w(TAG, "Not implemented: requestGoogleAccountsAccess(" + packageName + ")"); + PackageUtils.assertExtendedAccess(context); + if (SDK_INT >= 26) { + for (Account account : get(context).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE)) { + AccountManager.get(context).setAccountVisibility(account, packageName, VISIBILITY_VISIBLE); + } + Bundle res = new Bundle(); + res.putString("Error", "Ok"); + return res; + } else { + Log.w(TAG, "Not implemented: requestGoogleAccountsAccess(" + packageName + ")"); + } return null; } @Override - public int hasCapabilities(HasCababilitiesRequest request) throws RemoteException { + public int hasCapabilities(HasCapabilitiesRequest request) throws RemoteException { Log.w(TAG, "Not implemented: hasCapabilities(" + request.account + ", " + Arrays.toString(request.capabilities) + ")"); return 1; } @@ -205,7 +214,11 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { Log.d(TAG, "clearToken: token:" + token + " extras:" + extras); AccountManager.get(context).invalidateAuthToken(AuthConstants.DEFAULT_ACCOUNT_TYPE, token); - return null; + + Bundle res = new Bundle(); + res.putString("Error", "Ok"); + res.putBoolean("booleanResult", true); + return res; } @Override diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java index 55ff536b..e1b4749a 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java @@ -113,7 +113,9 @@ public class AuthRequest extends HttpFormClient.Request { public AuthRequest fromContext(Context context) { build(context); locale(Utils.getLocale(context)); - androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId()); + if (AuthPrefs.shouldIncludeAndroidId(context)) { + androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId()); + } return this; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java index 095496b0..f52b93c4 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java @@ -25,9 +25,9 @@ import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT; import static org.microg.gms.auth.AuthPrefs.isAuthVisible; -import static org.microg.gms.checkin.CheckinPrefs.hideLauncherIcon; -import static org.microg.gms.checkin.CheckinPrefs.isSpoofingEnabled; -import static org.microg.gms.checkin.CheckinPrefs.setSpoofingEnabled; +import static org.microg.gms.checkin.CheckinPreferences.hideLauncherIcon; +import static org.microg.gms.checkin.CheckinPreferences.isSpoofingEnabled; +import static org.microg.gms.checkin.CheckinPreferences.setSpoofingEnabled; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.common.Constants.GMS_VERSION_CODE; @@ -283,6 +283,10 @@ public class LoginActivity extends AssistantActivity { webView.loadUrl(buildUrl(tmpl, Utils.getLocale(this))); } + protected void runScript(String js) { + runOnUiThread(() -> webView.loadUrl("javascript:" + js)); + } + private void closeWeb(boolean programmaticAuth) { setMessage(R.string.auth_finalize); runOnUiThread(() -> webView.setVisibility(INVISIBLE)); @@ -418,12 +422,32 @@ public class LoginActivity extends AssistantActivity { Log.d(TAG, "JSBridge: addAccount"); } + @JavascriptInterface + public final void attemptLogin(String accountName, String password) { + Log.d(TAG, "JSBridge: attemptLogin"); + } + + @JavascriptInterface + public void backupSyncOptIn(String accountName) { + Log.d(TAG, "JSBridge: backupSyncOptIn"); + } + + @JavascriptInterface + public void clearOldLoginAttempts() { + Log.d(TAG, "JSBridge: clearOldLoginAttempts"); + } + @JavascriptInterface public final void closeView() { Log.d(TAG, "JSBridge: closeView"); closeWeb(false); } + @JavascriptInterface + public void fetchIIDToken(String entity) { + Log.d(TAG, "JSBridge: fetchIIDToken"); + } + @JavascriptInterface public final String fetchVerifiedPhoneNumber() { Log.d(TAG, "JSBridge: fetchVerifiedPhoneNumber"); @@ -458,12 +482,17 @@ public class LoginActivity extends AssistantActivity { @JavascriptInterface public final int getAuthModuleVersionCode() { - return 1; + return GMS_VERSION_CODE; } @JavascriptInterface public final int getBuildVersionSdk() { - return SDK_INT; + return Build.VERSION.SDK_INT; + } + + @JavascriptInterface + public int getDeviceContactsCount() { + return -1; } @JavascriptInterface @@ -537,15 +566,40 @@ public class LoginActivity extends AssistantActivity { } @JavascriptInterface - public final void setAccountIdentifier(String accountIdentifier) { + public final void setAccountIdentifier(String accountName) { Log.d(TAG, "JSBridge: setAccountIdentifier"); } + @JavascriptInterface + public void setAllActionsEnabled(boolean z) { + Log.d(TAG, "JSBridge: setAllActionsEnabled"); + } + @JavascriptInterface public final void setNewAccountCreated() { Log.d(TAG, "JSBridge: setNewAccountCreated"); } + @JavascriptInterface + public void setPrimaryActionEnabled(boolean z) { + Log.d(TAG, "JSBridge: setPrimaryActionEnabled"); + } + + @JavascriptInterface + public void setPrimaryActionLabel(String str, int i) { + Log.d(TAG, "JSBridge: setPrimaryActionLabel: " + str); + } + + @JavascriptInterface + public void setSecondaryActionEnabled(boolean z) { + Log.d(TAG, "JSBridge: setSecondaryActionEnabled"); + } + + @JavascriptInterface + public void setSecondaryActionLabel(String str, int i) { + Log.d(TAG, "JSBridge: setSecondaryActionLabel: " + str); + } + @JavascriptInterface public final void showKeyboard() { inputMethodManager.showSoftInput(webView, SHOW_IMPLICIT); diff --git a/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java b/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java index 89ee692f..fa4eb2bc 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java @@ -104,7 +104,7 @@ class AccountAuthenticator extends AbstractAccountAuthenticator { } } catch (Exception e) { Log.w(TAG, e); - return null; + throw new NetworkErrorException(e); } return null; } diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java index 98598a09..df7c245b 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java @@ -80,8 +80,7 @@ public class CheckinClient { DeviceIdentifier deviceIdent, PhoneInfo phoneInfo, LastCheckinInfo checkinInfo, Locale locale, List accounts, Boolean brandSpoof) { - CheckinRequest.Builder builder; - builder = new CheckinRequest.Builder() + CheckinRequest.Builder builder = new CheckinRequest.Builder() .accountCookie(new ArrayList<>()) .androidId(checkinInfo.getAndroidId()) .checkin(new CheckinRequest.Checkin.Builder() @@ -137,7 +136,7 @@ public class CheckinClient { .loggingId(new Random().nextLong()) // TODO: static .meid(deviceIdent.meid) .otaCert(Collections.singletonList("71Q6Rn2DDZl1zPDVaaeEHItd")) - .serial(Build.SERIAL) + .serial(Build.SERIAL != null && !Build.SERIAL.isEmpty() ? Build.SERIAL : null) .timeZone(TimeZone.getDefault().getID()) .userName((String) TODO) .userSerialNumber((Integer) TODO) diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java index 355800d1..b9432ac6 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java @@ -16,7 +16,7 @@ package org.microg.gms.checkin; -import static org.microg.gms.checkin.CheckinPrefs.isSpoofingEnabled; +import static org.microg.gms.checkin.CheckinPreferences.isSpoofingEnabled; import android.accounts.Account; import android.accounts.AccountManager; @@ -43,7 +43,7 @@ public class CheckinManager { LastCheckinInfo info = LastCheckinInfo.read(context); if (!force && info.getLastCheckin() > System.currentTimeMillis() - MIN_CHECKIN_INTERVAL) return null; - if (!CheckinPrefs.isEnabled(context)) + if (!CheckinPreferences.isEnabled(context)) return null; List accounts = new ArrayList<>(); AccountManager accountManager = AccountManager.get(context); @@ -71,7 +71,8 @@ public class CheckinManager { ContentResolver resolver = context.getContentResolver(); for (CheckinResponse.GservicesSetting setting : response.setting) { - GServices.setString(resolver, setting.name.utf8(), setting.value_.utf8()); + if (setting.name != null && setting.value_ != null) + GServices.setString(resolver, setting.name.utf8(), setting.value_.utf8()); } return info; diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java index bd5bfabb..3f53ebcd 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java @@ -80,7 +80,7 @@ public class CheckinService extends IntentService { protected void onHandleIntent(Intent intent) { try { ForegroundServiceContext.completeForegroundService(this, intent, TAG); - if (CheckinPrefs.isEnabled(this)) { + if (CheckinPreferences.isEnabled(this)) { LastCheckinInfo info = CheckinManager.checkin(this, intent.getBooleanExtra(EXTRA_FORCE_CHECKIN, false)); if (info != null) { Log.d(TAG, "Checked in as " + Long.toHexString(info.getAndroidId())); @@ -124,7 +124,7 @@ public class CheckinService extends IntentService { static void schedule(Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingIntent = PendingIntent.getService(context, TriggerReceiver.class.getName().hashCode(), new Intent(context, TriggerReceiver.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = PendingIntent.getService(context, TriggerReceiver.class.getName().hashCode(), new Intent(context, TriggerReceiver.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE); alarmManager.set(AlarmManager.RTC, Math.max(LastCheckinInfo.read(context).getLastCheckin() + REGULAR_CHECKIN_INTERVAL, System.currentTimeMillis() + BACKUP_CHECKIN_DELAY), pendingIntent); } } diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java index 4b6551bd..e41a954b 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java @@ -18,7 +18,6 @@ package org.microg.gms.checkin; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.os.Build.VERSION.SDK_INT; import static org.microg.gms.checkin.CheckinService.EXTRA_FORCE_CHECKIN; import static org.microg.gms.checkin.CheckinService.REGULAR_CHECKIN_INTERVAL; @@ -42,7 +41,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { try { boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()); - if (CheckinPrefs.isEnabled(context) || force) { + if (CheckinPreferences.isEnabled(context) || force) { if (LastCheckinInfo.read(context).getLastCheckin() > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) { CheckinService.schedule(context); return; @@ -54,7 +53,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { Intent subIntent = new Intent(context, CheckinService.class); subIntent.putExtra(EXTRA_FORCE_CHECKIN, force); startWakefulService(new ForegroundServiceContext(context), subIntent); - } else if (SDK_INT >= 23) { + } else { // no network, register a network callback to retry when we have internet NetworkRequest networkRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_INTERNET) diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java index d9881dd8..d19ce6ea 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java @@ -10,7 +10,6 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.os.Build; import android.text.TextUtils; import java.util.ArrayList; @@ -55,9 +54,7 @@ public class GcmDatabase extends SQLiteOpenHelper { public GcmDatabase(Context context) { super(context, DB_NAME, null, DB_VERSION); this.context = context; - if (Build.VERSION.SDK_INT >= 16) { - this.setWriteAheadLoggingEnabled(true); - } + this.setWriteAheadLoggingEnabled(true); } public static class App { diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index 6a6889ff..343d1721 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -19,8 +19,42 @@ package org.microg.gms.gcm; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.os.Build.VERSION.SDK_INT; import static org.microg.gms.common.PackageUtils.warnIfNotPersistentProcess; -import static org.microg.gms.gcm.GcmConstants.*; -import static org.microg.gms.gcm.McsConstants.*; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_OVERRIDE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_COLLAPSE_KEY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER; +import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA; +import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA_BASE64; +import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO; +import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL; +import static org.microg.gms.gcm.McsConstants.ACTION_ACK; +import static org.microg.gms.gcm.McsConstants.ACTION_CONNECT; +import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT; +import static org.microg.gms.gcm.McsConstants.ACTION_RECONNECT; +import static org.microg.gms.gcm.McsConstants.ACTION_SEND; +import static org.microg.gms.gcm.McsConstants.EXTRA_REASON; +import static org.microg.gms.gcm.McsConstants.MCS_CLOSE_TAG; +import static org.microg.gms.gcm.McsConstants.MCS_DATA_MESSAGE_STANZA_TAG; +import static org.microg.gms.gcm.McsConstants.MCS_HEARTBEAT_ACK_TAG; +import static org.microg.gms.gcm.McsConstants.MCS_HEARTBEAT_PING_TAG; +import static org.microg.gms.gcm.McsConstants.MCS_IQ_STANZA_TAG; +import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_REQUEST_TAG; +import static org.microg.gms.gcm.McsConstants.MCS_LOGIN_RESPONSE_TAG; +import static org.microg.gms.gcm.McsConstants.MSG_ACK; +import static org.microg.gms.gcm.McsConstants.MSG_CONNECT; +import static org.microg.gms.gcm.McsConstants.MSG_HEARTBEAT; +import static org.microg.gms.gcm.McsConstants.MSG_INPUT; +import static org.microg.gms.gcm.McsConstants.MSG_INPUT_ERROR; +import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT; +import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT_DONE; +import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT_ERROR; +import static org.microg.gms.gcm.McsConstants.MSG_OUTPUT_READY; +import static org.microg.gms.gcm.McsConstants.MSG_TEARDOWN; import android.annotation.SuppressLint; import android.app.AlarmManager; @@ -34,15 +68,16 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Messenger; import android.os.Parcelable; import android.os.PowerManager; import android.os.SystemClock; import android.os.UserHandle; +import android.util.Base64; import android.util.Log; import androidx.annotation.Nullable; @@ -66,7 +101,6 @@ import org.microg.gms.gcm.mcs.IqStanza; import org.microg.gms.gcm.mcs.LoginRequest; import org.microg.gms.gcm.mcs.LoginResponse; import org.microg.gms.gcm.mcs.Setting; -import org.microg.mgms.settings.SettingsProvider; import java.io.Closeable; import java.lang.reflect.Field; @@ -76,6 +110,7 @@ import java.net.SocketException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLContext; @@ -136,10 +171,10 @@ public class McsService extends Service implements Handler.Callback { @Nullable private Method addPowerSaveTempWhitelistAppMethod; @Nullable - @RequiresApi(Build.VERSION_CODES.S) + @RequiresApi(31) private Object powerExemptionManager; @Nullable - @RequiresApi(Build.VERSION_CODES.S) + @RequiresApi(31) private Method addToTemporaryAllowListMethod; private class HandlerThread extends Thread { @@ -154,7 +189,7 @@ public class McsService extends Service implements Handler.Callback { wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mcs"); wakeLock.setReferenceCounted(false); synchronized (McsService.class) { - rootHandler = new Handler(Looper.myLooper(), McsService.this); + rootHandler = new Handler(Objects.requireNonNull(Looper.myLooper()), McsService.this); if (connectIntent != null) { rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, connectIntent)); WakefulBroadcastReceiver.completeWakefulIntent(connectIntent); @@ -174,12 +209,12 @@ public class McsService extends Service implements Handler.Callback { super.onCreate(); TriggerReceiver.register(this); database = new GcmDatabase(this); - heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0); + heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), PendingIntent.FLAG_IMMUTABLE); alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); powerManager = (PowerManager) getSystemService(POWER_SERVICE); if (checkSelfPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") == PackageManager.PERMISSION_GRANTED) { try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SDK_INT >= 31) { Class powerExemptionManagerClass = Class.forName("android.os.PowerExemptionManager"); powerExemptionManager = getSystemService(powerExemptionManagerClass); addToTemporaryAllowListMethod = @@ -265,7 +300,7 @@ public class McsService extends Service implements Handler.Callback { AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); long delay = getCurrentDelay(); logd(context, "Scheduling reconnect in " + delay / 1000 + " seconds..."); - PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0); + PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), PendingIntent.FLAG_IMMUTABLE); alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi); } @@ -277,14 +312,13 @@ public class McsService extends Service implements Handler.Callback { closeAll(); } logd(context, "Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds..."); - // This is supposed to work even when running in idle and without battery optimization disabled alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent); } public synchronized static long getCurrentDelay() { long delay = currentDelay == 0 ? 5000 : currentDelay; - if (currentDelay < SettingsProvider.INTERVAL) currentDelay += 10000; - if (currentDelay >= SettingsProvider.INTERVAL && currentDelay < 600000) currentDelay += SettingsProvider.INTERVAL; + if (currentDelay < 60000) currentDelay += 10000; + if (currentDelay >= 60000 && currentDelay < 600000) currentDelay += 60000; return delay; } @@ -324,6 +358,7 @@ public class McsService extends Service implements Handler.Callback { String messageId = intent.getStringExtra(EXTRA_MESSAGE_ID); String collapseKey = intent.getStringExtra(EXTRA_COLLAPSE_KEY); + Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); intent.removeExtra(EXTRA_MESSENGER); Parcelable app = intent.getParcelableExtra(EXTRA_APP); @@ -341,8 +376,16 @@ public class McsService extends Service implements Handler.Callback { } intent.removeExtra(EXTRA_APP); + int ttl; try { - intent.hasExtra(EXTRA_TTL); + if (intent.hasExtra(EXTRA_TTL)) { + ttl = Integer.parseInt(intent.getStringExtra(EXTRA_TTL)); + if (ttl < 0 || ttl > maxTtl) { + ttl = maxTtl; + } + } else { + ttl = maxTtl; + } } catch (NumberFormatException e) { // TODO: error TtlUnsupported Log.w(TAG, e); @@ -535,15 +578,17 @@ public class McsService extends Service implements Handler.Callback { intent.setAction(ACTION_C2DM_RECEIVE); intent.putExtra(EXTRA_FROM, msg.from); intent.putExtra(EXTRA_MESSAGE_ID, msg.id); - if (msg.persistent_id != null) { - intent.putExtra(EXTRA_MESSAGE_ID, msg.persistent_id); + if (msg.persistent_id != null) intent.putExtra(EXTRA_MESSAGE_ID, msg.persistent_id); + if (msg.token != null) intent.putExtra(EXTRA_COLLAPSE_KEY, msg.token); + if (msg.raw_data != null) { + intent.putExtra(EXTRA_RAWDATA_BASE64, Base64.encodeToString(msg.raw_data.toByteArray(), Base64.DEFAULT)); + intent.putExtra(EXTRA_RAWDATA, msg.raw_data.toByteArray()); } if (app.wakeForDelivery) { intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); } else { intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); } - if (msg.token != null) intent.putExtra(EXTRA_COLLAPSE_KEY, msg.token); for (AppData appData : msg.app_data) { intent.putExtra(appData.key, appData.value_); } @@ -567,7 +612,7 @@ public class McsService extends Service implements Handler.Callback { sendOrderedBroadcast(intent, null); } else { List infos = getPackageManager().queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER); - if (infos == null || infos.isEmpty()) { + if (infos.isEmpty()) { logd(this, "No target for message, wut?"); } else { for (ResolveInfo resolveInfo : infos) { @@ -589,7 +634,7 @@ public class McsService extends Service implements Handler.Callback { } private void addPowerSaveTempWhitelistApp(String packageName) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (SDK_INT >= 31) { try { if (addToTemporaryAllowListMethod != null && powerExemptionManager != null) { logd(this, "Adding app " + packageName + " to the temp allowlist"); @@ -633,12 +678,12 @@ public class McsService extends Service implements Handler.Callback { rootHandler.sendMessage(rootHandler.obtainMessage(MSG_OUTPUT, type, 0, message)); } - private void sendOutputStream(int what, int arg, Object obj) { + private void sendOutputStream(int arg, Object obj) { McsOutputStream os = outputStream; if (os != null && os.isAlive()) { Handler outputHandler = os.getHandler(); if (outputHandler != null) - outputHandler.sendMessage(outputHandler.obtainMessage(what, arg, 0, obj)); + outputHandler.sendMessage(outputHandler.obtainMessage(McsConstants.MSG_OUTPUT, arg, 0, obj)); } } @@ -649,7 +694,7 @@ public class McsService extends Service implements Handler.Callback { handleInput(msg.arg1, (Message) msg.obj); return true; case MSG_OUTPUT: - sendOutputStream(MSG_OUTPUT, msg.arg1, msg.obj); + sendOutputStream(msg.arg1, msg.obj); return true; case MSG_INPUT_ERROR: case MSG_OUTPUT_ERROR: diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java index 1dcfec44..bc93194e 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java @@ -147,7 +147,7 @@ public class PushRegisterManager { resultBundle.putString(EXTRA_REGISTRATION_ID, attachRequestId(response.token, requestId)); } } else { - if (!request.app.equals(response.deleted) && !request.app.equals(response.token)) { + if (!request.app.equals(response.deleted) && !request.app.equals(response.token) && !request.sender.equals(response.token)) { database.noteAppRegistrationError(request.app, response.responseText); resultBundle.putString(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); } else { diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java b/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java index dbc21088..80c7fd83 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java @@ -123,7 +123,7 @@ public class RegisterRequest extends HttpFormClient.Request { public RegisterRequest extraParams(Bundle extraBundle) { for (String key : extraBundle.keySet()) { - if (!key.equals(GcmConstants.EXTRA_SENDER) && !key.equals(GcmConstants.EXTRA_DELETE)) { + if (!key.equals(GcmConstants.EXTRA_SENDER) && !key.equals(GcmConstants.EXTRA_DELETE) && !key.equals(GcmConstants.EXTRA_APP)) { extraParam(key, extraBundle.getString(key)); } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java index 245d5c2a..83d182aa 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java @@ -17,7 +17,6 @@ package org.microg.gms.gcm; import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.N; import static org.microg.gms.gcm.McsConstants.ACTION_CONNECT; import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT; import static org.microg.gms.gcm.McsConstants.EXTRA_REASON; @@ -31,7 +30,7 @@ import android.util.Log; import androidx.legacy.content.WakefulBroadcastReceiver; -import org.microg.gms.checkin.CheckinPrefs; +import org.microg.gms.checkin.CheckinPreferences; import org.microg.gms.checkin.LastCheckinInfo; import org.microg.gms.common.ForegroundServiceContext; @@ -44,7 +43,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { * "Project Svelte" is just there to f**k things up... */ public synchronized static void register(Context context) { - if (SDK_INT >= N && !registered) { + if (SDK_INT >= 24 && !registered) { IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"); context.getApplicationContext().registerReceiver(new TriggerReceiver(), intentFilter); registered = true; @@ -68,7 +67,7 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { if (LastCheckinInfo.read(context).getAndroidId() == 0) { Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); - if (CheckinPrefs.isEnabled(context)) { + if (CheckinPreferences.isEnabled(context)) { // Do a check-in if we are not actually checked in, // but should be, e.g. cleared app data Log.d(TAG, "Requesting check-in..."); diff --git a/play-services-core/src/main/java/org/microg/gms/gservices/GServices.java b/play-services-core/src/main/java/org/microg/gms/gservices/GServices.java index 79543f47..5c811f28 100644 --- a/play-services-core/src/main/java/org/microg/gms/gservices/GServices.java +++ b/play-services-core/src/main/java/org/microg/gms/gservices/GServices.java @@ -21,10 +21,12 @@ import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; +import org.microg.gms.base.core.BuildConfig; + public class GServices { - public static final Uri CONTENT_URI = Uri.parse("content://com.mgoogle.android.gsf.gservices"); - public static final Uri MAIN_URI = Uri.parse("content://com.mgoogle.android.gsf.gservices/main"); - public static final Uri OVERRIDE_URI = Uri.parse("content://com.mgoogle.android.gsf.gservices/override"); + public static final Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.BASE_PACKAGE_NAME + ".android.gsf.gservices"); + public static final Uri MAIN_URI = Uri.parse("content://" + BuildConfig.BASE_PACKAGE_NAME + ".android.gsf.gservices/main"); + public static final Uri OVERRIDE_URI = Uri.parse("content://" + BuildConfig.BASE_PACKAGE_NAME + ".android.gsf.gservices/override"); public static int setString(ContentResolver resolver, String key, String value) { ContentValues values = new ContentValues(); diff --git a/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java b/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java index 2a95655b..3d3a8189 100644 --- a/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java +++ b/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java @@ -29,6 +29,8 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import org.microg.gms.base.core.BuildConfig; + /** * Originally found in Google Services Framework (com.google.android.gsf), this provides a generic * key-value store, that is written by the checkin service and read from various Google Apps. @@ -37,7 +39,7 @@ import java.util.Set; * if certain "experiments" are enabled on the device. */ public class GServicesProvider extends ContentProvider { - public static final Uri CONTENT_URI = Uri.parse("content://com.mgoogle.android.gsf.gservices/"); + public static final Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.BASE_PACKAGE_NAME + ".android.gsf.gservices/"); public static final Uri MAIN_URI = Uri.withAppendedPath(CONTENT_URI, "main"); public static final Uri OVERRIDE_URI = Uri.withAppendedPath(CONTENT_URI, "override"); public static final Uri PREFIX_URI = Uri.withAppendedPath(CONTENT_URI, "prefix"); diff --git a/play-services-core/src/main/java/org/microg/mgms/settings/GoogleSettingsProvider.java b/play-services-core/src/main/java/org/microg/gms/settings/GoogleSettingsProvider.java similarity index 97% rename from play-services-core/src/main/java/org/microg/mgms/settings/GoogleSettingsProvider.java rename to play-services-core/src/main/java/org/microg/gms/settings/GoogleSettingsProvider.java index 3ab7f51c..da3e5094 100644 --- a/play-services-core/src/main/java/org/microg/mgms/settings/GoogleSettingsProvider.java +++ b/play-services-core/src/main/java/org/microg/gms/settings/GoogleSettingsProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.microg.mgms.settings; +package org.microg.gms.settings; import android.content.ContentProvider; import android.content.ContentValues; diff --git a/play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/LegacyAccountSettingsActivity.java old mode 100755 new mode 100644 similarity index 91% rename from play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java rename to play-services-core/src/main/java/org/microg/gms/ui/LegacyAccountSettingsActivity.java index fa099d49..fc015f5e --- a/play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/LegacyAccountSettingsActivity.java @@ -18,7 +18,6 @@ package org.microg.gms.ui; import android.accounts.Account; import android.accounts.AccountManager; -import android.os.Build; import android.os.Bundle; import androidx.annotation.Nullable; @@ -34,9 +33,10 @@ import org.microg.tools.ui.ResourceSettingsFragment; import static android.accounts.AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE; import static android.accounts.AccountManager.VISIBILITY_NOT_VISIBLE; import static android.accounts.AccountManager.VISIBILITY_VISIBLE; +import static android.os.Build.VERSION.SDK_INT; import static org.microg.gms.auth.AuthManager.PREF_AUTH_VISIBLE; -public class AccountSettingsActivity extends AbstractSettingsActivity { +public class LegacyAccountSettingsActivity extends AbstractSettingsActivity { @Override protected Fragment getFragment() { @@ -45,7 +45,7 @@ public class AccountSettingsActivity extends AbstractSettingsActivity { public static class AccountSettingsFragment extends ResourceSettingsFragment { public AccountSettingsFragment() { - preferencesResource = R.xml.preferences_account; + preferencesResource = R.xml.preferences_google_more; } @Override @@ -53,7 +53,7 @@ public class AccountSettingsActivity extends AbstractSettingsActivity { super.onCreatePreferences(savedInstanceState, rootKey); Preference pref = findPreference(PREF_AUTH_VISIBLE); if (pref != null) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + if (SDK_INT < 26) { pref.setVisible(false); } else { pref.setOnPreferenceChangeListener((preference, newValue) -> { diff --git a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java old mode 100755 new mode 100644 similarity index 66% rename from play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java rename to play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java index c60833ec..b65b3806 --- a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java @@ -1,6 +1,6 @@ package org.microg.gms.ui; -import android.os.Build; +import android.content.Intent; import android.os.Bundle; import androidx.annotation.Nullable; @@ -10,10 +10,12 @@ import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; -import com.google.android.material.color.DynamicColors; import com.mgoogle.android.gms.R; +import org.microg.gms.ui.settings.SettingsProvider; -public class SettingsActivity extends AppCompatActivity { +import static org.microg.gms.ui.settings.SettingsProviderKt.getAllSettingsProviders; + +public class MainSettingsActivity extends AppCompatActivity { private AppBarConfiguration appBarConfiguration; private NavController getNavController() { @@ -23,10 +25,18 @@ public class SettingsActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.settings_root_activity); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - DynamicColors.applyToActivityIfAvailable(this); + + Intent intent = getIntent(); + for (SettingsProvider settingsProvider : getAllSettingsProviders(this)) { + settingsProvider.preProcessSettingsIntent(intent); } + + setContentView(R.layout.settings_root_activity); + + for (SettingsProvider settingsProvider : getAllSettingsProviders(this)) { + settingsProvider.extendNavigation(getNavController()); + } + appBarConfiguration = new AppBarConfiguration.Builder(getNavController().getGraph()).build(); NavigationUI.setupActionBarWithNavController(this, getNavController(), appBarConfiguration); } @@ -35,4 +45,4 @@ public class SettingsActivity extends AppCompatActivity { public boolean onSupportNavigateUp() { return NavigationUI.navigateUp(getNavController(), appBarConfiguration) || super.onSupportNavigateUp(); } -} +} \ No newline at end of file diff --git a/play-services-core/src/main/java/org/microg/tools/AccountPickerActivity.java b/play-services-core/src/main/java/org/microg/tools/AccountPickerActivity.java index 4c596f32..69d87394 100755 --- a/play-services-core/src/main/java/org/microg/tools/AccountPickerActivity.java +++ b/play-services-core/src/main/java/org/microg/tools/AccountPickerActivity.java @@ -16,16 +16,15 @@ package org.microg.tools; -import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; /** * This is just an activity that forwards to the systems native account picker */ -public class AccountPickerActivity extends Activity { - private static final int REQUEST_CODE = AccountPickerActivity.class.hashCode(); +public class AccountPickerActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { @@ -36,16 +35,8 @@ public class AccountPickerActivity extends Activity { ComponentName.unflattenFromString("android/.accounts.ChooseTypeAndAccountActivity"); intent.setClassName(componentName.getPackageName(), componentName.getClassName()); intent.putExtras(extras); - startActivityForResult(intent, REQUEST_CODE); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE) { - setResult(resultCode, data); - finish(); - } else { - super.onActivityResult(requestCode, resultCode, data); - } + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + startActivity(intent); + finish(); } } diff --git a/play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt b/play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt new file mode 100644 index 00000000..e1fb7a21 --- /dev/null +++ b/play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.common + +import android.content.pm.PackageManager +import android.os.IBinder +import android.os.Parcel +import android.util.Log +import androidx.annotation.Keep +import com.google.android.gms.common.internal.CertData +import com.google.android.gms.common.internal.IGoogleCertificatesApi +import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper +import org.microg.gms.common.PackageUtils +import org.microg.gms.utils.warnOnTransactionIssues + +private const val TAG = "GoogleCertificates" + +@Keep +class GoogleCertificatesImpl : IGoogleCertificatesApi.Stub() { + private val googleCertificates = emptySet() + private val googleReleaseCertificates = emptySet() + + override fun getGoogleCertificates(): IObjectWrapper { + Log.d(TAG, "unimplemented Method: getGoogleCertificates") + return ObjectWrapper.wrap(googleCertificates.toTypedArray()) + } + + override fun getGoogleReleaseCertificates(): IObjectWrapper { + Log.d(TAG, "unimplemented Method: getGoogleReleaseCertificates") + return ObjectWrapper.wrap(googleReleaseCertificates.toTypedArray()) + } + + override fun isGoogleReleaseSigned(packageName: String, certData: IObjectWrapper): Boolean { + return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, ByteArray::class.java)) + } + + override fun isGoogleSigned(packageName: String, certData: IObjectWrapper): Boolean { + return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, ByteArray::class.java)) + } + + override fun isGoogleOrPlatformSigned(query: GoogleCertificatesQuery, packageManager: IObjectWrapper): Boolean { + val pm = ObjectWrapper.unwrapTyped(packageManager, PackageManager::class.java) + return if (query == null || query.callingPackage == null) { + false + } else if (query.getCertData() == null) { + if (pm == null) false else PackageUtils.isGooglePackage(pm, query.callingPackage) + } else { + PackageUtils.isGooglePackage(query.callingPackage, query.getCertData().bytes) + } + } + + override fun isPackageGoogleOrPlatformSigned(query: GoogleCertificatesLookupQuery): GoogleCertificatesLookupResponse { + return certificateLookup(query, true) + } + + override fun isPackageGoogleOrPlatformSignedAvailable(): Boolean { + return true + } + + override fun queryPackageSigned(query: GoogleCertificatesLookupQuery): GoogleCertificatesLookupResponse { + if (!isFineGrainedPackageVerificationAvailable) throw IllegalStateException("API unavailable") + return certificateLookup(query, false) + } + + override fun isFineGrainedPackageVerificationAvailable(): Boolean { + return true + } + + private fun certificateLookup(query: GoogleCertificatesLookupQuery, allowPlatform: Boolean): GoogleCertificatesLookupResponse { + val context = query.context + ?: return GoogleCertificatesLookupResponse(false, "context is null", 5, 1) + val packageManager = context.packageManager + ?: return GoogleCertificatesLookupResponse(false, "context has no package manager", 5, 1) + val callingPackage = query.callingPackage + ?: return GoogleCertificatesLookupResponse(false, "callingPackage is null", 5, 1) + val signatureDigest = PackageUtils.firstSignatureDigest(packageManager, callingPackage) + ?: return GoogleCertificatesLookupResponse(false, "callingPackage not found", 4, 1) + return if (PackageUtils.isGooglePackage(callingPackage, signatureDigest)) { + GoogleCertificatesLookupResponse(true, null, 1, 3) + } else { + GoogleCertificatesLookupResponse(false, "not allowed", 2, 1) + } + } + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = + warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } +} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/LoaderActivity.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/LoaderActivity.kt new file mode 100644 index 00000000..c1a4a2da --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/LoaderActivity.kt @@ -0,0 +1,165 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.accountsettings.ui + +import android.accounts.AccountManager +import android.content.Intent +import android.content.Intent.ACTION_VIEW +import android.content.Intent.CATEGORY_BROWSABLE +import android.content.Intent.FLAG_ACTIVITY_FORWARD_RESULT +import android.net.Uri +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT_TYPE +import org.microg.gms.common.PackageUtils +import org.microg.tools.AccountPickerActivity + +private const val TAG = "AccountSettingsLoader" + +private val ALLOWED_FALLBACK_PREFIXES = setOf("https://myaccount.google.com/", "https://takeout.google.com/") +private val BROWSABLE_SCREEN_IDS = setOf(1, 200, 400, 502, 527, 10003, 10050, 12700, 12701) +private val ACCOUNT_CHOOSER_URI = Uri.parse("https://accounts.google.com/AccountChooser") + +private const val QUERY_PARAM_CONTINUE = "continue" +private const val QUERY_PARAM_LANG = "hl" +private const val QUERY_PARAM_EMAIL = "Email" + +private const val EXTRA_ALLOWABLE_ACCOUNT_TYPES = "allowableAccountTypes" + +private const val REQUEST_ACCOUNT_PICKER = 1 + +@Suppress("DEPRECATION") +class LoaderActivity : AppCompatActivity() { + private var canAskForAccount = false + + private fun launchFallback() { + val fallbackUrl = intent?.getStringExtra(EXTRA_FALLBACK_URL) + + if (fallbackUrl == null) { + Log.d(TAG, "No fallback") + finishResult(RESULT_CANCELED) + } else if (fallbackUrl in ALLOWED_FALLBACK_PREFIXES) { + // TODO: Error screen? + Log.d(TAG, "Illegal fallback url") + finishResult(RESULT_CANCELED) + } else { + val fallbackAuth = intent?.getBooleanExtra(EXTRA_FALLBACK_AUTH, false) ?: false + val uri = if (fallbackAuth) { + val builder = ACCOUNT_CHOOSER_URI.buildUpon().appendQueryParameter(QUERY_PARAM_CONTINUE, fallbackUrl) + val accountName = intent?.getStringExtra(EXTRA_ACCOUNT_NAME) + if (!accountName.isNullOrBlank()) { + builder.appendQueryParameter(QUERY_PARAM_EMAIL, accountName) + } + val lang = Uri.parse(fallbackUrl).getQueryParameter(QUERY_PARAM_LANG) + if (lang != null) { + builder.appendQueryParameter(QUERY_PARAM_LANG, lang) + } + builder.build() + } else { + Uri.parse(fallbackUrl) + } + Log.d(TAG, "Opening fallback $fallbackUrl") + val intent = Intent(ACTION_VIEW, uri).apply { addCategory(CATEGORY_BROWSABLE) } + startActivity(intent) + finishResult(RESULT_OK) + } + } + + private fun launchMain() { + val requestedAccountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME) + val ignoreAccount = intent?.getBooleanExtra(EXTRA_IGNORE_ACCOUNT, false) ?: false + val accounts = AccountManager.get(this).getAccountsByType(DEFAULT_ACCOUNT_TYPE) + val account = if (requestedAccountName != null) { + val account = accounts.find { it.name == requestedAccountName } + if (account == null) { + // TODO: Error screen? + Log.d(TAG, "Account not found: $requestedAccountName") + return finishResult(RESULT_CANCELED) + } + account + } else if (accounts.isEmpty()) { + if (intent?.getStringExtra(EXTRA_FALLBACK_URL) != null) { + return launchFallback() + } else { + // TODO: Error screen? + Log.d(TAG, "No account configured") + return finishResult(RESULT_CANCELED) + } + } else if (accounts.size > 1) { + if (canAskForAccount) { + val intent = Intent(this, AccountPickerActivity::class.java) + intent.putExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES, arrayOf(DEFAULT_ACCOUNT_TYPE)) + startActivityForResult(intent, REQUEST_ACCOUNT_PICKER) + canAskForAccount = false + return + } else { + return finishResult(RESULT_CANCELED) + } + } else { + accounts.first() + } + + val intent = Intent(this, MainActivity::class.java).apply { + action = intent.action + if (intent.hasExtra(EXTRA_THEME_CHOICE)) putExtra(EXTRA_THEME_CHOICE, intent.getIntExtra(EXTRA_THEME_CHOICE, 0)) + putExtra(EXTRA_ACCOUNT_NAME, account.name) + if (ignoreAccount) putExtra(EXTRA_IGNORE_ACCOUNT, true) + putExtra(EXTRA_SCREEN_ID, intent.getIntExtra(EXTRA_SCREEN_ID, 1)) + for (it in intent.extras?.keySet().orEmpty()) { + if (it.startsWith(EXTRA_SCREEN_OPTIONS_PREFIX)) putExtra(it, intent.getStringExtra(it)) + } + putExtra(EXTRA_CALLING_PACKAGE_NAME, callingActivity?.packageName) + if (intent.action != ACTION_BROWSE_SETTINGS) { + addFlags(FLAG_ACTIVITY_FORWARD_RESULT) + } + } + + startActivity(intent) + + if (!isFinishing && !isChangingConfigurations) { + finishResult(RESULT_OK) + } + } + + @Deprecated("Deprecated in Java") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQUEST_ACCOUNT_PICKER) { + if (resultCode == RESULT_OK && data?.hasExtra(AccountManager.KEY_ACCOUNT_NAME) == true) { + intent.putExtra(EXTRA_ACCOUNT_NAME, data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)) + launchMain() + } else { + finishResult(resultCode) + } + } else { + super.onActivityResult(requestCode, resultCode, data) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + canAskForAccount = true + val extras = intent?.extras?.also { it.keySet() } + Log.d(TAG, "Invoked with ${intent.action} and extras $extras") + + super.onCreate(savedInstanceState) + + val isMainAllowed = if (intent == null || intent.action != ACTION_BROWSE_SETTINGS) + PackageUtils.isGooglePackage(this, callingActivity?.packageName) + else + extras?.getInt(EXTRA_SCREEN_ID, -1) in BROWSABLE_SCREEN_IDS + + if (!isMainAllowed) { + launchFallback() + } else if (!isFinishing && !isChangingConfigurations){ + launchMain() + } + } + + private fun finishResult(resultCode: Int) { + setResult(resultCode) + finish() + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt new file mode 100644 index 00000000..441a2e3b --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/MainActivity.kt @@ -0,0 +1,185 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.accountsettings.ui + +import android.accounts.Account +import android.accounts.AccountManager +import android.os.Bundle +import android.util.Log +import android.view.View +import android.webkit.WebView +import android.widget.ProgressBar +import android.widget.RelativeLayout +import android.widget.RelativeLayout.LayoutParams.MATCH_PARENT +import android.widget.RelativeLayout.LayoutParams.WRAP_CONTENT +import androidx.appcompat.app.AppCompatActivity +import org.microg.gms.auth.AuthConstants +import org.microg.gms.common.Constants + +private const val TAG = "AccountSettings" + +// TODO: There likely is some API to figure those out... +private val SCREEN_ID_TO_URL = hashMapOf( + 1 to "https://myaccount.google.com", + 200 to "https://myaccount.google.com/privacycheckup", + 203 to "https://myaccount.google.com/email", + 204 to "https://myaccount.google.com/phone", + 205 to "https://myaccount.google.com/birthday", + 206 to "https://myaccount.google.com/gender", + 210 to "https://myaccount.google.com/locationsharing", + 214 to "https://myaccount.google.com/dashboard", + 215 to "https://takeout.google.com", + 216 to "https://myaccount.google.com/inactive", + 219 to "https://myactivity.google.com/myactivity", + 220 to "https://www.google.com/maps/timeline", + 224 to "https://myactivity.google.com/activitycontrols?settings=search", + 227 to "https://myactivity.google.com/activitycontrols?settings=location", + 231 to "https://myactivity.google.com/activitycontrols?settings=youtube", + 235 to "https://myactivity.google.com/activitycontrols/youtube", + 238 to "https://www.google.com/setting/search/privateresults/", + 241 to "https://myaccount.google.com/communication-preferences", + 300 to "https://myaccount.google.com/language", + 301 to "https://drive.google.com/settings/storage", + 302 to "https://myaccount.google.com/deleteservices", + 303 to "https://myaccount.google.com/deleteaccount", + 307 to "https://payments.google.com/payments/home", + 308 to "https://myaccount.google.com/subscriptions", + 309 to "https://myaccount.google.com/purchases", + 310 to "https://myaccount.google.com/reservations", + 312 to "https://myaccount.google.com/accessibility", + 313 to "https://myaccount.google.com/inputtools", + 400 to "https://myaccount.google.com/security-checkup/", + 401 to "https://myaccount.google.com/signinoptions/password", + 403 to "https://myaccount.google.com/signinoptions/two-step-verification", + 406 to "https://myaccount.google.com/signinoptions/rescuephone", + 407 to "https://myaccount.google.com/recovery/email", + 409 to "https://myaccount.google.com/notifications", + 410 to "https://myaccount.google.com/device-activity", + 417 to "https://myaccount.google.com/find-your-phone", + 425 to "https://myaccount.google.com/account-enhanced-safe-browsing", + 426 to "https://myaccount.google.com/two-step-verification/authenticator", + 427 to "https://myaccount.google.com/two-step-verification/backup-codes", + 429 to "https://myaccount.google.com/two-step-verification/security-keys", + 430 to "https://myaccount.google.com/two-step-verification/prompt", + 431 to "https://myaccount.google.com/connections", + 432 to "https://myaccount.google.com/two-step-verification/phone-numbers", + 433 to "https://myaccount.google.com/signinoptions/passkeys", + 437 to "https://myaccount.google.com/signinoptions/passwordoptional", + 500 to "https://policies.google.com/privacy", + 503 to "https://policies.google.com/terms", + 519 to "https://myaccount.google.com/yourdata/maps", + 520 to "https://myaccount.google.com/yourdata/search", + 530 to "https://fit.google.com/privacy/settings", + 547 to "https://myactivity.google.com/product/search", + 562 to "https://myaccount.google.com/yourdata/youtube", + 10003 to "https://myaccount.google.com/personal-info", + 10004 to "https://myaccount.google.com/data-and-privacy", + 10005 to "https://myaccount.google.com/people-and-sharing", + 10006 to "https://myaccount.google.com/security", + 10007 to "https://myaccount.google.com/payments-and-subscriptions", + 10015 to "https://support.google.com/accounts", + 10050 to "https://myaccount.google.com/profile", + 10090 to "https://myaccount.google.com/profile/name", + 10704 to "https://www.google.com/account/about", + 10706 to "https://myaccount.google.com/profile/profiles-summary", + 10728 to "https://myaccount.google.com/data-and-privacy/how-data-improves-experience", + 10729 to "https://myaccount.google.com/data-and-privacy/data-visibility", + 10759 to "https://myaccount.google.com/address/home", + 10760 to "https://myaccount.google.com/address/work", +) + +private val ALLOWED_WEB_PREFIXES = setOf( + "https://accounts.google.com/", + "https://myaccount.google.com/", + "https://one.google.com/", + "https://myactivity.google.com/", + "https://timeline.google.com/", + "https://takeout.google.com/", + "https://www.google.com/maps/timeline", + "https://www.google.com/setting/", + "https://drive.google.com/settings/", + "https://drive.google.com/accounts/", + "https://drive.google.com/u/1/settings/", + "https://payments.google.com/", + "https://policies.google.com/", + "https://fit.google.com/privacy/settings", + "https://maps.google.com/maps/timeline" +) + +private val ACTION_TO_SCREEN_ID = hashMapOf( + ACTION_MY_ACCOUNT to 1, + ACTION_ACCOUNT_PREFERENCES_SETTINGS to 1, + ACTION_SECURITY_SETTINGS to 10006, + ACTION_PRIVACY_SETTINGS to 10004, +) + +@Suppress("DEPRECATION") +class MainActivity : AppCompatActivity() { + private lateinit var webView: WebView + + private fun getSelectedAccountName(): String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + val extras = intent?.extras?.also { it.keySet() } + Log.d(TAG, "Invoked with ${intent.action} and extras $extras") + super.onCreate(savedInstanceState) + + val screenId = intent?.getIntExtra(EXTRA_SCREEN_ID, -1).takeIf { it != -1 } ?: ACTION_TO_SCREEN_ID[intent.action] ?: 1 + val screenOptions = intent.extras?.keySet().orEmpty() + .filter { it.startsWith(EXTRA_SCREEN_OPTIONS_PREFIX) } + .map { it.substring(EXTRA_SCREEN_OPTIONS_PREFIX.length) to intent.getStringExtra(it) } + .toMap() + + val callingPackage = intent?.getStringExtra(EXTRA_CALLING_PACKAGE_NAME) ?: callingActivity?.packageName ?: Constants.GMS_PACKAGE_NAME + + val ignoreAccount = intent?.getBooleanExtra(EXTRA_IGNORE_ACCOUNT, false) ?: false + val accountName = if (ignoreAccount) null else { + val accounts = AccountManager.get(this).getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE) + val accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME) ?: intent.getParcelableExtra("account")?.name ?: getSelectedAccountName() + accounts.find { it.name.equals(accountName) }?.name + } + + if (accountName == null) { + Log.w(TAG, "No account, going without!") + } + + if (screenId in SCREEN_ID_TO_URL) { + val layout = RelativeLayout(this) + layout.addView(ProgressBar(this).apply { + layoutParams = RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { + addRule(RelativeLayout.CENTER_HORIZONTAL) + addRule(RelativeLayout.CENTER_VERTICAL) + } + isIndeterminate = true + }) + webView = WebView(this).apply { + layoutParams = RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) + visibility = View.INVISIBLE + } + layout.addView(webView) + setContentView(layout) + WebViewHelper(this, webView, ALLOWED_WEB_PREFIXES).openWebView(SCREEN_ID_TO_URL[screenId], accountName) + setResult(RESULT_OK) + } else { + Log.w(TAG, "Unknown screen id, can't open corresponding web page") + setResult(RESULT_CANCELED) + finish() + } + } + + override fun onDestroy() { + super.onDestroy() + } + + @Deprecated("Deprecated in Java") + override fun onBackPressed() { + if (this::webView.isInitialized && webView.canGoBack()) { + webView.goBack() + } else { + super.onBackPressed() + } + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt new file mode 100644 index 00000000..7ce98e9c --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/WebViewHelper.kt @@ -0,0 +1,130 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.accountsettings.ui + +import android.content.Intent +import android.content.Intent.URI_INTENT_SCHEME +import android.net.Uri +import android.util.Log +import android.view.View +import android.webkit.WebResourceRequest +import android.webkit.WebSettings +import android.webkit.WebView +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import androidx.webkit.WebResourceErrorCompat +import androidx.webkit.WebViewClientCompat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.microg.gms.auth.AuthManager +import org.microg.gms.common.Constants.GMS_PACKAGE_NAME +import org.microg.gms.common.PackageUtils +import java.net.URLEncoder +import java.util.* + +private const val TAG = "AccountSettingsWebView" + +class WebViewHelper(private val activity: AppCompatActivity, private val webView: WebView, private val allowedPrefixes: Set = emptySet()) { + fun openWebView(url: String?, accountName: String?) { + prepareWebViewSettings(webView.settings) + webView.webViewClient = object : WebViewClientCompat() { + override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceErrorCompat) { + Log.w(TAG, "Error loading: $error") + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + webView.visibility = View.VISIBLE + } + + @Deprecated("Deprecated in Java") + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + Log.d(TAG, "Navigating to $url") + if (url.startsWith("intent:")) { + try { + val intent = Intent.parseUri(url, URI_INTENT_SCHEME) + if (intent.`package` == GMS_PACKAGE_NAME || PackageUtils.isGooglePackage(activity, intent.`package`)) { + // Only allow to start Google packages + activity.startActivity(intent) + } else { + Log.w(TAG, "Ignoring request to start non-Google app") + } + } catch (e: Exception) { + Log.w(TAG, "Error invoking intent", e) + } + return false + } + if (allowedPrefixes.isNotEmpty() && allowedPrefixes.none { url.startsWith(it) }) { + try { + activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { addCategory(Intent.CATEGORY_BROWSABLE) }) + } catch (e: Exception) { + Log.w(TAG, "Error forwarding to browser", e) + } + return true + } + return false + } + } + + val urlWithLanguage: String? = addLanguageParam(url) + if (accountName != null) { + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + openWebWithAccount(accountName, urlWithLanguage) + } + } + } else { + loadWebViewUrl(urlWithLanguage) + } + } + + private fun loadWebViewUrl(url: String?) { + if (url != null) { + webView.loadUrl(url) + } else { + activity.finish() + } + } + + private fun addLanguageParam(url: String?): String? { + val language = Locale.getDefault().language + return if (language.isNotEmpty()) { + Uri.parse(url).buildUpon().appendQueryParameter("hl", language).toString() + } else { + url + } + } + + private fun openWebWithAccount(accountName: String, url: String?) { + try { + val service = "weblogin:continue=" + URLEncoder.encode(url, "utf-8") + val authManager = AuthManager(activity, accountName, GMS_PACKAGE_NAME, service) + val authUrl = authManager.requestAuth(false)?.auth + if (authUrl?.contains("WILL_NOT_SIGN_IN") == true) { + throw RuntimeException("Would not sign in") + } + Log.d(TAG, "Opening $authUrl") + webView.post { + loadWebViewUrl(authUrl) + } + } catch (e: Exception) { + Log.w(TAG, "Failed to get weblogin auth.", e) + activity.finish() + } + } + + private fun prepareWebViewSettings(settings: WebSettings) { + settings.javaScriptEnabled = true + settings.setSupportMultipleWindows(false) + settings.allowFileAccess = false + settings.databaseEnabled = false + settings.setNeedInitialFocus(false) + settings.useWideViewPort = false + settings.setSupportZoom(false) + settings.javaScriptCanOpenWindowsAutomatically = false + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt new file mode 100644 index 00000000..5ca88fe7 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/accountsettings/ui/extensions.kt @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.accountsettings.ui + +const val ACTION_BROWSE_SETTINGS = "com.google.android.gms.accountsettings.action.BROWSE_SETTINGS" +const val ACTION_MY_ACCOUNT = "com.google.android.gms.accountsettings.MY_ACCOUNT" +const val ACTION_ACCOUNT_PREFERENCES_SETTINGS = "com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS" +const val ACTION_PRIVACY_SETTINGS = "com.google.android.gms.accountsettings.PRIVACY_SETTINGS" +const val ACTION_SECURITY_SETTINGS = "com.google.android.gms.accountsettings.SECURITY_SETTINGS" + +const val EXTRA_CALLING_PACKAGE_NAME = "extra.callingPackageName" +const val EXTRA_IGNORE_ACCOUNT = "extra.ignoreAccount" +const val EXTRA_ACCOUNT_NAME = "extra.accountName" +const val EXTRA_SCREEN_ID = "extra.screenId" +const val EXTRA_SCREEN_OPTIONS_PREFIX = "extra.screen." +const val EXTRA_FALLBACK_URL = "extra.fallbackUrl" +const val EXTRA_FALLBACK_AUTH = "extra.fallbackAuth" +const val EXTRA_THEME_CHOICE = "extra.themeChoice" + +const val OPTION_SCREEN_FLAVOR = "screenFlavor" \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt index 5406bf2a..ba5d2b2e 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt @@ -1,8 +1,8 @@ package org.microg.gms.auth import android.content.Context -import org.microg.mgms.settings.SettingsContract -import org.microg.mgms.settings.SettingsContract.Auth +import org.microg.gms.settings.SettingsContract +import org.microg.gms.settings.SettingsContract.Auth object AuthPrefs { @@ -20,4 +20,11 @@ object AuthPrefs { } } + @JvmStatic + fun shouldIncludeAndroidId(context: Context): Boolean { + return SettingsContract.getSettings(context, Auth.getContentUri(context), arrayOf(Auth.INCLUDE_ANDROID_ID)) { c -> + c.getInt(0) != 0 + } + } + } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertManager.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertManager.kt index 8e5d5036..c99b3cc3 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertManager.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertManager.kt @@ -29,8 +29,8 @@ import org.microg.gms.gcm.RegisterRequest import org.microg.gms.gcm.completeRegisterRequest import org.microg.gms.profile.Build import org.microg.gms.profile.ProfileManager -import org.microg.mgms.settings.SettingsContract.CheckIn -import org.microg.mgms.settings.SettingsContract.getSettings +import org.microg.gms.settings.SettingsContract.CheckIn +import org.microg.gms.settings.SettingsContract.getSettings import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec import kotlin.random.Random @@ -97,7 +97,11 @@ class AppCertManager(private val context: Context) { } override fun deliverError(error: VolleyError) { - Log.d(TAG, "Error: ${Base64.encodeToString(error.networkResponse.data, 2)}") + if (error.networkResponse != null) { + Log.d(TAG, "Error: ${Base64.encodeToString(error.networkResponse.data, 2)}") + } else { + Log.d(TAG, "Error: ${error.message}") + } deviceKeyCacheTime = 0 deferredResponse.complete(null) } @@ -150,11 +154,12 @@ class AppCertManager(private val context: Context) { ) } else { Log.d(TAG, "Using fallback spatula header based on Android ID") - val androidId = getSettings(context, CheckIn.getContentUri(context), arrayOf(CheckIn.ANDROID_ID, CheckIn.SECURITY_TOKEN)) { cursor: Cursor -> cursor.getLong(0) } + val androidId = getSettings(context, CheckIn.getContentUri(context), arrayOf(CheckIn.ANDROID_ID)) { cursor: Cursor -> cursor.getLong(0) } SpatulaHeaderProto( packageInfo = SpatulaHeaderProto.PackageInfo(packageName, packageCertificateHash), deviceId = androidId ) + return null // TODO } Log.d(TAG, "Spatula Header: $proto") return Base64.encodeToString(proto.encode(), Base64.NO_WRAP) diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertService.kt index e73017e2..9b14af49 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertService.kt @@ -38,5 +38,5 @@ class AppCertServiceImpl(private val context: Context) : IAppCertService.Stub() return runBlocking { manager.getSpatulaHeader(packageName) } } - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags) { super.onTransact(code, data, reply, flags) } + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialsService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialsService.kt index adc0370b..4e933426 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialsService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialsService.kt @@ -52,5 +52,5 @@ class CredentialsServiceImpl : ICredentialsService.Stub() { callbacks.onStatus(Status.SUCCESS) } - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags) { super.onTransact(code, data, reply, flags) } + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/proxy/AuthProxyService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/proxy/AuthProxyService.kt index e1cedd59..ede33d26 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/proxy/AuthProxyService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/proxy/AuthProxyService.kt @@ -59,5 +59,5 @@ class AuthServiceImpl(private val context: Context, private val lifecycle: Lifec override fun getLifecycle(): Lifecycle = lifecycle - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags) { super.onTransact(code, data, reply, flags) } + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPreferences.kt similarity index 51% rename from play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt rename to play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPreferences.kt index 2953ed60..eb78d498 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPreferences.kt @@ -5,16 +5,21 @@ package org.microg.gms.checkin import android.content.Context -import org.microg.mgms.settings.SettingsContract -import org.microg.mgms.settings.SettingsContract.CheckIn -import org.microg.mgms.settings.SettingsContract.setSettings +import android.content.Intent +import org.microg.gms.settings.SettingsContract +import org.microg.gms.settings.SettingsContract.CheckIn +import org.microg.gms.settings.SettingsContract.setSettings -object CheckinPrefs { +object CheckinPreferences { @JvmStatic fun isEnabled(context: Context): Boolean { val projection = arrayOf(CheckIn.ENABLED) - return SettingsContract.getSettings(context, CheckIn.getContentUri(context), projection) { c -> + return SettingsContract.getSettings( + context, + CheckIn.getContentUri(context), + projection + ) { c -> c.getInt(0) != 0 } } @@ -22,7 +27,11 @@ object CheckinPrefs { @JvmStatic fun isSpoofingEnabled(context: Context): Boolean { val projection = arrayOf(CheckIn.BRAND_SPOOF) - return SettingsContract.getSettings(context, CheckIn.getContentUri(context), projection) { c -> + return SettingsContract.getSettings( + context, + CheckIn.getContentUri(context), + projection + ) { c -> c.getInt(0) != 0 } } @@ -41,4 +50,13 @@ object CheckinPrefs { } } -} + @JvmStatic + fun setEnabled(context: Context, enabled: Boolean) { + SettingsContract.setSettings(context, CheckIn.getContentUri(context)) { + put(CheckIn.ENABLED, enabled) + } + if (enabled) { + context.sendOrderedBroadcast(Intent(context, TriggerReceiver::class.java), null) + } + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt index b3056893..ab2dc042 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt @@ -16,8 +16,8 @@ package org.microg.gms.checkin import android.content.Context -import org.microg.mgms.settings.SettingsContract -import org.microg.mgms.settings.SettingsContract.CheckIn +import org.microg.gms.settings.SettingsContract +import org.microg.gms.settings.SettingsContract.CheckIn data class LastCheckinInfo( val lastCheckin: Long, diff --git a/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt b/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt index 1637643b..d42c9e03 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt @@ -9,9 +9,9 @@ import android.content.Context import android.content.Intent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.microg.mgms.settings.SettingsContract.CheckIn -import org.microg.mgms.settings.SettingsContract.getSettings -import org.microg.mgms.settings.SettingsContract.setSettings +import org.microg.gms.settings.SettingsContract.CheckIn +import org.microg.gms.settings.SettingsContract.getSettings +import org.microg.gms.settings.SettingsContract.setSettings import java.io.Serializable data class ServiceInfo(val configuration: ServiceConfiguration, val lastCheckin: Long, val androidId: Long) : Serializable diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt index b2402779..c506ed62 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt @@ -8,10 +8,11 @@ import android.net.ConnectivityManager import android.net.NetworkInfo import android.util.Log import org.microg.gms.gcm.TriggerReceiver.FORCE_TRY_RECONNECT -import org.microg.mgms.settings.SettingsContract -import org.microg.mgms.settings.SettingsContract.Gcm -import org.microg.mgms.settings.SettingsContract.setSettings -import org.microg.mgms.settings.SettingsProvider +import org.microg.gms.settings.SettingsContract +import org.microg.gms.settings.SettingsContract.Gcm +import org.microg.gms.settings.SettingsContract.setSettings +import kotlin.math.max +import kotlin.math.min data class GcmPrefs( val isGcmLogEnabled: Boolean, @@ -37,6 +38,9 @@ data class GcmPrefs( const val PREF_NETWORK_ROAMING = Gcm.NETWORK_ROAMING const val PREF_NETWORK_OTHER = Gcm.NETWORK_OTHER + private const val MIN_INTERVAL = 5 * 60 * 1000 // 5 minutes + private const val MAX_INTERVAL = 15 * 60 * 1000 // 15 minutes + @JvmStatic fun get(context: Context): GcmPrefs { return SettingsContract.getSettings(context, Gcm.getContentUri(context), Gcm.PROJECTION) { c -> @@ -67,6 +71,15 @@ data class GcmPrefs( gcmPrefs.setEnabled(context, config.enabled) } + fun setEnabled(context: Context, enabled: Boolean) { + val prefs = get(context) + setSettings(context, Gcm.getContentUri(context)) { + put(Gcm.ENABLE_GCM, enabled) + } + prefs.setEnabled(context, enabled) + } + + @JvmStatic fun clearLastPersistedId(context: Context) { setSettings(context, Gcm.getContentUri(context)) { @@ -75,6 +88,7 @@ data class GcmPrefs( } } + /** * Call this whenever the enabled state of GCM has changed. */ @@ -88,6 +102,7 @@ data class GcmPrefs( } } + @Suppress("DEPRECATION") fun getNetworkPrefForInfo(info: NetworkInfo?): String { if (info == null) return PREF_NETWORK_OTHER return if (info.isRoaming) PREF_NETWORK_ROAMING else when (info.type) { @@ -97,19 +112,20 @@ data class GcmPrefs( } } + @Suppress("DEPRECATION") fun getHeartbeatMsFor(info: NetworkInfo?): Int { return getHeartbeatMsFor(getNetworkPrefForInfo(info)) } fun getHeartbeatMsFor(pref: String): Int { return if (PREF_NETWORK_ROAMING == pref) { - if (networkRoaming != 0) networkRoaming * SettingsProvider.INTERVAL else learntMobileInterval + if (networkRoaming != 0) networkRoaming * 60000 else learntMobileInterval } else if (PREF_NETWORK_MOBILE == pref) { - if (networkMobile != 0) networkMobile * SettingsProvider.INTERVAL else learntMobileInterval + if (networkMobile != 0) networkMobile * 60000 else learntMobileInterval } else if (PREF_NETWORK_WIFI == pref) { - if (networkWifi != 0) networkWifi * SettingsProvider.INTERVAL else learntWifiInterval + if (networkWifi != 0) networkWifi * 60000 else learntWifiInterval } else { - if (networkOther != 0) networkOther * SettingsProvider.INTERVAL else learntOtherInterval + if (networkOther != 0) networkOther * 60000 else learntOtherInterval } } @@ -118,15 +134,15 @@ data class GcmPrefs( when (pref) { PREF_NETWORK_MOBILE, PREF_NETWORK_ROAMING -> setSettings(context, Gcm.getContentUri(context)) { val newInterval = (learntMobileInterval * 0.95).toInt() - put(Gcm.LEARNT_MOBILE, newInterval) + put(Gcm.LEARNT_MOBILE, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL))) } PREF_NETWORK_WIFI -> setSettings(context, Gcm.getContentUri(context)) { - val newInterval = (learntMobileInterval * 0.95).toInt() - put(Gcm.LEARNT_WIFI, newInterval) + val newInterval = (learntWifiInterval * 0.95).toInt() + put(Gcm.LEARNT_WIFI, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL))) } else -> setSettings(context, Gcm.getContentUri(context)) { - val newInterval = (learntMobileInterval * 0.95).toInt() - put(Gcm.LEARNT_OTHER, newInterval) + val newInterval = (learntOtherInterval * 0.95).toInt() + put(Gcm.LEARNT_OTHER, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL))) } } } @@ -136,28 +152,32 @@ data class GcmPrefs( when (pref) { PREF_NETWORK_MOBILE, PREF_NETWORK_ROAMING -> { if (time > learntMobileInterval / 4 * 3) { + val newInterval = (learntMobileInterval * 1.02).toInt() setSettings(context, Gcm.getContentUri(context)) { - put(Gcm.LEARNT_MOBILE, SettingsProvider.INTERVAL) + put(Gcm.LEARNT_MOBILE, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL))) } } } PREF_NETWORK_WIFI -> { if (time > learntWifiInterval / 4 * 3) { + val newInterval = (learntWifiInterval * 1.02).toInt() setSettings(context, Gcm.getContentUri(context)) { - put(Gcm.LEARNT_WIFI, SettingsProvider.INTERVAL) + put(Gcm.LEARNT_WIFI, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL))) } } } else -> { if (time > learntOtherInterval / 4 * 3) { + val newInterval = (learntOtherInterval * 1.02).toInt() setSettings(context, Gcm.getContentUri(context)) { - put(Gcm.LEARNT_OTHER, SettingsProvider.INTERVAL) + put(Gcm.LEARNT_OTHER, max(MIN_INTERVAL, min(newInterval, MAX_INTERVAL))) } } } } } + @Suppress("DEPRECATION") fun isEnabledFor(info: NetworkInfo?): Boolean { return isEnabled && info != null && getHeartbeatMsFor(info) >= 0 } @@ -168,5 +188,4 @@ data class GcmPrefs( put(Gcm.LAST_PERSISTENT_ID, newId) } } - } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt index d3e519bd..8d3b9a78 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt @@ -16,7 +16,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope -import org.microg.gms.checkin.CheckinPrefs +import org.microg.gms.checkin.CheckinPreferences import org.microg.gms.checkin.CheckinService import org.microg.gms.checkin.LastCheckinInfo import org.microg.gms.common.ForegroundServiceContext @@ -29,7 +29,7 @@ import kotlin.coroutines.suspendCoroutine private const val TAG = "GmsGcmRegister" private suspend fun ensureCheckinIsUpToDate(context: Context) { - if (!CheckinPrefs.isEnabled(context)) throw RuntimeException("Checkin disabled") + if (!CheckinPreferences.isEnabled(context)) throw RuntimeException("Checkin disabled") val lastCheckin = LastCheckinInfo.read(context).lastCheckin if (lastCheckin < System.currentTimeMillis() - CheckinService.MAX_VALID_CHECKIN_AGE) { val resultData: Bundle = suspendCoroutine { continuation -> @@ -51,7 +51,7 @@ private suspend fun ensureCheckinIsUpToDate(context: Context) { } } -private suspend fun ensureAppRegistrationAllowed(context: Context, database: GcmDatabase, packageName: String) { +private fun ensureAppRegistrationAllowed(context: Context, database: GcmDatabase, packageName: String) { if (!GcmPrefs.get(context).isEnabled) throw RuntimeException("GCM disabled") val app = database.getApp(packageName) if (app?.allowRegister == false) { @@ -65,7 +65,7 @@ suspend fun completeRegisterRequest(context: Context, database: GcmDatabase, req private val Intent.requestId: String? get() { - val kidString = getStringExtra(GcmConstants.EXTRA_KID) ?: return null + val kidString = getStringExtra(EXTRA_KID) ?: return null if (kidString.startsWith("|")) { val kid = kidString.split("\\|".toRegex()).toTypedArray() if (kid.size >= 3 && "ID" == kid[1]) { @@ -245,7 +245,7 @@ internal class PushRegisterHandler(private val context: Context, private val dat get() { val intent = Intent() intent.setPackage("com.google.example.invalidpackage") - return PendingIntent.getBroadcast(context, 0, intent, 0) + return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) } override fun handleMessage(msg: Message) { diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/AppHeadingPreference.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/AppHeadingPreference.kt new file mode 100644 index 00000000..c3cf6185 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/AppHeadingPreference.kt @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package org.microg.gms.ui +import android.content.Context +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.net.Uri +import android.provider.Settings +import android.util.AttributeSet +import android.util.Log +import android.view.View +import android.widget.ImageView +import androidx.appcompat.content.res.AppCompatResources +import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder +import org.microg.gms.base.core.R +class AppHeadingPreference : AppPreference, Preference.OnPreferenceClickListener { + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context) : super(context) + init { + layoutResource = R.layout.preference_app_heading + onPreferenceClickListener = this + } + override fun onPreferenceClick(preference: Preference): Boolean { + if (packageName != null) { + val intent = Intent() + intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + val uri: Uri = Uri.fromParts("package", packageName, null) + intent.data = uri + try { + context.startActivity(intent) + } catch (e: Exception) { + Log.w(TAG, "Failed to launch app", e) + } + return true + } else { + return false + } + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/AppIconPreference.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/AppIconPreference.kt index ef30f876..1570c691 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/AppIconPreference.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/AppIconPreference.kt @@ -11,10 +11,10 @@ import android.util.DisplayMetrics import android.widget.ImageView import androidx.preference.Preference import androidx.preference.PreferenceViewHolder +import android.content.pm.ApplicationInfo -class AppIconPreference : Preference { - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super( - context, attrs, defStyleAttr, defStyleRes) +class AppIconPreference : AppPreference { + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context) : super(context) diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/AppPreference.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/AppPreference.kt new file mode 100644 index 00000000..27626231 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/AppPreference.kt @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package org.microg.gms.ui +import android.content.Context +import android.content.pm.ApplicationInfo +import android.util.AttributeSet +import androidx.appcompat.content.res.AppCompatResources +import androidx.preference.Preference +abstract class AppPreference : Preference { + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context) : super(context) + init { + isPersistent = false + } + private var packageNameField: String? = null + var applicationInfo: ApplicationInfo? + get() = context.packageManager.getApplicationInfoIfExists(packageNameField) + set(value) { + if (value == null && packageNameField != null) { + title = null + icon = null + } else if (value != null) { + val pm = context.packageManager + title = value.loadLabel(pm) ?: value.packageName + icon = value.loadIcon(pm) ?: AppCompatResources.getDrawable(context, android.R.mipmap.sym_def_app_icon) + } + packageNameField = value?.packageName + } + var packageName: String? + get() = packageNameField + set(value) { + if (value == null && packageNameField != null) { + title = null + icon = null + } else if (value != null) { + val pm = context.packageManager + val applicationInfo = pm.getApplicationInfoIfExists(value) + title = applicationInfo?.loadLabel(pm)?.toString() ?: value + icon = applicationInfo?.loadIcon(pm) ?: AppCompatResources.getDrawable(context, android.R.mipmap.sym_def_app_icon) + } + packageNameField = value + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt index 17887575..8c668529 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt @@ -5,51 +5,163 @@ package org.microg.gms.ui + +import android.annotation.SuppressLint +import android.net.Uri import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment +import android.os.Handler +import android.text.format.DateUtils +import android.util.Log +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.lifecycle.lifecycleScope +import androidx.preference.ListPreference +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceFragmentCompat import com.mgoogle.android.gms.R -import com.mgoogle.android.gms.databinding.DeviceRegistrationFragmentBinding -import org.microg.gms.checkin.ServiceInfo +import org.microg.gms.checkin.CheckinPreferences import org.microg.gms.checkin.getCheckinServiceInfo -import org.microg.gms.checkin.setCheckinServiceConfiguration +import org.microg.gms.profile.ProfileManager +import org.microg.gms.profile.ProfileManager.PROFILE_AUTO +import org.microg.gms.profile.ProfileManager.PROFILE_NATIVE +import org.microg.gms.profile.ProfileManager.PROFILE_REAL +import org.microg.gms.profile.ProfileManager.PROFILE_SYSTEM +import org.microg.gms.profile.ProfileManager.PROFILE_USER +import java.io.File +import java.io.FileOutputStream -class DeviceRegistrationFragment : Fragment(R.layout.device_registration_fragment) { - private lateinit var binding: DeviceRegistrationFragmentBinding +class DeviceRegistrationFragment : PreferenceFragmentCompat() { + private lateinit var switchBarPreference: SwitchBarPreference + private lateinit var deviceProfile: ListPreference + private lateinit var importProfile: Preference + private lateinit var serial: Preference + private lateinit var statusCategory: PreferenceCategory + private lateinit var status: Preference + private lateinit var androidId: Preference + private val handler = Handler() + private val updateRunnable = Runnable { updateStatus() } + private lateinit var profileFileImport: ActivityResultLauncher - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - binding = DeviceRegistrationFragmentBinding.inflate(inflater, container, false) - binding.switchBarCallback = object : PreferenceSwitchBarCallback { - override fun onChecked(newStatus: Boolean) { - setEnabled(newStatus) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + profileFileImport = registerForActivityResult(ActivityResultContracts.GetContent(), this::onFileSelected) + } + private fun onFileSelected(uri: Uri?) { + if (uri == null) return + try { + val context = requireContext() + val file = File.createTempFile("profile_", ".xml", context.cacheDir) + context.contentResolver.openInputStream(uri)?.use { inputStream -> + FileOutputStream(file).use { inputStream.copyTo(it) } + } + val success = ProfileManager.importUserProfile(context, file) + file.delete() + if (success && ProfileManager.isAutoProfile(context, PROFILE_USER)) { + ProfileManager.setProfile(context, PROFILE_USER) + } + updateStatus() + } catch (e: Exception) { + Log.w(TAG, e) + } + } + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_device_registration) + } + @SuppressLint("RestrictedApi") + override fun onBindPreferences() { + switchBarPreference = preferenceScreen.findPreference("pref_checkin_enabled") ?: switchBarPreference + deviceProfile = preferenceScreen.findPreference("pref_device_profile") ?: deviceProfile + importProfile = preferenceScreen.findPreference("pref_device_profile_import") ?: importProfile + serial = preferenceScreen.findPreference("pref_device_serial") ?: serial + statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory + status = preferenceScreen.findPreference("pref_device_registration_status") ?: status + androidId = preferenceScreen.findPreference("pref_device_registration_android_id") ?: androidId + deviceProfile.setOnPreferenceChangeListener { _, newValue -> + ProfileManager.setProfile(requireContext(), newValue as String? ?: PROFILE_AUTO) + updateStatus() + true + } + importProfile.setOnPreferenceClickListener { + profileFileImport.launch("text/xml") + true + } + switchBarPreference.setOnPreferenceChangeListener { _, newValue -> + val newStatus = newValue as Boolean + CheckinPreferences.setEnabled(requireContext(), newStatus) + true + } + } + private fun configureProfilePreference() { + val context = requireContext() + val configuredProfile = ProfileManager.getConfiguredProfile(context) + val autoProfile = ProfileManager.getAutoProfile(context) + val autoProfileName = when (autoProfile) { + PROFILE_NATIVE -> getString(R.string.profile_name_native) + PROFILE_REAL -> getString(R.string.profile_name_real) + else -> ProfileManager.getProfileName(context, autoProfile) + } + val profiles = + mutableListOf(PROFILE_AUTO, PROFILE_NATIVE, PROFILE_REAL) + val profileNames = mutableListOf(getString(R.string.profile_name_auto, autoProfileName), getString(R.string.profile_name_native), getString(R.string.profile_name_real)) + if (ProfileManager.hasProfile(context, PROFILE_SYSTEM)) { + profiles.add(PROFILE_SYSTEM) + profileNames.add(getString(R.string.profile_name_system, ProfileManager.getProfileName(context, PROFILE_SYSTEM))) + } + if (ProfileManager.hasProfile(context, PROFILE_USER)) { + profiles.add(PROFILE_USER) + profileNames.add(getString(R.string.profile_name_user, ProfileManager.getProfileName(context, PROFILE_USER))) + } + for (profile in R.xml::class.java.declaredFields.map { it.name } + .filter { it.startsWith("profile_") } + .map { it.substring(8) } + .sorted()) { + val profileName = ProfileManager.getProfileName(context, profile) + if (profileName != null) { + profiles.add(profile) + profileNames.add(profileName) } } - return binding.root - } - - fun setEnabled(newStatus: Boolean) { - val appContext = requireContext().applicationContext - lifecycleScope.launchWhenResumed { - val info = getCheckinServiceInfo(appContext) - val newConfiguration = info.configuration.copy(enabled = newStatus) - setCheckinServiceConfiguration(appContext, newConfiguration) - displayServiceInfo(info.copy(configuration = newConfiguration)) - } - } - - private fun displayServiceInfo(serviceInfo: ServiceInfo) { - binding.checkinEnabled = serviceInfo.configuration.enabled + deviceProfile.entryValues = profiles.toTypedArray() + deviceProfile.entries = profileNames.toTypedArray() + deviceProfile.value = configuredProfile + deviceProfile.summary = + profiles.indexOf(configuredProfile).takeIf { it >= 0 }?.let { profileNames[it] } ?: "Unknown" } override fun onResume() { super.onResume() + switchBarPreference.isChecked = CheckinPreferences.isEnabled(requireContext()) + updateStatus() + } + override fun onPause() { + super.onPause() + handler.removeCallbacks(updateRunnable) + } + private fun updateStatus() { + handler.removeCallbacks(updateRunnable) + handler.postDelayed(updateRunnable, UPDATE_INTERVAL) val appContext = requireContext().applicationContext lifecycleScope.launchWhenResumed { - displayServiceInfo(getCheckinServiceInfo(appContext)) + configureProfilePreference() + serial.summary = ProfileManager.getSerial(appContext) + val serviceInfo = getCheckinServiceInfo(appContext) + statusCategory.isVisible = serviceInfo.configuration.enabled + if (serviceInfo.lastCheckin > 0) { + status.summary = getString( + R.string.checkin_last_registration, + DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0) + ) + androidId.isVisible = true + androidId.summary = serviceInfo.androidId.toString(16) + } else { + status.summary = getString(R.string.checkin_not_registered) + androidId.isVisible = false + } } } + companion object { + private const val UPDATE_INTERVAL = 1000L + } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt deleted file mode 100755 index a0b4ad13..00000000 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.microg.gms.ui - -import android.annotation.SuppressLint -import android.net.Uri -import android.os.Bundle -import android.os.Handler -import android.text.format.DateUtils -import android.util.Log -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.contract.ActivityResultContracts -import androidx.lifecycle.lifecycleScope -import androidx.preference.ListPreference -import androidx.preference.Preference -import androidx.preference.PreferenceCategory -import androidx.preference.PreferenceFragmentCompat -import com.mgoogle.android.gms.R -import org.microg.gms.checkin.getCheckinServiceInfo -import org.microg.gms.profile.ProfileManager -import org.microg.gms.profile.ProfileManager.PROFILE_AUTO -import org.microg.gms.profile.ProfileManager.PROFILE_NATIVE -import org.microg.gms.profile.ProfileManager.PROFILE_REAL -import org.microg.gms.profile.ProfileManager.PROFILE_SYSTEM -import org.microg.gms.profile.ProfileManager.PROFILE_USER -import java.io.File -import java.io.FileOutputStream - -@Suppress("Warnings") -class DeviceRegistrationPreferencesFragment : PreferenceFragmentCompat() { - private lateinit var deviceProfile: ListPreference - private lateinit var importProfile: Preference - private lateinit var serial: Preference - private lateinit var statusCategory: PreferenceCategory - private lateinit var status: Preference - private lateinit var androidId: Preference - private val handler = Handler() - private val updateRunnable = Runnable { updateStatus() } - private lateinit var profileFileImport: ActivityResultLauncher - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - profileFileImport = registerForActivityResult(ActivityResultContracts.GetContent(), this::onFileSelected) - } - - private fun onFileSelected(uri: Uri?) { - if (uri == null) return - try { - val context = requireContext() - val file = File.createTempFile("profile_", ".xml", context.cacheDir) - context.contentResolver.openInputStream(uri)?.use { inputStream -> - FileOutputStream(file).use { inputStream.copyTo(it) } - } - val success = ProfileManager.importUserProfile(context, file) - file.delete() - if (success && ProfileManager.isAutoProfile(context, PROFILE_USER)) { - ProfileManager.setProfile(context, PROFILE_USER) - } - updateStatus() - } catch (e: Exception) { - Log.w(TAG, e) - } - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.preferences_device_registration) - } - - @SuppressLint("RestrictedApi") - override fun onBindPreferences() { - deviceProfile = preferenceScreen.findPreference("pref_device_profile") ?: deviceProfile - importProfile = preferenceScreen.findPreference("pref_device_profile_import") ?: importProfile - serial = preferenceScreen.findPreference("pref_device_serial") ?: serial - statusCategory = preferenceScreen.findPreference("prefcat_device_registration_status") ?: statusCategory - status = preferenceScreen.findPreference("pref_device_registration_status") ?: status - androidId = preferenceScreen.findPreference("pref_device_registration_android_id") ?: androidId - - deviceProfile.setOnPreferenceChangeListener { _, newValue -> - ProfileManager.setProfile(requireContext(), newValue as String? ?: PROFILE_AUTO) - updateStatus() - true - } - importProfile.setOnPreferenceClickListener { - profileFileImport.launch("text/xml") - true - } - } - - private fun configureProfilePreference() { - val context = requireContext() - val configuredProfile = ProfileManager.getConfiguredProfile(context) - val autoProfileName = when (val autoProfile = ProfileManager.getAutoProfile(context)) { - PROFILE_NATIVE -> getString(R.string.profile_name_native) - PROFILE_REAL -> getString(R.string.profile_name_real) - else -> ProfileManager.getProfileName(context, autoProfile) - } - val profiles = - mutableListOf(PROFILE_AUTO, PROFILE_NATIVE, PROFILE_REAL) - val profileNames = mutableListOf(getString(R.string.profile_name_auto, autoProfileName), getString(R.string.profile_name_native), getString(R.string.profile_name_real)) - if (ProfileManager.hasProfile(context, PROFILE_SYSTEM)) { - profiles.add(PROFILE_SYSTEM) - profileNames.add(getString(R.string.profile_name_system, ProfileManager.getProfileName(context, PROFILE_SYSTEM))) - } - if (ProfileManager.hasProfile(context, PROFILE_USER)) { - profiles.add(PROFILE_USER) - profileNames.add(getString(R.string.profile_name_user, ProfileManager.getProfileName(context, PROFILE_USER))) - } - for (profile in R.xml::class.java.declaredFields.map { it.name } - .filter { it.startsWith("profile_") } - .map { it.substring(8) } - .sorted()) { - val profileName = ProfileManager.getProfileName(context, profile) - if (profileName != null) { - profiles.add(profile) - profileNames.add(profileName) - } - } - deviceProfile.entryValues = profiles.toTypedArray() - deviceProfile.entries = profileNames.toTypedArray() - deviceProfile.value = configuredProfile - deviceProfile.summary = - profiles.indexOf(configuredProfile).takeIf { it >= 0 }?.let { profileNames[it] } ?: "Unknown" - } - - override fun onResume() { - super.onResume() - updateStatus() - } - - override fun onPause() { - super.onPause() - handler.removeCallbacks(updateRunnable) - } - - private fun updateStatus() { - handler.removeCallbacks(updateRunnable) - handler.postDelayed(updateRunnable, UPDATE_INTERVAL) - val appContext = requireContext().applicationContext - lifecycleScope.launchWhenResumed { - configureProfilePreference() - serial.summary = ProfileManager.getSerial(appContext) - val serviceInfo = getCheckinServiceInfo(appContext) - statusCategory.isVisible = serviceInfo.configuration.enabled - if (serviceInfo.lastCheckin > 0) { - status.summary = getString( - R.string.checkin_last_registration, - DateUtils.getRelativeTimeSpanString(serviceInfo.lastCheckin, System.currentTimeMillis(), 0) - ) - androidId.isVisible = true - androidId.summary = serviceInfo.androidId.toString(16) - } else { - status.summary = getString(R.string.checkin_not_registered) - androidId.isVisible = false - } - } - } - - companion object { - private const val UPDATE_INTERVAL = 1000L - } -} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PreferenceSwitchBarCallback.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PreferenceSwitchBarCallback.kt deleted file mode 100755 index b66f1119..00000000 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PreferenceSwitchBarCallback.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.microg.gms.ui - -interface PreferenceSwitchBarCallback { - fun onChecked(newStatus: Boolean) -} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAdvancedFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAdvancedFragment.kt index d3cef96f..55b730f6 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAdvancedFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAdvancedFragment.kt @@ -14,7 +14,7 @@ import com.mgoogle.android.gms.R import org.microg.gms.gcm.GcmPrefs import org.microg.gms.gcm.getGcmServiceInfo import org.microg.gms.gcm.setGcmServiceConfiguration -import org.microg.mgms.settings.SettingsProvider +import org.microg.gms.settings.SettingsProvider class PushNotificationAdvancedFragment : PreferenceFragmentCompat() { private lateinit var networkMobile: ListPreference @@ -37,7 +37,7 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() { val appContext = requireContext().applicationContext lifecycleScope.launchWhenResumed { (newValue as? String)?.toIntOrNull()?.let { - setGcmServiceConfiguration(requireContext(), getGcmServiceInfo(appContext).configuration.copy(mobile = it)) + setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(mobile = it)) } updateContent() } @@ -47,7 +47,7 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() { val appContext = requireContext().applicationContext lifecycleScope.launchWhenResumed { (newValue as? String)?.toIntOrNull()?.let { - setGcmServiceConfiguration(appContext, getGcmServiceInfo(requireContext()).configuration.copy(wifi = it)) + setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(wifi = it)) } updateContent() } @@ -57,7 +57,7 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() { val appContext = requireContext().applicationContext lifecycleScope.launchWhenResumed { (newValue as? String)?.toIntOrNull()?.let { - setGcmServiceConfiguration(appContext, getGcmServiceInfo(requireContext()).configuration.copy(roaming = it)) + setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(roaming = it)) } updateContent() } @@ -67,7 +67,7 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() { val appContext = requireContext().applicationContext lifecycleScope.launchWhenResumed { (newValue as? String)?.toIntOrNull()?.let { - setGcmServiceConfiguration(appContext, getGcmServiceInfo(requireContext()).configuration.copy(other = it)) + setGcmServiceConfiguration(appContext, getGcmServiceInfo(appContext).configuration.copy(other = it)) } updateContent() } @@ -98,13 +98,13 @@ class PushNotificationAdvancedFragment : PreferenceFragmentCompat() { private fun getSummaryString(value: Int, learnt: Int): String = when (value) { -1 -> getString(R.string.service_status_disabled_short) 0 -> getString(R.string.service_status_enabled_short) + " / " + getString(R.string.gcm_status_pref_default) + ": " + getHeartbeatString(learnt) - else -> getString(R.string.service_status_enabled_short) + " / " + getString(R.string.gcm_status_pref_manual) + ": " + getHeartbeatString(value * SettingsProvider.INTERVAL) + else -> getString(R.string.service_status_enabled_short) + " / " + getString(R.string.gcm_status_pref_manual) + ": " + getHeartbeatString(value * 60000) } private fun getHeartbeatString(heartbeatMs: Int): String { return if (heartbeatMs < 120000) { (heartbeatMs / 1000).toString() + " " + getString(R.string.gcm_status_pref_sec) - } else (heartbeatMs / SettingsProvider.INTERVAL).toString() + " " + getString(R.string.gcm_status_pref_min) + } else (heartbeatMs / 60000).toString() + " " + getString(R.string.gcm_status_pref_min) } companion object { diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAllAppsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAllAppsFragment.kt index da3210e4..392e14d6 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAllAppsFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAllAppsFragment.kt @@ -56,16 +56,12 @@ class PushNotificationAllAppsFragment : PreferenceFragmentCompat() { lifecycleScope.launchWhenResumed { val apps = withContext(Dispatchers.IO) { val res = database.appList.map { app -> - app to context.packageManager.getApplicationInfoIfExists(app.packageName) - }.map { (app, applicationInfo) -> val pref = AppIconPreference(context) - pref.title = applicationInfo?.loadLabel(context.packageManager) ?: app.packageName + pref.packageName = app.packageName pref.summary = when { app.lastMessageTimestamp > 0 -> getString(R.string.gcm_last_message_at, DateUtils.getRelativeTimeSpanString(app.lastMessageTimestamp)) else -> null } - pref.icon = applicationInfo?.loadIcon(context.packageManager) - ?: AppCompatResources.getDrawable(context, android.R.mipmap.sym_def_app_icon) pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { findNavController().navigate(requireContext(), R.id.openGcmAppDetailsFromAll, bundleOf( "package" to app.packageName diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppFragment.kt index f83a5e5d..780e85e1 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppFragment.kt @@ -5,58 +5,135 @@ package org.microg.gms.ui -import android.content.Intent -import android.net.Uri +import android.annotation.SuppressLint import android.os.Bundle -import android.provider.Settings -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.content.res.AppCompatResources -import androidx.fragment.app.Fragment +import android.text.format.DateUtils +import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.TwoStatePreference import com.mgoogle.android.gms.R -import com.mgoogle.android.gms.databinding.PushNotificationAppFragmentBinding +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.microg.gms.gcm.GcmDatabase +import org.microg.gms.gcm.PushRegisterManager +class PushNotificationAppFragment : PreferenceFragmentCompat() { + private lateinit var appHeadingPreference: AppHeadingPreference + private lateinit var wakeForDelivery: TwoStatePreference + private lateinit var allowRegister: TwoStatePreference + private lateinit var status: Preference + private lateinit var unregister: Preference + private lateinit var unregisterCat: PreferenceCategory -class PushNotificationAppFragment : Fragment(R.layout.push_notification_fragment) { - lateinit var binding: PushNotificationAppFragmentBinding - val packageName: String? + private lateinit var database: GcmDatabase + private val packageName: String? get() = arguments?.getString("package") - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - binding = PushNotificationAppFragmentBinding.inflate(inflater, container, false) - binding.callbacks = object : PushNotificationAppFragmentCallbacks { - override fun onAppClicked() { - val intent = Intent() - intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS - val uri: Uri = Uri.fromParts("package", packageName, null) - intent.data = uri - try { - context!!.startActivity(intent) - } catch (e: Exception) { - Log.w(TAG, "Failed to launch app", e) + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_push_notifications_app) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + database = GcmDatabase(context) + } + + @SuppressLint("RestrictedApi") + override fun onBindPreferences() { + appHeadingPreference = preferenceScreen.findPreference("pref_push_app_heading") ?: appHeadingPreference + wakeForDelivery = preferenceScreen.findPreference("pref_push_app_wake_for_delivery") ?: wakeForDelivery + allowRegister = preferenceScreen.findPreference("pref_push_app_allow_register") ?: allowRegister + unregister = preferenceScreen.findPreference("pref_push_app_unregister") ?: unregister + unregisterCat = preferenceScreen.findPreference("prefcat_push_app_unregister") ?: unregisterCat + status = preferenceScreen.findPreference("pref_push_app_status") ?: status + wakeForDelivery.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> + database.setAppWakeForDelivery(packageName, newValue as Boolean) + database.close() + true + } + allowRegister.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> + val enabled = newValue as? Boolean ?: return@OnPreferenceChangeListener false + if (!enabled) { + val registrations = packageName?.let { database.getRegistrationsByApp(it) } ?: emptyList() + if (registrations.isNotEmpty()) { + showUnregisterConfirm(R.string.gcm_unregister_after_deny_message) } } + database.setAppAllowRegister(packageName, enabled) + database.close() + true + } + unregister.onPreferenceClickListener = Preference.OnPreferenceClickListener { + showUnregisterConfirm(R.string.gcm_unregister_confirm_message) + true + } + } + + + private fun showUnregisterConfirm(unregisterConfirmDesc: Int) { + val pm = requireContext().packageManager + val applicationInfo = pm.getApplicationInfoIfExists(packageName) + AlertDialog.Builder(requireContext()) + .setTitle(getString(R.string.gcm_unregister_confirm_title, applicationInfo?.loadLabel(pm) + ?: packageName)) + .setMessage(unregisterConfirmDesc) + .setPositiveButton(android.R.string.yes) { _, _ -> unregister() } + .setNegativeButton(android.R.string.no) { _, _ -> }.show() + } + + private fun unregister() { + lifecycleScope.launchWhenResumed { + withContext(Dispatchers.IO) { + for (registration in database.getRegistrationsByApp(packageName)) { + PushRegisterManager.unregister(context, registration.packageName, registration.signature, null, null) + } + } + updateDetails() } - childFragmentManager.findFragmentById(R.id.sub_preferences)?.arguments = arguments - return binding.root } override fun onResume() { super.onResume() - val context = requireContext() + updateDetails() + } + + private fun updateDetails() { lifecycleScope.launchWhenResumed { - val pm = context.packageManager - val applicationInfo = pm.getApplicationInfoIfExists(packageName) - binding.appName = applicationInfo?.loadLabel(pm)?.toString() ?: packageName - binding.appIcon = applicationInfo?.loadIcon(pm) - ?: AppCompatResources.getDrawable(context, android.R.mipmap.sym_def_app_icon) + appHeadingPreference.packageName = packageName + val app = packageName?.let { database.getApp(it) } + wakeForDelivery.isChecked = app?.wakeForDelivery ?: true + allowRegister.isChecked = app?.allowRegister ?: true + val registrations = packageName?.let { database.getRegistrationsByApp(it) } ?: emptyList() + unregisterCat.isVisible = registrations.isNotEmpty() + + val sb = StringBuilder() + if ((app?.totalMessageCount ?: 0L) == 0L) { + sb.append(getString(R.string.gcm_no_message_yet)) + } else { + sb.append(getString(R.string.gcm_messages_counter, app?.totalMessageCount, app?.totalMessageBytes)) + if (app?.lastMessageTimestamp != 0L) { + sb.append("\n").append(getString(R.string.gcm_last_message_at, DateUtils.getRelativeDateTimeString(context, app?.lastMessageTimestamp ?: 0L, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME))) + } + } + for (registration in registrations) { + sb.append("\n") + if (registration.timestamp == 0L) { + sb.append(getString(R.string.gcm_registered)) + } else { + sb.append(getString(R.string.gcm_registered_since, DateUtils.getRelativeDateTimeString(context, registration.timestamp, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME))) + } + } + status.summary = sb.toString() + + database.close() } } -} -interface PushNotificationAppFragmentCallbacks { - fun onAppClicked() -} + override fun onPause() { + super.onPause() + database.close() + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppPreferencesFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppPreferencesFragment.kt deleted file mode 100755 index 7106e506..00000000 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppPreferencesFragment.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ -@file:Suppress("DEPRECATION") - -package org.microg.gms.ui - -import android.annotation.SuppressLint -import android.os.Bundle -import android.text.format.DateUtils -import androidx.appcompat.app.AlertDialog -import androidx.lifecycle.lifecycleScope -import androidx.preference.Preference -import androidx.preference.PreferenceCategory -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.TwoStatePreference -import com.mgoogle.android.gms.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.microg.gms.gcm.GcmDatabase -import org.microg.gms.gcm.PushRegisterManager - -class PushNotificationAppPreferencesFragment : PreferenceFragmentCompat() { - private lateinit var wakeForDelivery: TwoStatePreference - private lateinit var allowRegister: TwoStatePreference - private lateinit var status: Preference - private lateinit var unregister: Preference - private lateinit var unregisterCat: PreferenceCategory - - private lateinit var database: GcmDatabase - private val packageName: String? - get() = arguments?.getString("package") - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.preferences_push_notifications_app) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - database = GcmDatabase(context) - } - - @SuppressLint("RestrictedApi") - override fun onBindPreferences() { - wakeForDelivery = preferenceScreen.findPreference("pref_push_app_wake_for_delivery") ?: wakeForDelivery - allowRegister = preferenceScreen.findPreference("pref_push_app_allow_register") ?: allowRegister - unregister = preferenceScreen.findPreference("pref_push_app_unregister") ?: unregister - unregisterCat = preferenceScreen.findPreference("prefcat_push_app_unregister") ?: unregisterCat - status = preferenceScreen.findPreference("pref_push_app_status") ?: status - wakeForDelivery.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - database.setAppWakeForDelivery(packageName, newValue as Boolean) - database.close() - true - } - allowRegister.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - val enabled = newValue as? Boolean ?: return@OnPreferenceChangeListener false - if (!enabled) { - val registrations = packageName?.let { database.getRegistrationsByApp(it) } ?: emptyList() - if (registrations.isNotEmpty()) { - showUnregisterConfirm(R.string.gcm_unregister_after_deny_message) - } - } - database.setAppAllowRegister(packageName, enabled) - database.close() - true - } - unregister.onPreferenceClickListener = Preference.OnPreferenceClickListener { - showUnregisterConfirm(R.string.gcm_unregister_confirm_message) - true - } - } - - - private fun showUnregisterConfirm(unregisterConfirmDesc: Int) { - val pm = requireContext().packageManager - val applicationInfo = pm.getApplicationInfoIfExists(packageName) - AlertDialog.Builder(requireContext()) - .setTitle(getString(R.string.gcm_unregister_confirm_title, applicationInfo?.loadLabel(pm) - ?: packageName)) - .setMessage(unregisterConfirmDesc) - .setPositiveButton(android.R.string.yes) { _, _ -> unregister() } - .setNegativeButton(android.R.string.no) { _, _ -> }.show() - } - - private fun unregister() { - lifecycleScope.launchWhenResumed { - withContext(Dispatchers.IO) { - for (registration in database.getRegistrationsByApp(packageName)) { - PushRegisterManager.unregister(context, registration.packageName, registration.signature, null, null) - } - } - updateDetails() - } - } - - override fun onResume() { - super.onResume() - updateDetails() - } - - private fun updateDetails() { - lifecycleScope.launchWhenResumed { - val app = packageName?.let { database.getApp(it) } - wakeForDelivery.isChecked = app?.wakeForDelivery ?: true - allowRegister.isChecked = app?.allowRegister ?: true - val registrations = packageName?.let { database.getRegistrationsByApp(it) } ?: emptyList() - unregisterCat.isVisible = registrations.isNotEmpty() - - val sb = StringBuilder() - if ((app?.totalMessageCount ?: 0L) == 0L) { - sb.append(getString(R.string.gcm_no_message_yet)) - } else { - sb.append(getString(R.string.gcm_messages_counter, app?.totalMessageCount, app?.totalMessageBytes)) - if (app?.lastMessageTimestamp != 0L) { - sb.append("\n").append(getString(R.string.gcm_last_message_at, DateUtils.getRelativeDateTimeString(context, app?.lastMessageTimestamp ?: 0L, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME))) - } - } - for (registration in registrations) { - sb.append("\n") - if (registration.timestamp == 0L) { - sb.append(getString(R.string.gcm_registered)) - } else { - sb.append(getString(R.string.gcm_registered_since, DateUtils.getRelativeDateTimeString(context, registration.timestamp, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME))) - } - } - status.summary = sb.toString() - - database.close() - } - } - - override fun onPause() { - super.onPause() - database.close() - } -} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt index cb5aaa18..d05afb3d 100755 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt @@ -2,61 +2,142 @@ * SPDX-FileCopyrightText: 2020, microG Project Team * SPDX-License-Identifier: Apache-2.0 */ + package org.microg.gms.ui +import android.annotation.SuppressLint import android.os.Bundle -import android.view.* -import androidx.fragment.app.Fragment +import android.os.Handler +import android.text.format.DateUtils +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import androidx.core.os.bundleOf import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceFragmentCompat import com.mgoogle.android.gms.R -import com.mgoogle.android.gms.databinding.PushNotificationFragmentBinding -import org.microg.gms.checkin.getCheckinServiceInfo -import org.microg.gms.gcm.ServiceInfo +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.microg.gms.checkin.CheckinPreferences +import org.microg.gms.gcm.GcmDatabase +import org.microg.gms.gcm.GcmPrefs import org.microg.gms.gcm.getGcmServiceInfo -import org.microg.gms.gcm.setGcmServiceConfiguration -@Suppress("Warnings") -class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) { - lateinit var binding: PushNotificationFragmentBinding +class PushNotificationFragment : PreferenceFragmentCompat() { + private lateinit var switchBarPreference: SwitchBarPreference + private lateinit var pushStatusCategory: PreferenceCategory + private lateinit var pushStatus: Preference + private lateinit var pushApps: PreferenceCategory + private lateinit var pushAppsAll: Preference + private lateinit var pushAppsNone: Preference + private lateinit var database: GcmDatabase + private val handler = Handler() + private val updateRunnable = Runnable { updateStatus() } - init { - setHasOptionsMenu(true) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + database = GcmDatabase(context) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - binding = PushNotificationFragmentBinding.inflate(inflater, container, false) - binding.switchBarCallback = object : PreferenceSwitchBarCallback { - override fun onChecked(newStatus: Boolean) { - setEnabled(newStatus) - } + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_push_notifications) + } + + @SuppressLint("RestrictedApi") + override fun onBindPreferences() { + switchBarPreference = preferenceScreen.findPreference("pref_push_enabled") ?: switchBarPreference + pushStatusCategory = preferenceScreen.findPreference("prefcat_push_status") ?: pushStatusCategory + pushStatus = preferenceScreen.findPreference("pref_push_status") ?: pushStatus + pushApps = preferenceScreen.findPreference("prefcat_push_apps") ?: pushApps + pushAppsAll = preferenceScreen.findPreference("pref_push_apps_all") ?: pushAppsAll + pushAppsNone = preferenceScreen.findPreference("pref_push_apps_none") ?: pushAppsNone + pushAppsAll.setOnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openAllGcmApps) + true } - return binding.root - } - - fun setEnabled(newStatus: Boolean) { - val appContext = requireContext().applicationContext - lifecycleScope.launchWhenResumed { - val info = getGcmServiceInfo(appContext) - val newConfiguration = info.configuration.copy(enabled = newStatus) - setGcmServiceConfiguration(appContext, newConfiguration) - displayServiceInfo(info.copy(configuration = newConfiguration)) + switchBarPreference.setOnPreferenceChangeListener { _, newValue -> + val newStatus = newValue as Boolean + GcmPrefs.setEnabled(requireContext(), newStatus) + true } } - private fun displayServiceInfo(serviceInfo: ServiceInfo) { - binding.gcmEnabled = serviceInfo.configuration.enabled - } - override fun onResume() { super.onResume() + + switchBarPreference.isEnabled = CheckinPreferences.isEnabled(requireContext()) + switchBarPreference.isChecked = GcmPrefs.get(requireContext()).isEnabled + + updateStatus() + updateContent() + } + + override fun onPause() { + super.onPause() + database.close() + handler.removeCallbacks(updateRunnable) + } + + private fun updateStatus() { + handler.postDelayed(updateRunnable, UPDATE_INTERVAL) val appContext = requireContext().applicationContext - lifecycleScope.launchWhenResumed { - displayServiceInfo(getGcmServiceInfo(appContext)) - binding.checkinEnabled = getCheckinServiceInfo(appContext).configuration.enabled + lifecycleScope.launchWhenStarted { + val statusInfo = getGcmServiceInfo(appContext) + switchBarPreference.isChecked = statusInfo.configuration.enabled + pushStatusCategory.isVisible = statusInfo != null && statusInfo.configuration.enabled + pushStatus.summary = if (statusInfo != null && statusInfo.connected) { + appContext.getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(statusInfo.startTimestamp, System.currentTimeMillis(), 0)) + } else { + appContext.getString(R.string.gcm_network_state_disconnected) + } } } + private fun updateContent() { + lifecycleScope.launchWhenResumed { + val context = requireContext() + val (apps, showAll) = withContext(Dispatchers.IO) { + val apps = database.appList.sortedByDescending { it.lastMessageTimestamp } + val res = apps.map { app -> + app to context.packageManager.getApplicationInfoIfExists(app.packageName) + }.mapNotNull { (app, info) -> + if (info == null) null else app to info + }.take(3).mapIndexed { idx, (app, applicationInfo) -> + val pref = AppIconPreference(context) + pref.order = idx + pref.applicationInfo = applicationInfo + pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openGcmAppDetails, bundleOf( + "package" to app.packageName + )) + true + } + pref.key = "pref_push_app_" + app.packageName + pref + }.let { it to (it.size < apps.size) } + database.close() + res + } + pushAppsAll.isVisible = showAll + pushApps.removeAll() + for (app in apps) { + pushApps.addPreference(app) + } + if (showAll) { + pushApps.addPreference(pushAppsAll) + } else if (apps.isEmpty()) { + pushApps.addPreference(pushAppsNone) + } + } + } + + init { + setHasOptionsMenu(true) + } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { menu.add(0, MENU_ADVANCED, 0, R.string.menu_advanced) super.onCreateOptionsMenu(menu, inflater) @@ -73,7 +154,7 @@ class PushNotificationFragment : Fragment(R.layout.push_notification_fragment) { } companion object { + private const val UPDATE_INTERVAL = 1000L private const val MENU_ADVANCED = Menu.FIRST } -} - +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt deleted file mode 100755 index 1a5caae1..00000000 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ -@file:Suppress("DEPRECATION") - -package org.microg.gms.ui - -import android.annotation.SuppressLint -import android.os.Bundle -import android.os.Handler -import android.text.format.DateUtils -import androidx.core.os.bundleOf -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import androidx.preference.Preference -import androidx.preference.PreferenceCategory -import androidx.preference.PreferenceFragmentCompat -import com.mgoogle.android.gms.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.microg.gms.gcm.GcmDatabase -import org.microg.gms.gcm.getGcmServiceInfo - -class PushNotificationPreferencesFragment : PreferenceFragmentCompat() { - private lateinit var pushStatusCategory: PreferenceCategory - private lateinit var pushStatus: Preference - private lateinit var pushApps: PreferenceCategory - private lateinit var pushAppsAll: Preference - private lateinit var pushAppsNone: Preference - private lateinit var database: GcmDatabase - private val handler = Handler() - private val updateRunnable = Runnable { updateStatus() } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - database = GcmDatabase(context) - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.preferences_push_notifications) - } - - @SuppressLint("RestrictedApi") - override fun onBindPreferences() { - pushStatusCategory = preferenceScreen.findPreference("prefcat_push_status") ?: pushStatusCategory - pushStatus = preferenceScreen.findPreference("pref_push_status") ?: pushStatus - pushApps = preferenceScreen.findPreference("prefcat_push_apps") ?: pushApps - pushAppsAll = preferenceScreen.findPreference("pref_push_apps_all") ?: pushAppsAll - pushAppsNone = preferenceScreen.findPreference("pref_push_apps_none") ?: pushAppsNone - pushAppsAll.onPreferenceClickListener = Preference.OnPreferenceClickListener { - findNavController().navigate(requireContext(), R.id.openAllGcmApps) - true - } - } - - override fun onResume() { - super.onResume() - updateStatus() - updateContent() - } - - override fun onPause() { - super.onPause() - database.close() - handler.removeCallbacks(updateRunnable) - } - - private fun updateStatus() { - try { - handler.postDelayed(updateRunnable, UPDATE_INTERVAL) - val appContext = requireContext().applicationContext - lifecycleScope.launchWhenStarted { - val statusInfo = getGcmServiceInfo(appContext) - pushStatusCategory.isVisible = statusInfo != null && statusInfo.configuration.enabled - pushStatus.summary = if (statusInfo.connected) { - appContext.getString(R.string.gcm_network_state_connected, DateUtils.getRelativeTimeSpanString(statusInfo.startTimestamp, System.currentTimeMillis(), 0)) - } else { - appContext.getString(R.string.gcm_network_state_disconnected) - } - } - } catch (e: Exception) {} - } - - private fun updateContent() { - val appContext = requireContext().applicationContext - val context = requireContext() - lifecycleScope.launchWhenResumed { - val (apps, showAll) = withContext(Dispatchers.IO) { - val apps = database.appList.sortedByDescending { it.lastMessageTimestamp } - val res = apps.map { app -> - app to appContext.packageManager.getApplicationInfoIfExists(app.packageName) - }.mapNotNull { (app, info) -> - if (info == null) null else app to info - }.take(3).mapIndexed { idx, (app, applicationInfo) -> - val pref = AppIconPreference(appContext) - pref.order = idx - pref.title = applicationInfo.loadLabel(appContext.packageManager) - pref.icon = applicationInfo.loadIcon(appContext.packageManager) - pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { - findNavController().navigate(context, R.id.openGcmAppDetails, bundleOf( - "package" to app.packageName - )) - true - } - pref.key = "pref_push_app_" + app.packageName - pref - }.let { it to (it.size < apps.size) } - database.close() - res - } - pushAppsAll.isVisible = showAll - pushApps.removeAll() - for (app in apps) { - pushApps.addPreference(app) - } - if (showAll) { - pushApps.addPreference(pushAppsAll) - } else if (apps.isEmpty()) { - pushApps.addPreference(pushAppsNone) - } - } - } - - companion object { - private const val UPDATE_INTERVAL = 1000L - } -} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt index 848e702b..e7f83c28 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt @@ -8,21 +8,25 @@ package org.microg.gms.ui import android.content.ComponentName -import android.content.Context import android.content.pm.PackageManager import android.os.Bundle +import android.util.Log import androidx.preference.Preference +import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreferenceCompat import com.mgoogle.android.gms.R -import org.microg.gms.checkin.CheckinPrefs +import org.microg.gms.checkin.CheckinPreferences import org.microg.gms.gcm.GcmDatabase -import org.microg.gms.gcm.getGcmServiceInfo -import org.microg.mgms.settings.SettingsContract +import org.microg.gms.gcm.GcmPrefs +import org.microg.gms.settings.SettingsContract +import org.microg.gms.ui.settings.SettingsProvider +import org.microg.gms.ui.settings.getAllSettingsProviders import org.microg.tools.ui.ResourceSettingsFragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController class SettingsFragment : ResourceSettingsFragment() { + private val createdPreferences = mutableListOf() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { super.onCreatePreferences(savedInstanceState, rootKey) @@ -35,11 +39,13 @@ class SettingsFragment : ResourceSettingsFragment() { findNavController().navigate(requireContext(), R.id.openGcmSettings) true } - findPreference(PREF_ABOUT)?.setOnPreferenceClickListener { - findNavController().navigate(requireContext(), R.id.openAbout) - true + findPreference(PREF_ABOUT)!!.apply { + onPreferenceClickListener = Preference.OnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openAbout) + true + } + summary = getString(org.microg.tools.ui.R.string.about_version_str, AboutFragment.getSelfVersion(context)) } - findPreference(PREF_ABOUT)?.summary = getString(R.string.about_version_str, AboutFragment.getSelfVersion(context)) findPreference(SettingsContract.CheckIn.HIDE_LAUNCHER_ICON)?.apply { setOnPreferenceChangeListener { _, newValue -> @@ -48,19 +54,51 @@ class SettingsFragment : ResourceSettingsFragment() { } } + + for (entry in getAllSettingsProviders(requireContext()).flatMap { it.getEntriesStatic(requireContext()) }) { + entry.createPreference() + } + } + + private fun SettingsProvider.Companion.Entry.createPreference(): Preference? { + val preference = Preference(requireContext()).fillFromEntry(this) + try { + if (findPreference(when (group) { + SettingsProvider.Companion.Group.HEADER -> "prefcat_header" + SettingsProvider.Companion.Group.GOOGLE -> "prefcat_google_services" + SettingsProvider.Companion.Group.OTHER -> "prefcat_other_services" + SettingsProvider.Companion.Group.FOOTER -> "prefcat_footer" + })?.addPreference(preference) == true) { + createdPreferences.add(preference) + return preference + } else { + Log.w(TAG, "Preference not added $key") + } + } catch (e: Exception) { + Log.w(TAG, "Failed adding preference $key", e) + } + return null + } + + private fun Preference.fillFromEntry(entry: SettingsProvider.Companion.Entry): Preference { + key = entry.key + title = entry.title + summary = entry.summary + icon = entry.icon + isPersistent = false + isVisible = true + setOnPreferenceClickListener { + findNavController().navigate(requireContext(), entry.navigationId) + true + } + return this } override fun onResume() { super.onResume() val context = requireContext() - lifecycleScope.launchWhenResumed { - updateDetails(context) - } - } - private suspend fun updateDetails(context: Context) { - val gcmServiceInfo = getGcmServiceInfo(context) - if (gcmServiceInfo.configuration.enabled) { + if (GcmPrefs.get(requireContext()).isEnabled) { val database = GcmDatabase(context) val regCount = database.registrationList.size database.close() @@ -69,13 +107,26 @@ class SettingsFragment : ResourceSettingsFragment() { findPreference(PREF_GCM)?.setSummary(R.string.service_status_disabled_short) } - findPreference(PREF_CHECKIN)?.setSummary(if (CheckinPrefs.isEnabled(context)) R.string.service_status_enabled_short else R.string.service_status_disabled_short) + findPreference(PREF_CHECKIN)?.setSummary(if (CheckinPreferences.isEnabled(context)) R.string.service_status_enabled_short else R.string.service_status_disabled_short) + + lifecycleScope.launchWhenResumed { + val entries = getAllSettingsProviders(requireContext()).flatMap { it.getEntriesDynamic(requireContext()) } + for (preference in createdPreferences) { + if (!entries.any { it.key == preference.key }) preference.isVisible = false + } + for (entry in entries) { + val preference = createdPreferences.find { it.key == entry.key } + if (preference != null) preference.fillFromEntry(entry) + else entry.createPreference() + } + } } companion object { const val PREF_ABOUT = "pref_about" const val PREF_GCM = "pref_gcm" const val PREF_CHECKIN = "pref_checkin" + const val PREF_PLAY = "pref_play" } init { diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/SwitchBarPreference.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/SwitchBarPreference.kt new file mode 100644 index 00000000..12802a1e --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/SwitchBarPreference.kt @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.ui + +import android.content.Context +import android.util.AttributeSet +import androidx.appcompat.widget.SwitchCompat +import androidx.preference.PreferenceViewHolder +import androidx.preference.TwoStatePreference +import org.microg.gms.base.core.R + +// TODO +class SwitchBarPreference : TwoStatePreference { + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context) : super(context) + + init { + layoutResource = R.layout.preference_switch_bar + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + holder.isDividerAllowedBelow = false + holder.isDividerAllowedAbove = false + val switch = holder.findViewById(R.id.switch_widget) as SwitchCompat + switch.setOnCheckedChangeListener(null) + switch.isChecked = isChecked + switch.setOnCheckedChangeListener { view, isChecked -> + if (!callChangeListener(isChecked)) { + view.isChecked = !isChecked + return@setOnCheckedChangeListener + } + this.isChecked = isChecked + } + holder.itemView.setBackgroundColor(when { + isChecked -> androidx.appcompat.R.attr.colorControlActivated + isEnabled -> androidx.appcompat.R.attr.colorButtonNormal + else -> androidx.appcompat.R.attr.colorControlHighlight + }) + } +} + +@Deprecated("Get rid") +interface PreferenceSwitchBarCallback { + fun onChecked(newStatus: Boolean) +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/TextPreference.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/TextPreference.kt index 956386a0..a718b876 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/TextPreference.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/TextPreference.kt @@ -7,6 +7,7 @@ package org.microg.gms.ui import android.content.Context import android.util.AttributeSet +import android.util.TypedValue import android.view.Gravity import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.LinearLayout @@ -15,8 +16,7 @@ import androidx.preference.Preference import androidx.preference.PreferenceViewHolder class TextPreference : Preference { - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super( - context, attrs, defStyleAttr, defStyleRes) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context) : super(context) diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/Utils.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/Utils.kt index 998770e3..b59142d3 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/Utils.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/Utils.kt @@ -15,6 +15,7 @@ import android.os.Bundle import android.provider.Settings import android.util.Log import androidx.annotation.IdRes +import androidx.annotation.RequiresApi import androidx.navigation.NavController import androidx.navigation.navOptions import androidx.navigation.ui.R @@ -41,12 +42,12 @@ fun NavController.navigate(context: Context, @IdRes resId: Int, args: Bundle? = fun Context.hideIcon(hide: Boolean) { packageManager.setComponentEnabledSetting( - ComponentName.createRelative(this, "org.microg.gms.ui.SettingsActivityLauncher"), - when (hide) { - true -> PackageManager.COMPONENT_ENABLED_STATE_DISABLED - false -> PackageManager.COMPONENT_ENABLED_STATE_ENABLED - }, - PackageManager.DONT_KILL_APP + ComponentName.createRelative(this, "org.microg.gms.ui.SettingsActivityLauncher"), + when (hide) { + true -> PackageManager.COMPONENT_ENABLED_STATE_DISABLED + false -> PackageManager.COMPONENT_ENABLED_STATE_ENABLED + }, + PackageManager.DONT_KILL_APP ) } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/settings/SettingsProvider.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/settings/SettingsProvider.kt new file mode 100644 index 00000000..ecebb042 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/settings/SettingsProvider.kt @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.ui.settings + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.util.Log +import androidx.navigation.NavController + +private const val TAG = "SettingsProvider" + +interface SettingsProvider { + fun getEntriesStatic(context: Context): List + suspend fun getEntriesDynamic(context: Context): List = getEntriesStatic(context) + + fun preProcessSettingsIntent(intent: Intent) + + fun extendNavigation(navController: NavController) + + companion object { + enum class Group { + HEADER, + GOOGLE, + OTHER, + FOOTER + } + + data class Entry( + val key: String, + val group: Group, + val navigationId: Int, + val title: String, + val summary: String? = null, + val icon: Drawable? = null, + ) + } +} + +fun getAllSettingsProviders(context: Context): List { + val metaData = runCatching { context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).metaData }.getOrNull() ?: Bundle.EMPTY + return metaData.keySet().asSequence().filter { + it.startsWith("org.microg.gms.ui.settings.entry:") + }.mapNotNull { + runCatching { metaData.getString(it) }.onFailure { Log.w(TAG, it) }.getOrNull() + }.mapNotNull { + runCatching { Class.forName(it) }.onFailure { Log.w(TAG, it) }.getOrNull() + }.filter { + SettingsProvider::class.java.isAssignableFrom(it) + }.mapNotNull { + runCatching { it.getDeclaredField("INSTANCE").get(null) as SettingsProvider }.onFailure { Log.w(TAG, it) }.getOrNull() + }.toList() +} \ No newline at end of file diff --git a/play-services-core/src/main/res/color/preferences_material_switch_color.xml b/play-services-core/src/main/res/color/preferences_material_switch_color.xml new file mode 100644 index 00000000..1a6c9037 --- /dev/null +++ b/play-services-core/src/main/res/color/preferences_material_switch_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/drawable/dots_horizontal.xml b/play-services-core/src/main/res/drawable/dots_horizontal.xml index 6e0db150..6278c1cd 100755 --- a/play-services-core/src/main/res/drawable/dots_horizontal.xml +++ b/play-services-core/src/main/res/drawable/dots_horizontal.xml @@ -1,7 +1,7 @@ + android:tint="?attr/colorPrimary"> diff --git a/play-services-core/src/main/res/drawable/ic_cloud_bell.xml b/play-services-core/src/main/res/drawable/ic_cloud_bell.xml index 3b7fb42e..10b85e9d 100644 --- a/play-services-core/src/main/res/drawable/ic_cloud_bell.xml +++ b/play-services-core/src/main/res/drawable/ic_cloud_bell.xml @@ -2,7 +2,7 @@ diff --git a/play-services-core/src/main/res/drawable/ic_device_login.xml b/play-services-core/src/main/res/drawable/ic_device_login.xml index 138e6d26..9d411c7b 100644 --- a/play-services-core/src/main/res/drawable/ic_device_login.xml +++ b/play-services-core/src/main/res/drawable/ic_device_login.xml @@ -1,7 +1,7 @@ + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/drawable/ic_play.xml b/play-services-core/src/main/res/drawable/ic_play.xml new file mode 100644 index 00000000..9db10ec6 --- /dev/null +++ b/play-services-core/src/main/res/drawable/ic_play.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/layout/device_registration_fragment.xml b/play-services-core/src/main/res/layout/device_registration_fragment.xml deleted file mode 100755 index fe6d1e51..00000000 --- a/play-services-core/src/main/res/layout/device_registration_fragment.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/play-services-core/src/main/res/layout/preference_app_heading.xml b/play-services-core/src/main/res/layout/preference_app_heading.xml new file mode 100644 index 00000000..cb7e4edf --- /dev/null +++ b/play-services-core/src/main/res/layout/preference_app_heading.xml @@ -0,0 +1,43 @@ + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/layout/preference_material_switch.xml b/play-services-core/src/main/res/layout/preference_material_switch.xml new file mode 100644 index 00000000..071caab2 --- /dev/null +++ b/play-services-core/src/main/res/layout/preference_material_switch.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/layout/preference_switch_bar.xml b/play-services-core/src/main/res/layout/preference_switch_bar.xml index d45909cd..9818305b 100755 --- a/play-services-core/src/main/res/layout/preference_switch_bar.xml +++ b/play-services-core/src/main/res/layout/preference_switch_bar.xml @@ -1,71 +1,42 @@ - - - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:gravity="center" + android:orientation="horizontal" + android:paddingStart="?attr/listPreferredItemPaddingStart" + android:paddingLeft="?attr/listPreferredItemPaddingLeft" + android:paddingEnd="?attr/listPreferredItemPaddingEnd" + android:paddingRight="?attr/listPreferredItemPaddingRight" + tools:background="?attr/colorControlActivated"> - + - - - - - - - - - - - - - - - - + app:thumbTint="?android:attr/textColorPrimaryInverse" + app:trackTint="?android:attr/textColorSecondaryInverse" + tools:checked="true" /> + \ No newline at end of file diff --git a/play-services-core/src/main/res/layout/push_notification_app_fragment.xml b/play-services-core/src/main/res/layout/push_notification_app_fragment.xml deleted file mode 100755 index 5e32fcee..00000000 --- a/play-services-core/src/main/res/layout/push_notification_app_fragment.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/play-services-core/src/main/res/layout/push_notification_fragment.xml b/play-services-core/src/main/res/layout/push_notification_fragment.xml deleted file mode 100755 index fdd799bd..00000000 --- a/play-services-core/src/main/res/layout/push_notification_fragment.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/play-services-core/src/main/res/layout/settings_root_activity.xml b/play-services-core/src/main/res/layout/settings_root_activity.xml index d854b3a3..18ea2241 100755 --- a/play-services-core/src/main/res/layout/settings_root_activity.xml +++ b/play-services-core/src/main/res/layout/settings_root_activity.xml @@ -9,6 +9,13 @@ android:layout_height="match_parent" android:orientation="vertical"> + + - إعداد Vanced microG. + إعداد Vanced+ microG. لحظة من فضلك… إدارة حساب Google عذراً… @@ -40,6 +40,11 @@ إرسال رسائل C2DM إلى تطبيقات أخرى تبادل الرسائل وتلقي إشعارات المزامنة من خوادم Google الوصول الموسع إلى خدمات Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history تسجيل جهاز Google المراسلة السحابية تشغيل @@ -68,11 +73,11 @@ إضافة حساب Google تلقي الإشعارات المراسلة السحابية هي موفر الإشعارات المستخدم من قبل العديد من تطبيقات الطرف الثالث. لاستخدامها يجب عليك تمكين تسجيل الجهاز. - حول Vanced microG + حول Vanced+ microG إخفاء أيقونة التطبيق من درج التطبيقات Huawei تسجيل الجهاز - يسجل جهازك في خِدْمَات Google وينشئ معرفًا فريدًا للجهاز. Vanced microG يحدد أجزاء أخرى غير اسم حسابك في Google من بيانات التسجيل. + يسجل جهازك في خِدْمَات Google وينشئ معرفًا فريدًا للجهاز. Vanced+ microG يحدد أجزاء أخرى غير اسم حسابك في Google من بيانات التسجيل. Android ID الحالة الافتراضي @@ -109,4 +114,28 @@ استيراد ملف تعريف مخصص استيراد مِلَفّ تعريف الجهاز من مِلَفّ الرقم التسلسلي + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-az/strings.xml b/play-services-core/src/main/res/values-az/strings.xml index 9933a195..740a9b11 100644 --- a/play-services-core/src/main/res/values-az/strings.xml +++ b/play-services-core/src/main/res/values-az/strings.xml @@ -40,6 +40,11 @@ Bu bir neçə dəqiqə çəkə bilər." digər proqramlara C2DM mesajları göndərin mesajlar mübadiləsi və Google serverlərindən sinxronizasiya bildirişləri almaq Google xidmətlərinə genişləndirilmiş giriş + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google cihaz qeydiyyatı Bulud mesajlaşma Açıq @@ -68,11 +73,11 @@ Bu bir neçə dəqiqə çəkə bilər." Google hesabı əlavə et Push bildirişlərini alın Cloud Messaging bir çox üçüncü tərəf proqramları tərəfindən istifadə edilən təkan bildiriş təminatçısıdır. Onu istifadə etmək üçün cihazın qeydiyyatını aktiv etməlisiniz. - MicroG haqqında + Vanced+ MicroG haqqında Proqramın ikonunu başladıcıdan gizlət Huawei Cihaz qeydiyyat et - Cihazınızı Google xidmətlərində qeydiyyatdan keçirir və unikal cihaz identifikatoru yaradır. microG qeydiyyat məlumatlarından Google hesabınızın adından başqa müəyyən bitləri ayırır. + Cihazınızı Google xidmətlərində qeydiyyatdan keçirir və unikal cihaz identifikatoru yaradır. Vanced+ microG qeydiyyat məlumatlarından Google hesabınızın adından başqa müəyyən bitləri ayırır. Android ID Status Defolt @@ -109,4 +114,28 @@ Bu bir neçə dəqiqə çəkə bilər." Fərdi profili idxal edin Fayldan cihaz profilini idxal edin Serial + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-be/strings.xml b/play-services-core/src/main/res/values-be/strings.xml index 9c6bfe7a..1152301e 100644 --- a/play-services-core/src/main/res/values-be/strings.xml +++ b/play-services-core/src/main/res/values-be/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Наладзіць Vanced microG. + Наладзіць Vanced+ microG. Секунду… Кіраванне уліковымі запісамі Google Прабачце… @@ -40,6 +40,11 @@ адпраўка C2DM паведамленняў іншым праграмам абменьвацца паведамленнямі і атрымліваць апавяшчэнні аб сінхранізацыі з серверамі Google Пашыраны доступ да сэрвісаў Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Рэгістрацыя прылады ў Google Cloud Messaging Укл. @@ -68,11 +73,11 @@ Дадаць уліковы запіс Google Атрымліваць push-апавяшчэнні Cloud Messaging дазваляе атрымліваць апавяшчэнні ад многіх праграм. Каб выкарыстоўваць GCM, уключыце рэгістрацыю прылады ў Google. - Пра Vanced microG + Пра Vanced+ microG Схаваць іконку microG з меню праграм Huawei Зарэгістраваць прыладу - Рэгіструе вашу прыладу ў сэрвісах Google і стварае ўнікальны ідэнтыфікатар прылады. Vanced microG выкарыстоўвае толькі імя ўліковага запісу для рэгістрацыі. + Рэгіструе вашу прыладу ў сэрвісах Google і стварае ўнікальны ідэнтыфікатар прылады. Vanced+ microG выкарыстоўвае толькі імя ўліковага запісу для рэгістрацыі. Android ID Стан Прадвызначана @@ -109,4 +114,28 @@ Імпарт карыстальніцкага профілю Імпарт профілю прылады з файла Серыйны + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-bn/strings.xml b/play-services-core/src/main/res/values-bn/strings.xml index 802f7e35..0156013d 100644 --- a/play-services-core/src/main/res/values-bn/strings.xml +++ b/play-services-core/src/main/res/values-bn/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Vanced microG সেটআপ। + Vanced+ microG সেটআপ। একটু অপেক্ষা করুন… গুগল একাউন্ট ম্যানেজার দুঃখিত… @@ -41,6 +41,11 @@ অন্য অ্যাপে C2DM বার্তা পাঠান বার্তা আদান-প্রদান করুন এবং গুগল সার্ভার থেকে সিঙ্ক বিজ্ঞপ্তি পান গুগল সার্ভারের বর্ধিত অ্যাক্সেস + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history গুগল ডিভাইস নিবন্ধন ক্লাউড ম্যাসেজিং চালু @@ -69,11 +74,11 @@ গুগল অ্যাকাউন্ট যুক্ত করুন পুশ বিজ্ঞপ্তি পান ক্লাউড ম্যাসেজিং হল পুশ বিজ্ঞপ্তি প্রদানকারী যা অনেক তৃতীয়-পক্ষ অ্যাপ্লিকেশনে ব্যবহার করা হয়। এটি ব্যবহার করতে অবশ্যই ডিভাইস রেজিস্ট্রেশন সচল করতে হবে। - Vanced microG সম্পর্কে + Vanced+ microG সম্পর্কে লঞ্চার থেকে আইকন লুকান হুয়াওয়েই ডিভাইস নিবন্ধন করুন - আপনার ডিভাইসটি গুগল সার্ভিসে নিবন্ধন করে এবং একটি ইউনিক ডিভাইস আইডি তৈরি করে। Vanced microG আপনার নিবন্ধন ডেটা থেকে গুগল একাউন্টের নাম ব্যতিত আপনার সনাক্তকারী বিটকে বিচ্যুত করে। + আপনার ডিভাইসটি গুগল সার্ভিসে নিবন্ধন করে এবং একটি ইউনিক ডিভাইস আইডি তৈরি করে। Vanced+ microG আপনার নিবন্ধন ডেটা থেকে গুগল একাউন্টের নাম ব্যতিত আপনার সনাক্তকারী বিটকে বিচ্যুত করে। অ্যান্ড্রয়েড আইডি অবস্থা পূর্ব-নির্ধারিত @@ -110,4 +115,28 @@ কাস্টম প্রোফাইল যুক্ত করুন ফাইল থেকে ডিভাইস প্রোফাইল যুক্ত করুন ক্রমিক + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-cs/strings.xml b/play-services-core/src/main/res/values-cs/strings.xml index fb87ec4b..7e021dd8 100644 --- a/play-services-core/src/main/res/values-cs/strings.xml +++ b/play-services-core/src/main/res/values-cs/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Nastavení Vanced microG. + Nastavení Vanced+ microG. Počkejte chviličku… Google Account Manager Promiňte… @@ -37,6 +37,11 @@ Tato akce může trvat několik minut." odesílat zprávy C2DM do ostatních aplikací vyměňovat si zprávy a přijímat oznámení o synchronizaci ze serverů Google Rozšířený přístup ke službám Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Registrace zařízení Google Cloud Messaging Zap. @@ -65,11 +70,11 @@ Tato akce může trvat několik minut." Přidat účet Google Přijímat push oznámení Cloud Messaging je poskytovatel push oznámení využívaný mnoha aplikacemi třetích stran. Chcete\u2011li jej použít, musíte povolit registraci zařízení. - O aplikaci Vanced microG + O aplikaci Vanced+ microG Skrýt ikonu ze spouštěče Huawei Registrovat zařízení - Zaregistruje vaše zařízení do služeb Google a vytvoří pro něj unikátní identifikátor. Vanced microG z dat nutných pro registraci odstraní všechny identifikační údaje kromě názvu vašeho účtu Google. + Zaregistruje vaše zařízení do služeb Google a vytvoří pro něj unikátní identifikátor. Vanced+ microG z dat nutných pro registraci odstraní všechny identifikační údaje kromě názvu vašeho účtu Google. Android ID Stav Výchozí @@ -106,4 +111,28 @@ Tato akce může trvat několik minut." Importovat vlastní profil Importovat profil zařízení ze souboru Serial + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-es-rES/strings.xml b/play-services-core/src/main/res/values-es-rES/strings.xml index 3483fae6..5b090832 100644 --- a/play-services-core/src/main/res/values-es-rES/strings.xml +++ b/play-services-core/src/main/res/values-es-rES/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Configurar Vanced microG. + Configurar Vanced+ microG. Solo un momento… Administrador de Cuentas Google Disculpa… @@ -40,6 +40,11 @@ Esto puede tardar unos minutos." enviar mensajes de C2DM a otras apps intercambiar mensajes y recibir notificaciones de sincronización de los servidores de Google Acceso extendido a los servicios de Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Registro de dispositivos Google Cloud Messaging Encendido @@ -68,11 +73,11 @@ Esto puede tardar unos minutos." Añadir cuenta de Google Recibir notificaciones push Cloud Messaging es un proveedor de notificaciónes push usado por muchas aplicaciones de terceros. Pará usarlo tienes que activar registro de dispositivos. - Acerca de Vanced microG + Acerca de Vanced+ microG Ocultar microG del lanzador Huawei Registrar dispositivo - Registra tu dispositivo a los servicios de Google y crea un identificador de dispositivo único. Vanced microG remueve de los datos de registro partes de identificación a parte de él nombre de tu cuenta de Google. + Registra tu dispositivo a los servicios de Google y crea un identificador de dispositivo único. Vanced+ microG remueve de los datos de registro partes de identificación a parte de él nombre de tu cuenta de Google. Android ID Estado Predeterminado @@ -109,4 +114,28 @@ Esto puede tardar unos minutos." Importar perfil personalizado Importar perfil del dispositivo desde archivo Número de serie + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-fr/strings.xml b/play-services-core/src/main/res/values-fr/strings.xml index 8ec904cb..0fcac240 100644 --- a/play-services-core/src/main/res/values-fr/strings.xml +++ b/play-services-core/src/main/res/values-fr/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Paramètres Vanced microG. + Paramètres Vanced+ microG. Juste un instant… Gestionnaire de comptes Google Désolé… @@ -40,6 +40,11 @@ Cela peut prendre quelques minutes." Envoie des messages C2DM aux autres applications échange des messages et reçoit les notifications de synchronisation provenant des serveurs Google Accès étendu aux services Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Enregistrement de l\'appareil Google Messagerie Cloud Activé @@ -68,11 +73,11 @@ Cela peut prendre quelques minutes." Ajouter un compte Google Recevoir vos notifications push Cloud Messaging est un fournisseur de notification push utilisé par de nombreuses applications tierces. Pour l\'utiliser, vous devez activer l\'enregistrement de l\'appareil. - À propos de Vanced microG + À propos de Vanced+ microG Masquer l’icône depuis le lanceur Huawei Enregistrer l\'appareil - Enregistre votre appareil aux services Google et crée un identifiant unique. Les bandes Vanced microG identifient les octets autres que le votre nom de compte Google depuis les données d\'enregistrement. + Enregistre votre appareil aux services Google et crée un identifiant unique. Les bandes Vanced+ microG identifient les octets autres que le votre nom de compte Google depuis les données d\'enregistrement. Identifiant Android Status Par défaut @@ -109,4 +114,28 @@ Cela peut prendre quelques minutes." Importer un profil personnalisé Importer un profil personnalisé à partir d\'un fichier Série + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-he/strings.xml b/play-services-core/src/main/res/values-he/strings.xml index d849981c..95511eb1 100644 --- a/play-services-core/src/main/res/values-he/strings.xml +++ b/play-services-core/src/main/res/values-he/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - הגדרת Vanced microG. + הגדרת Vanced+ microG. רק רגע… ניהול חשבון גוגל סליחה… @@ -37,6 +37,11 @@ לשלוח הודעות C2DM לאפליקציות אחרות להחליף הודעות ולקבל התראות סנכרון משרתי Google גישה מורחבת לשירותי גוגל + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history רישום מכשיר Google העברת הודעות בענן פועל @@ -56,4 +61,28 @@ הרשמה אחרונה: %1$s העדפות חשבון נתונים אישיים & פרטיות + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-hi/strings.xml b/play-services-core/src/main/res/values-hi/strings.xml index 5356059e..fdf8ea5a 100644 --- a/play-services-core/src/main/res/values-hi/strings.xml +++ b/play-services-core/src/main/res/values-hi/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - सेटअप Vanced microG. + सेटअप Vanced+ microG. सिर्फ एक सेकंड… गूगल खाता प्रबंधक माफ़ करना @@ -38,6 +38,11 @@ अन्य ऐप्स को C2DM संदेश भेजें संदेशों का आदान-प्रदान करें और Google सर्वर से समन्वयन सूचनाएं प्राप्त करें Google सेवाओं तक विस्तारित पहुंच + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history गूगल डिवाइस पंजीकरण क्लाउड मैसेजिंग चालू करें @@ -66,12 +71,12 @@ Google खाता जोड़ें पुश सूचनाएं प्राप्त करें क्लाउड मैसेजिंग एक पुश नोटिफिकेशन प्रदाता है जिसका उपयोग कई तृतीय-पक्ष एप्लिकेशन द्वारा किया जाता है। इसका उपयोग करने के लिए आपको डिवाइस पंजीकरण सक्षम करना होगा। - Vanced microG के बारे में + Vanced+ microG के बारे में लॉन्चर से आइकन छुपाएं Huawei रजिस्टर डिवाइस आपके उपकरण को Google सेवाओं में पंजीकृत करता है और एक अद्वितीय उपकरण पहचानकर्ता बनाता है। पंजीकरण डेटा से आपके Google खाते के नाम के अलावा अन्य बिट्स की पहचान करने वाली - Vanced microG स्ट्रिप्स। + Vanced+ microG स्ट्रिप्स। एंड्रॉयड ID स्थिति डिफॉल्ट @@ -108,4 +113,28 @@ कस्टम प्रोफ़ाइल आयात करें फ़ाइल से डिवाइस प्रोफ़ाइल आयात करें क्रम संख्या + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-id/strings.xml b/play-services-core/src/main/res/values-id/strings.xml index 84dbcf12..ac8b9869 100644 --- a/play-services-core/src/main/res/values-id/strings.xml +++ b/play-services-core/src/main/res/values-id/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Atur Vanced microG. + Atur Vanced+ microG. Tunggu sebentar… Pengelola akun Google Maaf… @@ -38,6 +38,11 @@ Ini bisa berlangsung beberapa menit." kirim pesan C2DM ke aplikasi lain menukar pesan dan menerima notifikasi sinkronisasi dari server Google Akses lebih ke layanan Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google device registration Cloud Messaging Hidup @@ -66,11 +71,11 @@ Ini bisa berlangsung beberapa menit." Tambahkan akun Google Terima notifikasi push Cloud Messaging adalah penyedia notifikasi push yang digunakan pada banyak aplikasi pihak ketiga. Untuk menggunakannya anda harus mengaktifkan device registration. - Tentang Vanced microG + Tentang Vanced+ microG Sembunyikan microG dari peluncur Huawei Registrasi perangkat - Registrasi perangkat anda ke layanan Google dan buat pengenal perangkat unik. Vanced microG strips mengenal bits selain dari akun Google anda dari data registrasi. + Registrasi perangkat anda ke layanan Google dan buat pengenal perangkat unik. Vanced+ microG strips mengenal bits selain dari akun Google anda dari data registrasi. Android ID Status Default @@ -107,4 +112,28 @@ Ini bisa berlangsung beberapa menit." Impor profil kustom Impor profil perangkat dari file Serial + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-it/strings.xml b/play-services-core/src/main/res/values-it/strings.xml index eb7e1ddc..07277b42 100755 --- a/play-services-core/src/main/res/values-it/strings.xml +++ b/play-services-core/src/main/res/values-it/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Configurazione di Vanced microG. + Configurazione di Vanced+ microG. Solo un secondo… Gestore Account Google Ci scusiamo… @@ -37,6 +37,11 @@ Questo potrà richiedere un paio di minuti" Invia i messaggi C2DM ad altre app scambia messaggi e ricevi notifiche di sincronizzazione dai server di Google Estendi accesso ai servizi Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Registrazione dispositivo Google Messaggistica Cloud Attiva @@ -65,11 +70,11 @@ Questo potrà richiedere un paio di minuti" Aggiungi account Google Ricevi notifiche push Messaggistica Cloud è un fornitore di notifiche push presente in molte applicazioni di terze parti. Per utilizzarlo è necessario attivare la registrazione del dispositivo. - Informazioni su Vanced microG + Informazioni su Vanced+ microG Nascondi microG dal launcher Huawei Registra dispositivo - Registra il tuo dispositivo sui servizi Google e crea un identificatore univoco del dispositivo. Verranno rimossi dai Servizi Vanced microG alcuni bit utili per identificare i dati di registrazione, oltre al nome dell\'account Google. + Registra il tuo dispositivo sui servizi Google e crea un identificatore univoco del dispositivo. Verranno rimossi dai Servizi Vanced+ microG alcuni bit utili per identificare i dati di registrazione, oltre al nome dell\'account Google. ID Android Stato Predefinito @@ -106,4 +111,28 @@ Questo potrà richiedere un paio di minuti" Importa profilo personalizzato Importa profilo dispositivo da file Seriale + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-ja/arrays.xml b/play-services-core/src/main/res/values-ja/arrays.xml index ef6517c1..ba0abe10 100644 --- a/play-services-core/src/main/res/values-ja/arrays.xml +++ b/play-services-core/src/main/res/values-ja/arrays.xml @@ -20,12 +20,12 @@ @string/service_status_disabled_short @string/gcm_status_pref_default - Ping間隔: 2 分 - Ping間隔: 5 分 - Ping間隔: 10 分 - Ping間隔: 15 分 - Ping間隔: 20 分 - Ping間隔: 30 分 + 更新間隔: 2 分 + 更新間隔: 5 分 + 更新間隔:10 分 + 更新間隔:15 分 + 更新間隔:20 分 + 更新間隔:30 分 - \ No newline at end of file + diff --git a/play-services-core/src/main/res/values-ja/plurals.xml b/play-services-core/src/main/res/values-ja/plurals.xml index ccbb1f5f..48708478 100644 --- a/play-services-core/src/main/res/values-ja/plurals.xml +++ b/play-services-core/src/main/res/values-ja/plurals.xml @@ -19,7 +19,7 @@ %1$d アプリを登録しました - %1$d 登録済みのアプリ + %1$d つの登録済みのアプリ diff --git a/play-services-core/src/main/res/values-ja/strings.xml b/play-services-core/src/main/res/values-ja/strings.xml index 7946c4d6..0d93256f 100644 --- a/play-services-core/src/main/res/values-ja/strings.xml +++ b/play-services-core/src/main/res/values-ja/strings.xml @@ -15,64 +15,69 @@ ~ limitations under the License. --> - Vended microGのセットアップ - 少々お待ちください… + Vended+ microGのセットアップ + お待ちください… Google アカウントの管理 - 申し訳ございません… - "デバイス上のアプリがGoogleアカウントにサインインしようとしています。 + 申し訳ございません + "端末上のアプリがGoogleアカウントにサインインしようとしています。 -意図的な場合は、 サインイン (または Huawei) を使用してください。 Googleのサインインページに接続するには、このブランドのデバイスがある場合) ボタンをクリックします。" +これがあなたによる要求の場合は、サインイン (または Huawei) を選択してログインを続行してください。" サインイン - "Googleのサーバーに接続してサインインします。 + "Googleのサーバーに接続しています... -これは数秒かかることがあります。" - "ネットワーク接続がありません。 +これには数秒かかることがあります。" + "ネットワークに接続できませんでした。 -一時的な問題が発生するか、データサービス用にAndroid端末がプロビジョニングされていない可能性があります モバイルネットワークに接続したときに再度試行するか、Wi-Fiネットワークに接続してください。" +一時的な問題が発生しているか、データサービス用 この端末がプロビジョニングされていない可能性があります。 +ネットワークの構成設定を確認し、再試行してください。" "Google サーバーとの通信に問題が発生しました。 -後でもう一度お試しください。" - "あなたのデバイスはあなたのアカウントに情報を保存するためにGoogleに連絡しています。 +後ほど、もう一度お試しください。" + "アカウント情報を保存するためにGoogleと通信しています... -これは数分かかることがあります。" - 内部状態の放送を聞いてください - c2DMメッセージを聞く - c2DMのメッセージを他のアプリに送信 +これには数分かかることがあります。" + C2DMメッセージを聞く + C2DMのメッセージを他のアプリに送信 メッセージの交換とGoogleサーバーからの同期通知の受信します Google サービスへの拡張アクセス + Googleアカウントマネージャー は、完全に実装されていません。 + Google アカウントを管理 + Google アカウント情報の管理 + YouTube のアクティビティの管理 + 視聴履歴や検索履歴などの YouTube のアクティビティを管理 Google デバイスの登録 クラウドメッセージ 有効 無効 - 高度な設定 + 通知間隔の設定 なし すべて表示 モバイルネットワーク Wi-Fi - ローミング中 - その他のネットワーク + ローミング + その他のネットワーク接続 概要 Google サービス 詳細 登録されていません - 最終登録: %1$s + 最終更新:%1$s アカウント設定 個人情報 & プライバシー サインイン & セキュリティ - Google にアプリのアクセス許可を付与する - 無効にすると、アプリ認証リクエストが Google に送信される前にユーザーに要求されます。 一部のアプリケーションは、無効になっている場合、Googleアカウントを使用できません。 + Google にアプリへのアクセス許可を付与する + 無効にすると、アプリの認証要求が Google に送信される前にユーザーに要求されます。これを無効にすると、一部のアプリでは Google アカウントの使用に失敗します。 アプリによるアカウントの検索を許可する - 有効にすると、このデバイス上のすべてのアプリケーションは、事前承認なしでGoogleアカウントのメールアドレスを見ることができます。 + 有効にすると、この端末上のすべてのアプリは、事前承認なしでGoogleアカウントのメールアドレスを見ることができます。 アカウント Google アカウントを追加 プッシュ通知を受け取る - クラウドメッセージングは、多くのサードパーティアプリケーションで使用されているプッシュ通知プロバイダです。それを使用するには、デバイス登録を有効にする必要があります。 - Vanced microGについて - ランチャーからアイコンを隠す + クラウドメッセージングは、Vanced で使用されているプッシュ通知プロバイダです。通知を使用するには、[Google デバイスの登録]を有効にする必要があります。 + Vanced+ microGについて + ホームからアプリを隠す Huawei - デバイスを登録 - デバイスをGoogleサービスに登録し、一意のデバイス識別子を作成します。 Vanced microGは、登録データからGoogleアカウント名以外の識別ビットを削除します。 + 端末を登録 + 端末を Google のサービスに登録し、一意の端末識別子を作成します。 Vanced+ microGは、登録されたデータからGoogleアカウント名以外の識別ビットを削除します。 Android ID 状態 既定 @@ -80,33 +85,57 @@ 秒 分 登録を解除 - これまでに受信したメッセージはありません - 最終メッセージ: %1$s + これまでに受信した通知はありません + 最新通知:%1$s 登録済 - 登録: %1$s + 登録:%1$s %1$s の登録解除しますか? 一部のアプリは自動的に再登録しない、または手動で登録するオプションを提供しない場合があります。 登録解除後にこれらのアプリが正常に動作しない可能性があります。\n続行しますか? 既に登録されているプッシュ通知の登録を拒否しました。\n今後プッシュメッセージを受信しないように登録を解除しますか? - メッセージ: %1$d (%2$d bytes) + 通知件数:%1$d (%2$d bytes) 切断されました %1$sから接続しました 登録を許可する プッシュ通知の登録をアプリに許可します。 プッシュメッセージでアプリを起動 バックグラウンドでアプリを起動し、受信するプッシュメッセージを受信します。 - 状態 + 接続状態 プッシュ通知を使用しているアプリ 登録済みのアプリ 未登録のアプリ プッシュ通知に使用するネットワーク 端末のプロファイル - プロファイルを選択 - 自国 - 実在する - カスタム: %s - 自動: %s - システム: %s - カスタム プロファイルをインポートする - ファイルから端末のプロファイルをインポート + プロファイル + ランダム値 + 端末に依存 + カスタム:%s + 規定:%s + システム:%s + カスタム プロファイルを使用 + ファイルからプロファイルをインポートします シリアル + ソースコードを閲覧 + GitHub でプロジェクトのソースコードを表示します + Google アカウントからログアウトしたい場合は、端末の設定 → アカウント に移動して該当するアカウントを選択し削除します + GitHub で Vanced+ microG の更新を確認する + 更新を確認 + 無効にすると、本来 Vanced+ microG によって行われる Google サーバーへのすべての接続が拒否されます。\nこれはサービス固有の設定を無視します。 + Google サーバーへの接続を許可する + Orbot がインストールされている必要があります。\nまだ実装されていません + Tor ネットワーク経由で Google に接続する + ビルド日時 + YouTube上のデータを閲覧 + YouTube のアクティビティを確認、削除できます。YouTube の利便性を向上させるために YouTube やその他の Google サービスのデータがどのように使用されているかを確認できます + Vanced+ microG で追加したGoogleアカウントはどうすれば削除できますか? + 端末の設定 → アカウント に移動して該当するアカウントを選択し削除します + GitLab で Vanced+ microG の更新を確認する + GitLab でプロジェクトのソースコードを表示します + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-ko/strings.xml b/play-services-core/src/main/res/values-ko/strings.xml index 5f6599ca..fa18f6d5 100644 --- a/play-services-core/src/main/res/values-ko/strings.xml +++ b/play-services-core/src/main/res/values-ko/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Vanced microG를 설정합니다 + Vanced+ microG를 설정합니다 잠시만 기다려주십시오… Google 계정 관리자 죄송합니다... @@ -40,6 +40,11 @@ C2DM 메세지를 다른 앱에 전달 Google 서버에서 메시지 교환 및 동기화 알림 수신 Google 서비스에 대한 확장된 액세스 + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google 기기 등록 클라우드 메시징 켜짐 @@ -68,11 +73,11 @@ Google 계정 추가 푸시 알림 수신 클라우드 메시징은 많은 타사 응용 프로그램에서 사용하는 푸시 알림 공급자입니다. 그것을 사용하려면 장치 등록을 활성화해야 합니다. - Vanced microG에 대하여 + Vanced+ microG에 대하여 런처에서 아이콘 숨기기 화웨이 장치 등록 - 기기를 Google 서비스에 등록하고 고유한 기기 식별자를 만듭니다. Vanced microG는 등록 데이터에서 Google 계정 이름 이외의 식별 비트를 제거합니다. + 기기를 Google 서비스에 등록하고 고유한 기기 식별자를 만듭니다. Vanced+ microG는 등록 데이터에서 Google 계정 이름 이외의 식별 비트를 제거합니다. 안드로이드 ID 상태 기본값 @@ -109,4 +114,28 @@ 사용자 지정 프로파일 불러오기 파일로부터 기기 프로파일 불러오기 일련번호 + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-lt/strings.xml b/play-services-core/src/main/res/values-lt/strings.xml index f44ceafd..bdf153d4 100644 --- a/play-services-core/src/main/res/values-lt/strings.xml +++ b/play-services-core/src/main/res/values-lt/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Nustatyti Vanced microG. + Nustatyti Vanced+ microG. Šiek tiek palaukite… Google paskyros valdytojas Atsiprašome… @@ -40,6 +40,11 @@ Tai gali užtrukti keletą minučių." siųsti C2DM pranešimus į kitas programas keistis pranešimais ir gauti sinchronizavimo pranešimus iš Google serverių Išplėstinė prieiga prie Google paslaugų + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google įrenginio registracija Pranešimai iš debesies Įjungta @@ -68,7 +73,7 @@ Tai gali užtrukti keletą minučių." Pridėti Google paskyrą Gauti aktyvuosius pranešimus Cloud Messaging yra tiesioginių pranešimų teikėjas, naudojamas daugelyje trečiųjų šalių programų. Norėdami jį naudoti, turite įjungti įrenginio registraciją. - Apie Vanced microG + Apie Vanced+ microG Slėpti piktogramą paleidimo priemonėje Huawei Registruoti įrenginį @@ -109,4 +114,28 @@ Tai gali užtrukti keletą minučių." Importuoti pasirinktinį profilį Importuokite įrenginio profilį iš failo Serijinis numeris + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-night-v23/themes.xml b/play-services-core/src/main/res/values-night-v23/themes.xml new file mode 100644 index 00000000..6ed526b7 --- /dev/null +++ b/play-services-core/src/main/res/values-night-v23/themes.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/values-night-v27/themes.xml b/play-services-core/src/main/res/values-night-v27/themes.xml new file mode 100644 index 00000000..0b22714a --- /dev/null +++ b/play-services-core/src/main/res/values-night-v27/themes.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/values-night-v31/themes.xml b/play-services-core/src/main/res/values-night-v31/themes.xml index de5386e8..7f89bd04 100644 --- a/play-services-core/src/main/res/values-night-v31/themes.xml +++ b/play-services-core/src/main/res/values-night-v31/themes.xml @@ -1,14 +1,11 @@ + - - + \ No newline at end of file diff --git a/play-services-core/src/main/res/values-pt-rBR/plurals.xml b/play-services-core/src/main/res/values-pt-rBR/plurals.xml index 35222efa..ab9eb731 100644 --- a/play-services-core/src/main/res/values-pt-rBR/plurals.xml +++ b/play-services-core/src/main/res/values-pt-rBR/plurals.xml @@ -19,7 +19,7 @@ %1$d aplicativo registrado - %1$d aplicativo registrado + %1$d aplicativos registrados diff --git a/play-services-core/src/main/res/values-pt-rBR/strings.xml b/play-services-core/src/main/res/values-pt-rBR/strings.xml index 14120ae8..1120864e 100644 --- a/play-services-core/src/main/res/values-pt-rBR/strings.xml +++ b/play-services-core/src/main/res/values-pt-rBR/strings.xml @@ -15,15 +15,16 @@ ~ limitations under the License. --> - Configuração do Vanece microG. + Vanced+ microG + Configurações do Vanced+ microG. Um momento… Gerenciador de Contas do Google Desculpe… "Um aplicativo em seu dispositivo está tentando entrar em uma conta do Google. -Se isso foi intencional, use o Login (ou Huawei, se você tiver um dispositivo com esta marca) para se conectar à página de login do Google, se não, pressione Cancelar para voltar ao aplicativo que fez com que este diálogo aparecesse." - Iniciar sessão - "Estabelecendo uma conexão com os servidores do Google para fazer ‘login’. +Se isso foi intencional, use o botão Entrar (ou Huawei, se você tiver um dispositivo com esta marca) para se conectar à página de login do Google." + Entrar + "Estabelecendo uma conexão com os servidores do Google para entrar em sua conta. Isso pode levar alguns segundos." "Você não tem uma conexão de rede. @@ -35,46 +36,66 @@ Tente novamente mais tarde." "Seu dispositivo está entrando em contato com o Google para salvar informações em sua conta. Isso pode demorar alguns minutos." + Ouvir transmissões de estado interno Escutar mensagens C2DM Enviar mensagens C2DM para outros aplicativos Trocar mensagens e receber notificações de sincronização de servidores do Google Acesso estendido aos serviços do Google + Gerenciador de Contas do Google não foi totalmente implementado + Gerenciar sua conta do Google + Gerenciar as informações da sua conta do Google + Gerenciar atividades no YouTube + Gerenciar atividades do YouTube, como histórico de exibição ou pesquisa + Registro de dispositivos do Google Cloud Messaging + Ligado Desligado + Avançado Nenhum Veja tudo + Rede móvel Wi-Fi Roaming Outras redes + + Sobre Serviços do Google Mais + Não registrado Último registro: %1$s + Preferências da conta Informações pessoais & privacidade Entrar & segurança + Confiar permissões de aplicativos no Google Quando desativado, o usuário é solicitado antes que uma solicitação de autorização de aplicativos seja enviada ao Google. Alguns aplicativos não usarão a conta do Google se isso for desativado. Permitir que aplicativos encontrem contas Quando ativado, todos os aplicativos deste dispositivo poderão ver o endereço de e-mail de suas contas do Google sem autorização prévia. + Conta Adicionar conta do Google Receba notificações push Cloud Messaging é um provedor de notificação push usado por muitos aplicativos de terceiros. Para usá-lo, você deve ativar ao registro do dispositivo. - Sobre o Vanced microG + + Sobre o Vanced+ microG Ocultar ícone no menu + Huawei + Registar dispositivo - Registre seu dispositivo nos serviços do Google e cria um identificador de dispositivo exclusivo. Vanced microG identifica e tiras bits que não sejam o nome da sua conta do Google a partir de dados de registro. + Registre seu dispositivo nos serviços do Google e cria um identificador de dispositivo exclusivo. Vanced+ microG identifica e tiras bits que não sejam o nome da sua conta do Google a partir de dados de registro. ID do Android Status + Padrão Manual segundos @@ -90,6 +111,7 @@ Isso pode demorar alguns minutos." Mensagens: %1$d (%2$d bytes) Desconectado Conectado à %1$s + Permitir registro Permita que o aplicativo se registre para notificações push. Iniciar aplicativo na mensagem push @@ -99,6 +121,7 @@ Isso pode demorar alguns minutos." Aplicativos registrados Aplicativos não registrados Redes para usar em notificações push + Perfil do dispositivo Selecionar perfil Nativo @@ -109,4 +132,28 @@ Isso pode demorar alguns minutos." Importar perfil personalizado Importar perfil do dispositivo a partir do arquivo Serial + Ver código fonte + Veja o código fonte do projeto no GitHub + Para remover a conta do Google conectada, vá para Configurações do dispositivo -> Conta. + Verifique se há atualizações do Vanced+ microG no GitHub + Verifique se há atualizações + Se desativado, todas as conexões aos servidores do Google normalmente feitas pelo Vanced+ microG serão negadas. Isso substitui as configurações específicas do serviço. + Permitir conexão com servidores do Google + Requer que o Orbot esteja instalado\nAinda não é compatível + Conecte-se ao Google através da rede Tor + Data da Build + Seus dados no YouTube + Navegue ou exclua sua atividade no YouTube e descubra como seus dados fazem com que o YouTube e outros serviços do Google funcionem melhor para você + Deseja remover sua conta do Google do Vanced+ microG? + Conta para removê-las.]]> + Verificar se há atualizações do Vanced + microG no GitLab + Ver código fonte do projeto no GitLab + Verifique a conexão + Verifique a conexão com a Internet do seu dispositivo + Voltar ao YouTube + Toque para sair do Vanced+ microG e voltar ao YouTube + Informações do pacote + Gerenciar contas do Google conectadas + Isso abrirá as configurações do seu dispositivo ! + Configurações diversas diff --git a/play-services-core/src/main/res/values-ru/strings.xml b/play-services-core/src/main/res/values-ru/strings.xml index 8e6b84a8..61269e80 100644 --- a/play-services-core/src/main/res/values-ru/strings.xml +++ b/play-services-core/src/main/res/values-ru/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Настроить Vanced microG. + Настроить Vanced+ microG. Секундочку… Настройки аккаунта Google Извините… @@ -40,6 +40,11 @@ отправка C2DM сообщений другим приложениям обмениваться сообщениями и получать уведомления о синхронизации с серверами Google Расширенный доступ к сервисам Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Регистрация устройства в Google Cloud Messaging Вкл. @@ -68,11 +73,11 @@ Добавить аккаунт Google Получать push-уведомления Cloud Messaging позволяет получать пуш-уведомления от многих приложений. Чтобы использовать GCM, включите регистрацию устройства в Google. - О Vanced microG + О Vanced+ microG Скрыть иконку microG из меню приложений Huawei Зарегистрировать устройство - Регистрирует ваше устройство в сервисах Google и создает уникальный идентификатор устройства. Vanced microG использует только имя аккаунта для регистрации. + Регистрирует ваше устройство в сервисах Google и создает уникальный идентификатор устройства. Vanced+ microG использует только имя аккаунта для регистрации. Android ID Состояние По умолчанию @@ -109,4 +114,28 @@ Импорт пользовательского профиля Импорт профиля устройства из файла Serial + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-th/strings.xml b/play-services-core/src/main/res/values-th/strings.xml index f37f43ed..3f2df561 100644 --- a/play-services-core/src/main/res/values-th/strings.xml +++ b/play-services-core/src/main/res/values-th/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - การติดตั้ง Vanced microG. + การติดตั้ง Vanced+ microG. รอสักครู่… จัดการบัญชี Google ขอโทษ... @@ -33,6 +33,11 @@ ฟังการถ่ายทอดสถานะภายใน แลกเปลี่ยนข้อความและรับการแจ้งเตือนการซิงค์จากเซิร์ฟเวอร์ของ Google ขยายการเข้าถึงบริการของ Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history การลงทะเบียนอุปกรณ์ Google การส่งข้อความบนคลาวด์  เปิด @@ -61,11 +66,11 @@ เพิ่มบัญชี Google รับการแจ้งเตือนแบบพุช Cloud Messaging เป็นผู้ให้บริการการแจ้งเตือนแบบพุชที่ใช้โดยแอปพลิเคชันบุคคลที่สามจำนวนมาก ในการใช้งาน คุณต้องเปิดใช้งานการลงทะเบียนอุปกรณ์  - เกี่ยวกับ Vanced microG + เกี่ยวกับ Vanced+ microG ซ่อนไอคอนจากลั้นเชอร์ Huawei ลงทะเบียนอุปกรณ์  - ลงทะเบียนอุปกรณ์ของคุณกับบริการของ Google และสร้างตัวระบุอุปกรณ์ที่ไม่ซ้ำกัน. Vanced microG strips identifying bits อื่นที่ไม่ใช่ชื่อบัญชี Google ของคุณจากข้อมูลการลงทะเบียน. + ลงทะเบียนอุปกรณ์ของคุณกับบริการของ Google และสร้างตัวระบุอุปกรณ์ที่ไม่ซ้ำกัน. Vanced+ microG strips identifying bits อื่นที่ไม่ใช่ชื่อบัญชี Google ของคุณจากข้อมูลการลงทะเบียน. Android ID สถานะ: ค่าเริ่มต้น @@ -97,4 +102,28 @@ ระบบ: %s นำเข้าโปรไฟล์ที่กำหนดเอง นำเข้าโปรไฟล์อุปกรณ์จากไฟล์  + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-tr/strings.xml b/play-services-core/src/main/res/values-tr/strings.xml index d800d0bc..d5f5ddf5 100644 --- a/play-services-core/src/main/res/values-tr/strings.xml +++ b/play-services-core/src/main/res/values-tr/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Vanced microG\'yi kurun. + Vanced+ microG\'yi kurun. Sadece bir saniye… Google Hesap Yöneticisi Afedersiniz… @@ -40,6 +40,11 @@ Bunu bilerek yaptıysanız, Google'ın oturum açma sayfasına bağlanmak için diğer uygulamalara C2DM mesajları gönderin Google sunucularından mesaj alışverişi yapın ve senkronizasyon bildirimleri alın Google hizmetlerine erişim gerektirir + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google cihaz kaydı Bulut ile mesajlaşma Açık @@ -68,11 +73,11 @@ Bunu bilerek yaptıysanız, Google'ın oturum açma sayfasına bağlanmak için Yeni Google Hesabı Anlık bildirimler alın Bulut mesajlaşma, birçok üçüncü taraf uygulaması tarafından kullanılan bir anlık bildirim sağlayıcısıdır. Bunu kullanmak için cihaz kaydını etkinleştirmelisiniz. - Vanced microG hakkında + Vanced+ microG hakkında Başlatıcı simgesini gizle Huawei Cihazı kaydet - Cihazınızı Google hizmetlerine kaydeder ve benzersiz bir cihaz tanımlayıcısı oluşturur. Vanced microG, kayıt verilerinden Google hesap adınız dışındaki bitleri tanımlayan şeritler. + Cihazınızı Google hizmetlerine kaydeder ve benzersiz bir cihaz tanımlayıcısı oluşturur. Vanced+ microG, kayıt verilerinden Google hesap adınız dışındaki bitleri tanımlayan şeritler. Android kimliği Durum Varsayılan @@ -109,4 +114,28 @@ Bunu bilerek yaptıysanız, Google'ın oturum açma sayfasına bağlanmak için Özel profil aktar Cihaza dosyadan içe aktar Seri + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-uk/strings.xml b/play-services-core/src/main/res/values-uk/strings.xml index f796cdc7..a63b5a67 100644 --- a/play-services-core/src/main/res/values-uk/strings.xml +++ b/play-services-core/src/main/res/values-uk/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Налаштувати Vanced microG. + Налаштувати Vanced+ microG. Секундочку… Менеджер облікових записів Google Вибачте… @@ -40,6 +40,11 @@ надсилати повідомлення C2DM іншим додаткам обмінюватися повідомленнями та отримувати сповіщення про синхронізацію від серверів Google Розширений доступ до сервісів Google + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Реєстрація пристрою Google Хмарне повідомлення Увімкнено @@ -68,11 +73,11 @@ Додати Google аккаунт Отримувати push-сповіщення Хмарне повідомлення – це провайдер push-сповіщень, який використовується багатьма сторонніми програмами. Щоб скористатися нею, необхідно увімкнути реєстрацію пристрою. - Про Vanced microG + Про Vanced+ microG Сховати значок з лаунчера Huawei Зареєструвати пристрій - Реєструє ваш пристрій у службах Google і створює унікальний ідентифікатор пристрою. Vanced microG видаляє з реєстраційних даних біти, відмінні від імені вашого аккаунта Google. + Реєструє ваш пристрій у службах Google і створює унікальний ідентифікатор пристрою. Vanced+ microG видаляє з реєстраційних даних біти, відмінні від імені вашого аккаунта Google. Android ID Стан Типово @@ -109,4 +114,28 @@ Імпорт користувацького профілю Імпортувати профіль пристрою з файлу Срійне ім\'я + View source code + View project source code on GitHub + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-v23/themes.xml b/play-services-core/src/main/res/values-v23/themes.xml new file mode 100644 index 00000000..9009e5c5 --- /dev/null +++ b/play-services-core/src/main/res/values-v23/themes.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/values-v27/themes.xml b/play-services-core/src/main/res/values-v27/themes.xml new file mode 100644 index 00000000..434c86ec --- /dev/null +++ b/play-services-core/src/main/res/values-v27/themes.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/values-v31/themes.xml b/play-services-core/src/main/res/values-v31/themes.xml index 1719a989..6e96e2b4 100644 --- a/play-services-core/src/main/res/values-v31/themes.xml +++ b/play-services-core/src/main/res/values-v31/themes.xml @@ -1,13 +1,11 @@ + - - + \ No newline at end of file diff --git a/play-services-core/src/main/res/values-vi/strings.xml b/play-services-core/src/main/res/values-vi/strings.xml index 27ef8de2..df22a69d 100644 --- a/play-services-core/src/main/res/values-vi/strings.xml +++ b/play-services-core/src/main/res/values-vi/strings.xml @@ -15,13 +15,13 @@ ~ limitations under the License. --> - Thiết lập Vanced microG. + Thiết lập Vanced+ microG. Hãy đợi giây lát… - Quản lý tài khoản Google + Quản lý/Thêm tài khoản Google Xin lỗi… "Một ứng dụng nào đó trên thiết bị của bạn đang yêu cầu đăng nhập vào tài khoản Google. -Nếu điều này do bạn thực hiện, hãy sử dụng nút Đăng nhập (hoặc Huawei nếu bạn có thiết bị hãng này) để kết nối đến máy chủ đăng nhập của Google. Nếu không, nhấn Hủy để quay lại ứng dụng khiến hộp thoại này xuất hiện." +Nếu điều này do bạn thực hiện, hãy sử dụng nút Đăng nhập (hoặc Huawei nếu bạn có thiết bị hãng này) để kết nối đến máy chủ đăng nhập của Google." Đăng nhập "Đang thiết lập kết nối đến máy chủ của Google để đăng nhập cho bạn. @@ -34,18 +34,23 @@ Nếu điều này do bạn thực hiện, hãy sử dụng nút Đăng nhậ Hãy thử lại sau." "Thiết bị của bạn đang kết nối với Google để lưu thông tin vào tài khoản của bạn. -Điều này có thể mất vài phút." +Điều này có thể mất vài phút..." nghe tình trạng nội bộ của các chương trình phát sóng nghe tin nhắn C2DM gửi tin nhắn C2DM đến các ứng dụng khác trao đổi tin nhắn và nhận thông báo đồng bộ hóa từ máy chủ Google Quyền truy cập mở rộng vào các dịch vụ của Google + Trình quản lý tài khoản Google chưa được triển khai đầy đủ + Quản lý tài khoản Google + Quản lý thông tin tài khoản Google + Quản lý hoạt động trên YouTube + Quản lý hoạt động trên YouTube, chẳng hạn như lịch sử xem hay tìm kiếm Đăng ký thiết bị Google Thông báo đẩy qua đám mây Bật Tắt Nâng cao - Trống + Không có gì ở đây cả, thật kỳ lạ... Xem tất cả Mạng di động Wi-Fi @@ -65,15 +70,17 @@ Hãy thử lại sau." Cho phép ứng dụng tìm tài khoản Khi được bật, tất cả các ứng dụng trên thiết bị này sẽ có thể xem địa chỉ email trong Tài khoản Google của bạn mà không cần ủy quyền trước. Tài khoản - Thêm tài khoản Google + Thêm tài khoản Google mới Nhận thông báo đẩy Thông báo đẩy qua đám mây hay Cloud Messaging là một nhà cung cấp dịch vụ thông báo đẩy được nhiều ứng dụng bên thứ ba tin dùng. Để sử dụng nó, bạn phải kích hoạt Đăng ký thiết bị. - Giới thiệu về Vanced microG + Giới thiệu về Vanced+ microG Ẩn biểu tượng khỏi launcher Huawei Đăng ký thiết bị - Đăng ký thiết bị của bạn với các dịch vụ của Google và tạo một mã nhận dạng thiết bị duy nhất. Vanced microG tách một ít thông tin nhận dạng không phải tên tài khoản Google của bạn từ dữ liệu đăng ký. + Đăng ký thiết bị của bạn với các dịch vụ của Google và tạo một mã nhận dạng thiết bị duy nhất. Vanced+ microG tách một ít thông tin nhận dạng không phải tên tài khoản Google của bạn từ dữ liệu đăng ký. ID Android + Xác thực với đăng ký thiết bị + Khi vô hiệu hóa, các yêu cầu xác thực sẽ không được liên kết với đăng ký thiết bị, điều này cho phép các thiết bị trái phép đăng nhập nhưng có thể gây ra những hậu quả về tài khoản. Trạng thái Mặc định Thủ công @@ -93,7 +100,7 @@ Hãy thử lại sau." Cho phép đăng ký Cho phép ứng dụng đăng ký nhận thông báo đẩy. Khởi động ứng dụng từ tin nhắn đẩy - Khởi động ứng dụng ở chế độ nền để nhận các tin nhắn đẩy đến. + Khởi động ứng dụng ở chế độ nền để nhận các thông báo đẩy đến. Trạng thái Ứng dụng sử dụng thông báo đẩy Ứng dụng đã đăng ký @@ -109,4 +116,29 @@ Hãy thử lại sau." Nhập hồ sơ cấu hình tùy chỉnh Nhập cấu hình thiết bị từ tệp Số sêri + Xem mã nguồn + Xem mã nguồn của dự án trên GitHub + Để gỡ bỏ tài khoản Google đã đăng nhập, hãy vào Cài đặt thiết bị -> Tài khoản. + Kiểm tra cập nhật mới của Vanced+ microG trên GitHub + Kiểm tra cập nhật mới + Nếu vô hiệu hóa, tất cả các kết nối tới máy chủ Google thường thực hiện bởi Vanced+ microG sẽ bị từ chối. Tùy chọn này sẽ ghi đè cài đặt đó. + Cho phép kết nối tới máy chủ Google + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Ngày dựng + Dữ liệu của bạn trong YouTube + Xem hoặc xóa hoạt động của bạn trên YouTube và khám phá cách dữ liệu của bạn có thể giúp YouTube và các dịch vụ khác của Google hoạt động hiệu quả hơn cho bạn. + Muốn đăng xuất hoặc gỡ tài khoản đã đăng nhập khỏi Vanced+ microG? + Quản lý Tài khoản và gỡ bỏ nó]]> hoặc nhấp vào đây. + Kiểm tra cập nhật Vanced+ microG trên GitLab + Xem mã nguồn dự án trên GitLab + Kiểm tra kết nối + Kiểm tra kết nối mạng của thiết bị + Quay lại YouTube Vanced+ + Nhấn để thoát Vanced+ microG và quay lại YouTube Vanced+ (nếu được cài đặt) + + Thông tin package + Quản lý tài khoản Google đã đăng nhập + Điều này sẽ mở cài đặt thiết bị của bạn ! + Cài đặt khác diff --git a/play-services-core/src/main/res/values-zh-rCN/strings.xml b/play-services-core/src/main/res/values-zh-rCN/strings.xml index ffcee5eb..5dc520b8 100644 --- a/play-services-core/src/main/res/values-zh-rCN/strings.xml +++ b/play-services-core/src/main/res/values-zh-rCN/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - 设置 Vanced microG。 + 设置 Vanced+ microG。 请稍等… Google 账号管理器 抱歉… @@ -40,6 +40,11 @@ 发送 C2DM 消息到其他应用 从 Google 服务器交换信息和接收同步通知 扩展谷歌服务的访问权限 + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google 设备注册 云端推送 打开 @@ -68,11 +73,11 @@ 添加 Google 账号 接收推送通知 云端推送是第三方应用推送通知的渠道,要使用必须启用设备注册。 - 关于 Vanced microG + 关于 Vanced+ microG 隐藏桌面图标 华为 注册设备 - 注册您的设备到 Google 服务并创建独特的设备标识,Vanced microG 从注册数据种识别您的 Google 账号 名称以外的项目。 + 注册您的设备到 Google 服务并创建独特的设备标识,Vanced+ microG 从注册数据种识别您的 Google 账号 名称以外的项目。 Android ID 状态 默认 @@ -109,4 +114,27 @@ 导入自定义配置 从文件导入设备配置 序列号 + View source code + View project source code on GitHub + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values-zh-rTW/strings.xml b/play-services-core/src/main/res/values-zh-rTW/strings.xml index ee2cf8bb..f4105881 100644 --- a/play-services-core/src/main/res/values-zh-rTW/strings.xml +++ b/play-services-core/src/main/res/values-zh-rTW/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - Vanced microG 設置 + Vanced+ microG 設置 請稍等一下… 管理Google帳號 很抱歉… @@ -42,6 +42,11 @@ 傳送C2DM資訊到其他應用. 與Google伺服器交換並接收同步訊息 授予額外權限給Google服務 + Google Account Manager not fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history Google 裝置註冊 連網通知推播 開啟 @@ -71,12 +76,12 @@ 新增 Google 帳戶 接收應用的推播通知 連網通知推播是第三方應用推送通知的方式, 若欲啟用, 您必須先註冊裝置. - 關於 Vanced microG - 於桌面隱藏 Vanced microG + 關於 Vanced+ microG + 於桌面隱藏 Vanced+ microG 華為 裝置註冊 開啟後將產生一組屬於此裝置的識別碼 -Vanced microG 會把 Google 帳戶名稱以外的識別位 +Vanced+ microG 會把 Google 帳戶名稱以外的識別位 從註冊資訊中移除 Android ID 狀態 @@ -117,4 +122,27 @@ Vanced microG 會把 Google 帳戶名稱以外的識別位 匯入自訂檔: 從文管匯入裝置設定檔 序號 + View source code + View project source code on GitHub + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them.]]> + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values/colors.xml b/play-services-core/src/main/res/values/colors.xml index 85c87c33..12402999 100755 --- a/play-services-core/src/main/res/values/colors.xml +++ b/play-services-core/src/main/res/values/colors.xml @@ -16,9 +16,9 @@ - #ff4285f4 - #ff3367d6 - #ffffab40 + #ff9a82db + #ff9a82db + #ff9a82db #ff9a82db diff --git a/play-services-core/src/main/res/values/resources.xml b/play-services-core/src/main/res/values/resources.xml index 73f39f80..36eb9eb3 100644 --- a/play-services-core/src/main/res/values/resources.xml +++ b/play-services-core/src/main/res/values/resources.xml @@ -1,6 +1,6 @@ - Vanced microG + Vanced+ microG \ No newline at end of file diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index bf394cbe..236f1198 100755 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -16,8 +16,8 @@ --> - Vanced microG - Setup Vanced microG. + Vanced+ microG + Setup Vanced+ microG. Just a sec… Google Account Manager @@ -40,10 +40,19 @@ Try again later." This can take a couple of minutes." listen to internal status broadcasts + read Google service configuration listen to C2DM messages send C2DM messages to other apps exchange messages and receive sync notifications from Google servers Extended access to Google services + Google Account Manager not yet fully implemented + Manage your Google account + Manage your Google account information + Manage YouTube activities + Manage YouTube activities such as watch or search history + View source code + View project source code on GitLab + Google device registration Cloud Messaging @@ -73,6 +82,8 @@ This can take a couple of minutes." Personal info & privacy Sign-in & security + Authenticate with device registration + When disabled, authentication requests won\'t be linked to the device registration, which allows unauthorized devices to sign in, but may have unforeseen consequences. Trust Google for app permissions When disabled, the user is asked before an apps authorization request is sent to Google. Some applications will fail to use the Google account if this is disabled. Allow apps to find accounts @@ -83,13 +94,13 @@ This can take a couple of minutes." Receive push notifications Cloud Messaging is a push notification provider used by many third-party applications. To use it you must enable device registration. - About Vanced microG + About Vanced+ microG Hide icon from launcher Huawei Register device - Registers your device to Google services and creates a unique device identifier. Vanced microG strips identifying bits other than your Google account name from registration data. + Registers your device to Google services and creates a unique device identifier. Vanced+ microG strips identifying bits other than your Google account name from registration data. Android ID Status @@ -129,4 +140,26 @@ This can take a couple of minutes." Import custom profile Import device profile from file Serial + To remove signed in Google account, go to Device Settings -> Account. + Check for Vanced+ microG updates on GitHub + Check for updates + If disabled, all connections to Google servers usually done by Vanced+ microG will be denied. This overrides service specific settings. + Allow connecting to Google servers + Requires Orbot to be installed\nNot yet supported + Connect to Google via Tor network + Build date + Your data in YouTube + Browse or delete your YouTube activity, and discover how your data makes YouTube and other Google services work better for you + Want to remove your Google account from Vanced+ microG? + Account to remove them]]> or click here. + Check for Vanced+ microG updates on GitLab + View project source code on GitLab + Check connection + Check your device internet connection + Back to YouTube Vanced+ + Tap to exit Vanced+ microG and back to YouTube Vanced+ (if installed) + Package information + Manage signed in Google accounts + This will open your device settings ! + Miscellaneous settings diff --git a/play-services-core/src/main/res/values/themes.xml b/play-services-core/src/main/res/values/themes.xml index d5cb5705..8e651928 100755 --- a/play-services-core/src/main/res/values/themes.xml +++ b/play-services-core/src/main/res/values/themes.xml @@ -22,6 +22,15 @@ @color/m3_theme_primary_dark + + + + + + + + diff --git a/play-services-core/src/main/res/xml/authenticator.xml b/play-services-core/src/main/res/xml/authenticator.xml index 72809e56..52772beb 100644 --- a/play-services-core/src/main/res/xml/authenticator.xml +++ b/play-services-core/src/main/res/xml/authenticator.xml @@ -17,7 +17,7 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/play-services-core/src/main/res/xml/preferences_auth.xml b/play-services-core/src/main/res/xml/preferences_auth.xml index 7ff14e56..18007889 100755 --- a/play-services-core/src/main/res/xml/preferences_auth.xml +++ b/play-services-core/src/main/res/xml/preferences_auth.xml @@ -18,16 +18,30 @@ - + - + - + + + + diff --git a/play-services-core/src/main/res/xml/preferences_device_registration.xml b/play-services-core/src/main/res/xml/preferences_device_registration.xml index ecd4b469..3fa45eb5 100755 --- a/play-services-core/src/main/res/xml/preferences_device_registration.xml +++ b/play-services-core/src/main/res/xml/preferences_device_registration.xml @@ -5,7 +5,14 @@ --> + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + + @@ -13,16 +20,11 @@ android:key="pref_device_profile" android:persistent="false" android:title="@string/profile_name" - tools:summary="Automatic (Google Pixel 3, Android 11)" /> + tools:summary="Automatic" /> - + tools:summary="Last registration: XX hours ago" /> + tools:summary="" /> + - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/play-services-core/src/main/res/xml/preferences_push_notifications.xml b/play-services-core/src/main/res/xml/preferences_push_notifications.xml index 20862ae7..416cfac8 100755 --- a/play-services-core/src/main/res/xml/preferences_push_notifications.xml +++ b/play-services-core/src/main/res/xml/preferences_push_notifications.xml @@ -7,6 +7,11 @@ + + @@ -29,6 +34,13 @@ android:selectable="false" android:title="@string/prefcat_status_title" tools:summary="Connected since 15 minutes ago" /> + + + + + diff --git a/play-services-core/src/main/res/xml/preferences_start.xml b/play-services-core/src/main/res/xml/preferences_start.xml index 363fa233..e7372cee 100755 --- a/play-services-core/src/main/res/xml/preferences_start.xml +++ b/play-services-core/src/main/res/xml/preferences_start.xml @@ -16,14 +16,14 @@ - + + android:icon="@drawable/ic_manage_account" + android:title="@string/account_manager_title" + android:summary="@string/manage_google_account_summary"> + android:targetClass="org.microg.gms.ui.GoogleMoreFragment$AsActivity" + android:targetPackage="@string/var_pref_start_gms_package" /> - - - - + + + + + + + + diff --git a/play-services-core/src/main/res/xml/profile_googlevr.xml b/play-services-core/src/main/res/xml/profile_googlevr.xml new file mode 100644 index 00000000..1d0c7fba --- /dev/null +++ b/play-services-core/src/main/res/xml/profile_googlevr.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/xml/profile_lineage_xperiaxz2.xml b/play-services-core/src/main/res/xml/profile_lineage_xperiaxz2.xml new file mode 100644 index 00000000..6aa36bfa --- /dev/null +++ b/play-services-core/src/main/res/xml/profile_lineage_xperiaxz2.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/xml/profile_projectinfinityx_redminote7.xml b/play-services-core/src/main/res/xml/profile_projectinfinityx_redminote7.xml new file mode 100644 index 00000000..f92014ae --- /dev/null +++ b/play-services-core/src/main/res/xml/profile_projectinfinityx_redminote7.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/xml/profile_sdk_gphone64_arm64_33.xml b/play-services-core/src/main/res/xml/profile_sdk_gphone64_arm64_33.xml new file mode 100644 index 00000000..62d223c5 --- /dev/null +++ b/play-services-core/src/main/res/xml/profile_sdk_gphone64_arm64_33.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/play-services-iid-api/build.gradle b/play-services-iid-api/build.gradle index 603c53b4..34ed3abb 100644 --- a/play-services-iid-api/build.gradle +++ b/play-services-iid-api/build.gradle @@ -26,9 +26,13 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } + buildFeatures { + aidl true + } + namespace 'org.microg.gms.iid.api' } dependencies { diff --git a/play-services-iid-api/src/main/AndroidManifest.xml b/play-services-iid-api/src/main/AndroidManifest.xml index e979258e..ca617cfb 100644 --- a/play-services-iid-api/src/main/AndroidManifest.xml +++ b/play-services-iid-api/src/main/AndroidManifest.xml @@ -15,4 +15,4 @@ ~ limitations under the License. --> - \ No newline at end of file + \ No newline at end of file diff --git a/play-services-iid-api/src/main/java/com/google/android/gms/iid/MessengerCompat.java b/play-services-iid-api/src/main/java/com/google/android/gms/iid/MessengerCompat.java index 1cc87ae3..7aebf9b4 100644 --- a/play-services-iid-api/src/main/java/com/google/android/gms/iid/MessengerCompat.java +++ b/play-services-iid-api/src/main/java/com/google/android/gms/iid/MessengerCompat.java @@ -26,7 +26,7 @@ import android.os.Parcelable; import android.os.RemoteException; public class MessengerCompat implements Parcelable { - private final Messenger messenger; + private Messenger messenger; private IMessengerCompat messengerCompat; public MessengerCompat(IBinder binder) { @@ -48,7 +48,7 @@ public class MessengerCompat implements Parcelable { } public IBinder getBinder() { - return messenger.getBinder(); + return messenger != null ? messenger.getBinder() : messengerCompat.asBinder(); } @Override @@ -57,7 +57,8 @@ public class MessengerCompat implements Parcelable { } public void send(Message message) throws RemoteException { - messenger.send(message); + if (messenger != null) messenger.send(message); + else messengerCompat.send(message); } @Override diff --git a/play-services-tasks/build.gradle b/play-services-tasks/build.gradle index 0eb84bbf..068aeb92 100644 --- a/play-services-tasks/build.gradle +++ b/play-services-tasks/build.gradle @@ -22,9 +22,10 @@ android { } compileOptions { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 17 + targetCompatibility = 17 } + namespace 'org.microg.gms.tasks' } dependencies { diff --git a/play-services-tasks/src/main/AndroidManifest.xml b/play-services-tasks/src/main/AndroidManifest.xml index c7b833fe..c256eb44 100644 --- a/play-services-tasks/src/main/AndroidManifest.xml +++ b/play-services-tasks/src/main/AndroidManifest.xml @@ -4,4 +4,4 @@ ~ SPDX-License-Identifier: Apache-2.0 --> - + diff --git a/proguard.flags b/proguard.flags index fba8c5e2..7e287ce2 100644 --- a/proguard.flags +++ b/proguard.flags @@ -11,16 +11,24 @@ -dontwarn com.caverock.androidsvg.** -dontwarn org.slf4j.** -dontwarn org.codehaus.jackson.** +-dontwarn com.android.location.provider.** +-dontwarn java.lang.instrument.ClassFileTransformer +-dontwarn sun.misc.SignalHandler +-dontwarn com.android.org.conscrypt.SSLParametersImpl +-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl # Disable ProGuard Notes, they won't help here -dontnote # Keep dynamically loaded GMS classes -keep public class com.google.android.gms.common.security.ProviderInstallerImpl { public *; } +-keep public class com.google.android.gms.dynamic.IObjectWrapper { public *; } +-keep public class com.google.android.gms.dynamite.descriptors.** { public *; } # Keep AutoSafeParcelables -keep public class * extends org.microg.safeparcel.AutoSafeParcelable { @org.microg.safeparcel.SafeParceled *; + (...); } # Keep form data @@ -29,7 +37,12 @@ } # Keep our stuff --keep class org.microg.** { *; } +-keep class org.microg** { *; } +-keep class com.google.android.gms** { *; } +-keep class com.mgoogle** { *; } +-keepdirectories org/microg** +-keepdirectories com/google/android/gms** +-keepdirectories com/mgoogle** # Keep asInterface method cause it's accessed from SafeParcel -keepattributes InnerClasses diff --git a/settings.gradle b/settings.gradle index af7e406b..78cd762d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,5 @@ +include ':fake-signature' + include ':play-services-basement' include ':play-services-tasks'