From 1b86f7c38cab7576434ae4e3b49e620d96e677dd Mon Sep 17 00:00:00 2001 From: Cuynu Date: Thu, 28 Mar 2024 23:52:45 +0700 Subject: [PATCH] revert GmsCore microG upstream --- .gitignore | 14 + Android.mk | 41 + LICENSE | 177 ++++ LICENSES/Apache-2.0.txt | 208 +++++ LICENSES/CC-BY-4.0.txt | 324 +++++++ LICENSES/CC0-1.0.txt | 121 +++ build.gradle | 68 ++ gradle.properties | 5 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58910 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 233 +++++ gradlew.bat | 104 +++ play-services-api/build.gradle | 42 + play-services-api/gradle.properties | 33 + .../android/auth/IAuthManagerService.aidl | 23 + .../gms/auth/AccountChangeEventsRequest.aidl | 3 + .../gms/auth/AccountChangeEventsResponse.aidl | 3 + .../gms/auth/GetHubTokenInternalResponse.aidl | 3 + .../android/gms/auth/GetHubTokenRequest.aidl | 3 + .../gms/auth/HasCababilitiesRequest.aidl | 3 + .../gms/auth/api/credentials/Credential.aidl | 3 + .../api/credentials/CredentialRequest.aidl | 3 + .../credentials/internal/DeleteRequest.aidl | 3 + .../internal/GeneratePasswordRequest.aidl | 3 + .../internal/ICredentialsCallbacks.aidl | 10 + .../internal/ICredentialsService.aidl | 15 + .../api/credentials/internal/SaveRequest.aidl | 3 + .../gms/auth/api/internal/IAuthCallbacks.aidl | 8 + .../gms/auth/api/internal/IAuthService.aidl | 11 + .../gms/auth/api/proxy/ProxyRequest.aidl | 3 + .../gms/auth/api/proxy/ProxyResponse.aidl | 3 + .../gms/auth/appcert/IAppCertService.aidl | 6 + .../gms/checkin/internal/ICheckinService.aidl | 7 + .../internal/GoogleCertificatesQuery.aidl | 3 + .../gms/common/internal/ICertData.aidl | 8 + .../internal/IGoogleCertificatesApi.aidl | 12 + .../common/internal/ISignInButtonCreator.aidl | 7 + .../common/server/FavaDiagnosticsEntity.aidl | 3 + .../android/gms/http/IGoogleHttpService.aidl | 7 + .../gms/people/internal/IPeopleCallbacks.aidl | 12 + .../gms/people/internal/IPeopleService.aidl | 44 + .../android/gms/auth/AccountChangeEvent.java | 23 + .../gms/auth/AccountChangeEventsRequest.java | 45 + .../gms/auth/AccountChangeEventsResponse.java | 27 + .../gms/auth/GetHubTokenInternalResponse.java | 14 + .../android/gms/auth/GetHubTokenRequest.java | 18 + .../gms/auth/HasCababilitiesRequest.java | 16 + .../google/android/gms/auth/TokenData.java | 62 ++ .../gms/auth/api/credentials/Credential.java | 160 ++++ .../credentials/CredentialPickerConfig.java | 67 ++ .../api/credentials/CredentialRequest.java | 45 + .../gms/auth/api/credentials/HintRequest.java | 92 ++ .../gms/auth/api/credentials/IdToken.java | 36 + .../api/credentials/IdentityProviders.java | 15 + .../credentials/internal/DeleteRequest.java | 23 + .../internal/GeneratePasswordRequest.java | 23 + .../api/credentials/internal/SaveRequest.java | 23 + .../gms/auth/api/proxy/ProxyRequest.java | 24 + .../gms/auth/api/proxy/ProxyResponse.java | 15 + .../AccountRecoveryGuidanceRequest.java | 44 + .../dataservice/AccountRemovalRequest.java | 44 + .../ConfirmCredentialsRequest.java | 24 + .../firstparty/dataservice/TokenRequest.java | 42 + .../ConfirmCredentialsWorkflowRequest.java | 34 + .../firstparty/shared/AccountCredentials.java | 36 + .../firstparty/shared/AppDescription.java | 24 + .../firstparty/shared/CaptchaSolution.java | 25 + .../android/gms/common/internal/CertData.java | 66 ++ .../internal/GoogleCertificatesQuery.java | 81 ++ .../common/server/FavaDiagnosticsEntity.java | 24 + .../internal/ParcelableLoadImageOptions.java | 24 + .../gms/people/model/AccountMetadata.java | 24 + .../gms/people/model/AvatarReference.java | 27 + play-services-base-api/build.gradle | 44 + .../common/api/internal/IStatusCallback.aidl | 7 + .../android/gms/common/data/DataHolder.aidl | 3 + .../android/gms/common/images/WebImage.aidl | 3 + .../auth/api/signin/GoogleSignInAccount.java | 12 + .../android/gms/common/data/DataHolder.java | 314 +++++++ .../android/gms/common/images/WebImage.java | 68 ++ play-services-base-core/build.gradle | 37 + .../src/main/AndroidManifest.xml | 13 + .../microg/gms/AbstractGmsServiceBroker.java | 101 +++ .../main/java/org/microg/gms/BaseService.java | 74 ++ .../java/org/microg/gms/DummyService.java | 36 + .../gms/common/DeviceConfiguration.java | 161 ++++ .../microg/gms/common/DeviceIdentifier.java | 63 ++ .../gms/common/ForegroundServiceContext.java | 120 +++ .../gms/common/ForegroundServiceInfo.java | 18 + .../org/microg/gms/common/HttpFormClient.java | 272 ++++++ .../microg/gms/common/MultiListenerProxy.java | 224 +++++ .../org/microg/gms/common/NonCancelToken.java | 28 + .../org/microg/gms/common/PackageUtils.java | 373 ++++++++ .../java/org/microg/gms/common/PhoneInfo.java | 35 + .../gms/common/RemoteListenerProxy.java | 135 +++ .../java/org/microg/gms/common/Utils.java | 67 ++ .../microg/gms/common/PackageSpoofUtils.kt | 155 ++++ .../kotlin/org/microg/gms/profile/Build.kt | 98 +++ .../org/microg/gms/profile/ProfileManager.kt | 341 ++++++++ .../org/microg/gms/utils/BinderUtils.kt | 24 + .../microg/gms/utils/FileXmlResourceParser.kt | 127 +++ .../microg/mgms/settings/SettingsContract.kt | 125 +++ .../microg/mgms/settings/SettingsProvider.kt | 280 ++++++ .../drawable/ic_foreground_notification.xml | 17 + .../src/main/res/values-ar/strings.xml | 11 + .../src/main/res/values-az/strings.xml | 11 + .../src/main/res/values-be/strings.xml | 11 + .../src/main/res/values-bn/strings.xml | 11 + .../src/main/res/values-de/strings.xml | 10 + .../src/main/res/values-es-rES/strings.xml | 11 + .../src/main/res/values-fr/strings.xml | 11 + .../src/main/res/values-he/strings.xml | 11 + .../src/main/res/values-hi/strings.xml | 11 + .../src/main/res/values-hu/strings.xml | 11 + .../src/main/res/values-id/strings.xml | 11 + .../src/main/res/values-it/strings.xml | 11 + .../src/main/res/values-ja/strings.xml | 11 + .../src/main/res/values-ko/strings.xml | 11 + .../src/main/res/values-lt/strings.xml | 11 + .../src/main/res/values-pt-rBR/strings.xml | 11 + .../src/main/res/values-ru/strings.xml | 11 + .../src/main/res/values-th/strings.xml | 11 + .../src/main/res/values-tr/strings.xml | 11 + .../src/main/res/values-uk/strings.xml | 11 + .../src/main/res/values-vi/strings.xml | 11 + .../src/main/res/values-zh-rCN/strings.xml | 11 + .../src/main/res/values-zh-rTW/strings.xml | 12 + .../src/main/res/values/strings.xml | 12 + play-services-base/build.gradle | 44 + play-services-base/gradle.properties | 33 + .../src/main/AndroidManifest.xml | 30 + .../gms/common/GoogleApiAvailability.java | 53 ++ .../gms/common/GooglePlayServicesClient.java | 42 + .../gms/common/GooglePlayServicesUtil.java | 135 +++ .../android/gms/common/api/AccountInfo.java | 24 + .../google/android/gms/common/api/Api.java | 70 ++ .../android/gms/common/api/GoogleApi.java | 28 + .../gms/common/api/GoogleApiActivity.java | 6 + .../gms/common/api/GoogleApiClient.java | 219 +++++ .../android/gms/common/api/HasApiKey.java | 9 + .../android/gms/common/api/PendingResult.java | 20 + .../gms/common/api/internal/ApiKey.java | 12 + .../android/gms/common/data/DataBuffer.java | 67 ++ .../gms/common/api/AbstractPendingResult.java | 29 + .../org/microg/gms/common/api/ApiClient.java | 27 + .../gms/common/api/ApiClientBuilder.java | 26 + .../gms/common/api/ApiClientSettings.java | 9 + .../gms/common/api/ConnectionCallbacks.java | 41 + .../gms/common/api/GoogleApiClientImpl.java | 145 ++++ .../gms/common/api/GoogleApiManager.java | 21 + .../api/OnConnectionFailedListener.java | 25 + .../gms/common/api/ResultCallbackHandler.java | 59 ++ .../src/main/res/values/version.xml | 20 + play-services-basement/build.gradle | 58 ++ play-services-basement/gradle.properties | 33 + .../google/android/gms/common/api/Status.aidl | 3 + .../common/internal/GetServiceRequest.aidl | 3 + .../gms/common/internal/ICancelToken.aidl | 5 + .../gms/common/internal/IGmsCallbacks.aidl | 7 + .../common/internal/IGmsServiceBroker.aidl | 15 + .../internal/ValidateAccountRequest.aidl | 3 + .../android/gms/dynamic/IObjectWrapper.aidl | 8 + .../android/gms/common/ConnectionResult.java | 274 ++++++ .../google/android/gms/common/Feature.java | 36 + .../gms/common/api/CommonStatusCodes.java | 37 + .../android/gms/common/api/Releasable.java | 24 + .../google/android/gms/common/api/Result.java | 27 + .../gms/common/api/ResultCallback.java | 33 + .../google/android/gms/common/api/Scope.java | 69 ++ .../google/android/gms/common/api/Status.java | 97 +++ .../gms/common/internal/BinderWrapper.java | 51 ++ .../common/internal/GetServiceRequest.java | 88 ++ .../internal/ValidateAccountRequest.java | 23 + .../android/gms/dynamic/ObjectWrapper.java | 100 +++ .../org/microg/gms/auth/AuthConstants.java | 26 + .../java/org/microg/gms/common/Constants.java | 29 + .../org/microg/gms/common/GmsService.java | 58 ++ .../java/org/microg/gms/common/PublicApi.java | 48 ++ .../java/org/microg/gms/gcm/GcmConstants.java | 44 + .../android/gms/dynamic/ObjectWrapper.kt | 18 + play-services-core-proto/build.gradle | 29 + .../src/main/proto/appcert.proto | 32 + .../src/main/proto/auth.proto | 17 + .../src/main/proto/checkin.proto | 270 ++++++ .../src/main/proto/mcs.proto | 270 ++++++ play-services-core/build.gradle | 107 +++ .../microg-ui-tools/build.gradle | 44 + .../tools/ui/AbstractAboutFragment.java | 151 ++++ .../tools/ui/AbstractDashboardActivity.java | 86 ++ .../tools/ui/AbstractSettingsActivity.java | 57 ++ .../tools/ui/AbstractSettingsFragment.java | 40 + .../java/org/microg/tools/ui/Condition.java | 296 +++++++ .../org/microg/tools/ui/DialogPreference.java | 114 +++ .../tools/ui/ResourceSettingsFragment.java | 39 + .../java/org/microg/tools/ui/SwitchBar.java | 222 +++++ .../ui/SwitchBarResourceSettingsFragment.java | 65 ++ .../org/microg/tools/ui/ToggleSwitch.java | 59 ++ .../src/main/res/drawable/ic_expand_less.xml | 30 + .../src/main/res/drawable/ic_expand_more.xml | 30 + .../res/layout-v21/preference_material.xml | 87 ++ .../src/main/res/layout/about_root.xml | 78 ++ .../src/main/res/layout/condition_card.xml | 135 +++ .../main/res/layout/dashboard_activity.xml | 35 + .../layout/preference_widget_radiobutton.xml | 24 + .../src/main/res/layout/settings_activity.xml | 40 + .../src/main/res/layout/switch_bar.xml | 43 + .../src/main/res/values-ar/strings.xml | 25 + .../src/main/res/values-az/strings.xml | 25 + .../src/main/res/values-be/strings.xml | 25 + .../src/main/res/values-bn/strings.xml | 25 + .../src/main/res/values-cs/strings.xml | 25 + .../src/main/res/values-de/strings.xml | 25 + .../src/main/res/values-eo/strings.xml | 19 + .../src/main/res/values-es-rES/strings.xml | 25 + .../src/main/res/values-es/strings.xml | 19 + .../src/main/res/values-fr/strings.xml | 25 + .../src/main/res/values-he/strings.xml | 25 + .../src/main/res/values-hi/strings.xml | 25 + .../src/main/res/values-hu/strings.xml | 25 + .../src/main/res/values-id/strings.xml | 25 + .../src/main/res/values-it/strings.xml | 25 + .../src/main/res/values-ja/strings.xml | 25 + .../src/main/res/values-ko/strings.xml | 25 + .../src/main/res/values-lt/strings.xml | 25 + .../src/main/res/values-pl/strings.xml | 25 + .../src/main/res/values-pt-rBR/strings.xml | 25 + .../src/main/res/values-ro/strings.xml | 19 + .../src/main/res/values-ru/strings.xml | 25 + .../src/main/res/values-sr/strings.xml | 20 + .../src/main/res/values-th/strings.xml | 23 + .../src/main/res/values-tr/strings.xml | 25 + .../src/main/res/values-uk/strings.xml | 25 + .../src/main/res/values-vi/strings.xml | 25 + .../src/main/res/values-zh-rCN/strings.xml | 25 + .../src/main/res/values-zh-rTW/strings.xml | 25 + .../src/main/res/values/colors.xml | 21 + .../src/main/res/values/strings.xml | 29 + play-services-core/proguard-rules.pro | 61 ++ .../src/main/AndroidManifest.xml | 421 +++++++++ .../src/main/ic_launcher-playstore.png | Bin 0 -> 30880 bytes .../com/google/android/gms/auth/GetToken.java | 30 + .../android/gms/auth/TokenActivity.java | 54 ++ .../gms/common/GoogleCertificatesImpl.java | 70 ++ .../security/ProviderInstallerImpl.java | 225 +++++ .../common/ui/SignInButtonCreatorImpl.java | 33 + .../gms/security/ProviderInstaller.java | 61 ++ .../gms/auth/AccountContentProvider.java | 129 +++ .../java/org/microg/gms/auth/AuthManager.java | 245 ++++++ .../gms/auth/AuthManagerServiceImpl.java | 206 +++++ .../java/org/microg/gms/auth/AuthRequest.java | 196 +++++ .../org/microg/gms/auth/AuthResponse.java | 121 +++ .../org/microg/gms/auth/SignInService.java | 38 + .../gms/auth/login/AssistantActivity.java | 106 +++ .../microg/gms/auth/login/LoginActivity.java | 559 ++++++++++++ .../loginservice/AccountAuthenticator.java | 145 ++++ .../auth/loginservice/GoogleLoginService.java | 41 + .../org/microg/gms/checkin/CheckinClient.java | 170 ++++ .../microg/gms/checkin/CheckinManager.java | 79 ++ .../microg/gms/checkin/CheckinService.java | 130 +++ .../microg/gms/checkin/TriggerReceiver.java | 73 ++ .../java/org/microg/gms/gcm/GcmDatabase.java | 316 +++++++ .../org/microg/gms/gcm/GcmLegacyData.java | 110 +++ .../java/org/microg/gms/gcm/McsConstants.java | 47 ++ .../org/microg/gms/gcm/McsInputStream.java | 186 ++++ .../org/microg/gms/gcm/McsOutputStream.java | 140 +++ .../java/org/microg/gms/gcm/McsService.java | 796 ++++++++++++++++++ .../microg/gms/gcm/PushRegisterManager.java | 170 ++++ .../org/microg/gms/gcm/RegisterRequest.java | 156 ++++ .../org/microg/gms/gcm/RegisterResponse.java | 42 + .../java/org/microg/gms/gcm/SendReceiver.java | 44 + .../org/microg/gms/gcm/TriggerReceiver.java | 115 +++ .../microg/gms/gcm/UnregisterReceiver.java | 47 ++ .../microg/gms/gservices/DatabaseHelper.java | 103 +++ .../org/microg/gms/gservices/GServices.java | 73 ++ .../gms/gservices/GServicesProvider.java | 128 +++ .../microg/gms/people/ContactSyncService.java | 44 + .../org/microg/gms/people/DatabaseHelper.java | 125 +++ .../org/microg/gms/people/PeopleManager.java | 126 +++ .../org/microg/gms/people/PeopleService.java | 38 + .../microg/gms/people/PeopleServiceImpl.java | 157 ++++ .../java/org/microg/gms/ui/AboutFragment.java | 49 ++ .../gms/ui/AccountSettingsActivity.java | 72 ++ .../org/microg/gms/ui/GoogleMoreFragment.java | 30 + .../org/microg/gms/ui/SettingsActivity.java | 40 + .../gms/ui/SettingsDashboardActivity.java | 36 + .../mgms/settings/GoogleSettingsProvider.java | 54 ++ .../microg/tools/AccountPickerActivity.java | 51 ++ .../kotlin/org/microg/gms/auth/AuthPrefs.kt | 27 + .../microg/gms/auth/appcert/AppCertManager.kt | 176 ++++ .../microg/gms/auth/appcert/AppCertService.kt | 42 + .../credentials/CredentialPickerActivity.kt | 38 + .../auth/credentials/CredentialsService.kt | 56 ++ .../microg/gms/auth/proxy/AuthProxyService.kt | 63 ++ .../org/microg/gms/checkin/CheckinPrefs.kt | 52 ++ .../org/microg/gms/checkin/LastCheckinInfo.kt | 90 ++ .../org/microg/gms/checkin/ServiceInfo.kt | 46 + .../kotlin/org/microg/gms/gcm/GcmPrefs.kt | 191 +++++ .../org/microg/gms/gcm/PushRegisterService.kt | 350 ++++++++ .../kotlin/org/microg/gms/gcm/ServiceInfo.kt | 89 ++ .../org/microg/gms/ui/AppIconPreference.kt | 31 + .../kotlin/org/microg/gms/ui/Constants.kt | 8 + .../gms/ui/DeviceRegistrationFragment.kt | 55 ++ .../DeviceRegistrationPreferencesFragment.kt | 164 ++++ .../gms/ui/PreferenceSwitchBarCallback.kt | 10 + .../ui/PushNotificationAdvancedFragment.kt | 113 +++ .../gms/ui/PushNotificationAllAppsFragment.kt | 111 +++ .../gms/ui/PushNotificationAppFragment.kt | 62 ++ .../PushNotificationAppPreferencesFragment.kt | 137 +++ .../microg/gms/ui/PushNotificationFragment.kt | 79 ++ .../ui/PushNotificationPreferencesFragment.kt | 128 +++ .../org/microg/gms/ui/SettingsFragment.kt | 84 ++ .../org/microg/gms/ui/TextPreference.kt | 35 + .../main/kotlin/org/microg/gms/ui/Utils.kt | 59 ++ .../src/main/res/drawable/dots_horizontal.xml | 10 + .../src/main/res/drawable/ic_add_account.xml | 10 + .../drawable/ic_baseline_hide_source_24.xml | 10 + .../src/main/res/drawable/ic_cloud_bell.xml | 12 + .../src/main/res/drawable/ic_device_login.xml | 10 + .../src/main/res/drawable/ic_expand_apps.xml | 11 + .../src/main/res/drawable/ic_info_outline.xml | 10 + .../main/res/drawable/ic_manage_account.xml | 16 + .../layout/device_registration_fragment.xml | 40 + .../src/main/res/layout/login_assistant.xml | 99 +++ .../res/layout/login_assistant_loading.xml | 38 + .../layout/preference_category_no_label.xml | 9 + .../res/layout/preference_progress_bar.xml | 17 + .../main/res/layout/preference_switch_bar.xml | 71 ++ .../layout/push_notification_app_fragment.xml | 81 ++ .../res/layout/push_notification_fragment.xml | 44 + .../res/layout/settings_root_activity.xml | 19 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3963 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 4470 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3963 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2450 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2544 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2450 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 5547 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 6284 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 5547 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 9071 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 11328 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 9071 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 13090 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 16911 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 13090 bytes .../src/main/res/navigation/nav_settings.xml | 62 ++ .../src/main/res/values-ar/arrays.xml | 31 + .../src/main/res/values-ar/plurals.xml | 25 + .../src/main/res/values-ar/strings.xml | 143 ++++ .../src/main/res/values-az/arrays.xml | 31 + .../src/main/res/values-az/plurals.xml | 25 + .../src/main/res/values-az/strings.xml | 143 ++++ .../src/main/res/values-be/arrays.xml | 31 + .../src/main/res/values-be/plurals.xml | 25 + .../src/main/res/values-be/strings.xml | 143 ++++ .../src/main/res/values-bn/arrays.xml | 31 + .../src/main/res/values-bn/plurals.xml | 25 + .../src/main/res/values-bn/strings.xml | 144 ++++ .../src/main/res/values-cs/strings.xml | 140 +++ .../src/main/res/values-es-rES/arrays.xml | 31 + .../src/main/res/values-es-rES/plurals.xml | 25 + .../src/main/res/values-es-rES/strings.xml | 143 ++++ .../src/main/res/values-fr/arrays.xml | 31 + .../src/main/res/values-fr/plurals.xml | 25 + .../src/main/res/values-fr/strings.xml | 143 ++++ .../src/main/res/values-he/strings.xml | 90 ++ .../src/main/res/values-hi/arrays.xml | 31 + .../src/main/res/values-hi/plurals.xml | 25 + .../src/main/res/values-hi/strings.xml | 142 ++++ .../src/main/res/values-id/arrays.xml | 31 + .../src/main/res/values-id/plurals.xml | 25 + .../src/main/res/values-id/strings.xml | 141 ++++ .../src/main/res/values-it/arrays.xml | 31 + .../src/main/res/values-it/plurals.xml | 25 + .../src/main/res/values-it/strings.xml | 140 +++ .../src/main/res/values-ja/arrays.xml | 31 + .../src/main/res/values-ja/plurals.xml | 25 + .../src/main/res/values-ja/strings.xml | 143 ++++ .../src/main/res/values-ko/arrays.xml | 31 + .../src/main/res/values-ko/plurals.xml | 25 + .../src/main/res/values-ko/strings.xml | 143 ++++ .../src/main/res/values-lt/arrays.xml | 31 + .../src/main/res/values-lt/plurals.xml | 25 + .../src/main/res/values-lt/strings.xml | 143 ++++ .../src/main/res/values-night-v31/themes.xml | 14 + .../src/main/res/values-night/themes.xml | 8 + .../src/main/res/values-pt-rBR/arrays.xml | 31 + .../src/main/res/values-pt-rBR/plurals.xml | 25 + .../src/main/res/values-pt-rBR/strings.xml | 161 ++++ .../src/main/res/values-ru/arrays.xml | 31 + .../src/main/res/values-ru/plurals.xml | 25 + .../src/main/res/values-ru/strings.xml | 143 ++++ .../src/main/res/values-th/strings.xml | 131 +++ .../src/main/res/values-tr/arrays.xml | 31 + .../src/main/res/values-tr/plurals.xml | 25 + .../src/main/res/values-tr/strings.xml | 143 ++++ .../src/main/res/values-uk/arrays.xml | 31 + .../src/main/res/values-uk/plurals.xml | 25 + .../src/main/res/values-uk/strings.xml | 143 ++++ .../src/main/res/values-v31/themes.xml | 13 + .../src/main/res/values-vi/arrays.xml | 31 + .../src/main/res/values-vi/plurals.xml | 25 + .../src/main/res/values-vi/strings.xml | 144 ++++ .../src/main/res/values-zh-rCN/arrays.xml | 31 + .../src/main/res/values-zh-rCN/plurals.xml | 25 + .../src/main/res/values-zh-rCN/strings.xml | 142 ++++ .../src/main/res/values-zh-rTW/arrays.xml | 31 + .../src/main/res/values-zh-rTW/plurals.xml | 25 + .../src/main/res/values-zh-rTW/strings.xml | 150 ++++ .../src/main/res/values/arrays.xml | 41 + .../src/main/res/values/bools.xml | 18 + .../src/main/res/values/colors.xml | 24 + .../src/main/res/values/dimens.xml | 17 + .../res/values/ic_launcher_background.xml | 4 + .../src/main/res/values/permissions.xml | 20 + .../src/main/res/values/plurals.xml | 23 + .../src/main/res/values/resources.xml | 6 + .../src/main/res/values/signature.xml | 21 + .../src/main/res/values/strings.xml | 165 ++++ .../src/main/res/values/themes.xml | 34 + .../src/main/res/xml/authenticator.xml | 24 + .../main/res/xml/network_security_config.xml | 14 + .../src/main/res/xml/preferences_account.xml | 48 ++ .../src/main/res/xml/preferences_auth.xml | 33 + .../xml/preferences_device_registration.xml | 47 ++ .../main/res/xml/preferences_gcm_advanced.xml | 57 ++ .../main/res/xml/preferences_google_more.xml | 56 ++ .../xml/preferences_push_notifications.xml | 46 + ...references_push_notifications_all_apps.xml | 39 + .../preferences_push_notifications_app.xml | 43 + .../src/main/res/xml/preferences_start.xml | 76 ++ play-services-iid-api/build.gradle | 40 + play-services-iid-api/gradle.properties | 33 + .../android/gms/iid/IMessengerCompat.aidl | 7 + .../android/gms/iid/MessengerCompat.java | 94 +++ play-services-tasks/build.gradle | 37 + play-services-tasks/gradle.properties | 33 + .../android/gms/tasks/CancellationToken.java | 88 ++ .../gms/tasks/CancellationTokenSource.java | 46 + .../android/gms/tasks/Continuation.java | 62 ++ .../DuplicateTaskCompletionException.java | 32 + .../android/gms/tasks/OnCanceledListener.java | 22 + .../android/gms/tasks/OnCompleteListener.java | 26 + .../android/gms/tasks/OnFailureListener.java | 27 + .../android/gms/tasks/OnSuccessListener.java | 26 + .../gms/tasks/OnTokenCanceledListener.java | 24 + .../gms/tasks/RuntimeExecutionException.java | 25 + .../gms/tasks/SuccessContinuation.java | 42 + .../com/google/android/gms/tasks/Task.java | 292 +++++++ .../gms/tasks/TaskCompletionSource.java | 115 +++ .../android/gms/tasks/TaskExecutors.java | 33 + .../com/google/android/gms/tasks/Tasks.java | 63 ++ .../gms/tasks/CancellationTokenImpl.java | 32 + .../microg/gms/tasks/CancelledExecutor.java | 34 + .../microg/gms/tasks/CompletedExecutor.java | 33 + .../gms/tasks/ContinuationExecutor.java | 46 + .../gms/tasks/ContinuationWithExecutor.java | 52 ++ .../org/microg/gms/tasks/FailureExecutor.java | 33 + .../tasks/SuccessContinuationExecutor.java | 54 ++ .../org/microg/gms/tasks/SuccessExecutor.java | 33 + .../java/org/microg/gms/tasks/TaskImpl.java | 241 ++++++ .../org/microg/gms/tasks/UpdateExecutor.java | 27 + .../org/microg/gms/tasks/UpdateListener.java | 14 + .../UpdateListenerLifecycleObserver.java | 98 +++ proguard.flags | 52 ++ settings.gradle | 19 + 468 files changed, 28387 insertions(+) create mode 100644 .gitignore create mode 100644 Android.mk create mode 100644 LICENSE create mode 100644 LICENSES/Apache-2.0.txt create mode 100644 LICENSES/CC-BY-4.0.txt create mode 100644 LICENSES/CC0-1.0.txt create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 play-services-api/build.gradle create mode 100644 play-services-api/gradle.properties create mode 100644 play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java create mode 100644 play-services-base-api/build.gradle create mode 100644 play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl create mode 100644 play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl create mode 100644 play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl create mode 100644 play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java create mode 100644 play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java create mode 100644 play-services-base-api/src/main/java/com/google/android/gms/common/images/WebImage.java create mode 100644 play-services-base-core/build.gradle create mode 100644 play-services-base-core/src/main/AndroidManifest.xml create mode 100644 play-services-base-core/src/main/java/org/microg/gms/AbstractGmsServiceBroker.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/BaseService.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/DummyService.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/DeviceConfiguration.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/DeviceIdentifier.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/ForegroundServiceInfo.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/HttpFormClient.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/NonCancelToken.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/PackageUtils.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/PhoneInfo.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java create mode 100644 play-services-base-core/src/main/java/org/microg/gms/common/Utils.java create mode 100644 play-services-base-core/src/main/kotlin/org/microg/gms/common/PackageSpoofUtils.kt create mode 100644 play-services-base-core/src/main/kotlin/org/microg/gms/profile/Build.kt create mode 100644 play-services-base-core/src/main/kotlin/org/microg/gms/profile/ProfileManager.kt create mode 100644 play-services-base-core/src/main/kotlin/org/microg/gms/utils/BinderUtils.kt create mode 100644 play-services-base-core/src/main/kotlin/org/microg/gms/utils/FileXmlResourceParser.kt create mode 100644 play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsContract.kt create mode 100644 play-services-base-core/src/main/kotlin/org/microg/mgms/settings/SettingsProvider.kt create mode 100644 play-services-base-core/src/main/res/drawable/ic_foreground_notification.xml create mode 100644 play-services-base-core/src/main/res/values-ar/strings.xml create mode 100644 play-services-base-core/src/main/res/values-az/strings.xml create mode 100644 play-services-base-core/src/main/res/values-be/strings.xml create mode 100644 play-services-base-core/src/main/res/values-bn/strings.xml create mode 100644 play-services-base-core/src/main/res/values-de/strings.xml create mode 100644 play-services-base-core/src/main/res/values-es-rES/strings.xml create mode 100644 play-services-base-core/src/main/res/values-fr/strings.xml create mode 100644 play-services-base-core/src/main/res/values-he/strings.xml create mode 100644 play-services-base-core/src/main/res/values-hi/strings.xml create mode 100644 play-services-base-core/src/main/res/values-hu/strings.xml create mode 100644 play-services-base-core/src/main/res/values-id/strings.xml create mode 100644 play-services-base-core/src/main/res/values-it/strings.xml create mode 100644 play-services-base-core/src/main/res/values-ja/strings.xml create mode 100644 play-services-base-core/src/main/res/values-ko/strings.xml create mode 100644 play-services-base-core/src/main/res/values-lt/strings.xml create mode 100644 play-services-base-core/src/main/res/values-pt-rBR/strings.xml create mode 100644 play-services-base-core/src/main/res/values-ru/strings.xml create mode 100644 play-services-base-core/src/main/res/values-th/strings.xml create mode 100644 play-services-base-core/src/main/res/values-tr/strings.xml create mode 100644 play-services-base-core/src/main/res/values-uk/strings.xml create mode 100644 play-services-base-core/src/main/res/values-vi/strings.xml create mode 100644 play-services-base-core/src/main/res/values-zh-rCN/strings.xml create mode 100644 play-services-base-core/src/main/res/values-zh-rTW/strings.xml create mode 100644 play-services-base-core/src/main/res/values/strings.xml create mode 100644 play-services-base/build.gradle create mode 100644 play-services-base/gradle.properties create mode 100644 play-services-base/src/main/AndroidManifest.xml create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/Api.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/HasApiKey.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/internal/ApiKey.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ConnectionCallbacks.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/OnConnectionFailedListener.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java create mode 100644 play-services-base/src/main/res/values/version.xml create mode 100644 play-services-basement/build.gradle create mode 100644 play-services-basement/gradle.properties create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/api/Status.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/Feature.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/api/Result.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/api/Scope.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java create mode 100644 play-services-basement/src/main/java/org/microg/gms/auth/AuthConstants.java create mode 100644 play-services-basement/src/main/java/org/microg/gms/common/Constants.java create mode 100644 play-services-basement/src/main/java/org/microg/gms/common/GmsService.java create mode 100644 play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java create mode 100644 play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java create mode 100644 play-services-basement/src/main/kotlin/com/google/android/gms/dynamic/ObjectWrapper.kt create mode 100644 play-services-core-proto/build.gradle create mode 100644 play-services-core-proto/src/main/proto/appcert.proto create mode 100644 play-services-core-proto/src/main/proto/auth.proto create mode 100644 play-services-core-proto/src/main/proto/checkin.proto create mode 100644 play-services-core-proto/src/main/proto/mcs.proto create mode 100644 play-services-core/build.gradle create mode 100644 play-services-core/microg-ui-tools/build.gradle create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractAboutFragment.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractDashboardActivity.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsFragment.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/Condition.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/DialogPreference.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ResourceSettingsFragment.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBarResourceSettingsFragment.java create mode 100644 play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/ToggleSwitch.java create mode 100644 play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_less.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/drawable/ic_expand_more.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout-v21/preference_material.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout/about_root.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout/condition_card.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout/dashboard_activity.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout/preference_widget_radiobutton.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/layout/switch_bar.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-ar/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-az/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-be/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-bn/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-cs/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-de/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-eo/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-es-rES/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-es/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-fr/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-he/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-hi/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-hu/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-id/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-it/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-ja/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-ko/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-lt/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-pl/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-pt-rBR/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-ro/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-ru/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-sr/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-th/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-tr/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-uk/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-vi/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-zh-rCN/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values-zh-rTW/strings.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values/colors.xml create mode 100644 play-services-core/microg-ui-tools/src/main/res/values/strings.xml create mode 100644 play-services-core/proguard-rules.pro create mode 100644 play-services-core/src/main/AndroidManifest.xml create mode 100644 play-services-core/src/main/ic_launcher-playstore.png create mode 100644 play-services-core/src/main/java/com/google/android/gms/auth/GetToken.java create mode 100644 play-services-core/src/main/java/com/google/android/gms/auth/TokenActivity.java create mode 100644 play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java create mode 100644 play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java create mode 100644 play-services-core/src/main/java/com/google/android/gms/common/ui/SignInButtonCreatorImpl.java create mode 100644 play-services-core/src/main/java/com/google/android/gms/security/ProviderInstaller.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/AuthResponse.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/SignInService.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/login/AssistantActivity.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java create mode 100644 play-services-core/src/main/java/org/microg/gms/auth/loginservice/GoogleLoginService.java create mode 100755 play-services-core/src/main/java/org/microg/gms/checkin/CheckinClient.java create mode 100755 play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java create mode 100755 play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java create mode 100755 play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/GcmLegacyData.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/McsInputStream.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/McsOutputStream.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/McsService.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/RegisterResponse.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/SendReceiver.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/UnregisterReceiver.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gservices/DatabaseHelper.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gservices/GServices.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java create mode 100644 play-services-core/src/main/java/org/microg/gms/people/ContactSyncService.java create mode 100644 play-services-core/src/main/java/org/microg/gms/people/DatabaseHelper.java create mode 100644 play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java create mode 100644 play-services-core/src/main/java/org/microg/gms/people/PeopleService.java create mode 100644 play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java create mode 100755 play-services-core/src/main/java/org/microg/gms/ui/AboutFragment.java create mode 100755 play-services-core/src/main/java/org/microg/gms/ui/AccountSettingsActivity.java create mode 100755 play-services-core/src/main/java/org/microg/gms/ui/GoogleMoreFragment.java create mode 100755 play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java create mode 100755 play-services-core/src/main/java/org/microg/gms/ui/SettingsDashboardActivity.java create mode 100644 play-services-core/src/main/java/org/microg/mgms/settings/GoogleSettingsProvider.java create mode 100755 play-services-core/src/main/java/org/microg/tools/AccountPickerActivity.java create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertManager.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/appcert/AppCertService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialPickerActivity.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/credentials/CredentialsService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/proxy/AuthProxyService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/checkin/CheckinPrefs.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/checkin/LastCheckinInfo.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/checkin/ServiceInfo.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/gcm/GcmPrefs.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/gcm/ServiceInfo.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/AppIconPreference.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/ui/Constants.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/DeviceRegistrationPreferencesFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/PreferenceSwitchBarCallback.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAdvancedFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAllAppsFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationAppPreferencesFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationFragment.kt create mode 100755 play-services-core/src/main/kotlin/org/microg/gms/ui/PushNotificationPreferencesFragment.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/ui/TextPreference.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/ui/Utils.kt create mode 100755 play-services-core/src/main/res/drawable/dots_horizontal.xml create mode 100644 play-services-core/src/main/res/drawable/ic_add_account.xml create mode 100644 play-services-core/src/main/res/drawable/ic_baseline_hide_source_24.xml create mode 100644 play-services-core/src/main/res/drawable/ic_cloud_bell.xml create mode 100644 play-services-core/src/main/res/drawable/ic_device_login.xml create mode 100755 play-services-core/src/main/res/drawable/ic_expand_apps.xml create mode 100755 play-services-core/src/main/res/drawable/ic_info_outline.xml create mode 100644 play-services-core/src/main/res/drawable/ic_manage_account.xml create mode 100755 play-services-core/src/main/res/layout/device_registration_fragment.xml create mode 100755 play-services-core/src/main/res/layout/login_assistant.xml create mode 100755 play-services-core/src/main/res/layout/login_assistant_loading.xml create mode 100755 play-services-core/src/main/res/layout/preference_category_no_label.xml create mode 100755 play-services-core/src/main/res/layout/preference_progress_bar.xml create mode 100755 play-services-core/src/main/res/layout/preference_switch_bar.xml create mode 100755 play-services-core/src/main/res/layout/push_notification_app_fragment.xml create mode 100755 play-services-core/src/main/res/layout/push_notification_fragment.xml create mode 100755 play-services-core/src/main/res/layout/settings_root_activity.xml create mode 100644 play-services-core/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 play-services-core/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 play-services-core/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 play-services-core/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 play-services-core/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 play-services-core/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 play-services-core/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 play-services-core/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 play-services-core/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 play-services-core/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 play-services-core/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 play-services-core/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 play-services-core/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 play-services-core/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 play-services-core/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 play-services-core/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 play-services-core/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100755 play-services-core/src/main/res/navigation/nav_settings.xml create mode 100644 play-services-core/src/main/res/values-ar/arrays.xml create mode 100644 play-services-core/src/main/res/values-ar/plurals.xml create mode 100644 play-services-core/src/main/res/values-ar/strings.xml create mode 100644 play-services-core/src/main/res/values-az/arrays.xml create mode 100644 play-services-core/src/main/res/values-az/plurals.xml create mode 100644 play-services-core/src/main/res/values-az/strings.xml create mode 100644 play-services-core/src/main/res/values-be/arrays.xml create mode 100644 play-services-core/src/main/res/values-be/plurals.xml create mode 100644 play-services-core/src/main/res/values-be/strings.xml create mode 100644 play-services-core/src/main/res/values-bn/arrays.xml create mode 100644 play-services-core/src/main/res/values-bn/plurals.xml create mode 100644 play-services-core/src/main/res/values-bn/strings.xml create mode 100644 play-services-core/src/main/res/values-cs/strings.xml create mode 100644 play-services-core/src/main/res/values-es-rES/arrays.xml create mode 100644 play-services-core/src/main/res/values-es-rES/plurals.xml create mode 100644 play-services-core/src/main/res/values-es-rES/strings.xml create mode 100644 play-services-core/src/main/res/values-fr/arrays.xml create mode 100644 play-services-core/src/main/res/values-fr/plurals.xml create mode 100644 play-services-core/src/main/res/values-fr/strings.xml create mode 100644 play-services-core/src/main/res/values-he/strings.xml create mode 100644 play-services-core/src/main/res/values-hi/arrays.xml create mode 100644 play-services-core/src/main/res/values-hi/plurals.xml create mode 100644 play-services-core/src/main/res/values-hi/strings.xml create mode 100644 play-services-core/src/main/res/values-id/arrays.xml create mode 100644 play-services-core/src/main/res/values-id/plurals.xml create mode 100644 play-services-core/src/main/res/values-id/strings.xml create mode 100644 play-services-core/src/main/res/values-it/arrays.xml create mode 100755 play-services-core/src/main/res/values-it/plurals.xml create mode 100755 play-services-core/src/main/res/values-it/strings.xml create mode 100644 play-services-core/src/main/res/values-ja/arrays.xml create mode 100644 play-services-core/src/main/res/values-ja/plurals.xml create mode 100644 play-services-core/src/main/res/values-ja/strings.xml create mode 100644 play-services-core/src/main/res/values-ko/arrays.xml create mode 100644 play-services-core/src/main/res/values-ko/plurals.xml create mode 100644 play-services-core/src/main/res/values-ko/strings.xml create mode 100644 play-services-core/src/main/res/values-lt/arrays.xml create mode 100644 play-services-core/src/main/res/values-lt/plurals.xml create mode 100644 play-services-core/src/main/res/values-lt/strings.xml create mode 100644 play-services-core/src/main/res/values-night-v31/themes.xml create mode 100644 play-services-core/src/main/res/values-night/themes.xml create mode 100644 play-services-core/src/main/res/values-pt-rBR/arrays.xml create mode 100644 play-services-core/src/main/res/values-pt-rBR/plurals.xml create mode 100644 play-services-core/src/main/res/values-pt-rBR/strings.xml create mode 100644 play-services-core/src/main/res/values-ru/arrays.xml create mode 100644 play-services-core/src/main/res/values-ru/plurals.xml create mode 100644 play-services-core/src/main/res/values-ru/strings.xml create mode 100644 play-services-core/src/main/res/values-th/strings.xml create mode 100644 play-services-core/src/main/res/values-tr/arrays.xml create mode 100644 play-services-core/src/main/res/values-tr/plurals.xml create mode 100644 play-services-core/src/main/res/values-tr/strings.xml create mode 100644 play-services-core/src/main/res/values-uk/arrays.xml create mode 100644 play-services-core/src/main/res/values-uk/plurals.xml create mode 100644 play-services-core/src/main/res/values-uk/strings.xml create mode 100644 play-services-core/src/main/res/values-v31/themes.xml create mode 100644 play-services-core/src/main/res/values-vi/arrays.xml create mode 100644 play-services-core/src/main/res/values-vi/plurals.xml create mode 100644 play-services-core/src/main/res/values-vi/strings.xml create mode 100644 play-services-core/src/main/res/values-zh-rCN/arrays.xml create mode 100644 play-services-core/src/main/res/values-zh-rCN/plurals.xml create mode 100644 play-services-core/src/main/res/values-zh-rCN/strings.xml create mode 100644 play-services-core/src/main/res/values-zh-rTW/arrays.xml create mode 100644 play-services-core/src/main/res/values-zh-rTW/plurals.xml create mode 100644 play-services-core/src/main/res/values-zh-rTW/strings.xml create mode 100755 play-services-core/src/main/res/values/arrays.xml create mode 100755 play-services-core/src/main/res/values/bools.xml create mode 100755 play-services-core/src/main/res/values/colors.xml create mode 100755 play-services-core/src/main/res/values/dimens.xml create mode 100644 play-services-core/src/main/res/values/ic_launcher_background.xml create mode 100755 play-services-core/src/main/res/values/permissions.xml create mode 100755 play-services-core/src/main/res/values/plurals.xml create mode 100644 play-services-core/src/main/res/values/resources.xml create mode 100755 play-services-core/src/main/res/values/signature.xml create mode 100755 play-services-core/src/main/res/values/strings.xml create mode 100755 play-services-core/src/main/res/values/themes.xml create mode 100644 play-services-core/src/main/res/xml/authenticator.xml create mode 100644 play-services-core/src/main/res/xml/network_security_config.xml create mode 100755 play-services-core/src/main/res/xml/preferences_account.xml create mode 100755 play-services-core/src/main/res/xml/preferences_auth.xml create mode 100755 play-services-core/src/main/res/xml/preferences_device_registration.xml create mode 100755 play-services-core/src/main/res/xml/preferences_gcm_advanced.xml create mode 100755 play-services-core/src/main/res/xml/preferences_google_more.xml create mode 100755 play-services-core/src/main/res/xml/preferences_push_notifications.xml create mode 100755 play-services-core/src/main/res/xml/preferences_push_notifications_all_apps.xml create mode 100755 play-services-core/src/main/res/xml/preferences_push_notifications_app.xml create mode 100755 play-services-core/src/main/res/xml/preferences_start.xml create mode 100644 play-services-iid-api/build.gradle create mode 100644 play-services-iid-api/gradle.properties create mode 100644 play-services-iid-api/src/main/aidl/com/google/android/gms/iid/IMessengerCompat.aidl create mode 100644 play-services-iid-api/src/main/java/com/google/android/gms/iid/MessengerCompat.java create mode 100644 play-services-tasks/build.gradle create mode 100644 play-services-tasks/gradle.properties create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationToken.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationTokenSource.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/DuplicateTaskCompletionException.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCanceledListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnTokenCanceledListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/SuccessContinuation.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskExecutors.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/CancellationTokenImpl.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/CancelledExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/CompletedExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationWithExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/FailureExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessContinuationExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListener.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListenerLifecycleObserver.java create mode 100644 proguard.flags create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ec5fff5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +gen/ +bin/ +build/ +.gradle/ +.idea/ +user.gradle +local.properties +.directory +play-services-core/release + +.settings +.classpath +.project \ No newline at end of file diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..2f7c68b6 --- /dev/null +++ b/Android.mk @@ -0,0 +1,41 @@ +# Copyright (c) 2015 μg Project Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := GmsCore +LOCAL_MODULE_TAGS := optional +LOCAL_PACKAGE_NAME := GmsCore + +gmscore_root := $(LOCAL_PATH) +gmscore_dir := play-services-core +gmscore_out := $(TARGET_COMMON_OUT_ROOT)/obj/APPS/$(LOCAL_MODULE)_intermediates +gmscore_build := $(gmscore_dir)/build +gmscore_apk := $(gmscore_build)/outputs/apk/release/play-services-core-release-unsigned.apk + +$(gmscore_root)/$(gmscore_apk): + rm -Rf $(gmscore_root)/$(gmscore_build) + mkdir -p $(ANDROID_BUILD_TOP)/$(gmscore_out) + ln -s $(ANDROID_BUILD_TOP)/$(gmscore_out) $(ANDROID_BUILD_TOP)/$(gmscore_root)/$(gmscore_build) + echo "sdk.dir=$(ANDROID_HOME)" > $(gmscore_root)/local.properties + cd $(gmscore_root) && git submodule update --recursive --init + cd $(gmscore_root)/$(gmscore_dir) && JAVA_TOOL_OPTIONS="$(JAVA_TOOL_OPTIONS) -Dfile.encoding=UTF8" ../gradlew assembleRelease + +LOCAL_CERTIFICATE := platform +LOCAL_SRC_FILES := $(gmscore_apk) +LOCAL_MODULE_CLASS := APPS +LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) + +include $(BUILD_PREBUILT) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 00000000..4ed90b95 --- /dev/null +++ b/LICENSES/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 00000000..3f92dfc5 --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,324 @@ +Creative Commons Attribution 4.0 International Creative Commons Corporation +("Creative Commons") is not a law firm and does not provide legal services +or legal advice. Distribution of Creative Commons public licenses does not +create a lawyer-client or other relationship. Creative Commons makes its licenses +and related information available on an "as-is" basis. Creative Commons gives +no warranties regarding its licenses, any material licensed under their terms +and conditions, or any related information. Creative Commons disclaims all +liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions +that creators and other rights holders may use to share original works of +authorship and other material subject to copyright and certain other rights +specified in the public license below. The following considerations are for +informational purposes only, are not exhaustive, and do not form part of our +licenses. + +Considerations for licensors: Our public licenses are intended for use by +those authorized to give the public permission to use material in ways otherwise +restricted by copyright and certain other rights. Our licenses are irrevocable. +Licensors should read and understand the terms and conditions of the license +they choose before applying it. Licensors should also secure all rights necessary +before applying our licenses so that the public can reuse the material as +expected. Licensors should clearly mark any material not subject to the license. +This includes other CC-licensed material, or material used under an exception +or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a licensor +grants the public permission to use the licensed material under specified +terms and conditions. If the licensor's permission is not necessary for any +reason–for example, because of any applicable exception or limitation to copyright–then +that use is not regulated by the license. Our licenses grant only permissions +under copyright and certain other rights that a licensor has authority to +grant. Use of the licensed material may still be restricted for other reasons, +including because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be marked +or described. Although not required by our licenses, you are encouraged to +respect those requests where reasonable. More considerations for the public +: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution +4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to +be bound by the terms and conditions of this Creative Commons Attribution +4.0 International Public License ("Public License"). To the extent this Public +License may be interpreted as a contract, You are granted the Licensed Rights +in consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the Licensor +receives from making the Licensed Material available under these terms and +conditions. + +Section 1 – Definitions. + +a. Adapted Material means material subject to Copyright and Similar Rights +that is derived from or based upon the Licensed Material and in which the +Licensed Material is translated, altered, arranged, transformed, or otherwise +modified in a manner requiring permission under the Copyright and Similar +Rights held by the Licensor. For purposes of this Public License, where the +Licensed Material is a musical work, performance, or sound recording, Adapted +Material is always produced where the Licensed Material is synched in timed +relation with a moving image. + +b. Adapter's License means the license You apply to Your Copyright and Similar +Rights in Your contributions to Adapted Material in accordance with the terms +and conditions of this Public License. + +c. Copyright and Similar Rights means copyright and/or similar rights closely +related to copyright including, without limitation, performance, broadcast, +sound recording, and Sui Generis Database Rights, without regard to how the +rights are labeled or categorized. For purposes of this Public License, the +rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. Effective Technological Measures means those measures that, in the absence +of proper authority, may not be circumvented under laws fulfilling obligations +under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, +and/or similar international agreements. + +e. Exceptions and Limitations means fair use, fair dealing, and/or any other +exception or limitation to Copyright and Similar Rights that applies to Your +use of the Licensed Material. + +f. Licensed Material means the artistic or literary work, database, or other +material to which the Licensor applied this Public License. + +g. Licensed Rights means the rights granted to You subject to the terms and +conditions of this Public License, which are limited to all Copyright and +Similar Rights that apply to Your use of the Licensed Material and that the +Licensor has authority to license. + +h. Licensor means the individual(s) or entity(ies) granting rights under this +Public License. + +i. Share means to provide material to the public by any means or process that +requires permission under the Licensed Rights, such as reproduction, public +display, public performance, distribution, dissemination, communication, or +importation, and to make material available to the public including in ways +that members of the public may access the material from a place and at a time +individually chosen by them. + +j. Sui Generis Database Rights means rights other than copyright resulting +from Directive 96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, as amended and/or succeeded, +as well as other essentially equivalent rights anywhere in the world. + +k. You means the individual or entity exercising the Licensed Rights under +this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + +1. Subject to the terms and conditions of this Public License, the Licensor +hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, +irrevocable license to exercise the Licensed Rights in the Licensed Material +to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + +2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions +and Limitations apply to Your use, this Public License does not apply, and +You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + +4. Media and formats; technical modifications allowed. The Licensor authorizes +You to exercise the Licensed Rights in all media and formats whether now known +or hereafter created, and to make technical modifications necessary to do +so. The Licensor waives and/or agrees not to assert any right or authority +to forbid You from making technical modifications necessary to exercise the +Licensed Rights, including technical modifications necessary to circumvent +Effective Technological Measures. For purposes of this Public License, simply +making modifications authorized by this Section 2(a)(4) never produces Adapted +Material. + + 5. Downstream recipients. + +A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed +Material automatically receives an offer from the Licensor to exercise the +Licensed Rights under the terms and conditions of this Public License. + +B. No downstream restrictions. You may not offer or impose any additional +or different terms or conditions on, or apply any Effective Technological +Measures to, the Licensed Material if doing so restricts exercise of the Licensed +Rights by any recipient of the Licensed Material. + +6. No endorsement. Nothing in this Public License constitutes or may be construed +as permission to assert or imply that You are, or that Your use of the Licensed +Material is, connected with, or sponsored, endorsed, or granted official status +by, the Licensor or others designated to receive attribution as provided in +Section 3(a)(1)(A)(i). + + b. Other rights. + +1. Moral rights, such as the right of integrity, are not licensed under this +Public License, nor are publicity, privacy, and/or other similar personality +rights; however, to the extent possible, the Licensor waives and/or agrees +not to assert any such rights held by the Licensor to the limited extent necessary +to allow You to exercise the Licensed Rights, but not otherwise. + +2. Patent and trademark rights are not licensed under this Public License. + +3. To the extent possible, the Licensor waives any right to collect royalties +from You for the exercise of the Licensed Rights, whether directly or through +a collecting society under any voluntary or waivable statutory or compulsory +licensing scheme. In all other cases the Licensor expressly reserves any right +to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following +conditions. + + a. Attribution. + +1. If You Share the Licensed Material (including in modified form), You must: + +A. retain the following if it is supplied by the Licensor with the Licensed +Material: + +i. identification of the creator(s) of the Licensed Material and any others +designated to receive attribution, in any reasonable manner requested by the +Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + +v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + +B. indicate if You modified the Licensed Material and retain an indication +of any previous modifications; and + +C. indicate the Licensed Material is licensed under this Public License, and +include the text of, or the URI or hyperlink to, this Public License. + +2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner +based on the medium, means, and context in which You Share the Licensed Material. +For example, it may be reasonable to satisfy the conditions by providing a +URI or hyperlink to a resource that includes the required information. + +3. If requested by the Licensor, You must remove any of the information required +by Section 3(a)(1)(A) to the extent reasonably practicable. + +4. If You Share Adapted Material You produce, the Adapter's License You apply +must not prevent recipients of the Adapted Material from complying with this +Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to +Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, +reuse, reproduce, and Share all or a substantial portion of the contents of +the database; + +b. if You include all or a substantial portion of the database contents in +a database in which You have Sui Generis Database Rights, then the database +in which You have Sui Generis Database Rights (but not its individual contents) +is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or +a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace +Your obligations under this Public License where the Licensed Rights include +other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. Unless otherwise separately undertaken by the Licensor, to the extent possible, +the Licensor offers the Licensed Material as-is and as-available, and makes +no representations or warranties of any kind concerning the Licensed Material, +whether express, implied, statutory, or other. This includes, without limitation, +warranties of title, merchantability, fitness for a particular purpose, non-infringement, +absence of latent or other defects, accuracy, or the presence or absence of +errors, whether or not known or discoverable. Where disclaimers of warranties +are not allowed in full or in part, this disclaimer may not apply to You. + +b. To the extent possible, in no event will the Licensor be liable to You +on any legal theory (including, without limitation, negligence) or otherwise +for any direct, special, indirect, incidental, consequential, punitive, exemplary, +or other losses, costs, expenses, or damages arising out of this Public License +or use of the Licensed Material, even if the Licensor has been advised of +the possibility of such losses, costs, expenses, or damages. Where a limitation +of liability is not allowed in full or in part, this limitation may not apply +to You. + +c. The disclaimer of warranties and limitation of liability provided above +shall be interpreted in a manner that, to the extent possible, most closely +approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights +licensed here. However, if You fail to comply with this Public License, then +Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section +6(a), it reinstates: + +1. automatically as of the date the violation is cured, provided it is cured +within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + +c. For the avoidance of doubt, this Section 6(b) does not affect any right +the Licensor may have to seek remedies for Your violations of this Public +License. + +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material +under separate terms or conditions or stop distributing the Licensed Material +at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or +conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed +Material not stated herein are separate from and independent of the terms +and conditions of this Public License. + +Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not +be interpreted to, reduce, limit, restrict, or impose conditions on any use +of the Licensed Material that could lawfully be made without permission under +this Public License. + +b. To the extent possible, if any provision of this Public License is deemed +unenforceable, it shall be automatically reformed to the minimum extent necessary +to make it enforceable. If the provision cannot be reformed, it shall be severed +from this Public License without affecting the enforceability of the remaining +terms and conditions. + +c. No term or condition of this Public License will be waived and no failure +to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation +upon, or waiver of, any privileges and immunities that apply to the Licensor +or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative +Commons may elect to apply one of its public licenses to material it publishes +and in those instances will be considered the "Licensor." The text of the +Creative Commons public licenses is dedicated to the public domain under the +CC0 Public Domain Dedication. Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as otherwise +permitted by the Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative Commons" +or any other trademark or logo of Creative Commons without its prior written +consent including, without limitation, in connection with any unauthorized +modifications to any of its public licenses or any other arrangements, understandings, +or agreements concerning use of licensed material. For the avoidance of doubt, +this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/CC0-1.0.txt b/LICENSES/CC0-1.0.txt new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/LICENSES/CC0-1.0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..2c713c7c --- /dev/null +++ b/build.gradle @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2013, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +buildscript { + ext.kotlinVersion = '1.8.20' + ext.coroutineVersion = '1.6.4' + + ext.annotationVersion = '1.6.0' + ext.appcompatVersion = '1.6.1' + ext.coreVersion = '1.9.0' + ext.collectionVersion = '1.3.0-dev01' + ext.fragmentVersion = '1.5.5' + ext.lifecycleVersion = '2.5.1' + ext.materialVersion = '1.8.0' + ext.navigationVersion = '2.5.3' + ext.preferenceVersion = '1.2.0' + ext.webkitVersion = '1.6.0' + + ext.circleimageviewVersion = '3.1.0' + ext.volleyVersion = '1.2.1' + ext.wireVersion = '4.5.5' + + + ext.androidBuildGradleVersion = '8.0.0' + ext.androidBuildVersionTools = '33.0.2' + + ext.androidMinSdk = 23 + ext.androidTargetSdk = 30 + ext.androidCompileSdk = 33 + + repositories { + mavenCentral() + google() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.0.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" + classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion" + } +} + + +def gmsVersion = "24.09.13" +def gmsVersionCode = Integer.parseInt(gmsVersion.replaceAll('\\.', '')) + +def ourVersionBase = "0.3.0" + +def ourVersionCode = gmsVersionCode * 1000 +def ourVersionName = "$ourVersionBase.$gmsVersionCode" + +allprojects { + apply plugin: 'idea' + + group = 'org.microg.gms' + version = ourVersionName + ext.appVersionCode = ourVersionCode + ext.isReleaseVersion = false +} + +subprojects { + repositories { + mavenCentral() + google() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..86630672 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +android.useAndroidX=true +org.gradle.jvmargs=-Xmx4096m -XX:+UseParallelGC +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..62d4c053550b91381bbd28b1afc82d634bf73a8a GIT binary patch literal 58910 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

F*SiWTmgptEPVdg~<2Eeqh-;tM<(ig$ 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ℜA_D?n* z*Df-)`R3(&r*CS?vy;c($e67PGE1;1-G5<;A@}E*oU$5AgEDWQ*|%?w&(tN4_5}yU zzZCyAcca{sm4F(@DTQOK*yQ zNqt~1@MUVeTWWmdf>q1Ui(jZRnla1qtHn;YNy|MyH3nWON?)+kCPMG~HP+f*NuT_6 z5(j(IR98%hGVGd}GIQ#|H#|*_>&|3;WB+^F-5}iWnQ6G5^ua%ArYp?)k{shV6p1t? zKUn6FAz|L1lo)@(z9+hEV}?jK=N6UbUxk~uD<9Z6FOKi>1-6i6&9p?xXUUe=5?HM^ zPP(^eYG1_J=VzlNcJQWel=F8ses|1mPI`}C0{6NFR}1e>cr7P$Xs+KIx1TmvKILIb z&B}jfIdM+!D3M5=@>!uoM)|s&ilD1~Y~7R?wmxGSwz+l95#@0-!R4Q%%o}0CLWUw@;%y+ zyTX}$+J&0TD?Z{}VpAJFmtHaSnySBV&4H~qx2#I~Gi~R8Ju%hmeS4m)D*D6Zbi=Y^ zn$CS4_lG>wKIgkG(A;3u9NoTgmBc!0{=6{0zV#P%?kp~udu6xzg0u}*&8CuT&OX>C z%b$>yBJ%q8G``2*R*PJexiR73vEK&KLd>&krf=N2e$B>omgu&^u?vOaS z>Z)1Kw{E^7-pT2*W*4JndFp2_zAePY{%2L=@=O4*zeZM3${i90V+2mC_SNlJU*R81*T~p07mFpc>siw%PQ|{^4 z9@we#H?a0LuHxBtxBF$@6U&z;76h-dZPI&rY}1~A@4;&tEzfK%EY~Y2x*!_GX4sze zW6{~J$oZndwq0*s)-PO<_(r4e+)0}quXN@lXnC(&{+}PUx%cal#Iscl3=Fp!7#NhW zH}^bY>vbW`JlEcXT!##J+Wwz;`AkG`4_DmRq=T>5Y4NZ)UaY=*>W0SK6_L+r>}mrtA4RlXHWnNzL;(sCBs8N#a@t$Kn3HPqb2R z2kSkWWbmthmcK01K}XpqLQw^}+jXyQO}@N&FY|w1RG(Vrh&&48U|?ty!tK+%;*!Li z9N1Fqxe=8$BITn0|7|+1Gnw&HfRlK}?5Lb#{fAmDhn<`ZbA=QXTUeINeZx0fakKI3 zH#&_~0l914_r9K5H9x|2>5Uwxi^qODWbe(t7`E2k_O<-CVAtCGH1nHxbuysvhpRVA2$N6xp z&#zqqapyPPQ+V84A-sOiPMPnIbbWq=&D*zDCUDPQ8N)rzvo8v~d;GNgRE79G-Q)>t zJs#NJ*%N-~w^Kzuhdjr_X2lv0H(|Ro((;L!>MAuM$E@z?U*@z|d-zz$&+A8v!%@Mj z5s49jS0ye?Hh!F0 zO4((Vk8QKOGEMeoO`E(XW0~`BPa)Tpi?9{WbemW^N3bZJH!CO-lI~-VF0drNGMGvAYLJn>> z)O={owXgd|6t9zKl}sxpTL8Ye@t$&f2{8k z!!)C1MVNN(kyXB@SNhG~{`zE=*Mh9$i~LMDTXvb6rq0w=`MSR5!TyKux#k^Oa^OhYG|S3f_4wJk18tM0ZcFK%6F9%dG)Cq5antnarbi7X2bgZi`+uS`P-A>NwSIEQFt4`iOFY2;rV%wFF>19*0X5TN_J%`bJ z17mNoVZwrvpL(IPd#fh>mpfH`>1@mOs%@#?Rc}n{|KY7!X~`-baq!sx-;!^;7BQDj z(lPbp46}<}^6ZuCKIJ{y%e|!^s)k&i?#F$?jzwGRhbBwFe%0HTn@_xb>HFl{7oDQ& zD)Woxi~8%VKh!+RTYiz@ltN&AZ0uhNm)OG+r(Q`Nf3TYS_wfhox!I2itl(xpF0e+r z{R>0jw1;U~%}4mx9MQcJc2UaHfqfT4`H@*K91r?4-BPyQ7cH1zoBAoT<=1ODmq@nH z=bYZjyYDm)HkuK?vtbsGSl{K{)5B^dW6Zj~ew7UATQpOQMZZeATdkln{Vecoc@YRwsh8I&R5^^$e)*k%37};c1@Q z>y?_0@vKu@#ItJe+3bj#8})4GKPtZYz9jbc^vxAa+EI7zKIhuPa;VXGhhKuo?8=h8 z3!bO0P>oo!q-{z|bBWLSS!)&*6fkYARsUx_bE?dV1$xa@*WB%I<($ZyQkf;~|6toR zhlwvZI=|?oJe;b%ccs2{u3o^5yDe)A|MRw#mD#$ztJOBN zJ=(bK{MjvdpBvh6?0iGP2<5c%ftjx!jQ=dP66*u zXuF@eJ$Bb!C*3-QMK#{hTG3py1dd90_Nck)8~A?6i)h=l;qQsO)&G?9cU*8;bn%F{ z{-3~O#+$vPy?9N0&!4$D_q*l$n=|j)*Vq4H3OJ<9T=*n4!Y@GM#0Q6E+E!gfF{Tq7 zJdaB?2Y&srPdm;mt0{3y&5`@Xr!yn;Wx{QiakJkkygl!R*uL|tw&NR z^0=j;RXe&4)`WhKzEirLueEl~rClGk#k~7{DE-EnxRB?kc-LpKs;%Ld%QtIHZ=KQ_ zCw2AWHmTnsDQn(bIr_RR*S{kF=e*T1raAAzc04~}vuDNib3U)@Zg0~}ym4aQ`+^y- zKC1iO&9qIO@xGZSW0g}{bURN|~DU+KyD$N=UO8W$Mbe>>yKBSi#D0O_rACJ19B2oJax?W7@Q*%~5ux#N*NtGKq zZa3#RontZbdBnFMP3YVeA2aEN^0q%WoGF%ix;NyX_Lln>w)$UKq+X(U>v8GEb#)n0w`p2P~B&K83lJ`V-$M0cj7$pR`8OsYD(S6+F4n$&Yf z=>8r<&e$z$MYpb98SFyyP=+S%J)~m6t?L zKHs7+waDY~OSwn$Z6uZ`oS(39=A|=SpK|PiXBA9%{^5>-?1RlqW-c!*oUrUe&BFKv zg`N%G%_ZW~Gh^hIyo?cCvU_JxmDs81se5!URTNG*B*XPdC{AjLRh-)V(@P^2&z|~N z(sQ2ggI4eA)QZvx_fBUo`89qZVyv+M|Sd*$-0)G0!KiBg>WgU?TPIzDbw zSo@?`YQFrE&~LCCTX%vVO*1+QRa( z^4`*qdA;{_*Nd~ftf}*#VD_n1DqdvC*+aF;(u{di`^_@LvhK(&d7G1UNAQW?yGd$7 zOXglGE}T$StZ|_FiR9F@)CtKl`~^xU?$(^N*k+-rb(7KW_I{0MLCr9B73GyVwn zu1~on{mAonhuiC2XSUhCGR})C4OE_KGTCQg>DSlU+syNJ7sNV$-nN!^>+S8PIeEJZ zyw7gq^#ER!x%Q#CpABt};m$*1Z@#{ z+$z7*8U3?DZKqwln;R9{nXzHNfW)#ex0}?^76GbwaK%4b}UR;UGzAh{t#!v^LXJ-_cvR8-fLEJ z@4Xi_d2)B`uI7``(=*Ixs+qbyN(ia+ zTkkg8**$r!#=Yw={#Yzk8~M23(0A{ZElRSrJH_tI@?eO3-hZiT&82k}o{vl7^75KE zTz4i!q^sRtY{SQ63a`H=5MC79u=S4a$O>uLBsR%y7#Q# zatt4cUG8$7_lVSt}RjBwx519JGr&OLdZO8$*7~Zb?(syHR@I z7#B|SS^1VV=#ln?YpkU@!Uyb3 z&g6$%gDOJj@vVPcEOG`MGL3-}+5nVUe}BJWlW~|CkxpEk1YKt{Jj9 zVUqVcJAGL91)M0U>XEztviwW+T)748rByTIF21b(vcWI>f_up?f!c`Wby62zO6}w0 zo*#BqYx275FST|>E!WPRf6V;FAI*E~KQI63)@!dU&t9^xGymGp%{9hZC$>BMPD=b{ zIXC}d^g^d~Z5`jA@tRK6s43akYu~@%yzE)dq=JTZHP-cx&-Fh|FZpK_zx=b~Kdz$w zYRN?(!%P23)yF<}u4zAa|3SW2@$z%+Ki%fOUnD+L&&vO#^f_J8L)=}HxevYNG?wgB ze`BB(cDv)qVYfZ}DPr5Mw=CJ*`taA6IO%P|5BX9)ZMfN;Yk5mNZqCxz(H&aJ5j)PV z6<-|veD_bex%~zE&oS4qUuIPoQQBK3+|T2@qTaq*#<>7B#_JKq*}JwEzT ztl{=lwX`p-yL-IP7Oc)Zc6giM>uE{~cZ4_Y785&u_2q%C*0*|x-ICkHcU=3&edul& zlknSp247xG-YU}(woGxCfh}Ki+fpVj_elY_gIeJS06q~DYTg!a5yOT)P&1(zotg-@>>%Y$2hy7SL@p` zo)>>)_X&PIYp`$IvPWeqX=$$oICr%Ni1MW0mpX0qONI6IpV+Fv=kGrrwyKZ6C)ddQ zlC`rlAzNi$UvlCH<`72pRSVn}SQl%3*jjp2NTbq5d%FJhpY0u{1(hMsk4rrK-NI0} zsyg+!NYTwba}DR#AMxi}ynM;ej~q)kO`U(vzuR?Bl7Hu>`6(yd!V0YZhnz^;Un;o# zPMhvR}EPdC9l68&uuFds2sIl>CrN-)eA6UHwN^ZWMs(r6)q1mOZ zTR3S&GY=ljKNx+3|}MP_T}m5FItou^wnyV{mSL>)O>dga603D=Tca<0hU;0eij zmhx%Q92V10Vx_ZwKW$l4e(XcQuW8;@%tBjxUuk8Z%~Y&B(>i0x3E!Ew56_v!yL*-w zkMN~VjnB44(FIlEr{{()3kX_%Bbe*Tnb7DLE!RbgzD{}}6Y2eJ;jBK(nfczk8q#Og zdwH)rS5Q=zaWmqHUO{#7H}@5D8n0^2HdU*daEp^5!YUl@1w%+Na?E>}B&>&ZFB_pC*X z3+IJjvirKHYu&NWJQw1eQwiPM~Pbx9lz+OZhvd%49R7hC1NQv+&8b3$p8E@s%MI&%}e1go=RWUj5byY zul#gn_KByC?@LQ;t6XL+7E$3nzxC-~lT6`RCw)`DUvSy;B)r=BUY*v8Zzmu3WZC~x zZ~4<~vUI7=6aJrCr?lBuY?!~y&cn65Z_;KFt7(nJtSM`kNNy3Bq#sn2R}gmk)Xr&n z3;Z3GCk#`8<1zJAfz{C0uU7TJK+!XknW&y#jk@Xz%OjB+rPFzTxmTf0ytEi$D4Ih$9Y z@Yf4|ua<_rTAatQrT-qc!#?>9uUk9y!`0q5cycpL*1sU0I7gtOu5%wxaM|O(3Os8s z>P7j*A6&5`ul}0p@0Y6$Ob&6`#xGv4eRKU1=MwgmyhWd1NI!UV^i}rD*(K^fI3uP{ zG-KIT*ZVCiWXZ)?sk>*T?mB-_I~%wD;=}{>8uIt0T7O!cy}sg|?<|8uQ~#PwXnQ75 z@yq3(QN@MpA(i^N304llTwZ&=2WNIKU-VOBs_fEF)`mfiXNtAkwA}eC-z{yeWa;3V z)gN^AyW^G&k%>Gy`+`p@?3>~L_(B~^RXk_!cf|+m*aX%uyeeVKFLsxwI;KTnSJ=zd zDrau3aXvWZ>g~WAX8bXFi>hDR-FRaYQ75oC?w8oH($q-F-SWvvyFH&+`X%=E`P%z^ zPS`7$R>@#xk@iz@nuz<=P&*&fR8i(FOKwGLD(?EC7R9qg?{?&dm|(7H=G!YSZd|nU zX!y-LOjo)NUESvxbyMgQ?}eNEGUpzhzgTHMZTG}u$}g53xS+Vpb77k1ymV&ctp`qQ z)k^vMgYVMzh4cBZXuq_snEWMp`c`LI+a*Cu_IU1iwsQOXRU7QqOw#zz%~NdpN6vOm zY0bQ@hrP{PxB|mGDBXMJ3@wt+^_JC$vPo%7zxe&7pa#Rq zO;T|5cO?>+1(!o2q`utc$;I z1ei>f5d*~$t>_?z7o>AXP@M@drZ>5e!rZnd$qBBrN$D` zoYEv!k&JiCpUpkdpY$l#mpyaY;WGBPjlnWY%fAM9m%I_;Z`~N4(Y!@k=6-B}qy1j@ zgbsK6*c<-hvTbIq92-_=?5?`kpz`QIfw-@0wfq`ej^B6t=YE#ZkYi!l?|Zc&Qa53% zu)!{lh?~s$F>dw;{-_^(8OZV?`NPS{558G6{9*alE1wu&!FAxTVcI{J$}Q3bcRTOC z;Oz1IAlm#-bImo^%?_oK*J8Y)UuFhWZ@l;OOYkqrnquCJ`3vVS&bPPu&11Xzz`p=7 zX8x5M+#I=i{yH`86*Q+2}Y-?d1^vWPQKTE8QOnHVAA`rX{!yQJKUeE zoxc?+dV}xz6itV)Gf_+~d=tXAU7LO4xn)T!yW$TakLkSKCgDfFdvEx2I^u`uk!!0z z9MzwgR^PhzooJL({Ry`O!xeEyXNWVM3%)&tz4%%4r|VvgQzPxyEV-{?-@zn&;&cA2 z6&Ld>ugy-Fq`qH#sdvlr4Le*eeQmy?derfVI`}3tt<0>#UtRH94Q3C;VF7)+0QsPB#=An`H$}e)->$ zl#vuGuJPoF7(XK z^JPMl>{m~{f03)^&xgI-^6wX3ej@y!Fw;1{u~bEuEg@}AvQ2xerRDNf4%31e=FHio z#$auo>f6w)b@_QzjZMPiXXk9br{23YcmC-@^$mxFD;CW*yfODXXOCm0gXxRL*Wb0z zR2LN8@P6<7#!vD2ea3msr}FRAqwR3|u;1!iJ`)3j2`dAGJoW)jpZs+AZl>1B+4+|e zM2`RGpB|c*-1E@khFYoe)ux7SZ;5j$)AIif=D2Z~EP&c-K(pW0sAD$T>TElb0zwle6!< zDLg)*`oF;=IZ5TVq~m2Jq2(3#MP9MD^S2)gwBELkd-+KLv)s9g;V)%Ae~&TUxpZ~x zbwi!KnvJI%dfZ~Za}@LOtXy|`Yq|c<3+j3vg{#kRFkXG#>XBdgoSxRDooS`d9<@H# zbxu{f^`b>lUgY=%jw?CanL`&VPq*GQJ6EYzumaF)7DvR>WnX0 z(Z^IKIw^OuOyYW{S}4yiV&n5ju*g&7L&PlgOE#*03D+g3o%^w#zrWRub6w07p{Fqu zlV^NDIW96E$+ka`b@V+)?ZQ|N?-X%CwQ5np zHIuyLB=etFPL2}$5EeA`Sf}WdnzOa>#<9QKE~p>buM}hN>cZ8Y-qIZqu;}Zro`Nc^ zIm;%83Z1DA^m!J2z;ynF&zv(k6%7?#?q9Q;pFE{=qo-oDanG446Bo*OrT1((9`RXH z?W*j;{IWwHo^F|MMYumt;R;-}B<#D!R?iz#CVkZ`+S$RnveuveIUi3veekMl zdBC%sT7~5rO9TV%s^$HOzdOzR_H6XD>gBawy_b=J;XM-rg9Y}q8jx6$;aFUpnwyl9 zTBPEWpPZQEl383*l$lgol9`_e8Kj>Yc-!l+gMjVqCB17uioGvqmCq7oK5FhdLCjG@ zOQUID?(L;{y`DFhb=5CT5UlyY{70j(Ec(bQg?(qv%$#Xloc8_g->0GujHe@)7|d;K zmds6yJ+nI^p&)zH)R+!c|e@h4u6(@@Jv?rQU6FXs?pHtXf_ zg-_RKMlALBxp+S6!&bJg8r_aPyl%fXrLc3x2DJOusr=`ecK^kJQ~6~H_EM|TGx#Ds zw>fUf=21Q(kQ%njl?`R)7Pnv;0xhGx&C>&vZXD96r3Teh|3c}ayiI|IW+J_ZIgBBB@?pbUvu$!Zf9z;! zt_VA%{=t)Teo||ojrg(Usy6-SJx)B_taDf7qvbq%{X`D^4<8nF=y&``l8B#FAQI2_ zSbNQ%P`Q1_9OBboN-M>2J)W&or`?m^{!y0m{?YFu zqBiFQpSIkV_JZk2&$`vmLOeI>Too$#ap=&`FK52o`t<0_p<73HzWH+N(Iuy}Z%qv+ z&V)YQHAzde%5bX(L)FJM4eK3`X;!NX6b`dCeLdoL+2924 z<><~o>}zXYPTHoJp!exN683c@3|>MUKcZ0n+| zn@nxCs@`X}y!zR)lK*;QV9JH^wcj$&fDs`a|7 zbAGg5N?w)vc$d}Q%+>>wDtFKgO(>4uy2XD#!NiRe9C_T9HTfgeqkF6c|PyEoWQ-Ac`=$V1PxZUi!fFtrLe*{G(*FW?( z`qy)v^~=sTr&?yty3!?iH>}&|EpONihpCxYS!8ko8JXCmcXRjUWv7;2pYI&p7Odq` zdbn?0@Ig1z+QS8va-mkno?iac5<{I5QB)dv@ zzviX6r7QUR#7%;a+)w?}ENt3XA9S;*bxvoHsGF(ws~6Wx9b_efO$oZ*8Ny z>sHRM=>I;0@3`A+?ym{K%yUYz*Xv)~^kDYXoApZPmzAtqwefq&-@ieR)6%4^?PRiF zoz-m!+q2u{?U~0f^lkXQZ?W0G`||y}N?w^=MJEF~c4cUWUr7xXE?DyQY`%-ny}fF8 z{$Jj?XWgX2{Szt|)fD|_czgTTp*!|_-ideg7X9b8lx97uwj<%qCzah|T0gEiZS2{8 zS^o4EpM*VE(-&;qyd_V{d`J8)!PiU787Jzmm|Yw$v5B?()BeLnMb^IG19UFm(yLec zlN30=aLS~sEzd*0-@NEu&0TYP)74GlM=CEb=(!pFTOe|w`8f^Sdktkg51K7_8RZv+ zN4m>miIryb*b+-%8JDXUD?@Qb1^mnAAVIE&hvewoMl z?uFcq#nLwyzxFzLY~cknzIA*o>`ta%LSNnEQF2M<`4jMNA%nbI)1nDS)Q-iR*Q=WI zdfwe7H)0g}tv=}dEuR%o^+3Tn$a=2sw#;dDR7}d+}&Y} zv;2>ACj8F*5?;8X?eJ#ac$;6za(}&ox%?+x4A$GbgFSe8`JtssJA{N&tI89vExmp$ zUTgk=)g^uYeeUme^)~8A%FOYdv~~A(!!1$@L5EFcUfRYTwVnFLtZ$v0n@0HYcRNnF zYrYITl-=^)rg!oSmF?A<50$5UN=}*k)7C+ea}tr(&J^hwVdT{L;xWHMN}_fxEviTsU3OCq(G2bLgG$!uQ<8T|4I4 zx^>Q0)Q&8fc%Of_>g#!HqhB9Zy*|rBYWhc($WL$ovB!R~dCcCC|Gm=c(vy#U-a4gg8|pf2*eWGe>{CB&oW7?=ph!8oxsKE0IrP`#kw#AK^4H$J}H<6Ei`myN} ztvR;)*C@#^C@RW+{p8$Mi_+ztKZH}hcq)AqS(?Rryff@^-SXE*E(g5sRMNZq_sf6L z?H=6~7j%Pv9k|q6#UT5i8Fi^cOa0l`hgcXGYB(7f46s+d!Ii}&sks3~`30#(C7G$k z9*KD=Ik1Bcdcz>+8^=!Cps1sJfr1BrQgzW zpYG8;A|Dw-bUc1oe~ma2WUKe6OXPIX#2rnm>-I<=sIG|b;$N~u?V|j(WRY1-qK(gf z6xAqN)#m2amGvoE3GQ}!d}UUdGxu^+W63`2ZHa2iOuUb$%`7#!9C_uEW$c1)O^H(H zW;Cc@*{tzEc6(T+txf8TUp{%suG_`8Ox>b4Ig(R%?~0pkbHsb&)KgPfe=XOsmh`)I zD=UgW|5lXR;@b~@gk8I6{w$|k`1pm*lV^EuKP%LyQo8(-c7aid#`O({gx#Y0zlbce zJnqrHd@Il6KW!&-U)*YQ*uW!gY;3vwfs)UL4bvvsn@R7TdBM0ZapsrGYhQKMF1<;~ zb2gt_)Rz{1?MsXEm4!Yl^=8g%`*exrRd%KPvPpB@Ql-u*2QFrbaBi_(c-Cm{!X)0;T9=GIPSpA< zmT`Y|m0h~kv9^8ZZ-(t$>TRjKchzmC*udM{-kdu-C9iCmZtm25=k~Uh#ii>m)-p@* zN?dv3fpX-8wMyLXj($gZb5`x>oYJLRqjTZ+oA1r(+R>kQw0^i8UH-%8=<^**A92^L ze575o_K|dr_JxSbvPz~?Qko0isedr^%{$_BYMWVid9}XAvlrW*Md&QPrXg2n#}j^Y z=P6}nd2Y|X0~>9x|C_BLxX6zQ@+E9#!YVG^yuVv(^dId~-oF2T-vtrP$G49D=oPA5oBHAS5+gUy%$t&b)h@zTLY&Tnw<@ z%bhxN=3nE#%#~L@ir;9ndNkGRLUq0LT;tsV)5|Y9d|CMHaHWZnYp~o|=I;eh>gwg( z%P%My7;k6}YqZ|sv(z`?i1e3dg===&_CJ5OV86-B6OVs%=)YL}O*raTlil^wrhBq; zz9eM+zUX&9w&H}lIa}lpg>$=-1rAF%+HO}ix9?v(<@m$noHEBeO?Kv#8NM?4dsju_ z$cnr@X9{O*77P+rwDgu}@I9})scQDj`U94KoUW{wDC(T^B>a@-76H=}Qc5or-sx<; z{pOTqRZUM_YrL)gM!rd>PAK-({qcEiFk9frcZ>dT&wm~pby?$EZn1u^KKWn3m&;Aw zaBKKMiBML%*3(?9>unqUPip$r*S;dS;G?{cO~SJ4cc;ZCwH1c%{v(fC9KtI+)kx^td-?3}cFwOXB)uZr5f56nLn_86CQPf49UL(O=z@#pz}-@f~L{(k&^ z#sy5Lt(F|P^dOPx*`x#q*JgLl3ZI8IGg*!NPuQq#UU6^|+j$8I_CEOvzs159xzi5@ zyzQueHZ7a|+P7iyHb?f1#dG8;dgj@fy)4*ps?Io&Ju=bOy< zJSn}{cZ%2Mm}pr={mZ%YH2c)gRnMGWX06XEDGC47qhYP|wJi2W$NkMq7QcFYv-m~b zccoP>hh1HDUcK$xp1;53#XI*?!OfkyFT>$ zw%1qJ?!7)ce>Gc5!i-7@hc?rTw)WO(av?IE&(W7oAinUa|5E!%40QnyM!j z@EEOU(Ol`smO8=piq2PtlbjPa38^SH+k`jG$ePf?v#D#=Ryl=AMx(VXo~IN-RZlRR zdIYiEbT~U(PJxxtNH{C)KiZ(6`=1s3qMQs2G9nBN8rYix(4_BLk(ykJI8{9Sd%f^; z$^Y|oe?EB}VZ@fhBY3P~LJkiLhxU>iGag-Y-kQ^)xYp7;{kOw4&E>)mL}p#QRI)@n zch-b=B^PGS5X+G=UnqR5v1kulf`m}L$`lEDU^KA@o=uuy`>L$?yX%Q=C3iMz1Zm9;snKW zSB0KeS60aLzkl8>|KOqLvO0qn{*Qk|=GV*!_iz8ebN+c{g;`7ghd+U8^*!P8{11g~ z>L<1Pw|jtg>$2 zN&gc&RwP*OJN0C}rpIHU=D09}=1A7?4_^i1rd-&t-`djR!d*j?8+Y%U8<|`%o+RIP? zW=3y2A6c&4zu-veffEz{am)A}&UtQIcxB0%H!}?N5}o@qd$;#TM_Dc2K7Z2|nY-Ec zZ_dnZTAq-(Eivt7(A^oC6X!gxI(08U!erL9DJ^R+7Jay_ST^@sFzYV2ghoGO-&wkE z3^!H9Cl-ZlXWJTh&Gb(4vMpy6&l**{m)mrAUCbxVmk*iLw0P#u-?H^eXPoZyd#i6I zr|?#AQ5L*P4q= z%{-B9Ev^@N`x@5dZRh7LDR%gjcF=38Q|KZMSJqEP&rY~_U%$k3NoJE#iDGir%NBKA zC%0)rk=NC_%O+gBdo$)>Szr0-A8kF;Q}+10lfRz3zG{i|+$pSaPBy>(r*y92=AWSH zzSuoqPtSfyp58W}W7DrC&N6$s{ArcxHt)?jcZCYMGOk%fMn6|>o?;;5!RfnQSR?V| z3jTk(rw)aSa!oi~c43an?B^4I-$@9_UDJJU%^f!T+R*Ju%YT`;-F#U5-f!ZiUo*l^ z+<6gW{hs4xhknS3OY%Aa3zx6d==0#1m*OEAuDM=gqwnc=rk^U4bZ@R!aTT-Px?{;A zC9_Q&oM->%5gR zoo~HHPIUd@*P${8vO{HDwsZBC+BPlLTKD+3ippEft9-iaSN~v)3!He|Q`A1iGgXH3 z?7V=F%Flin3cuca<3(S~e8aELQ(4%5i2M9d z6@FE-OSN*tWu25uhrI7BQd(XklQnblp@q^ngZBp&_T>IGW&Y2$KlGz@++R8Om4Qo7 z&&p^MRg<6JTB{s+r_Xc!a`SSV+J4tRjz{G`s*Bb?^N~0nC|Mu)G5nA3vHPKanEy}N zu>O2chfDt)qc@!=IBQRJR@g0H-ec(O@7}X!y{36l^Q6Sq@P$u8u3hu1{uA{4^5oMs z78Z#!m(TgW`pE6~ju9b|YlN=-*q5Jrx2YP)+xEmnZ@iS zz3vx$b^W$v@@&mr4v9RsmaS+$I#oXHPezgIq^W`T`I(<_EHhHOeWl`c`s5rR!Tq{+ zIWD*Eyu5zU*X&2B+y3-vUryaVv`g%Me;1eY###Rq&n&KY+M017?vDHJ>lfB>&Cb|= zesjFrZ@%+&tZ%Jte}`_l|Nf@=<$b3&$9EfS&yH%}ncO|I%Hr&2&och1&dvR0b5|@B zyhx`mbF&cJ-?PgivsljmSoHb?&o@GWX@U05OOqty zj$P)Nc79PJTkpA>T+i2d&RJV>F3d8$d}6zjal~60Gwb$jmgMf@ZPzTL)z4jvcSxRJ zaG32lZ(*79ZJnfW{Yn1>ZCcX>N@XuJx|-xEWna7S|LEBWr~fmhU+|~0+}2ojyR(S@ zXf6A@<9I5|-D^8T<{i%U+ex9OFM>NZ%Nk*E0lX!VK3Yp+}8YozNf%wx+f*)*{z zo$c-Ak3k-Xm5k=L?2!M%dC}*SaG%SuqOV+!^lTc;C&jJt<+!vuW7^4g<`?xGmMb5r zU9;jO^CKQ5mWLmfN#^Y1Qx&^s(yiP6yx{wWh`hN+x1BokN>%h%fIHU|qwKA#n`TFr z%FX1ef3d@8zM4|>rXs%E?I)SPe>oPNzM;9!THoR24=^ieKdwwI5)_HQ<*zB#Ye)@#-m&`=(bWj zYZqUK&|JHHG0fj~+?47%x|BgQXDP3xzgSzBkh4aI-YF0D*d@#@A#WA7{!+X-PvKtA zk=4OlRte2m9((3_hPcR97fGMi&6VK`6bpDwc^h9|%jVeTx8A1zR>T~kswgehLgU0n z0}-FV@`{d{!Zsa??xz(qCcP9~WdH1AvUlM0X^Zr9*Xiwt={<&H_9yEl_dobz*Y6Z2AhGV@ZM^YhA5LAP*1=Ws$| z^MxEmY?-;cU*A&NwzbaLeoB9kmj5yVp-UMiifaWg9XXyDc|A8_I*;?>`a|~@>6=_U zBLAS@=$`C^fN5L4BtE}qS^a)z@w0oN4PXD*0^DvoFj-rb@w#kCb~yg|K($~+q^zZE zr7UY(#nrDdk;xrt#mjkaO}xKRGE911x;%e%ZuFLl-QFIn9!Bq}KGiDyMty;r{ch&A zq{HHVCk6Lh-s&B8UH9s3X3ZU{{eR;6tLHYnIr05mu3~Or;*A|=zHa?Le|?ScqO8@S zD+Oog^$NBf&UxH3|8vmvYQLAVtt@M%O1@cAn-;xk61Ve`R_^vKYjhVa$e#4&*0h7R zIwyL+@07UFl$afN;LO4Mt0lVPN>07vxi;m{SDB@{UQ&`h*W-0|>&M=gesLpt$JG+q z{?iK=Xj@0j>RnOP_0D-~sYF;Hw{v(0XX(L=U(Sg)^Y1L^pSk+iHu)=ti4V-2f9-m0 z6nJD`rx2gdox|R3hj`Um6fM_xCRTF>J~T28EPcpRy-j_&@4~hV9CbyqS0~(k^VL|O zCezT=m}k8hUvAz!m(;Sf6)(=+Z}VzhJZ)B;u5*1WiwDPFYo(NxmRn|RSv@&5CHW`! z2Kgs0h3(gNFa<{}yeh+KDVw4NYE9l$u=*FBm$uj>l zr2fx%G=IW+!A~ORr+hK7P1bMLP2G;(d%51cb#nv@1H&6G1_o=)lnYCWh+$0JiE?dN zbhvb=$Ui@$lLri*3UPWY6y0#8Wyuk5@2#GJUMqXsx|cQGkh1n^GZ097bcW@j|BrW3 z`+i5oFDhBP>(`tAA04M}ul3Ut5XwAd6lwYVP3^aP&#ix#eR_T0zn<}f*}g!7p6{tO z3lca!*X&rFz*sJ-^GHtJ{>kd!T$g@$96WyFO9JO<+Zji>64k;jv7dGrj>1})eXF|#!2b5<+4zPixb5z?rvK?(^dJr z!L_vWS7kS+ZH(T&R(Eqi8ONuPCnh(9FNB#MSB<$NcyLmLvCqDUc}w2j3QYG5pEz;L zn(){I1(1VNAFK z*VfO=Vp!j7bG*~l;HYV;baL`zlct5wo>=Hy6@KEjIb)Tmmv(v|Puz*BS^K=D4)yi4 zvm}0WFkiJG)n?Ixw6jf1E0tE<-?v5D+C|7#{oij{W}#Wyv#s0SsV-it)ApxcG<^3Y zuesd%?rT!sZdxq;-A8U)L^`X@;oOxKqRKpfm%m+A!TVTC%|P$5w`jhC`eSX;en+Xv zHR-~;dTj15WXNBskr3UsGKqb+R@aT-#DmNB>|492WxeErLyfBsInU((Jw?@bdZ^%0 zlcjfdRtetysd~r9wX~*Wmc*>|?S_R;rS~+aeCIixtzvliNB1u-(`zRVU#oDn&NNJy zSpPk;G`jfph83&UyI2|B@s9F6TWa>n-Z5vTKwF6Vw&*WX>M?J9CRK6nm0y%UN#1GV zWY_tIv2iVX&&ssvZ5I5xP_4Df?1P;5x+pU~n~&Qr1#3q(Wz^l5D%G1EcF!W>W)jEt z-KDd>|1?^;Upn*ZyA4xIcBjs}lG4A}Z~5(gn;&{u?=x|%mufp`ZzQ2#DWxwob-~Om z;l`Kq57aGbhSJ)w<9<**j_TL{@1XQHlIbTTB>L+&jJ>fXE`r0avBUxQ_ zJx_kE`0L^^`{Ogmpg&*kzxbkXcBh2>_NI4SQN(6VOx5<$%? z(T?|X6l8wLF&8TR?l*XF{1?B3>M41?5CN%)hZtWLvaY|f?cYz!e=U9!SYDle5;Vbl z*$H9eC#~ft_&>MgH&3utiuuEKo%1qJ(wE-LSzRx(T|VD_Cbj)kpus(nediwP{t|wv z>|Of4_+06uI|6|d{~c-Qxw(JBgonxdlYj8v+NH@5*!0l$;f3hGpGztxg^9B=>@Qek zB-?F&Jc0G0dOAz=%0{bgFIPwUKHN6r`9D_FHbL#$bCvFl3=G~(SnE!t;>ocny)-v9 zuf(+?Iklh!v7lw*SubWokyig-#*4RZi^|o0b?DueQ1&}vTuNMr9vqtX?sDb~w=|)X zXI89#y6V9p_74k>sOW}n;Ycf-TwV0O-2cSyB_|ikFJ>GCiNeJ?|UG ztM-4kwM*h$Twi<*KI(eIR!T@j@97M|kXpSPxnlh@q%H(5nL10KS&Db%BenV6ttE$q zn}p+6UEmg#bw9(c^e*<4#j>Ov1#zu2Noto?KX0&I64RyED7{%_;lic=iX1P*J$gIa zXokkmlxNY0k2a)rs$HEPCNSlfQ844~XUFni>nAT~3vd(c;}mT=XCLRf;kW63Jz>rM zn~#KapM2GyF_qa+xTK`?+T}{W_kO#LSt~bw*S4Cr=GK*|))7~iA6w(~I(yoq1wEx_ z4c45RQ@S|)$=7$=td9pjsXO$o(TZs6~SmD`+j5-audP!wv$qm#-bWvVN8Nc_kArHyg913a)`18Y&g{ zm%ZMkdiO+G@UPo8KbZdnJ~C?+^5Ke&D0_Bx-}kne^Y5?EXZUVWexbC$t6-9k?hdn- zI{i7DSU(3ZYx$qFBkl1NcGWkfHcvY__h`Aiu+bC#mzlEc8Q(dMJeAfYwd->V!qsgw z5+_~^>P(P0QuyGecg4HbnPU4c?)Iz z4-(0Z5A9I(TDXw;{oD)ENB*w*xS`A|aN6q@`;%8*#ZP(S_FA=|c6XAX-gfK!eL}nb z>vEXi?&ObpV!ZB5T5vAY!p5ta+cH-V1wPd3$4NM_if&P`=8&l@AY;6SPz{3q1KaYq5il=f^kl^tNgU6LdJaVmy0Cs zbv_W#bN#@j*X=lUql}}Jq~xrg^1~VWcTaI;x_VS`gddu-zN=O6&?<|Qod-pnNJ zWAk=%Y}0i#`@Ajh8JCdNvUMx2Uk$lmY(s_=UXC= zcRbFwSo~C1ckQhxH>cAx=3Rb!SSYD*{!h>7%~w*LrQgO+5Rfr9p5-B$nAWxW^ej{T zb1jF{qD;j?yv4K2HyMF&q*W&8DumntugT%?d?xBjWo z<*0=V(jrfX>{<9JXsuvi-!vhn-xZ~@OXl*0S6Bpf@7l=YD*4**utEM0;R_<~(_iO) z>{}+ebe_4%{H{fp8;jx#EnmKlX$|+EDqX%~n!a?_-Hbh5RZ6nTAOAN#y-;;dCo1#p z61}KZyWO$_7HHk*msY90c_?%11}ANQNmy*SRt`u^dzrtgM}A28Qw zEZn?{$H*?|gP~pG#ZwD{s~Wd-nkqCk?X7S;sA3m7!DY_NJppxx^|UNnFLQmH%6IC$ zD!<*~mZtL`TX+0k+O+<|)+XyiU(Tg3U2*8+&8ON?v;Mhe-|zYAtIm~v*-W?HfcrJ0 z)i3U4eqk)n&(F72R9sTsbyw6Uf<0kns!^5OZqL;R=NN~6i#e%!(NA>i@>El2=h%-? zv5pTLcjaE(5$U`w@>bGy$tXST6Q^&eseUL1F&GBck(lM$Ws`JC_{)wzHD-n~7} zaJ@O^Z=)KE<$O<@wkZ`&2Yw#UsOm^~XsW@ebFcc>bLK^?6Cds1R_wdIvuh!#8X_)FP-C9+B{{qK*gyEvr@jCv{3on%~3D7M&!fl5UHax zq|>^z3-?C;_|?34LeVBIv+q`MQ$Ea&y{$Uo`i^o5_m~Oh7VH+MJC}I$He?nh96DEK zudgn%;@zZK;%#rHt&(ZGar3e0^A*X@B-|6zTCAPar*ybod&^Q&#`d!8)bpYfTa_PH z*!FCzl5jp^5g?u{Bdm8!R_xrx+NQFi1<74IU;p9t3OdujA=6NCNye9*$8Wsph`pHC zb<(|KYVhXPwgc<039P<+aM2fo<+~-{C^YLW&pKf$ujKAOL3-)0Hif^vax>*uF@EgZ znRoY@m%+B}@AZ;Qu6@=$bn@KX{tLTj$Oz=we&{LRtdwBm*yP3`t?I$Aslub~r4ZL! z6?@oemda|TzLH}LpE$N(YRoV`AAfwx>l1?ZvR2taHolLId0!}4g$fkSl2Y9uoOjS_ z=5eua_sS=~ z@V3m6CC67KiU<0!$SXw&e-wHj^I_phxs~6pw!c`_xWlF6bgAOGs=}YumR0*#O*byB zicETbZTFqf+RCNpQYU_@THg`#w@ZRIexl{`!Wrd`CezIf84tOaa?kfwRJ*oeYSPTT zZ_g#qP%mCT(P>)0m*M&CGkz}*sEa(}cc?n@-l8oJJi5`xQ~tc|TECQmf#E3w##9wr zEfSDeRGeA_I|Hw^$C2xhfq?7(jICR_%AW4($Zn2XsKL?Iq~7s1Ao>!^q*o7PDj4`x zm+dHd$n&|K|NZ~#pqogJS-40fxpbU=mR5W(tc!E=8I#^CNB3Q-nRIPq?6psNoR@pg zO)Z?N$A2fzS5QgqOs}qMX6nL-C#!_)A0F*8xS4s@EclXTS<8~w!d|a}Wb{p+>ni>Y zpS(oj%ZIu9;w!`x9@IB1ynZ77jpVBam0xHhGAg2J)!xhu4Es5;_G^(EBv7w9rj?`? zxn&j=mjst4L8T#sEl0y%`w0gM{PWYa-Jlx~An?9JbfNARR>2JoPE)%@xEwt{+}{@a z+Uch1ZPyys`G>*}xHNku{aOBTW>4X{yvC&HZ4yHN=bSz>Cx4%%wQ+Ubzki=Z4=9AG zm?&TJa1L?eRuW2*_>#o&-J;F%9rxK4EPaN3>@tsq_RN@~@KEu%a-Sw!|4%PnmPI^w zJ@1}yEoz%{R(qmPT(8QPwYrg~)k1wEr}Z`^9XVMQb2X%RQsTDjH~-{wd(Y9`*!_L2 z;SS-du*3|(rloV&Zb`D4e7SPc7S*pkbK)oShQ8@`+P>g)LE)>VDo17GOtpU)%Di#^zWOKcT;%fqzgf=H#6_w z+a}03Y5JLGi}vK!a`heOnf-hc>$#1`*7y|e?d(!iYjbRK(0wkkB((PU2@T^_9`pN* zW-}Is+iiK+#9z0i$mU}r+h&({z4MR}{$D{FRiWjEbW zQr)z;xmD|f>{XG!rfM_hI)p?mjdslT>G zXixWcl{tN7yI;$2>=v!ndqy_Ab%C;6Jgfc+2|5H^q5sTeKy(-YH*{Xzl-ZLb zC#&*WLH6cjp>+(`x_}T>7i*UWGd*{w($fzH+|%kbY(N2i|#0|Gm$!W{BsX zxJ>cHw~IT<88ZJe-1{0ZW81!e&aRi|xrSZH5KhPuT${uBR;QIAdgBU~Ty^&lwF2(D zE{<#RE}wE`Y!^Hrd+4QNCi4w_77LrY#+%V9vz68!co_EOjYU;|Lz1!Tx*rp@`|Oy~ zuW4l*)eloFQh&Go{vnA2sk4@!%r&;%Y~@+h{A_j}XE|%j{zkte223CKbt&?FywN3C z`q8L6@z&czOlLi}e_=;$)Gn2DS-gmefkBNOy7DAN~-$B5~7}bj!W(X1-ncL-3E@z6ma>7mx5iXg4|+ zcfRP-B)YiS(h6@k^aV0!_LNRGS+_Vtq0^H&cteTL>lukB zLiSY6?an$WXm&W@FuI+jq;lDHI#?_^@^I6-j$huqni*1{@vEutjBj=)% ztA%Wq^Jw4x>KkqMc(z`o-;_OE<)>q;y!d-P>rZDIq`cqzCHLo(-uD$wD^EM!C_4GH zx6OA|sX>uS+JqLFzIV&ld@lW0cRB9s&M&jw+Wu|4r2TJNw4S(W_SNZqD|CDE4ODil zS^4Cam}HfWpyP`fsV}lmon5izMKG_l>@JioyXp{YUNc3P=h&+4y4v5I-M=1nn0xX-teBtI5Hzsu%E`OXRw%y>oLFF2@lo=N1ZW^DF+i&{fbFY19 z+r@3twr}=v71r^{Tbcf<&zO-R|1W!2ZsE(%Pp`gemW}^pRKESKb6kTF*O!=OFL{@` zJA4)KjJ*&tf8x^iE$&M)P11d~v7gncb4;2Z!n@bxo06mT{KegC4<|^Un3%iZWuJ{7 zQ{$2iJ$1`3$U8mH2#bq)l-xa4N%}@NhjX@}$)-7c63rIkMsk<=y87;vt6cAXmaY=g z7CWDhL7Q>Ig_2yxV#NT@=bo25_BD7bRvE6j8F1xM_>wcVs}_dc5W94yb4k{g6K<2| zgtx}?RenF|e$;>FQQHl7nI+e`8u@Is(Bxq~zHEuM1fP)bgnFW_jx#X|8`8`!NZHYShH>dg>4corD%5AmOOw-k2!Drv@Nqkivx_jMi%S&-) zK8JT6Dk#1b|F@Jke9zrQ(`VmGnQ(1Q?%an{1aeHWQt$3tK5ey1=QCa3wRx+yDSq=^ zsPe#wXYDI~-RS*^H%gb^&@j(6(%9}By#4yBti9_d-&&-9q9WW`Bs0P~;H{Hix(*NX zw8)Z>+nP3IJg1-hXi2;fUA^zc1;yy-$v@p+ZO>Y^YSvVriyL`2PkKGY=cn`g4Zadh zt6~MOl-^jJpT6^#%cSie_%fn1Zl_h1?#Zv%(7MX$^6|i%PhN5y;@G;a*uC~mT}+?S z;d9a_wf_sG-MzzYGgt8N8i{8TkM`b4p3oh3_?kw#ixXe@3m@)xqH~URG$qb?xa0T) zJF(@DKGm|${xRjTF{^5h;gLT-LOw9eH`e)CwJZPKjBCQ1z0>B|zVSF{*~1wUZ>Yjw zz9A`G%Jgo@I{T|%(&Xl3&+0KPR*6;JJ7apo#?Ftwj+i(b~n)LCsnR*T{-1 z5_&(|w$ynz9KOrn600R9{y)0#1?z<8MNd1kUcYVNvz}r7FMGNBv-#`X)%unn*>Eq| z_0`wFldG1!{9090*d}+qhW~x#sn1snda~NO8Z~%wnO0?q-c7W)&vp6ql<@LsS9XqR zEKMKd1ab-m=07wm+yDC5DQOMcwXD0t{gl@$FD&v~85!I;ZK`I#WQw{tTq!cxFaP{}v|0qOr zhMt{!ig>gBHv!$H?n}a>@@G42kWc32|Xtt-~8FLvp+f~&3j9yH!+ zUzfx*nI&?~vJ6p&8FfLNOHMq~Sf|*VndN1iH=%L=#K#jXoR%vqFs094DCQBqXy)!a z8=2qzYE3kIZsk{@^}Ek<>w)x`<1(N1s*|=haKF30^Tn+lE#EWaTs!t9OgqG_;P-il z%C9FnldkZc=LnuC;4*&?+eOy&LiR=T&pk@kY|zj67c}WV+L-7Tt<_P!ObiU~Sr{1H zh$&T^6N^(l^NLgRiZe?x%Tj|&iZb)ko%3@+GoJ7TYDGvSPm%< zof=>8#pk>B-mso9cZJ=Ym1enj`VT5E&AZb3`P%GKr>F53T0U-j^;1bo;$w9EtTX}5 z^M9Y-xVGUz?lP?{&5iF)EI;KjTQX~VT9=GS!O4SCG3>7kIG5gEDdUrLitYEW7xn*d z7Qg9KICRCei=%t-T_vAo?^Dz>zWs6VzC7Qc_TX$o{ggjEz7Z0x=ksp-X)P43FOG~j zrfz3hQ)DT0Jm#(Kn({px1D=@XPx`_-z4E;ZgU^C96MI@SH>QV587p%HzKB*@xi;=QxKQ3OTX&^Ttapq=(BqKyrTJEG^YY)t3#?Kj?OmCrot%dFq~7j3w>=1tJK3v3JwYJv<5w!|b@@4Wo- zJV?tAQZWXXB$lK?+JI*xUX}}oiu~Vec6P~SM(>GQu7xfyCY3ZcS%+o2uG!+cR!m!u z=i~&wCB~&`mzt#C?e6IE2rO_e6t@Xl^PuVu|Npk6AW&Of9p*=5pc6mA(H8FCw^|;>9 zSpX1-PJ!YPkV4`#jyewWuv4UTFKJy9?$-rdZ&k7;^RRMxug@{ zxb8HyS5~?lT3qv2BHli(QGG+~p~*hKrnD?qwNYLW;k+(!=H7L=GdwxZ3#Pnf;ixT} zwJfiEiOKPcvW?U9PWx?B$<>{BDf*G-;-!bBWUicXnV$H{JagOP+uiDy;xz@nsdDI8 z@viiDz9ITiW&=Z~x{_hz1dmfP;l-t#=Y_YfchYBOnbrN;igA9Z?6siGZJRb_Z#G-A zbxEx9)k~=sdUvOOm+}lQzHDX~XY;gb>HoQx&ZKxJluK`p&YaP9TdiP0U0a;?yOodn zX3qS`Tg$YNPuua@+@l({FAw(as$IR!&~?HpJ?(W%f0^8V@SZ`Uh@Gb>^sCfmDZ|>e zYL)*#J>n8kQ$2rj+RZ%m%gwu9a>Yc?e}AuSW}Dff)SsbGHw({j?f9sB_f|l{-JHJG zwB5G9!ls7^Mnu^s&)htDS9SBVCpwyI^LHFJ?$c-wvZ`Kr#@q5#%xT#RZ?`(G)Nqco zFrHueadmEhG?i;nM%aa>rCie%h8`@6 zS@ghdjbO*zsJT8uYi>zhZ(gCtzixI=$@*WhL2IKQU0KQLT^@3|C^Y=`qSdojrcK&D zGb&iG@_OE#HODcbaxa8FagwMzNIw;Rv7 z1hW4AxU6Nn$e!MEkv-jax%eK~vc4}?xwY%E=Jf?qr#F>d5xjeC`Pn}Tb^#w0qyG!q z&3jV1KSreH5Z`~1`JO*w9CX8MxcDA!IIuMRhA?Z?^2oE3JD!O&n4Vps%VBbE$uicg z<=0oO;C;So&E>1PXSe1^{W!`UwIEMw?b-J-R|+zDgV(T|zS=)se1^*F-OI#8c&9Tl zC&ew)JaQ&@*US=jo5;CWKCAEN;aH<86uiPXDMKveVn>Gm(lry`Oj$5dOY)r7y8D-x zP0e3^;8*7NpT?PcMWbxiS)Y59sy!`;|J{t2KAJC1u5^2v%3-_hMCMGJox2+zcb{Li zC$Vg97UCZZezr5gc*{srge#HVI{v~<`ML8~eJ)W!(E|{YJ_lM@< zy=Gc{i&Uz3mi~1Yv})Rv@rL8j#be477Wujhbe>aB_?g)6F1?mpkugdC)DgLmuIq;W zdm5!S@{4!;jZnIG6L+`>n-ARWc zY%dy~sNVQMLhIM+DHqIVT#c9dEA;T*dV}8@=YHv#wVgWibFT%PURC=tlDldH*Y(MHt6$1Eon+;HS;K6_ zoNHcji`m;_o=Mwgcf_xwVNYTD+XkBUROodb@F&r3L(F2WJ_ z_955QhA$sYJ>z_ut1!eE5*~I!~p48#Wdx_339FDO*PjA|^p&;Pc$=5IPWYl+izOk{2(fD91 z_VviDV+9uv*8l%>Q%7$3oKp|y&OW&MZDK3O)^`u~*I(Lb^ZZKKCYeVT&${vjgzpE3 z)E}^Y-fsvOz3Xeko|!xuIpdyKxB z#l@L<>5$6Ou_zs>rkp!9*Z*>W$npQ>(s!?kN@{iV`?%a>-o!gmaRukOb_w-a2?htl z#jl;2^-e6`deO(;{4bY(I0>~L5wBygpYrj}wlj?0Y^Nu^n{&Ik?)k>I^Y`zsW1ewz zQXfl^)ua>Zo?05EQSMykS8w)A>MES_ct+}lIlAYITx7OOw8j7U*j@c?K)|nH)DyX{toxROvxJ5%%2qydomN-pon3p>@c4&i>pQ=H)BGJR z_%k4Ay3ye$g)tIeLYJ$WpFKY9)})KdX2z=|+1e(AO~1`nGRMfE-;h~UM`A~ZH`}`e zn?BdJpZ}(HB>qzQn>xd`l37x%YI}3yD~ZFLQgb)l`OA9gZ)X0@Wn96h=2b+O>=#|T zOw*>#M?XE~#oMV%zDwD9W=W}JcC6sMc+w!#-=q6Nz1I0}0w=}mly*61wVZK$7WnDT zA%<%Hxt?}lrQJekQWEcOoy9Y1clHq+`c<}j! zX)SZoMdlkFNchLTXt#uh&wHoebsXPM9#3UDY?fzmMAK7U)9F)cPy2)F#3oPHWipT3 z7tXgX^4YfVWLckN3~!)lJb&Pn$FKjty*$ApO%C>5inxHj~5-evGvCWVi7s7x`7Ii zX>aH6-}j#(;K*JJp%W`F7-*GW52@-|c_KNcwU3i^vX+RCw#JH%kRsm|QFbEe!2zL?(&CcH>Jy={** z5C7+b9}<3k}Xc=?sc9!S`X+H@16M~HrV&ywya62dun+O2kiUOwf|Ww+xFM3 zo78o7?=$%~NAZkntlAyXCDSavwI$07sPw5+?-!3PIOcx&&GO!jUuWe177aS>=PmJU zmTGUw^_Q6|S15K)X}dh*@`e6jsl23!gL~SdBLX(8NaQ@tQ`|Un&7KcF8`C(iYj0mw zHzRSw^ypQ0(`H!7J+IrlqA_Z@re(&rXZ|;W+fLT6T-&spC;4FUyxFRXD{f=*&f?FMR@nu_;>FMvp3&c zT2{0!$3Sn=!t+brM-P6I zwYlg#PebpK&{0V}<;S1jELh*G^=iJL+A6!Ko8~U}75B2I21gk=%{+cjd1w2NW%;Y* zZ|7^9hMqjIOd$EP?5?BEcNeX%p8Ac;X>q@ZRvgF8vP;Sq?yLPWRbEt@2`!q@r!DyN z|3yRB4vV$MDn*N9yKkV48qQFCaAr3X1H&pd1_pCtx=WBufZm~>8Uil<>)u_v7-%K4 zo@-wN&t0x&BL{&l4!w&ykq1)Rj`XLRY+jP>d0Vi)&Avsyj#0Vu;pYFxR>b!{-^S}_ zrl}Wu{&elR;&-;sztsKx`jflCb&6_=f%3xn`+pf|C;2rogdSq@QWA63@$@LR?p0R` zv#1F@5-galbWO6#%;)wObID1psr`-`*6eJD*RINxwqE;e-@|*L{9U#4~Ed0A3tO{UPKw_WEC0^lyK8-E)s!v{U54Y=mMr-=PvxGzfzN%GvlpU` zrMJD?UU6~N|69JN&2_)}FExL}z0vbZ(AmtLrH_}Mw6?!Bv2I@Q=@Ur~$Jpj|E|?j$ zH}U#mr)}rYXMATpH(&T;%rUWTnIG1DYf98MOO5>A)Ad$zTl%kO8!yhRR$^03=Fyi> zd%Wsc;bWeq|Ko;IO1af$8z z)Qj_+cB!1To4PmP?Y4Wh4@{O88?CRgy}119%RsG?jj1noE%xrxb717OD@x&=v~*I6 zO0aRdM~#YVqhi(f0`=c*iI<)VGzi{NJXz7b=mEdWG^L$J)m=|4TeiR9S?T{`XTy%* zHwA0CpL-PVF30h7XH|EN3#wT{k7`aF8EEE6UG(y zZrI4?Ofpg2?Yp~L;%(m4unzH=GjDQ#5q(+D_4COr<+MX7k^i13O){O-xx#Kub*-E8 zfv0}KGma@o`9CUbIKNoHgLS*7U*K6Iji4(QFTb2#b~5;g&CSe7rzOyDaeI>HJYzW{ z1H)owjI*thb29ZYN6{hq0i!;cI_bWbw4=ze{hK2~^f4kc)n1tE>t4Qt`O9iOBXTr#{o9+@nLJ-KyC7}n)Nd}A z1l@Liws?8ld#d2HbL-Z6edSLy%Dfd8@h{}px~(Q>T~cPIoIW0w^+>~P(fkuDrmt8r zlaI~md+hwc*te2P93wnT+mFpV75)3@5yovCpZ$NyT>GxJc)ru&5aVo%3Acp&Uai$G zR9@ur!Xhhramk$-vvvr0|MpGk57~F7dyimvocg6x47XeE{!01K-WJ_lUhT2sZ~yGw zTP`nrd2;=!+$EpR6hAnooyc~S!MbJf%kK^v_Hh>@zk7YFIQ0E;j9_B*Cyka@DwE@% z{=eQ{-Sy#)j{-|gS43xN+JCuwvg~qc#lq?@)t_1D_406hWcpB<9&xHvQhVpRN2%*X zr|Vlkza{an^Q!dWm-j!MI$_ZC{GwId^VIyJ|IvB;=R|(gwpZF8QOchAb?WBn>FA3X z79IQUx|)fBVH+!vvxbn!hm3JTMvSJ0{Pz=f6!`b8>glP%DN!sgfr8yz+)oD{nXe#~ z?bE}dror|-gdm3xl&QIkLlapM;*@ zm~(!smgC-Ye^+^YoYg*2_?h3aV8)lTgzbwLw;i`%|H10%&Kde8T3hOMpM7f&-=p|4 zW5@XkUCqxnwz2c*pARW7uS>lt{q*u0fmFq35=H5q$uAynd+QRo&c@#R;nY8tyDx4m zsMWo}nfrXFcALSSHUniRo*0K?4r0wtHYNrQJcjyHd2|vcCO)aZv)p=Jc&QU}|Av$Y zR>veHEaMN(XgkicwD@D%i9d(q@7^hU^JB`(6WNQZ`CqPk?)v+y#0_RYf!}$3U;prhWkLFx(?xZcnQG=XFX;BV#g@q# z#eT+F<;=_46={X?IsR>YCk;dszEsU0F`MEW8B=!h-LxO;51gDmJ#LS~qRxw}>n9uTySYw7 zZ0*MjmG;kOf8Y1LG;M!P^?$(wN?|2mlHJ<%#Ca=uH(Q0AIr(&ATjfsQYgaZbSa@VY z%=TF8I|%`wT4UL^K6)F?<7|3+>%47!-*o=(yiu%S9P?S~&`r^Ox{obZwy~ETm~hjQ zFZcY~_I+>7wC`Qq_`&tXPae0crMmkcbtr7$*dF%z&$63ej_ymI$$Q+4vs&d&?aenc zKQNy@9~-~&%mULz9kb3o$!>q-GNZ3xb{c!+yK^F|N{)4}nyT_!@4(YM`r;_Aq6g@^r3H-fqDi&o?aHG-b~IxP%jbxo0ihpJyH` zxNQmF>-wb2T)D^pXx^*({zf{)Ykjru{>0qdMO%ErcUM-PXlR_$sH&4)ac0BK$;qxP zVOM!|%r`jyKJsdxps=mLrpHSf15+d}?f;w>@%KD;$}9BT=Q}R2)^p|)u1im(Wf}4k zT689VI?`_MywGMts7?E#eT~cZwXVuJ^H*x^ikrbN7Qd1d+B4U7@;gnTtl!L6#NRM= zDlMFxtF_3b_xQ`rjPnjC_$!?@_4#b~WM`j{`~e|<*OqD4zR6CPcDLRZJgIv}v0r$n zYR^Nxt7nwjtDmfV(&(q&>-$ZV?e&_fs_tWw{l_C!_c)oEM*m^S?W=qKBKyDV|G-jbUVxOt+pZvSz!R~1Q#$DNudR6q38ukt)$GV3_YX6c@oLs{o1zkB#I<-scc zqzA>zE^)UVvKKpb`J+ju+wwRMjXmO8C8730g6iGxmz0EF7f*cg>2#TXKyvz;BPYt| zE_`o!H^E4-XoDQ3T_JCO(`pnanMP+My_BaS#liq9m(bHnzgyiM&n*M#(XN8^a zaqBBcE<6_Xl&5cl&W^AE@3u#a zMY+CNn3Fhv!}c>dHn;Y_mEk>iC(*?sqV8?Wtw#^nnRxd#rO)Wds93fC-YX@x&8PN< z@%`GA@Ithqpjo+HcjMW2{~A8_$}*eT7h8F%irmmwW7xL3;iOkUU#C`%@Fc!)$yvKX zPu8^m{3>-e%4hRL2ccOvcDI@e2(NqW=Tjp-dye{@-67YzlJ~9GzU`&CxMkB4&G-J? z^EaC=OFk5&OnFSKnmi|XBca^uVg&7QqM%NFmDI%>+e^^NEGjz?_{zm;E3 z<1L@c%=C--w%Hw3?`fyqA`@zE7IyDkq|H5T;_2wLypx(!m&8Vx!N zh=-2-Wc`$LlgbRYzuLJhcFE#ze;C6OUKngXAH3qRx<|^rEx!4V2Rvj`bNntueST@$ z_+e7;f@ixdQkMqZGPrZR=&;cbH@~>3>`SJV@+^n%1y6Q)Ri9+PeeU#chkehNyxIT6 z#*zDYzZ$FOnF$PeQetH6Gi?@F0DQ1S^S3imNoB{ z+?j@I*)=YHH)l^f=${kz;A;JhxX*7q>kpl}#xmQ}EI4NwYnwmQvh1~1qUQN`j4QvL zRJMFv<1TCaBDFZ!UHp;Nu89{GE(|^{z_w_{WyAjHzzehI{e8Y%(cr4cZH6m%6{h97 zru@2UChnD>QhF(o$E=!#Np@4fj@?gp7Wqbr-uiA@wVHQD-;^T?wXC;acuP7pu{*71 z=|Az^MD>%PhK*f-=&h+4;%fJ&3;qr;TD+Shb<=K+^kZcU^DZ|wsoYcvaj0q0vU3!k ze^GVcg7BK7npUSnI4|@YH|&4ZbSS>i^Jsjbv0~S}qdOOrOtVSXe0Y7Um0({-zTNsb zTiy!RbIA%0&zP3Xy6ODntm237RvV(2;}ud@J&62ts_=olokgVYin|_1UH`i^%`bP@ z>|hzAH1DS3)-r)x&Qs=89av%5{iCz&=Woq5N2vjXlH5KnZn(8 z(|5-qp%d?fZk*kec(d8Ifmbny;rQ{ozxQTeJo7@b^G`sze|czWXzA7KSN+$^R(}&{ zi2M{HXZ3Mo*1vGm{ns~&&CmGJ%%^X6@uTLy6~(`ddH*ke=>PKvuif>9kGW+le*_&) zlvV!tyhi5W#hN$A4X?^vZxIRa{-NrAT;k-_Ba4&wRQ!Duyx;Wd4^h{7eX)yvtHz&| zSS7hI&FxpJNa;kT{XN%~1RO7^IW#x5M=iN@$^+s3A{(a{u994Dp|(P80n3~vPv)(e z)#>Hm{eDV~jsD~t4_2s6F1_NhQEmCF8QQ@nGgqB7k^P*yt1rsZH}KqrB`(L4svNya zudJLZrFnTt%<&@C54kg+Oy{a{)w=36HGb7qJ>$^gU3#0J@R-h8dvZ?AnrTw|=d3&# zV>)N~wl!Z**DU?cv|lG=zj)@R2zO)F(?T*&JziC?oLZA6x&CC(ex2Yl-Rm1e!&Y33 zth0=+d@d9FDs`#e^HW;;O#+1HwVS-!6m{*#Q=6?j!X~RV2FiEN*qHh-!6xq*>*KI; zRi}#{!AfFXTlBl%z2G|5)S2NkxwPrz>88TXc0%UL9|M$1I%oQ-Xl&Sd`R6%t9}D|U zwnBF$H;P|pNiHSaYAd}`L!InYPk9CozT5%GZutR z{Mi&37=3f8t*7MTODm+CVwarE=GyAs)cH^&;JaAATk1EUB}e zJkQ0wW76`P)?M2+uUxa;yO$+>T8>mse&#lo?zN`Ae5y~j^ZZ$2sh8B@sa>{p#>ope zm1aawatl5GIjD<2-}-RSjgM;*zD;oRlNNbC<&m-S+MZlFAKPYySv@9C1H`|os$9rf zbor!o>9=#dTAayueG)}VQnlX~Ddn-f5?egEG}L+a^voMO&NRGu>BZJM`*^HV4C@CD zuI!uVL^ed2?hM$Fb>q!pt0O0wHJv&Nm^9+VB|T@~+EW7f=5Yq^e2-M91hS-C|=R1X%I zn}3}((Z%O)T;g}vnG-C}&I*gU_^zePHJo>5!5)RO2#dLzkJdaf$#hH8@O`=`@mEXj zyS@i{2e0JV?{fRJMW@-%!R-E79`irtPv;qb+57O&@{_K!SWIua?RnSJCQ-ocb%{l{ zyX|mVwD(>@0d3!Sf>DZyN&Q7@A zn_aqmhqdX=JdcPn389HA6l04F1yz54;OXQ%Y_NROr3o!1+*<8-4EKHrk&bn`v3AX! zl81+MpSHc3vO{tAlH(J+pBg#qP1oj-RqcMfl<(-FRYuPH6vMR^>1 z<+`TJ?U7=L+o|`}DtO6wPlmf6Cr=Xa3Q7OxezI0S}+el$$SivcpQ6 zk@x4qn4A;Mp9H?o%#*#?FX8&}QPRZEQBTk0r_?d)k|3dBew>oY{E#2~CW6@{jwm&XQo!)LrP!aMK zVULdPUYxnd@srymV_h>}8^IpcuJSe^*`vl<=^>Io#MWJZvb@bkt3UIT@l?LRgUb@V zbo$&5*&fxs=%FOh6>xD~UqZ{XBTl+Ub!BZfinT?|`MC1ird?}|SM4)OczgKzlS1cj z+OBHTa^h-!K3jMC)uAcXN0(h$QS^G+rXc5e6VzW`QB3Ys{XR*tv#rMNymZi!&bUj5 zJIlY9Z%ep#y?!nadyD@ExB134InG-SX_H>$6hnr#a@M7>nw$rVe zKDrf$ANsZKVAW~ijdeY`nx)!N++{4)+g465(3w~>MRe+pDb3PXtN%TEmtZl^b%G72 z&=vlo3-NjTj@{nA)Ol`l+@9*_JDpb-O{%&sRMx`v+(g)H*NhL-ZK7lMXZMtt1^aE= zmw!g)n6=ew;U=-JEwgrCX};w=`PJ3JYrAJJi++&2bYa5E+ZSu@?)^OXwnp8$s6^v%brG*WLWu+zlCj?$qu5%X{efLRr~w;Vq5r#lJZD8Be^7mr~oM z`p!Y8^7f&-AN!sho%qz|Uc+G?-{&`N-ep*LT3`y_3YKN9fW*QpTq--|7;qy6G#a!iSuh-;d|86>-$S{4paK_D9RkkZx-al)) zcId^HdHdrhUe=rcxlqe$qgaEu`pQ+)7t4HgYkQ!U{U`U|$DKL7`JuH3S-x(1{9bU$ zL+M#>%YSAI7Fj+jy2)5^>es_uJBItlCn^s;+S9!DoZJj%{%4Q;`gmr`l|Rh;7XEQ( zEx+cJHLY(%bK@d^I%e$s@ZO*EW2BF^4&&n-CHDrYx5wnx-9Npt{qOgm$IWd-7hkwz zo{?mCwBpyq>v#C~?tGK^PNbYsKz4J*VSmn~i(|9H0O>y~4+Me@s;_gZ9{`zfU!@P0J8 z+Hm7F@ef}gi3r8`HF=6Th8~FCut9usc8yu8pk|loTCodEf1d6<@yK?nPx|D{uA?QZ zTwi~*S|TF-;a5<}%}Bd+ zcW>)GPfx77s^fKb;ht5S*l)Zzve6ZpS>&Y^d4%k&(sS&=$o}` z)y3$^;Zv5!g#A@`c1i1BP?_lUS2LAL-IvT0io9BpJMWG4zejdD%k(C#Teo>1|K;{e z-hwZ$O+1@(r#vSuRo`#@jeOURuI1{12fx|)BnbJq3cuRGr(I}w`zuEh^R~xKUlvG) zCn;Y}5KhiDmOCb9)64zw`i-vm$4Yy=PkrCOJAX0j@rzo@38yNyoYc&io>bzYY4y}6 zXS!NTs&JWy`l3s%dOMlKPy2S9j63o)@LwI9^;63)Cr)EKU#rJK}L%PB>Un&;Wy!h}d^XBp!r!D1QJ&Ant_{8c>nlo!<-X80p zSt$HO*`{f}f%)=Nzkg_&o;{j;vSj;;x?>j~&xvekO4hhOVfn)>-rxNReQkzEH>da5 zHwpFiEtp#3jXM5u-&c69ag85|5KJ0PgT6^X*Ptoab z=?gEc4f(gtMC6i;af|jkrIZOB)*6re#0A3*&E{=%?U=qTd(Q1OAD%I!nNBeelUw!i z$+e2^J+AZEXP$Ds7rAv`SBl@fiOqIr&K+#a-rMv0e(0CPz6BdEu_^YfFELo*rWow1 zf9_8CpXN=C5%UfOGyQ$2{>=B$%Cjl)hj&=?Ki|{%eBZ?MWnbp{o>gCX!Sd|qAJ@Hk zzG&D6N9>Yh*VXxIEd1+(j;`Olg|?T^OP00;KeRj-@Fnxbl@D{;p5NrU`;m2-Y~beK z{K-l)`5v;~R^55N!1~aJKda=6XP@RzkSp9it-@yi%nv&sFo>CjEo<1x-~FPyPG{Ns zX;-SWbjz;@{gG+kv(By0y=L1CE7dg3qY(mLDkRe&VLMUYVTh zqQ63_#Ts$}IN$7#FS^-|uxMS@}nKEZCO-+hi zyXQi?so3tVKTnFbKChg2{MNIWtrt`>ULCcPGFh*bdH$!#(vFOs`wE(Kr* zi)8=(GZuRubcXQ6ojMWpV#@IfPJ_$dlB%0G$Y;-(@Axde<>l7Nl11jlPwI}{RXlbx zUo!rG&+`wA^Nt^%V|gaQvTc5MU%Y^K{1m1=Dw{uU(mANTiD$i+b)Ba5$60sUu0K`X zqqzQ&Z{^&I^5s)+C~H<*zEwR{c-+0?(s?8H@}S-GAB*?g<2T^dFEZ_~p4&8CXP4=c zo2O+y6v`iV^61?i(=olXc^22Tl(>Q~-g}M(oSQ8E(Y#`Aq`-vbAGJR{Pnve8+dRUm za>d8b6_X>MZ{6hfboSbl>w{$C$L<}kOSPXRX(TOe(|szwaecwlRh#T`yw=Y)xVv(8*52iB z9OGCIXMS$uDizNUTztp%ci;ZQKXw){99|`2aiPxs-lKW>!57Op^!Gp2{p){Zb9ra_ z{=-7|wt0Wl-}3*j$h^=EQh(epRZfjtQhVKLJ^RzG4Cl}Sv z-}~tYOGRJOmbNrYrn5y%bGLo-ewBFN=)YIZ{qFkW54J}(2hXzzxff8F5t!Gz{6z7o z<{OcLPiBU`-s*7o^%Vccx}03SJtsVE^DjHH@Fz%j8?ox^p8R_4%~$nAt^BQD7uEJV zU!Le&ytcjkXz$+@nPFEyF}6r)>D=(%Z>1Kx%;8A#%cmB+~4ME`$6&8 zBjtN)@|y$x+uDBT%wFJsvCaNb(Yn}Pd&ARq>3#K%Pkv6SthOlSO1_wQS1WE^--b^f zceUDswx3db9=3C8`pQqbmF*eFA8gm_Q(lqQ;AhvfGX6ldh-;XMvC49>A0OZU;D4UB zE^R%Z-%g9~7uRe*IA@FX&C@y0&!znH|1+Jt*zeonLvw%bk1d~;_~y)s-}A2sXLfF7 zV&Sr`-Z)phFq6d_>r?AxUxo>5#(jMH7j0xos{5(kNp=PX1|h5?OW;xc6r@>w$jH*v z@XT`QbkRDu+b2F9>3*eJHaWyMIeOYtiAf9}gSPk0o?x*p_|dUrnz}w~I@g3;(^x!F zc>5H+fQ*|9b24w5GjmQ5nUqizvG3qR>-#^B&0jEQ&Wz`^=_=mWb{M)pKXd2%x!U>P z?>*Nu{&jq>{XfYAjDPf+%4TSN68w>HO|i3EYYj)!xkU7Tqq zM^9U8Uie2R7GDG9j3er=KPu_1{1IXscj$EDo<)zYa%``)Y7(n)JDM$6a){^g=12c_ z8pMfzH*Y!m{OCFTE04Q(s7#9&Vu%m>IMraEl%x2*u>SRreG@s!EYN?=>}ow(cE+X8hq+#*8NHm7C$8&!@Z_uI%IAz$KflDx zci_X4%%$v?rp-Gg#ms&4%z??fLJfi*Kf5$(p0{sW(J{@+M;|mMN`IObXi&6mu~vw@ zUGfc+$|X^c3m33#>QODe=`poYs^|2!rTvjF9|?V2nX&4?%rhb^U-tQ~(%s;!JTvB) zT<8V4^#&G+($nhnvU$Fr)8govt?68G{=>3v@8>+PH9z_|mRyRtuzB;Yse;+zkt6I8kc?1JAal~>2@K1Yg@Ob9l1K&gKpgvT%WO|(0I0~n9BX6 z^5kni{!*viw%wVTd+!*R&^-Qa9DDBn_IZEde4f%Ok-Oi1ud>X&;co6bC1YikR=QVt zZ&5cx*^8A^T;^Qe?3&E|D{|ez)aRA5Z@pXWUnf@o|McJf(A7DGQC90$r!iG7xP1LY zPvjP{o@B|3-*Qbq{BFH0#rK$t^RB|n9dQvU-PtdW|C_}w^Hb_xKeH6y!-ghyvw3}y z)AlKGT>HRS6L2`XBKmOoM_bB!xbJHbJ?JOvs7L0{emswKJ4E-qRvm-V^vjou%PnB%O4`IUuu0hQnXCg zTk^SNZPxx1qRD^Ow!Pl5>LJ^_iy!#bU7HkrSZ4R)GQFu&jB0~2XZIYFpPpK>+;VnHv*Tgh}tYy*)ivK`{S(- zwo3W)J3aT!_EbN%!2Ph@-+Kzu??r6qn-{I@ofvL=Xj|RN7V$H4)~36BSu1DA!4$nG zO@rB=+iKTdv&z<+0k$7@%-`%~G0&?vb+gEvOUDoYG0J`3Ud#}9@pXW~fvNoOAJ5W_ zi<5uy>%ubA=skP>3B0Pb?{Pfk++f6KACh&?<4)V89nAOt-7?{4{gtn{tBqZRht;}iU96ACr46YX zQ-0Vo$u8_WAQkm@Ms)1|>#u%Uo2N`T{K>xQShjlj+CTg97hgGd{^MMppN{{8Z?sCj zY3sGE5aoYpdgOzZr_{qf=Rf?3iy8ZqL=>ZB4-8;s(ESf3zlPsfGM|f55h6bwa6Io(#@J9cj7&jx|lOC>~-B0B0Fj+b&OJC$!MJ6RyJ{8vnSZPE$L zoribtX|J%aWuKe!=itf%D<)4}*zh3aQ>}-G-u;7j^A#ucyxY}!xA^1>xysiZVQc;@ zUj4e>|K1_z>!&?$=)Ybno~RoCWUd6eMX-Ya-Pbqqq@R@v-Epu{ze#Zx`wdToIlAI*-Lbscb&hk_2R|RS z_0K1kC089fyH?FXBKS`GPU+c~W~H;otnpYKRu-NtvG`LDkKT?BOY^nWt0HEJ9i1`d z@rLQ{U-Ez4R?rl9yz0cnZ93;YEjXSQe>)Ml%)^t%Kz{9P700Ca#XZ+7e|J@XONh_> zFm1I~TwrL)(t2J_`K!E(X6=0Ox%1=i^U_ayELFeRsK4PZdG5N;DejT?5cFa7e>!Vs2z3=^s9oqLV7wugBXkQmQmr(SXoPu!2ZCg%mn{n?$%gX63 zug_|DOl?gxT>r3uRatj_dh_O!I?npleH(gaT55To;qtC1=33PHF_d+q>u#^3vZ{$6 zr}y5nDv#S-I$3Gwq}aq~TE#6k?FVOUSM2aym}1?QXR#)$Xm1kNU(w>2xY#Y(rfH$7 zSG?{DZ*zS+iS2vXQU=z$tsgf}5!o`;Hmve{)78UrD;INRO|jO>6tk^TtLVGJvpP&% zW^v%-Y0DQZFEI1Anvil_b7%cW?>qzZ5Qk}dLnr9n=3RbhKIh4@2Qn`w3wb*8SBaL) z-4I(KvrShs`-O|&VU1%VJabE8&3y9=ChL6sap^bL_l+9L3}?f0-#yd4eL8=G?!5GC zW%u^WOx|>HLsz?0n!)M?_CD&*enuVs;&S(i<1+7+{SKGUZ_~Q^LE^KNn99P7|E1q+ zPMP}EH%^iNwts)yyB_T_ruWk_mp(idsAC@Ax?swhzDoz9*hK3l?^}H+cGcCrxiJgt zHN5R5czt$xyw|gGz0hm%_GD3%en1Le;4Q|RkL;#AQdG+R-BIWIX7l{oiD5tEo3xTb zF1uUno;a@h<@XQf9WqI8TOuwW&F^@>=&eBK);YWSpPYXreuQs=(n9$|1-~?8Y#r2^ z;xBw#Vf3o}jcVV5IIXWQRQ@==3OEoD{Qc#I-wL*dE>=rtDL(03`DEc`&$Oee|L$#^ z&>}CB*VNf=&}bU2Q8slSd(ylh7q06nCyp@QQmVL|bxOj>WzG5JZ=SbR6qz?=+wtz& ztN7^igfP8=#jywVuH10n^x*QwFAa}fgpbEezG9(tvywCDLuU{AhVPiNV@@@U3=G`N z3=CFS@<>sBR%&tyF^Bffo#gF*$U&s_JS&S!jeWr3MQ;wU%#tX5tQ^>*y+}pkyR-E) z!`yZrX3qa!;c9w+82@QJ7FlI@!{xEgyO(n;?|&& z?bictXTC+gxGB5V<;Bbw=lWl$UibNNM|FzuHMTyXkA1CuGFt8NohzR1wf}mp!%9P` z-KOzYOH#ep)!uKM;vk3`Nf{(S!b**QD+)bH8Hcz`iy zx&nW%QjW;N$d+bm%Hb4SFo$vs%T2k`{HgZ zXRQbmzawkrY-l(TIG_9W7FJp7j3_>1_C$q?dxh_~D@wA(zT0(0+P%AMr+KhamoSs5 zbZQb;e(lK}?h7tn``usS=G$Ji&2mCh<+e@R>LRK&G>Z#-63kQeHn4by>G1vtiKsZa zsO#(gDQPaOAqyB>nU=UOkU0`?JY1ylF{4-Rx{SI_YmdpBSUSGVs#v@A;s)Nemx~W) zd^^LNer4vOJOPV8$4yt>j$NSV)+lmi(*EKN?3Q021fFKpRQ~ zP3QE|dX~JAFI7~#xcQW};XbabEbmkbmhJnprNM0f!kPLdx~yk)7Fn;%&}BWVwr5P9m7~X=Vd|g8vbv^yu^aH$6wdHQKvvpvAteX;n zYhz$o(x}P6zyR0g>*(j{<{BKL=j(=U0!S0gViphwj6oL9h9-G@I=I2AK?F$0ey9#e zsA4!4qzq;iC;~uyh#y~}YJ^@VfMLK?)yvCvGBPkoGBYsfLv(}5C5^I-3=H{2>H1Kc zVFozm=a+y^?l167%*+ceNh~Ts9HEEqtxQIp)$*(i3@TiZ3)8?RGk}6pnVD$AA&nk% zqcZ~k)h=RSV7SD7i@Gd2Ueq9^2_sb@)J{F$NixjsIc5{xit#|!wq&=+JTt5 zq_Kb#zkx2PX^EvdB`^yDit^KoQj3dyGK))6^HPhjyZ`mn-@1}a3=BRj3=Ad^o519f zMlL@5)&&&hS5!KdmSm*nm1HI-!cQ_m_s|sO_h&BhGcZ^wFfb^B&4v(58cPN6+JN1` zm5n}&7BVm}oMK>LFoc){CYLm}D&RGaxbRT4tl`+k#=y|R1KIEZHX9tDrHWXMcdbay zDJ{+{OLfc4Np(z4PAx9>P0UM7#}(ic-n8gcGBGf;v7o2L8V#%#xECd+$=TSQ`lO7hV6g}TgPtw}g93`z%N+3;joq{l`>np^GchojutKUR zn1M?g!^5ze=98a}Ga-3-tyk}5WMFvD1lgB`FpN18t6>3&B^i#z#i_YTIjKb|KKaRs zIWC#SB}JJ@rHBLD(R1&X=cw1-BW!sVMc5YX!GM0BFT%{kB&=qlH^)$J_eFQ{DqGas zF%hO0reif7n*Ooem5FXN`t5@VqjR$G8SRR47a_W-=ojxGOg&xVLd(Z+^bj|3u@IW+!s^IyUnqe2A z;BI!K8-Kld>t^JeRS?E&m1DRKW;`)nHgt<>(QZUQSaiD@!y+X2Y%to1_6TDy*5NZ2dAJAN%s*)78zamVZ^dUOcuWa?+A+GRDriRuBTQv# zBf?bhp`}<4a74FgDcX6c2#c0=5^E78c%Y}FqFaD|`Xs`FnLR{Ufafeqbeqsmq(j*B zegY9TITLd}9lD)0Xord+>|8LJ2s`l|F@|mx`k^cct4_}%!YVvRw4mD*f_DA|!lqMm ziLeQh92~KphJkJs`Y8hltIjVV+A8!-hUk`{Z{0>%a(^+=mO%0=(!n<9wxI73M%Z#^ zDbcn-c1>b$FQ9M4L|AZR1&J0A3NiFea0si8uO`AOf?^g1HU$G<%aJz@mV z_K+Yf3*AWAGVF1IzIp&*=K8}JW`ctQb1eb7JJBa05N7T+4NqaatP$PKAkDB=B4~6A g#0TLejW '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" \ No newline at end of file diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/play-services-api/build.gradle b/play-services-api/build.gradle new file mode 100644 index 00000000..f78981e9 --- /dev/null +++ b/play-services-api/build.gradle @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' + +android { + compileSdk androidCompileSdk + buildToolsVersion '33.0.2' + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = "1.8" + } + namespace 'org.microg.gms.api' + buildFeatures { + aidl true + } +} + +dependencies { + api project(':play-services-basement') + api project(':play-services-iid-api') + api project(':play-services-base-api') +} \ No newline at end of file diff --git a/play-services-api/gradle.properties b/play-services-api/gradle.properties new file mode 100644 index 00000000..cac2a521 --- /dev/null +++ b/play-services-api/gradle.properties @@ -0,0 +1,33 @@ +# +# Copyright 2013-2016 microG Project Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +POM_NAME=Play Services Internal API +POM_DESCRIPTION=Interfaces and objects for IPC between Play Services Library and Play Services Core + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsApi + +POM_SCM_URL=https://github.com/microg/android_external_GmsApi +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl b/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl new file mode 100644 index 00000000..ae33a334 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl @@ -0,0 +1,23 @@ +package com.google.android.auth; + +import android.os.Bundle; +import android.accounts.Account; + +import com.google.android.gms.auth.AccountChangeEventsResponse; +import com.google.android.gms.auth.AccountChangeEventsRequest; +import com.google.android.gms.auth.GetHubTokenRequest; +import com.google.android.gms.auth.GetHubTokenInternalResponse; +import com.google.android.gms.auth.HasCababilitiesRequest; + +interface IAuthManagerService { + Bundle getToken(String accountName, String scope, in Bundle extras) = 0; + Bundle clearToken(String token, in Bundle extras) = 1; + AccountChangeEventsResponse getChangeEvents(in AccountChangeEventsRequest request) = 2; + + Bundle getTokenWithAccount(in Account account, String scope, in Bundle extras) = 4; + Bundle getAccounts(in Bundle extras) = 5; + Bundle removeAccount(in Account account) = 6; + Bundle requestGoogleAccountsAccess(String packageName) = 7; + int hasCapabilities(in HasCababilitiesRequest request) = 8; + GetHubTokenInternalResponse getHubToken(in GetHubTokenRequest request, in Bundle extras) = 9; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl new file mode 100644 index 00000000..206b1a9d --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable AccountChangeEventsRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl new file mode 100644 index 00000000..832e5859 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/AccountChangeEventsResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable AccountChangeEventsResponse; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl new file mode 100644 index 00000000..f73f91bb --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenInternalResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable GetHubTokenInternalResponse; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl new file mode 100644 index 00000000..74de2776 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/GetHubTokenRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable GetHubTokenRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl new file mode 100644 index 00000000..c0c080cd --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable HasCababilitiesRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl new file mode 100644 index 00000000..82536b93 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/Credential.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials; + +parcelable Credential; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl new file mode 100644 index 00000000..d000f32e --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/CredentialRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials; + +parcelable CredentialRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl new file mode 100644 index 00000000..7fa0d4ab --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials.internal; + +parcelable DeleteRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl new file mode 100644 index 00000000..814f68da --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials.internal; + +parcelable GeneratePasswordRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl new file mode 100644 index 00000000..da248c33 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsCallbacks.aidl @@ -0,0 +1,10 @@ +package com.google.android.gms.auth.api.credentials.internal; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.auth.api.credentials.Credential; + +interface ICredentialsCallbacks { + void onStatusAndCredential(in Status status, in Credential credential) = 0; + void onStatus(in Status status) = 1; + void onStatusAndString(in Status status, String string) = 2; +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl new file mode 100644 index 00000000..89c6c7e2 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/ICredentialsService.aidl @@ -0,0 +1,15 @@ +package com.google.android.gms.auth.api.credentials.internal; + +import com.google.android.gms.auth.api.credentials.CredentialRequest; +import com.google.android.gms.auth.api.credentials.internal.ICredentialsCallbacks; +import com.google.android.gms.auth.api.credentials.internal.DeleteRequest; +import com.google.android.gms.auth.api.credentials.internal.GeneratePasswordRequest; +import com.google.android.gms.auth.api.credentials.internal.SaveRequest; + +interface ICredentialsService { + void request(ICredentialsCallbacks callbacks, in CredentialRequest request) = 0; + void save(ICredentialsCallbacks callbacks, in SaveRequest request) = 1; + void delete(ICredentialsCallbacks callbacks, in DeleteRequest request) = 2; + void disableAutoSignIn(ICredentialsCallbacks callbacks) = 3; + void generatePassword(ICredentialsCallbacks callbacks, in GeneratePasswordRequest request) = 4; +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl new file mode 100644 index 00000000..b5bcb593 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/credentials/internal/SaveRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.credentials.internal; + +parcelable SaveRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl new file mode 100644 index 00000000..ae8e79c5 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthCallbacks.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.auth.api.internal; + +import com.google.android.gms.auth.api.proxy.ProxyResponse; + +interface IAuthCallbacks { + void onProxyResponse(in ProxyResponse response) = 0; + void onSpatulaHeader(String spatulaHeader) = 1; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl new file mode 100644 index 00000000..a5f6a537 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/internal/IAuthService.aidl @@ -0,0 +1,11 @@ +package com.google.android.gms.auth.api.internal; + +import com.google.android.gms.auth.api.internal.IAuthCallbacks; +//import com.google.android.gms.auth.api.proxy.ProxyGrpcRequest; +import com.google.android.gms.auth.api.proxy.ProxyRequest; + +interface IAuthService { + void performProxyRequest(IAuthCallbacks callbacks, in ProxyRequest request) = 0; +// void performProxyGrpcRequest(IAuthCallback callbacks, in ProxyGrpcRequest request) = 1; + void getSpatulaHeader(IAuthCallbacks callbacks) = 2; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl new file mode 100644 index 00000000..fa5885f8 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.proxy; + +parcelable ProxyRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl new file mode 100644 index 00000000..eae21f3c --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/api/proxy/ProxyResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.api.proxy; + +parcelable ProxyResponse; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl new file mode 100644 index 00000000..ee7f7338 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/auth/appcert/IAppCertService.aidl @@ -0,0 +1,6 @@ +package com.google.android.gms.auth.appcert; + +interface IAppCertService { + boolean fetchDeviceKey() = 0; + String getSpatulaHeader(String packageName) = 1; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl new file mode 100644 index 00000000..973de92f --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/checkin/internal/ICheckinService.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.checkin.internal; + +interface ICheckinService { + String getDeviceDataVersionInfo(); + long getLastCheckinSuccessTime(); + String getLastSimOperator(); +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl new file mode 100644 index 00000000..ae02e068 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.internal; + +parcelable GoogleCertificatesQuery; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl new file mode 100644 index 00000000..19839d7d --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ICertData.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.dynamic.IObjectWrapper; + +interface ICertData { + IObjectWrapper getWrappedBytes(); + int remoteHashCode(); +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl new file mode 100644 index 00000000..da539f32 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl @@ -0,0 +1,12 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.common.internal.GoogleCertificatesQuery; +import com.google.android.gms.dynamic.IObjectWrapper; + +interface IGoogleCertificatesApi { + IObjectWrapper getGoogleCertficates(); + IObjectWrapper getGoogleReleaseCertificates(); + boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData); + boolean isGoogleSigned(String packageName, IObjectWrapper certData); + boolean isGoogleOrPlatformSigned(in GoogleCertificatesQuery query, IObjectWrapper packageManager); +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl new file mode 100644 index 00000000..f6c53996 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.common.internal; + +import com.google.android.gms.dynamic.IObjectWrapper; + +interface ISignInButtonCreator { + IObjectWrapper createSignInButton(IObjectWrapper context, int size, int color); // returns View +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl b/play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl new file mode 100644 index 00000000..69b856bc --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.server; + +parcelable FavaDiagnosticsEntity; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl new file mode 100644 index 00000000..58ea0ba5 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/http/IGoogleHttpService.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.http; + +import android.os.Bundle; + +interface IGoogleHttpService { + Bundle checkUrl(String url); +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl new file mode 100644 index 00000000..75db9fae --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleCallbacks.aidl @@ -0,0 +1,12 @@ +package com.google.android.gms.people.internal; + +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +import com.google.android.gms.common.data.DataHolder; + +interface IPeopleCallbacks { + void onDataHolder(int code, in Bundle resolution, in DataHolder holder) = 1; + void onDataHolders(int code, in Bundle resolution, in DataHolder[] holders) = 3; + void onParcelFileDescriptor(int code, in Bundle resolution, in ParcelFileDescriptor fileDescriptor, in Bundle extras) = 4; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl new file mode 100644 index 00000000..62ae1478 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/people/internal/IPeopleService.aidl @@ -0,0 +1,44 @@ +package com.google.android.gms.people.internal; + +import android.os.Bundle; + +import com.google.android.gms.people.internal.IPeopleCallbacks; +import com.google.android.gms.common.server.FavaDiagnosticsEntity; +import com.google.android.gms.common.internal.ICancelToken; + +interface IPeopleService { + // void loadOwners1(IPeopleCallbacks var1, boolean var2, boolean var3, String var4, String var5); + // void loadCirclesOld(IPeopleCallbacks var1, String var2, String var3, String var4, int var5, String var6); + // void loadPeopleOld(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, int var6, boolean var7, long var8); + // void loadAvatarLegacy(IPeopleCallbacks var1, String var2, int var3, int var4); + // void loadContactImageLegacy(IPeopleCallbacks var1, long var2, boolean var4); + // void blockPerson(IPeopleCallbacks var1, String var2, String var3, String var4, boolean var5); + // Bundle syncRawContact(in Uri var1); + // void loadPeopleForAggregation8(IPeopleCallbacks var1, String var2, String var3, String var4, boolean var5, int var6); + // void setSyncToContactsSettings(IPeopleCallbacks var1, String var2, boolean var3, in String[] var4); + + // Bundle startSync(String var1, String var2); + // void requestSync(IPeopleCallbacks var1, String var2, String var3, in Uri var4); + // void updatePersonCirclesOld(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, in List var6); + // boolean isSyncToContactsEnabled(); + // Bundle requestSyncOld(String var1, String var2); + // void setAvatar(IPeopleCallbacks var1, String var2, String var3, in Uri var4, boolean var5); + // void loadCircles(IPeopleCallbacks var1, String var2, String var3, String var4, int var5, String var6, boolean var7); + // Bundle requestSyncOld19(String var1, String var2, long var3); + // void loadPeople20(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, int var6, boolean var7, long var8, String var10, int var11); + // void loadPeopleLive(IPeopleCallbacks var1, String var2, String var3, String var4, int var5, String var6); + // void updatePersonCircles(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5, in List var6, in FavaDiagnosticsEntity var7); + // void loadRemoteImageLegacy(IPeopleCallbacks var1, String var2); + // void loadContactsGaiaIds24(IPeopleCallbacks var1, String var2, String var3); + // Bundle requestSyncOld25(String var1, String var2, long var3, boolean var5); + // void addCircle(IPeopleCallbacks var1, String var2, String var3, String var4, String var5); + // void addPeopleToCircle(IPeopleCallbacks var1, String var2, String var3, String var4, in List var5); + + Bundle registerDataChangedListener(IPeopleCallbacks callbacks, boolean register, String var3, String var4, int scopes) = 10; + void loadCircles(IPeopleCallbacks callbacks, String account, String pageGaiaId, String circleId, int type, String var6, boolean var7) = 18; + Bundle requestSync(String account, String var2, long var3, boolean var5, boolean var6) = 204; + void loadOwners(IPeopleCallbacks callbacks, boolean var2, boolean var3, String account, String var5, int sortOrder) = 304; + void loadPeopleForAggregation(IPeopleCallbacks callbacks, String account, String var3, String filter, int var5, boolean var6, int var7, int var8, String var9, boolean var10, int var11, int var12) = 401; + ICancelToken loadOwnerAvatar(IPeopleCallbacks callbacks, String account, String pageId, int size, int flags) = 504; + ICancelToken loadAutocompleteList(IPeopleCallbacks callbacks, String account, String pageId, boolean directorySearch, String var5, String query, int autocompleteType, int var8, int numberOfResults, boolean var10) = 506; +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java new file mode 100644 index 00000000..d9a19568 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEvent.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountChangeEvent extends AutoSafeParcelable { + public static Creator CREATOR = new AutoCreator<>(AccountChangeEvent.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java new file mode 100644 index 00000000..cf8561df --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsRequest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import static org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT_TYPE; + +import android.accounts.Account; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountChangeEventsRequest extends AutoSafeParcelable { + @Field(3) + @Deprecated + private final String accountName; + @Field(4) + private final Account account; + + public AccountChangeEventsRequest(String accountName, Account account) { + this.accountName = accountName; + this.account = account; + } + + public Account getAccount() { + if (account != null) return account; + if (accountName != null) return new Account(accountName, DEFAULT_ACCOUNT_TYPE); + return null; + } + + public static Creator CREATOR = new AutoCreator<>(AccountChangeEventsRequest.class); + +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java new file mode 100644 index 00000000..10491379 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/AccountChangeEventsResponse.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountChangeEventsResponse extends AutoSafeParcelable { + + public AccountChangeEventsResponse() { + } + + public static Creator CREATOR = new AutoCreator<>(AccountChangeEventsResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java new file mode 100644 index 00000000..4845ed41 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GetHubTokenInternalResponse extends AutoSafeParcelable { + @Field(2) + public String status; + public static final Creator CREATOR = new AutoCreator<>(GetHubTokenInternalResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java new file mode 100644 index 00000000..2c4e973c --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GetHubTokenRequest extends AutoSafeParcelable { + @Field(1) + public String accountName; + @Field(2) + public String service; + @Field(3) + public String packageName; + public static final Creator CREATOR = new AutoCreator<>(GetHubTokenRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java new file mode 100644 index 00000000..c9907fa8 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth; + +import android.accounts.Account; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class HasCababilitiesRequest extends AutoSafeParcelable { + @Field(1) + public Account account; + public static final Creator CREATOR = new AutoCreator<>(HasCababilitiesRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java b/play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java new file mode 100644 index 00000000..df3768c8 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/TokenData.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth; + +import com.google.android.gms.common.api.Scope; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +import java.util.List; + +public class TokenData extends AutoSafeParcelable { + + @SafeParceled(2) + public final String token; + + @SafeParceled(3) + public final Long expiry; + + @SafeParceled(5) + public final boolean isOAuth; + + @SafeParceled(value = 6, subClass = Scope.class) + public final List scopes; + + public TokenData() { + token = null; + expiry = null; + isOAuth = false; + scopes = null; + } + + public TokenData(String token, Long expiry, boolean isOAuth, List scopes) { + this.token = token; + this.expiry = expiry; + this.isOAuth = isOAuth; + this.scopes = scopes; + } + + public TokenData(String token, Long expiry) { + this.token = token; + this.expiry = expiry; + this.isOAuth = false; + this.scopes = null; + } + + public static final Creator CREATOR = new AutoCreator<>(TokenData.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java new file mode 100644 index 00000000..9ade5cc0 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/Credential.java @@ -0,0 +1,160 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import android.net.Uri; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +@PublicApi +public class Credential extends AutoSafeParcelable { + + @Field(1) + private String id; + @Field(2) + private String name; + @Field(3) + private Uri profilePictureUri; + @Field(value = 4, subClass = IdToken.class) + private List tokens; + @Field(5) + private String password; + @Field(6) + private String accountType; + @Field(7) + private String generatedPassword; + + private Credential() { + } + + /** + * Returns the type of federated identity account used to sign in the user. While this may be + * any string, it is strongly recommended that values from {@link com.google.android.gms.auth.api.credentials.IdentityProviders} + * are used, which are the login domains for common identity providers. + * + * @return A string identifying the federated identity provider associated with this account, + * typically in the form of the identity provider's login domain. null will be returned if the + * credential is a password credential. + */ + public String getAccountType() { + return accountType; + } + + /** + * Returns the credential identifier, typically an email address or user name, though it may + * also be some encoded unique identifier for a federated identity account. + */ + public String getId() { + return id; + } + + /** + * Returns the display name of the credential, if available. Typically, the display name will + * be the name of the user, or some other string which the user can easily recognize and + * distinguish from other accounts they may have. + */ + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Credential)) return false; + + Credential that = (Credential) o; + + if (!Objects.equals(id, that.id)) return false; + if (!Objects.equals(name, that.name)) return false; + if (!Objects.equals(profilePictureUri, that.profilePictureUri)) + return false; + if (!Objects.equals(password, that.password)) + return false; + if (!Objects.equals(accountType, that.accountType)) + return false; + return Objects.equals(generatedPassword, that.generatedPassword); + + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{id, name, profilePictureUri, password, accountType, generatedPassword}); + } + + public static class Builder { + private final String id; + private String name; + private final Uri profilePictureUri; + private final String password; + private String accountType; + + @PublicApi(exclude = true) + public List tokens; + @PublicApi(exclude = true) + private final String generatedPassword; + + /** + * Copies the information stored in an existing credential, in order to allow that information to be modified. + * + * @param credential the existing credential + */ + public Builder(Credential credential) { + this.id = credential.id; + this.name = credential.name; + this.profilePictureUri = credential.profilePictureUri; + this.password = credential.password; + this.accountType = credential.accountType; + this.tokens = credential.tokens; + this.generatedPassword = credential.generatedPassword; + } + + public Credential build() { + Credential credential = new Credential(); + credential.id = id; + credential.name = name; + credential.profilePictureUri = profilePictureUri; + credential.password = password; + credential.accountType = accountType; + credential.tokens = tokens; + credential.generatedPassword = generatedPassword; + return credential; + } + + /** + * Specifies the account type for a federated credential. The value should be set to + * identity provider's login domain, such as "https://accounts.google.com" for Google + * accounts. The login domains for common identity providers are listed in {@link IdentityProviders}. + * + * @param accountType The type of the account. Typically, one of the values in {@link IdentityProviders}. + */ + public Builder setAccountType(String accountType) { + this.accountType = accountType; + return this; + } + + /** + * Sets the display name for the credential, which should be easy for the user to recognize + * as associated to the credential, and distinguishable from other credentials they may + * have. This string will be displayed more prominently than, or instead of, the account ID + * whenever available. In most cases, the name of the user is sufficient. + */ + public Builder setName(String name) { + this.name = name; + return this; + } + + } + + public static final Creator CREATOR = new AutoCreator<>(Credential.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java new file mode 100644 index 00000000..41ca2191 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialPickerConfig.java @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +@PublicApi +public class CredentialPickerConfig extends AutoSafeParcelable { + + @Field(1) + private final boolean showAddAccountButton; + @Field(2) + private final boolean showCancelButton; + @Field(3) + private final boolean forNewAccount; + + public CredentialPickerConfig(boolean showAddAccountButton, boolean showCancelButton, boolean forNewAccount) { + this.showAddAccountButton = showAddAccountButton; + this.showCancelButton = showCancelButton; + this.forNewAccount = forNewAccount; + } + + /** + * @deprecated It was determined that this method was not useful for developers. + */ + @Deprecated + public boolean isForNewAccount() { + return forNewAccount; + } + + @NonNull + @Override + public String toString() { + return "CredentialPickerConfig{" + + "showAddAccountButton=" + showAddAccountButton + + ", showCancelButton=" + showCancelButton + + '}'; + } + + public static class Builder { + private final boolean showAddAccountButton; + private final boolean showCancelButton; + private final boolean forNewAccount; + + public Builder(boolean showAddAccountButton, boolean showCancelButton, boolean forNewAccount) { + this.showAddAccountButton = showAddAccountButton; + this.showCancelButton = showCancelButton; + this.forNewAccount = forNewAccount; + } + + public CredentialPickerConfig build() { + return new CredentialPickerConfig(showAddAccountButton, showCancelButton, forNewAccount); + } + + } + + public static final Creator CREATOR = new AutoCreator<>(CredentialPickerConfig.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java new file mode 100644 index 00000000..c4961d14 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/CredentialRequest.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class CredentialRequest extends AutoSafeParcelable { + + @Field(1) + private final boolean passwordLoginSupported; + + public CredentialRequest(boolean passwordLoginSupported) { + this.passwordLoginSupported = passwordLoginSupported; + } + + /** + * @deprecated Use {@link #isPasswordLoginSupported()} + */ + @Deprecated + public boolean getSupportsPasswordLogin() { + return isPasswordLoginSupported(); + } + + public boolean isPasswordLoginSupported() { + return passwordLoginSupported; + } + + public static final Creator CREATOR = new AutoCreator(CredentialRequest.class); + + public static class Builder { + + public Builder() { + } + + public void setAccountTypes(String... accountTypes) { + accountTypes.clone(); + } + } +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java new file mode 100644 index 00000000..3f9336f4 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/HintRequest.java @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +import java.util.Arrays; + +/** + * Parameters for requesting the display of the hint picker, via {@link CredentalsApi#getHintPickerIntent()}. + * Instances can be created using {@link HintRequest.Builder}. + */ +@PublicApi +public class HintRequest extends AutoSafeParcelable { + + @Field(1) + private final CredentialPickerConfig hintPickerConfig; + @Field(2) + private final boolean emailAddressIdentifierSupported; + @Field(3) + private final boolean phoneNumberIdentifierSupported; + @Field(4) + private final String[] accountTypes; + @Field(5) + private final boolean idTokenRequested; + @Field(6) + private final String serverClientId; + @Field(7) + private final String idTokenNonce; + + public HintRequest(CredentialPickerConfig hintPickerConfig, boolean emailAddressIdentifierSupported, boolean phoneNumberIdentifierSupported, String[] accountTypes, boolean idTokenRequested, String serverClientId, String idTokenNonce) { + this.hintPickerConfig = hintPickerConfig; + this.emailAddressIdentifierSupported = emailAddressIdentifierSupported; + this.phoneNumberIdentifierSupported = phoneNumberIdentifierSupported; + this.accountTypes = accountTypes; + this.idTokenRequested = idTokenRequested; + this.serverClientId = serverClientId; + this.idTokenNonce = idTokenNonce; + } + + public static final Creator CREATOR = new AutoCreator<>(HintRequest.class); + + @NonNull + @Override + public String toString() { + return "HintRequest{" + + "hintPickerConfig=" + hintPickerConfig + + ", emailAddressIdentifierSupported=" + emailAddressIdentifierSupported + + ", phoneNumberIdentifierSupported=" + phoneNumberIdentifierSupported + + ", accountTypes=" + Arrays.toString(accountTypes) + + ", idTokenRequested=" + idTokenRequested + + ", serverClientId='" + serverClientId + '\'' + + ", idTokenNonce='" + idTokenNonce + '\'' + + '}'; + } + + public static class Builder { + private final CredentialPickerConfig hintPickerConfig; + private final boolean emailAddressIdentifierSupported; + private final boolean phoneNumberIdentifierSupported; + private final String[] accountTypes; + private final String serverClientId; + private final String idTokenNonce; + + public Builder(CredentialPickerConfig hintPickerConfig, boolean emailAddressIdentifierSupported, boolean phoneNumberIdentifierSupported, String[] accountTypes, String serverClientId, String idTokenNonce) { + this.hintPickerConfig = hintPickerConfig; + this.emailAddressIdentifierSupported = emailAddressIdentifierSupported; + this.phoneNumberIdentifierSupported = phoneNumberIdentifierSupported; + this.accountTypes = accountTypes; + this.serverClientId = serverClientId; + this.idTokenNonce = idTokenNonce; + } + + /** + * Builds a {@link HintRequest}. + */ + public HintRequest build() { + boolean idTokenRequested = true; + return new HintRequest(hintPickerConfig, emailAddressIdentifierSupported, phoneNumberIdentifierSupported, accountTypes, idTokenRequested, serverClientId, idTokenNonce); + } + + } +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java new file mode 100644 index 00000000..5d84423e --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdToken.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; + +@PublicApi +public class IdToken extends AutoSafeParcelable { + + @Field(1) + private final String accountType; + + @Field(2) + private final String id; + + public IdToken(String accountType, String id) { + this.accountType = accountType; + this.id = id; + } + + /** + * Returns {@code AccountManager} account type for the token. + */ + public String getAccountType() { + return accountType; + } + + public static final Creator CREATOR = new AutoCreator<>(IdToken.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java new file mode 100644 index 00000000..1d974993 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/IdentityProviders.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.auth.api.credentials; + +/** + * Identity provider constants for use with {@link CredentialRequest.Builder#setAccountTypes(String...)} + */ +public final class IdentityProviders { +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java new file mode 100644 index 00000000..e560ddc5 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/DeleteRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.api.credentials.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class DeleteRequest extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(DeleteRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java new file mode 100644 index 00000000..5fd6fcb7 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/GeneratePasswordRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.api.credentials.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GeneratePasswordRequest extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(GeneratePasswordRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java new file mode 100644 index 00000000..24a9d92f --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/credentials/internal/SaveRequest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.api.credentials.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class SaveRequest extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(SaveRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java new file mode 100644 index 00000000..dc1fbd39 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.proxy; + +import androidx.annotation.NonNull; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ProxyRequest extends AutoSafeParcelable { + + @Field(1) + public String url; + + @NonNull + @Override + public String toString() { + return url; + } + + public static final Creator CREATOR = new AutoCreator<>(ProxyRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java new file mode 100644 index 00000000..263d2e20 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.proxy; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ProxyResponse extends AutoSafeParcelable { + @Field(1) + public int gmsStatusCode; + + public static final Creator CREATOR = new AutoCreator<>(ProxyResponse.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java new file mode 100644 index 00000000..1c267ed1 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRecoveryGuidanceRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.accounts.Account; + +import org.microg.gms.auth.AuthConstants; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class AccountRecoveryGuidanceRequest extends AutoSafeParcelable { + + @SafeParceled(2) + @Deprecated + public final String accountName; + @SafeParceled(3) + public final Account account; + + public AccountRecoveryGuidanceRequest(String accountName) { + this.accountName = accountName; + this.account = new Account(accountName, AuthConstants.DEFAULT_ACCOUNT_TYPE); + } + + public AccountRecoveryGuidanceRequest(Account account) { + this.accountName = account.name; + this.account = account; + } + + public static final Creator CREATOR = new AutoCreator<>(AccountRecoveryGuidanceRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java new file mode 100644 index 00000000..dc90a7d0 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/AccountRemovalRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.accounts.Account; + +import org.microg.gms.auth.AuthConstants; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class AccountRemovalRequest extends AutoSafeParcelable { + + @SafeParceled(2) + @Deprecated + public final String accountName; + @SafeParceled(3) + public final Account account; + + public AccountRemovalRequest(String accountName) { + this.accountName = accountName; + this.account = new Account(accountName, AuthConstants.DEFAULT_ACCOUNT_TYPE); + } + + public AccountRemovalRequest(Account account) { + this.accountName = account.name; + this.account = account; + } + + public static final Creator CREATOR = new AutoCreator(AccountRemovalRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java new file mode 100644 index 00000000..9edad8b2 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ConfirmCredentialsRequest.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ConfirmCredentialsRequest extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(ConfirmCredentialsRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java new file mode 100644 index 00000000..73062668 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/dataservice/TokenRequest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.accounts.Account; +import android.os.Bundle; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +// TODO +public class TokenRequest extends AutoSafeParcelable{ + + @SafeParceled(3) + public String accountName; + @SafeParceled(4) + public Bundle extras; + @SafeParceled(9) + public String consent; + @SafeParceled(15) + public String accountType; + + public Account getAccount() { + return new Account(accountName, accountType); + } + + public static final Creator CREATOR = new AutoCreator<>(TokenRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java new file mode 100644 index 00000000..b8e5ed63 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/delegate/ConfirmCredentialsWorkflowRequest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.delegate; + +import android.accounts.Account; +import android.os.Bundle; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class ConfirmCredentialsWorkflowRequest extends AutoSafeParcelable { + @SafeParceled(2) + public String accountName; + @SafeParceled(4) + public Bundle extras; + @SafeParceled(5) + public Account account; + + public static final Creator CREATOR = new AutoCreator<>(ConfirmCredentialsWorkflowRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java new file mode 100644 index 00000000..67e7c7db --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AccountCredentials.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.shared; + +import android.accounts.Account; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +// TODO +public class AccountCredentials extends AutoSafeParcelable { + @SafeParceled(3) + public String accountName; + @SafeParceled(9) + public String accountType; + + public Account getAccount() { + return new Account(accountName, accountType); + } + + public static final Creator CREATOR = new AutoCreator<>(AccountCredentials.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java new file mode 100644 index 00000000..1ccdf4f6 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/AppDescription.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.shared; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AppDescription extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(AppDescription.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java new file mode 100644 index 00000000..ce1d3f61 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/auth/firstparty/shared/CaptchaSolution.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.auth.firstparty.shared; + +import org.microg.safeparcel.AutoSafeParcelable; + +// TODO +public class CaptchaSolution extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(CaptchaSolution.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java b/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java new file mode 100644 index 00000000..62b1abfd --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/common/internal/CertData.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import android.os.RemoteException; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; + +import java.util.Arrays; + +public class CertData extends ICertData.Stub { + private final byte[] bytes; + private final int hashCode; + + public CertData(byte[] bytes) { + this.bytes = bytes; + if (bytes.length < 25) throw new RuntimeException("CertData to small"); + hashCode = Arrays.hashCode(Arrays.copyOfRange(bytes, 0, 25)); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ICertData)) return false; + ICertData cert = (ICertData) obj; + try { + if (cert.remoteHashCode() != hashCode()) return false; + return Arrays.equals(ObjectWrapper.unwrapTyped(cert.getWrappedBytes(), byte[].class), getBytes()); + } catch (RemoteException e) { + return false; + } + } + + public byte[] getBytes() { + return bytes; + } + + @Override + public IObjectWrapper getWrappedBytes() { + return ObjectWrapper.wrap(getBytes()); + } + + @Override + public int remoteHashCode() { + return hashCode(); + } +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java b/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java new file mode 100644 index 00000000..02dcc3e9 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.internal; + +import android.os.IBinder; +import android.os.RemoteException; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class GoogleCertificatesQuery extends AutoSafeParcelable { + @SafeParceled(1) + private final String packageName; + @SafeParceled(2) + private final IBinder certDataBinder; + private CertData certData; + @SafeParceled(3) + private final boolean allowNonRelease; + + public GoogleCertificatesQuery(String packageName, IBinder certDataBinder, boolean allowNonRelease) { + this.packageName = packageName; + this.certDataBinder = certDataBinder; + this.allowNonRelease = allowNonRelease; + } + + public String getPackageName() { + return packageName; + } + + public CertData getCertData() { + if (certData == null && certDataBinder != null) { + ICertData iCertData = null; + if (certDataBinder instanceof CertData) { + certData = (CertData) certDataBinder; + } else if (certDataBinder instanceof IObjectWrapper) { + certData = ObjectWrapper.unwrapTyped((IObjectWrapper) certDataBinder, CertData.class); + if (certData == null) { + byte[] bytes = ObjectWrapper.unwrapTyped((IObjectWrapper) certDataBinder, byte[].class); + if (bytes != null) { + certData = new CertData(bytes); + } + } + if (certData == null) { + iCertData = ObjectWrapper.unwrapTyped((IObjectWrapper) certDataBinder, ICertData.class); + } + } else if (certDataBinder instanceof ICertData) { + iCertData = (ICertData) certDataBinder; + } + if (iCertData != null) { + try { + byte[] bytes = ObjectWrapper.unwrapTyped(iCertData.getWrappedBytes(), byte[].class); + if (bytes != null) { + certData = new CertData(bytes); + } + } catch (RemoteException e) { + // Ignore + } + } + } + return certData; + } + + public static final Creator CREATOR = new AutoCreator(GoogleCertificatesQuery.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java b/play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java new file mode 100644 index 00000000..d32e668b --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.server; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class FavaDiagnosticsEntity extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(FavaDiagnosticsEntity.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java b/play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java new file mode 100644 index 00000000..64322959 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/people/internal/ParcelableLoadImageOptions.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.people.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class ParcelableLoadImageOptions extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(ParcelableLoadImageOptions.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java b/play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java new file mode 100644 index 00000000..dfb64544 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/people/model/AccountMetadata.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.people.model; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AccountMetadata extends AutoSafeParcelable { + + public static Creator CREATOR = new AutoCreator<>(AccountMetadata.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java b/play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java new file mode 100644 index 00000000..6e3da8e2 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/people/model/AvatarReference.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.people.model; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class AvatarReference extends AutoSafeParcelable { + + public static final Creator CREATOR = new AutoCreator<>(AvatarReference.class); + + public AvatarReference() { + } +} diff --git a/play-services-base-api/build.gradle b/play-services-base-api/build.gradle new file mode 100644 index 00000000..59dc1be7 --- /dev/null +++ b/play-services-base-api/build.gradle @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.library' + +dependencies { + api project(':play-services-basement') +} + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion androidBuildVersionTools + + aidlPackageWhiteList "com/google/android/gms/common/data/DataHolder.aidl" + aidlPackageWhiteList "com/google/android/gms/common/images/WebImage.aidl" + aidlPackageWhiteList "com/google/android/gms/common/api/internal/IStatusCallback.aidl" + + defaultConfig { + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + namespace 'org.microg.gms.base.api' + buildFeatures { + aidl true + } +} \ No newline at end of file diff --git a/play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl b/play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl new file mode 100644 index 00000000..6355a13b --- /dev/null +++ b/play-services-base-api/src/main/aidl/com/google/android/gms/common/api/internal/IStatusCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.common.api.internal; + +import com.google.android.gms.common.api.Status; + +interface IStatusCallback { + void onResult(in Status status); +} diff --git a/play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl b/play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl new file mode 100644 index 00000000..3bbd1174 --- /dev/null +++ b/play-services-base-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.data; + +parcelable DataHolder; diff --git a/play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl b/play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl new file mode 100644 index 00000000..e31f15ab --- /dev/null +++ b/play-services-base-api/src/main/aidl/com/google/android/gms/common/images/WebImage.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common.images; + +parcelable WebImage; diff --git a/play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java b/play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java new file mode 100644 index 00000000..031c5b37 --- /dev/null +++ b/play-services-base-api/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.signin; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class GoogleSignInAccount extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(GoogleSignInAccount.class); +} diff --git a/play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java b/play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java new file mode 100644 index 00000000..77acbb1b --- /dev/null +++ b/play-services-base-api/src/main/java/com/google/android/gms/common/data/DataHolder.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.gms.common.data; + +import android.annotation.SuppressLint; +import android.database.Cursor; +import android.database.CursorWindow; +import android.os.Bundle; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import org.microg.gms.common.PublicApi; +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Class for accessing collections of data, organized into columns. This provides the backing + * support for DataBuffer. Much like a cursor, the holder supports the notion of a current + * position, and has methods for extracting various types of data from named columns. + */ +@PublicApi(until = "1") +public class DataHolder extends AutoSafeParcelable implements Closeable { + @SafeParceled(1) + private final String[] columns; + + @SafeParceled(2) + private final CursorWindow[] windows; + + @SafeParceled(3) + private final int statusCode; + + @SafeParceled(4) + private final Bundle metadata; + + private boolean closed = false; + private Map columnIndices; + + protected static final int FIELD_TYPE_NULL = 0; + protected static final int FIELD_TYPE_INTEGER = 1; + protected static final int FIELD_TYPE_FLOAT = 2; + protected static final int FIELD_TYPE_STRING = 3; + protected static final int FIELD_TYPE_BLOB = 4; + + /** + * Creates a data holder with the specified data. + * + * @param columns The column names corresponding to the data in the given windows. + * @param windows The {@link CursorWindow} instances holding the data. + * @param statusCode The status code of this {@link DataHolder}. + * @param metadata The metadata associated with this {@link DataHolder} (may be null). + */ + public DataHolder(String[] columns, CursorWindow[] windows, int statusCode, Bundle metadata) { + this.columns = columns; + this.windows = windows; + this.statusCode = statusCode; + this.metadata = metadata; + validateContents(); + } + + /** + * Creates a data holder wrapping the provided cursor, with provided status code and metadata. + * + * @param cursor The cursor containing the data. + * @param statusCode The status code of this {@link DataHolder}. + * @param metadata The metadata associated with this {@link DataHolder} (may be null). + */ + public DataHolder(Cursor cursor, int statusCode, Bundle metadata) { + this(cursor.getColumnNames(), createCursorWindows(cursor), statusCode, metadata); + } + + /** + * Get a {@link DataHolder.Builder} to create a new {@link DataHolder} manually. + * + * @param columns The array of column names that the object supports. + * @return {@link DataHolder.Builder} object to work with. + */ + public static Builder builder(String[] columns) { + return new Builder(columns); + } + + @SuppressLint({"NewApi", "ObsoleteSdkInt"}) + static int getCursorType(Cursor cursor, int i) { + return cursor.getType(i); + } + + /** + * Closes the data holder, releasing all of its resources and making it completely invalid. + */ + @Override + public void close() { + synchronized (this) { + if (!closed) { + closed = true; + for (CursorWindow window : windows) { + window.close(); + } + } + } + } + + @SuppressWarnings("deprecation") + private static CursorWindow[] createCursorWindows(Builder builder) { + if (builder.columns.length == 0) return new CursorWindow[0]; + List windows = new ArrayList<>(); + try { + CursorWindow current = null; + for (int rowIndex = 0; rowIndex < builder.rows.size(); rowIndex++) { + Map row = builder.rows.get(rowIndex); + if (current == null || !current.allocRow()) { + current = new CursorWindow(false); + current.setStartPosition(rowIndex); + current.setNumColumns(builder.columns.length); + windows.add(current); + if (!current.allocRow()) { + windows.remove(current); + return windows.toArray(new CursorWindow[0]); + } + } + for (int columnIndex = 0; columnIndex < builder.columns.length; columnIndex++) { + Object val = row.get(builder.columns[columnIndex]); + if (val == null) { + current.putNull(rowIndex, columnIndex); + } else if (val instanceof String) { + current.putString((String) val, rowIndex, columnIndex); + } else if (val instanceof Long) { + current.putLong((Long) val, rowIndex, columnIndex); + } else if (val instanceof Integer) { + current.putLong((Integer) val, rowIndex, columnIndex); + } else if (val instanceof Boolean) { + if ((Boolean) val) + current.putLong(1, rowIndex, columnIndex); + } else if (val instanceof byte[]) { + current.putBlob((byte[]) val, rowIndex, columnIndex); + } else if (val instanceof Double) { + current.putDouble((Double) val, rowIndex, columnIndex); + } else if (val instanceof Float) { + current.putDouble((Float) val, rowIndex, columnIndex); + } else { + throw new IllegalArgumentException("Unsupported object for column " + columnIndex + ": " + val); + } + } + } + } catch (RuntimeException e) { + for (CursorWindow window : windows) { + window.close(); + } + throw e; + } + return windows.toArray(new CursorWindow[0]); + } + + private static CursorWindow[] createCursorWindows(Cursor cursor) { + if (cursor.getColumnCount() == 0) return new CursorWindow[0]; + List windows = new ArrayList<>(); + CursorWindow current = null; + int rowIndex = 0; + while (cursor.moveToNext()) { + if (current == null || !current.allocRow()) { + current = new CursorWindow(false); + current.setStartPosition(rowIndex); + current.setNumColumns(cursor.getColumnCount()); + windows.add(current); + if (!current.allocRow()) { + windows.remove(current); + return windows.toArray(new CursorWindow[0]); + } + } + for (int i = 0; i < cursor.getColumnCount(); i++) { + switch (getCursorType(cursor, i)) { + case FIELD_TYPE_NULL: + current.putNull(rowIndex, i); + break; + case FIELD_TYPE_BLOB: + current.putBlob(cursor.getBlob(i), rowIndex, i); + break; + case FIELD_TYPE_FLOAT: + current.putDouble(cursor.getDouble(i), rowIndex, i); + break; + case FIELD_TYPE_INTEGER: + current.putLong(cursor.getLong(i), rowIndex, i); + break; + case FIELD_TYPE_STRING: + current.putString(cursor.getString(i), rowIndex, i); + break; + } + } + rowIndex++; + } + cursor.close(); + return windows.toArray(new CursorWindow[0]); + } + + /** + * Gets the number of rows in the data holder. + * + * @return the number of rows in the data holder. + */ + public int getCount() { + int c = 0; + if (windows != null) { + for (CursorWindow window : windows) { + c += window.getNumRows(); + } + } + return c; + } + + /** + * Retrieves the string value for a given column at the provided position. + * This will throw an {@link IllegalArgumentException} if the column does not exist, the + * position is invalid, or the data holder has been closed. + * + * @param column The column to retrieve. + * @param row The row to retrieve the data from. + * @param windowIndex Index of the cursor window to extract the data from. + * @return The string value in that column. + */ + public String getString(String column, int row, int windowIndex) { + return windows[windowIndex].getString(row, columnIndices.get(column)); + } + + @NonNull + @Override + public String toString() { + return "DataHolder{" + + "columns=" + Arrays.toString(columns) + + ", windows=" + Arrays.toString(windows) + + ", statusCode=" + statusCode + + ", metadata=" + metadata + + '}'; + } + + public void validateContents() { + columnIndices = new HashMap<>(); + for (int i = 0; i < columns.length; i++) { + columnIndices.put(columns[i], i); + } + } + + /** + * Helper class to build {@link DataHolder} instances containing arbitrary data. + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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