From 839248fbefdf2bce27df39d24dc9fc022379704f Mon Sep 17 00:00:00 2001 From: Marvin W Date: Thu, 24 Aug 2023 12:48:06 +0200 Subject: [PATCH 01/26] Add details of Google Certificate lookup API --- .../gms/appinivite/AppInviteActivity.kt | 5 +- .../org/microg/gms/common/PackageUtils.java | 1 + play-services-basement/build.gradle | 2 + .../common/GoogleCertificatesLookupQuery.aidl | 3 + .../GoogleCertificatesLookupResponse.aidl | 3 + .../gms/common/GoogleCertificatesQuery.aidl | 3 + .../internal/GoogleCertificatesQuery.aidl | 3 - .../internal/IGoogleCertificatesApi.aidl | 18 ++-- .../common/GoogleCertificatesLookupQuery.java | 41 +++++++++ .../GoogleCertificatesLookupResponse.java | 38 ++++++++ .../GoogleCertificatesQuery.java | 32 +++---- .../android/gms/dynamite/DynamiteModule.java | 2 - .../src/main/AndroidManifest.xml | 1 + .../chimera/DynamiteModuleInitializer.java | 25 ++++++ .../gms/common/GoogleCertificatesImpl.java | 76 ---------------- .../gms/common/GoogleCertificatesImpl.kt | 90 +++++++++++++++++++ .../google/android/gms/maps/model/LatLng.java | 2 +- 17 files changed, 234 insertions(+), 111 deletions(-) create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl delete mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java rename play-services-basement/src/main/java/com/google/android/gms/common/{internal => }/GoogleCertificatesQuery.java (71%) create mode 100644 play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java delete mode 100644 play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java create mode 100644 play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt diff --git a/play-services-appinvite/core/src/main/kotlin/org/microg/gms/appinivite/AppInviteActivity.kt b/play-services-appinvite/core/src/main/kotlin/org/microg/gms/appinivite/AppInviteActivity.kt index a2dfff61c..1073d3b24 100644 --- a/play-services-appinvite/core/src/main/kotlin/org/microg/gms/appinivite/AppInviteActivity.kt +++ b/play-services-appinvite/core/src/main/kotlin/org/microg/gms/appinivite/AppInviteActivity.kt @@ -7,14 +7,13 @@ package org.microg.gms.appinivite import android.content.Intent import android.net.Uri -import android.os.Build +import android.os.Build.VERSION.SDK_INT import android.os.Bundle import android.os.LocaleList import android.util.Log import android.view.ViewGroup import android.view.Window import android.widget.ProgressBar -import android.widget.RelativeLayout import androidx.appcompat.app.AppCompatActivity import androidx.core.content.pm.PackageInfoCompat import androidx.core.os.bundleOf @@ -146,7 +145,7 @@ class ProtobufPostRequest, O>(url: String, private val i: I, p override fun getHeaders(): Map { val headers = HashMap(super.getHeaders()) - headers["Accept-Language"] = if (Build.VERSION.SDK_INT >= 24) LocaleList.getDefault().toLanguageTags() else Locale.getDefault().language + headers["Accept-Language"] = if (SDK_INT >= 24) LocaleList.getDefault().toLanguageTags() else Locale.getDefault().language headers["X-Android-Package"] = Constants.GMS_PACKAGE_NAME headers["X-Android-Cert"] = Constants.GMS_PACKAGE_SIGNATURE_SHA1 return headers diff --git a/play-services-base/core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-base/core/src/main/java/org/microg/gms/common/PackageUtils.java index f30fdf3aa..949f6401b 100644 --- a/play-services-base/core/src/main/java/org/microg/gms/common/PackageUtils.java +++ b/play-services-base/core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -76,6 +76,7 @@ public class PackageUtils { KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.walletnfcrel", "82759e2db43f9ccbafce313bc674f35748fabd7a"); KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.recorder", "394d84cd2cf89d3453702c663f98ec6554afc3cd"); KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.messaging", "0980a12be993528c19107bc21ad811478c63cefc"); + KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.tachyon", "a0bc09af527b6397c7a9ef171d6cf76f757becc3"); } public static boolean isGooglePackage(Context context, String packageName) { diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle index 41f4a7641..02f91adcf 100644 --- a/play-services-basement/build.gradle +++ b/play-services-basement/build.gradle @@ -23,6 +23,8 @@ dependencies { api "androidx.collection:collection:1.0.0" api "androidx.core:core:1.2.0" api "androidx.fragment:fragment:1.0.0" + + annotationProcessor project(':safe-parcel-processor') } android { diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl new file mode 100644 index 000000000..fab15aa19 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupQuery.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable GoogleCertificatesLookupQuery; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl new file mode 100644 index 000000000..e9b4bff32 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesLookupResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable GoogleCertificatesLookupResponse; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl new file mode 100644 index 000000000..e6aedfefd --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/GoogleCertificatesQuery.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable GoogleCertificatesQuery; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl deleted file mode 100644 index ae02e068b..000000000 --- a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GoogleCertificatesQuery.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package com.google.android.gms.common.internal; - -parcelable GoogleCertificatesQuery; \ No newline at end of file diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl index c75982bb6..6d54ad261 100644 --- a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGoogleCertificatesApi.aidl @@ -1,12 +1,18 @@ package com.google.android.gms.common.internal; -import com.google.android.gms.common.internal.GoogleCertificatesQuery; +import com.google.android.gms.common.GoogleCertificatesLookupQuery; +import com.google.android.gms.common.GoogleCertificatesLookupResponse; +import com.google.android.gms.common.GoogleCertificatesQuery; import com.google.android.gms.dynamic.IObjectWrapper; interface IGoogleCertificatesApi { - IObjectWrapper getGoogleCertificates(); - IObjectWrapper getGoogleReleaseCertificates(); - boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData); - boolean isGoogleSigned(String packageName, IObjectWrapper certData); - boolean isGoogleOrPlatformSigned(in GoogleCertificatesQuery query, IObjectWrapper packageManager); + IObjectWrapper getGoogleCertificates() = 0; + IObjectWrapper getGoogleReleaseCertificates() = 1; + boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData) = 2; + boolean isGoogleSigned(String packageName, IObjectWrapper certData) = 3; + boolean isGoogleOrPlatformSigned(in GoogleCertificatesQuery query, IObjectWrapper packageManager) = 4; + GoogleCertificatesLookupResponse isPackageGoogleOrPlatformSigned(in GoogleCertificatesLookupQuery query) = 5; + boolean isPackageGoogleOrPlatformSignedAvailable() = 6; + GoogleCertificatesLookupResponse queryPackageSigned(in GoogleCertificatesLookupQuery query) = 7; + boolean isFineGrainedPackageVerificationAvailable() = 8; } \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java new file mode 100644 index 000000000..4fc54a197 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupQuery.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common; + +import android.content.Context; +import android.os.IBinder; +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; +import org.microg.safeparcel.AutoSafeParcelable; + +public class GoogleCertificatesLookupQuery extends AutoSafeParcelable { + @Field(1) + private String callingPackage; + @Field(2) + private boolean allowTestKeys; + @Field(3) + private boolean ignoreTestKeysOverride; + @Field(4) + private IObjectWrapper contextWrapper; + private Context context; + @Field(5) + private boolean isChimeraPackage; + @Field(6) + private boolean includeHashesInErrorMessage; + + public String getCallingPackage() { + return callingPackage; + } + + public Context getContext() { + if (context == null && contextWrapper != null) { + context = ObjectWrapper.unwrapTyped(contextWrapper, Context.class); + } + return context; + } + + public static final Creator CREATOR = findCreator(GoogleCertificatesLookupQuery.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java new file mode 100644 index 000000000..90e0ee3a4 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesLookupResponse.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common; + +import android.os.Parcel; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class GoogleCertificatesLookupResponse extends AbstractSafeParcelable { + @Field(1) + public final boolean result; + @Field(2) + public final String errorMessage; + @Field(3) + public final int statusValue; + @Field(4) + public final int firstPartyStatusValue; + + @Constructor + public GoogleCertificatesLookupResponse(@Param(1) boolean result, @Param(2) String errorMessage, @Param(3) int statusValue, @Param(4) int firstPartyStatusValue) { + this.result = result; + this.errorMessage = errorMessage; + this.statusValue = statusValue; + this.firstPartyStatusValue = firstPartyStatusValue; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + CREATOR.writeToParcel(this, out, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GoogleCertificatesLookupResponse.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesQuery.java similarity index 71% rename from play-services-basement/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java rename to play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesQuery.java index 0e863145f..395311df6 100644 --- a/play-services-basement/src/main/java/com/google/android/gms/common/internal/GoogleCertificatesQuery.java +++ b/play-services-basement/src/main/java/com/google/android/gms/common/GoogleCertificatesQuery.java @@ -1,43 +1,35 @@ /* - * Copyright (C) 2019 microG Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 */ -package com.google.android.gms.common.internal; +package com.google.android.gms.common; import android.os.IBinder; import android.os.RemoteException; +import com.google.android.gms.common.internal.CertData; +import com.google.android.gms.common.internal.ICertData; import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; -import org.microg.safeparcel.SafeParceled; +@Hide public class GoogleCertificatesQuery extends AutoSafeParcelable { @Field(1) - private String packageName; + private String callingPackage; @Field(2) private IBinder certDataBinder; private CertData certData; @Field(3) - private boolean allowNonRelease; - @Field(4) private boolean allowTestKeys; + @Field(4) + private boolean ignoreTestKeysOverride; - public String getPackageName() { - return packageName; + public String getCallingPackage() { + return callingPackage; } public CertData getCertData() { diff --git a/play-services-basement/src/main/java/com/google/android/gms/dynamite/DynamiteModule.java b/play-services-basement/src/main/java/com/google/android/gms/dynamite/DynamiteModule.java index 70d45e546..2d708c988 100644 --- a/play-services-basement/src/main/java/com/google/android/gms/dynamite/DynamiteModule.java +++ b/play-services-basement/src/main/java/com/google/android/gms/dynamite/DynamiteModule.java @@ -52,10 +52,8 @@ public class DynamiteModule { public interface VersionPolicy { interface IVersions { - /* renamed from: zza */ int getLocalVersion(@NonNull Context context, @NonNull String moduleId); - /* renamed from: zzb */ int getRemoteVersion(@NonNull Context context, @NonNull String moduleId, boolean forceStaging) throws LoadingException; IVersions Default = new IVersions() { diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index e4ba0dc40..ece083c1f 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -493,6 +493,7 @@ + diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java b/play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java new file mode 100644 index 000000000..3605e8ac0 --- /dev/null +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/DynamiteModuleInitializer.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.chimera; + +import android.annotation.SuppressLint; +import android.util.Log; +import androidx.annotation.Keep; + +import android.content.Context; + +@Keep +public class DynamiteModuleInitializer { + private static final String TAG = "DynamiteModule"; + + public static void initializeModuleV1(Context context) { + initializeModuleV2(context, "com.google.android.gms".equals(context.getPackageName())); + } + + public static void initializeModuleV2(Context context, boolean withGmsPackage) { + Log.d(TAG, "initializeModuleV2 context: " + context + ", withGmsPackage: " + withGmsPackage); + } +} diff --git a/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java b/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java deleted file mode 100644 index 8eefeeeaf..000000000 --- a/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 microG Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.gms.common; - -import android.content.pm.PackageManager; -import android.os.IBinder; -import android.os.RemoteException; -import androidx.annotation.Keep; -import android.util.Log; - -import com.google.android.gms.common.internal.CertData; -import com.google.android.gms.common.internal.GoogleCertificatesQuery; -import com.google.android.gms.common.internal.IGoogleCertificatesApi; -import com.google.android.gms.dynamic.IObjectWrapper; -import com.google.android.gms.dynamic.ObjectWrapper; - -import org.microg.gms.common.PackageUtils; - -import java.util.Collections; -import java.util.Set; - -@Keep -public class GoogleCertificatesImpl extends IGoogleCertificatesApi.Stub { - private static final String TAG = "GmsCertImpl"; - private Set googleCertificates = Collections.emptySet(); - private Set googleReleaseCertificates = Collections.emptySet(); - - @Override - public IObjectWrapper getGoogleCertificates() throws RemoteException { - Log.d(TAG, "unimplemented Method: getGoogleCertificates"); - return ObjectWrapper.wrap(googleCertificates.toArray(new IBinder[0])); - } - - @Override - public IObjectWrapper getGoogleReleaseCertificates() throws RemoteException { - Log.d(TAG, "unimplemented Method: getGoogleReleaseCertificates"); - return ObjectWrapper.wrap(googleReleaseCertificates.toArray(new IBinder[0])); - } - - @Override - public boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData) throws RemoteException { - return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, byte[].class)); - } - - @Override - public boolean isGoogleSigned(String packageName, IObjectWrapper certData) throws RemoteException { - return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, byte[].class)); - } - - @Override - public boolean isGoogleOrPlatformSigned(GoogleCertificatesQuery query, IObjectWrapper packageManager) throws RemoteException { - PackageManager pm = ObjectWrapper.unwrapTyped(packageManager, PackageManager.class); - if (query == null || query.getPackageName() == null) { - return false; - } else if (query.getCertData() == null) { - if (pm == null) return false; - return PackageUtils.isGooglePackage(pm, query.getPackageName()); - } else { - return PackageUtils.isGooglePackage(query.getPackageName(), query.getCertData().getBytes()); - } - } -} diff --git a/play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt b/play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt new file mode 100644 index 000000000..e1fb7a210 --- /dev/null +++ b/play-services-core/src/main/kotlin/com/google/android/gms/common/GoogleCertificatesImpl.kt @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.common + +import android.content.pm.PackageManager +import android.os.IBinder +import android.os.Parcel +import android.util.Log +import androidx.annotation.Keep +import com.google.android.gms.common.internal.CertData +import com.google.android.gms.common.internal.IGoogleCertificatesApi +import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper +import org.microg.gms.common.PackageUtils +import org.microg.gms.utils.warnOnTransactionIssues + +private const val TAG = "GoogleCertificates" + +@Keep +class GoogleCertificatesImpl : IGoogleCertificatesApi.Stub() { + private val googleCertificates = emptySet() + private val googleReleaseCertificates = emptySet() + + override fun getGoogleCertificates(): IObjectWrapper { + Log.d(TAG, "unimplemented Method: getGoogleCertificates") + return ObjectWrapper.wrap(googleCertificates.toTypedArray()) + } + + override fun getGoogleReleaseCertificates(): IObjectWrapper { + Log.d(TAG, "unimplemented Method: getGoogleReleaseCertificates") + return ObjectWrapper.wrap(googleReleaseCertificates.toTypedArray()) + } + + override fun isGoogleReleaseSigned(packageName: String, certData: IObjectWrapper): Boolean { + return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, ByteArray::class.java)) + } + + override fun isGoogleSigned(packageName: String, certData: IObjectWrapper): Boolean { + return PackageUtils.isGooglePackage(packageName, ObjectWrapper.unwrapTyped(certData, ByteArray::class.java)) + } + + override fun isGoogleOrPlatformSigned(query: GoogleCertificatesQuery, packageManager: IObjectWrapper): Boolean { + val pm = ObjectWrapper.unwrapTyped(packageManager, PackageManager::class.java) + return if (query == null || query.callingPackage == null) { + false + } else if (query.getCertData() == null) { + if (pm == null) false else PackageUtils.isGooglePackage(pm, query.callingPackage) + } else { + PackageUtils.isGooglePackage(query.callingPackage, query.getCertData().bytes) + } + } + + override fun isPackageGoogleOrPlatformSigned(query: GoogleCertificatesLookupQuery): GoogleCertificatesLookupResponse { + return certificateLookup(query, true) + } + + override fun isPackageGoogleOrPlatformSignedAvailable(): Boolean { + return true + } + + override fun queryPackageSigned(query: GoogleCertificatesLookupQuery): GoogleCertificatesLookupResponse { + if (!isFineGrainedPackageVerificationAvailable) throw IllegalStateException("API unavailable") + return certificateLookup(query, false) + } + + override fun isFineGrainedPackageVerificationAvailable(): Boolean { + return true + } + + private fun certificateLookup(query: GoogleCertificatesLookupQuery, allowPlatform: Boolean): GoogleCertificatesLookupResponse { + val context = query.context + ?: return GoogleCertificatesLookupResponse(false, "context is null", 5, 1) + val packageManager = context.packageManager + ?: return GoogleCertificatesLookupResponse(false, "context has no package manager", 5, 1) + val callingPackage = query.callingPackage + ?: return GoogleCertificatesLookupResponse(false, "callingPackage is null", 5, 1) + val signatureDigest = PackageUtils.firstSignatureDigest(packageManager, callingPackage) + ?: return GoogleCertificatesLookupResponse(false, "callingPackage not found", 4, 1) + return if (PackageUtils.isGooglePackage(callingPackage, signatureDigest)) { + GoogleCertificatesLookupResponse(true, null, 1, 3) + } else { + GoogleCertificatesLookupResponse(false, "not allowed", 2, 1) + } + } + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = + warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } +} diff --git a/play-services-maps/src/main/java/com/google/android/gms/maps/model/LatLng.java b/play-services-maps/src/main/java/com/google/android/gms/maps/model/LatLng.java index f8233075a..6002f7b9e 100644 --- a/play-services-maps/src/main/java/com/google/android/gms/maps/model/LatLng.java +++ b/play-services-maps/src/main/java/com/google/android/gms/maps/model/LatLng.java @@ -19,8 +19,8 @@ package com.google.android.gms.maps.model; import android.os.Parcel; import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; import com.google.android.gms.common.internal.safeparcel.SafeParcelable; -import org.microg.gms.common.PublicApi; import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import org.microg.gms.common.PublicApi; /** * An immutable class representing a pair of latitude and longitude coordinates, stored as degrees. -- GitLab From ecfc7c74a4946e07213acc5248d37d5f0af1f9dd Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 30 Aug 2023 14:52:39 +0200 Subject: [PATCH 02/26] Location: Handle rare sidecases Fixes #1963, #2005 --- .../org/microg/gms/location/manager/LocationManager.kt | 2 ++ .../microg/gms/location/manager/LocationManagerInstance.kt | 3 ++- .../microg/gms/location/manager/LocationRequestManager.kt | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt index 2e7af7386..a652e861d 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt @@ -210,6 +210,8 @@ class LocationManager(private val context: Context, private val lifecycle: Lifec } } catch (e: SecurityException) { // Ignore + } catch (e: Exception) { + // Ignore } } diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt index f9d8a08b5..b2bbaa179 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt @@ -29,6 +29,7 @@ import com.google.android.gms.location.internal.* import com.google.android.gms.location.internal.DeviceOrientationRequestUpdateData.REMOVE_UPDATES import com.google.android.gms.location.internal.DeviceOrientationRequestUpdateData.REQUEST_UPDATES import org.microg.gms.common.NonCancelToken +import org.microg.gms.location.hasNetworkLocationServiceBuiltIn import org.microg.gms.utils.warnOnTransactionIssues class LocationManagerInstance( @@ -158,7 +159,7 @@ class LocationManagerInstance( lifecycleScope.launchWhenStarted { val locationManager = context.getSystemService() val gpsPresent = locationManager?.allProviders?.contains(GPS_PROVIDER) == true - val networkPresent = locationManager?.allProviders?.contains(NETWORK_PROVIDER) == true + val networkPresent = locationManager?.allProviders?.contains(NETWORK_PROVIDER) == true || context.hasNetworkLocationServiceBuiltIn() val gpsUsable = gpsPresent && locationManager?.isProviderEnabled(GPS_PROVIDER) == true && context.packageManager.checkPermission(ACCESS_FINE_LOCATION, clientIdentity.packageName) == PERMISSION_GRANTED val networkUsable = networkPresent && locationManager?.isProviderEnabled(NETWORK_PROVIDER) == true && diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt index 39dcc603b..2354bd441 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt @@ -364,7 +364,7 @@ class LocationRequestManager(private val context: Context, private val lifecycle return request.priority } val maxUpdateDelayMillis: Long - get() = max(request.maxUpdateDelayMillis, intervalMillis) + get() = max(max(request.maxUpdateDelayMillis, intervalMillis), 1000L) val intervalMillis: Long get() = request.intervalMillis val updatesPending: Int @@ -415,6 +415,7 @@ class LocationRequestManager(private val context: Context, private val lifecycle fun check() { if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission") + if (effectiveGranularity > permissionGranularity) throw RuntimeException("Lack of permission") if (timePendingMillis < 0) throw RuntimeException("duration limit reached (active for ${(SystemClock.elapsedRealtime() - start).formatDuration()}, duration ${request.durationMillis.formatDuration()})") if (updatesPending <= 0) throw RuntimeException("max updates reached") if (callback?.asBinder()?.isBinderAlive == false) throw RuntimeException("Binder died") @@ -426,7 +427,7 @@ class LocationRequestManager(private val context: Context, private val lifecycle if (lastLocation != null && location.distanceTo(lastLocation!!) < request.minUpdateDistanceMeters) return false if (lastLocation == location) return false val returnedLocation = if (effectiveGranularity > permissionGranularity) { - throw RuntimeException("lack of permission") + throw RuntimeException("Lack of permission") } else { if (!context.noteAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) { throw RuntimeException("app op denied") -- GitLab From 4d5f6bd9044e0e72bdc6a0eaab72af88e0f5e3be Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 2 Sep 2023 23:15:09 +0200 Subject: [PATCH 03/26] Move phenotype in own module --- play-services-api/build.gradle | 1 + .../gms/phenotype/ExperimentToken.java | 12 ----- play-services-clearcut/build.gradle | 4 +- play-services-phenotype/build.gradle | 44 +++++++++++++++++++ .../src/main/AndroidManifest.xml | 8 ++++ .../android/gms/phenotype/Configuration.java | 5 ++- .../gms/phenotype/ExperimentTokens.java | 5 ++- .../google/android/gms/phenotype/Flag.java | 7 ++- .../gms/phenotype/GenericDimension.java | 6 ++- play-services-vision-common/build.gradle | 4 +- settings.gradle | 1 + 11 files changed, 72 insertions(+), 25 deletions(-) delete mode 100644 play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentToken.java create mode 100644 play-services-phenotype/build.gradle create mode 100644 play-services-phenotype/src/main/AndroidManifest.xml rename {play-services-api => play-services-phenotype}/src/main/java/com/google/android/gms/phenotype/Configuration.java (67%) rename {play-services-api => play-services-phenotype}/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java (78%) rename {play-services-api => play-services-phenotype}/src/main/java/com/google/android/gms/phenotype/Flag.java (94%) rename {play-services-api => play-services-phenotype}/src/main/java/com/google/android/gms/phenotype/GenericDimension.java (58%) diff --git a/play-services-api/build.gradle b/play-services-api/build.gradle index 6512e6204..08fa06439 100644 --- a/play-services-api/build.gradle +++ b/play-services-api/build.gradle @@ -40,4 +40,5 @@ android { dependencies { api project(':play-services-base') + api project(':play-services-phenotype') } diff --git a/play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentToken.java b/play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentToken.java deleted file mode 100644 index 0d618f2f9..000000000 --- a/play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentToken.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.google.android.gms.phenotype; - -import org.microg.safeparcel.AutoSafeParcelable; - -public class ExperimentToken extends AutoSafeParcelable { - public static final Creator CREATOR = new AutoCreator<>(ExperimentToken.class); -} diff --git a/play-services-clearcut/build.gradle b/play-services-clearcut/build.gradle index 282c0105d..1b2e7d8dc 100644 --- a/play-services-clearcut/build.gradle +++ b/play-services-clearcut/build.gradle @@ -34,12 +34,10 @@ apply from: '../gradle/publish-android.gradle' description = 'microG implementation of play-services-clearcut' dependencies { - implementation project(':play-services-api') - // Dependencies from play-services-clearcut:17.0.0 api "androidx.core:core:1.0.0" api project(':play-services-base') api project(':play-services-basement') -// api project(':play-services-phenotype') + api project(':play-services-phenotype') api project(':play-services-tasks') } diff --git a/play-services-phenotype/build.gradle b/play-services-phenotype/build.gradle new file mode 100644 index 000000000..c2dab9a8e --- /dev/null +++ b/play-services-phenotype/build.gradle @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +dependencies { + // Dependencies from play-services-phenotype:17.0.0 + api "androidx.core:core:1.0.0" + api project(':play-services-base') + api project(':play-services-basement') + api project(':play-services-tasks') + + annotationProcessor project(':safe-parcel-processor') +} + +android { + namespace "com.google.android.gms.phenotype" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + buildFeatures { + aidl = true + } + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +apply from: '../gradle/publish-android.gradle' + +description = 'microG implementation of play-services-phenotype' diff --git a/play-services-phenotype/src/main/AndroidManifest.xml b/play-services-phenotype/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3408ac920 --- /dev/null +++ b/play-services-phenotype/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/play-services-api/src/main/java/com/google/android/gms/phenotype/Configuration.java b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Configuration.java similarity index 67% rename from play-services-api/src/main/java/com/google/android/gms/phenotype/Configuration.java rename to play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Configuration.java index 70b4d286e..a9591500f 100644 --- a/play-services-api/src/main/java/com/google/android/gms/phenotype/Configuration.java +++ b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Configuration.java @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-FileCopyrightText: 2020 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ @@ -14,5 +14,6 @@ public class Configuration extends AutoSafeParcelable { public Flag[] flags; @Field(4) public String[] removeNames; - public static final Creator CREATOR = new AutoCreator<>(Configuration.class); + + public static final Creator CREATOR = findCreator(Configuration.class); } diff --git a/play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java similarity index 78% rename from play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java rename to play-services-phenotype/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java index d3f858fec..8cc08eea3 100644 --- a/play-services-api/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java +++ b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/ExperimentTokens.java @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-FileCopyrightText: 2020 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ @@ -26,5 +26,6 @@ public class ExperimentTokens extends AutoSafeParcelable { public byte[][] directs; @Field(10) public int[] genericDimensions; - public static final Creator CREATOR = new AutoCreator<>(ExperimentTokens.class); + + public static final Creator CREATOR = findCreator(ExperimentTokens.class); } diff --git a/play-services-api/src/main/java/com/google/android/gms/phenotype/Flag.java b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Flag.java similarity index 94% rename from play-services-api/src/main/java/com/google/android/gms/phenotype/Flag.java rename to play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Flag.java index 4d0a03af7..12aa84575 100644 --- a/play-services-api/src/main/java/com/google/android/gms/phenotype/Flag.java +++ b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/Flag.java @@ -1,12 +1,14 @@ /* - * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-FileCopyrightText: 2020 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ package com.google.android.gms.phenotype; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class Flag extends AutoSafeParcelable { @Field(2) public String name; @@ -98,5 +100,6 @@ public class Flag extends AutoSafeParcelable { public static final int DATA_TYPE_DOUBLE = 3; public static final int DATA_TYPE_STRING = 4; public static final int DATA_TYPE_BYTES = 5; - public static final Creator CREATOR = new AutoCreator<>(Flag.class); + + public static final Creator CREATOR = findCreator(Flag.class); } diff --git a/play-services-api/src/main/java/com/google/android/gms/phenotype/GenericDimension.java b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/GenericDimension.java similarity index 58% rename from play-services-api/src/main/java/com/google/android/gms/phenotype/GenericDimension.java rename to play-services-phenotype/src/main/java/com/google/android/gms/phenotype/GenericDimension.java index f10f83c0c..f2aa1764b 100644 --- a/play-services-api/src/main/java/com/google/android/gms/phenotype/GenericDimension.java +++ b/play-services-phenotype/src/main/java/com/google/android/gms/phenotype/GenericDimension.java @@ -1,17 +1,19 @@ /* - * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-FileCopyrightText: 2020 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ package com.google.android.gms.phenotype; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class GenericDimension extends AutoSafeParcelable { @Field(1) public int a; @Field(2) public int b; - public static final Creator CREATOR = new AutoCreator<>(GenericDimension.class); + public static final Creator CREATOR = findCreator(GenericDimension.class); } diff --git a/play-services-vision-common/build.gradle b/play-services-vision-common/build.gradle index d9b3ffe72..0f80125f3 100644 --- a/play-services-vision-common/build.gradle +++ b/play-services-vision-common/build.gradle @@ -37,7 +37,7 @@ dependencies { // Dependencies from play-services-vision-common:19.1.3 api project(':play-services-base') api project(':play-services-basement') - //api project(':play-services-clearcut') + api project(':play-services-clearcut') //api project(':play-services-flags') - //api project(':play-services-phenotype') + api project(':play-services-phenotype') } diff --git a/settings.gradle b/settings.gradle index 392116057..cdf4df481 100644 --- a/settings.gradle +++ b/settings.gradle @@ -31,6 +31,7 @@ include ':play-services-measurement-base' include ':play-services-nearby' include ':play-services-oss-licenses' include ':play-services-pay' +include ':play-services-phenotype' include ':play-services-places' include ':play-services-places-placereport' include ':play-services-recaptcha' -- GitLab From fc00907f3168558c103c389c352ee66e39e7c0a8 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 2 Sep 2023 23:16:30 +0200 Subject: [PATCH 04/26] Maps: identify bitmaps by hash Needs slightly more time if apps take care of only registering one bitmap once but saves a lot of time and memory when they don't (and many don't) --- .../gms/maps/mapbox/model/BitmapDescriptorFactory.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt index 6e46de389..ebe931181 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt @@ -27,6 +27,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap import com.mapbox.mapboxsdk.maps.Style import org.microg.gms.maps.mapbox.R import org.microg.gms.maps.mapbox.runOnMainLooper +import org.microg.gms.utils.toHexString +import java.nio.ByteBuffer +import java.security.MessageDigest object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { @@ -189,7 +192,13 @@ object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { } } - override fun fromBitmap(bitmap: Bitmap): IObjectWrapper = registerBitmap("bitmap-${bitmap.hashCode()}") { bitmap } + private fun Bitmap.sha256(): String { + val bytes = ByteArray(rowBytes * height) + copyPixelsToBuffer(ByteBuffer.wrap(bytes)) + return MessageDigest.getInstance("SHA-256").digest(bytes).toHexString() + } + + override fun fromBitmap(bitmap: Bitmap): IObjectWrapper = registerBitmap("bitmap-${bitmap.sha256()}") { bitmap } override fun fromPath(absolutePath: String): IObjectWrapper = registerBitmap("path-$absolutePath") { BitmapFactory.decodeFile(absolutePath) } -- GitLab From 31eadb3c7980f5ed740959aa3590884615009664 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 2 Sep 2023 23:18:14 +0200 Subject: [PATCH 05/26] Auth: Add a bunch of Sign-In related code --- .../firstparty/dataservice/TokenRequest.java | 20 +- .../android/auth/IAuthManagerService.aidl | 4 +- ...esRequest.aidl => GetAccountsRequest.aidl} | 2 +- .../gms/auth/HasCapabilitiesRequest.aidl | 3 + .../auth/account/data/IBundleCallback.aidl | 7 + .../data/IGetAccountChangeEventsCallback.aidl | 8 + .../account/data/IGetAccountsCallback.aidl | 7 + .../account/data/IGetHubTokenCallback.aidl | 8 + .../data/IGetTokenWithDetailsCallback.aidl | 7 + .../auth/account/data/IGoogleAuthService.aidl | 27 +++ .../data/IHasCapabilitiesCallback.aidl | 7 + .../dataservice/ClearTokenRequest.aidl | 3 + .../android/gms/auth/GetAccountsRequest.java | 14 +- .../gms/auth/GetHubTokenInternalResponse.java | 2 + .../android/gms/auth/GetHubTokenRequest.java | 2 + ...quest.java => HasCapabilitiesRequest.java} | 6 +- .../google/android/gms/auth/TokenData.java | 22 +- .../gms/auth/api/proxy/ProxyRequest.java | 2 + .../gms/auth/api/proxy/ProxyResponse.java | 2 + .../dataservice/ClearTokenRequest.java | 19 ++ .../api/signin/internal/ISignInCallbacks.aidl | 2 +- .../gms/auth/api/signin/SignInAccount.java | 21 ++ .../signin/internal/SignInConfiguration.java | 20 ++ play-services-base/build.gradle | 2 + .../signin/internal/AuthAccountResult.aidl | 3 + .../gms/signin/internal/ISignInCallbacks.aidl | 10 + .../RecordConsentByConsentResultResponse.aidl | 3 + .../auth/api/signin/GoogleSignInAccount.java | 84 ++++++- .../auth/api/signin/GoogleSignInOptions.java | 126 +++++++++- .../signin/GoogleSignInOptionsExtension.java | 19 ++ ...oogleSignInOptionsExtensionParcelable.java | 48 ++++ .../google/android/gms/common/api/Api.java | 11 + .../android/gms/common/api/GoogleApi.java | 3 +- .../gms/common/api/GoogleApiClient.java | 7 +- .../com/google/android/gms/signin/SignIn.java | 15 ++ .../android/gms/signin/SignInClient.java | 17 ++ .../android/gms/signin/SignInOptions.java | 11 + .../signin/internal/AuthAccountResult.java | 30 +++ .../internal/CheckServerAuthResult.java | 14 +- .../RecordConsentByConsentResultResponse.java | 12 + .../signin/internal/RecordConsentRequest.java | 15 +- .../org/microg/gms/common/DummyApiClient.java | 4 +- .../java/org/microg/gms/common/GmsClient.java | 7 +- .../org/microg/gms/common/GmsConnector.java | 5 +- .../gms/common/api/ApiClientBuilder.java | 2 +- .../gms/common/api/ApiClientSettings.java | 10 + .../gms/common/api/GoogleApiClientImpl.java | 17 +- .../gms/common/api/GoogleApiManager.java | 18 +- .../gms/common/api/PendingGoogleApiCall.java | 3 +- .../common/api/ReturningGoogleApiCall.java | 3 +- .../api/VoidReturningGoogleApiCall.java | 3 +- .../microg/gms/signin/SignInClientImpl.java | 101 ++++++++ .../java/org/microg/gms/signin/Storage.java | 80 +++++++ play-services-basement/build.gradle | 1 + .../android/gms/common/ConnectionResult.aidl | 3 + .../com/google/android/gms/common/Scopes.java | 4 + .../org/microg/gms/common/GmsService.java | 4 +- .../microg/gms/cast/CastApiClientBuilder.java | 4 +- .../CastRemoteDisplayApiClientBuilder.java | 4 +- .../gms/clearcut/LogEventParcelable.java | 4 +- .../src/main/AndroidManifest.xml | 46 ++-- .../gms/auth/AccountContentProvider.java | 2 - .../java/org/microg/gms/auth/AuthManager.java | 74 +++++- .../gms/auth/AuthManagerServiceImpl.java | 20 +- .../java/org/microg/gms/auth/AuthRequest.java | 64 +++++- .../org/microg/gms/auth/AuthResponse.java | 9 + .../microg/gms/auth/login/LoginActivity.java | 4 +- .../loginservice/AccountAuthenticator.java | 1 - .../microg/gms/checkin/CheckinManager.java | 2 +- .../gms/gservices/GServicesProvider.java | 2 - .../org/microg/gms/people/DatabaseHelper.java | 1 + .../org/microg/gms/people/PeopleManager.java | 3 +- .../auth/account/data/GoogleAuthService.kt | 97 ++++++++ .../gms/auth/signin/AuthSignInActivity.kt | 217 ++++++++++++++++++ .../gms/auth/signin/AuthSignInService.kt | 98 +++++++- .../gms/auth/signin/SignInDefaultService.kt | 142 ++++++++++++ .../org/microg/gms/auth/signin/extensions.kt | 97 ++++++++ .../main/res/drawable/ic_add_account_alt.xml | 11 + .../main/res/layout/signin_account_row.xml | 56 +++++ .../src/main/res/layout/signin_confirm.xml | 100 ++++++++ .../src/main/res/layout/signin_picker.xml | 100 ++++++++ .../src/main/res/values/strings.xml | 10 + .../ActivityRecognitionApiClientBuilder.java | 4 +- .../LocationServicesApiClientBuilder.java | 4 +- .../wearable/WearableApiClientBuilder.java | 4 +- .../microg/safeparcel/SafeParcelProcessor.kt | 9 +- 86 files changed, 1960 insertions(+), 139 deletions(-) rename play-services-auth-base/src/main/aidl/com/google/android/gms/auth/{HasCababilitiesRequest.aidl => GetAccountsRequest.aidl} (52%) create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCapabilitiesRequest.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IBundleCallback.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountChangeEventsCallback.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountsCallback.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetHubTokenCallback.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetTokenWithDetailsCallback.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGoogleAuthService.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IHasCapabilitiesCallback.aidl create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.aidl rename play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java => play-services-auth-base/src/main/java/com/google/android/gms/auth/GetAccountsRequest.java (68%) rename play-services-auth-base/src/main/java/com/google/android/gms/auth/{HasCababilitiesRequest.java => HasCapabilitiesRequest.java} (58%) create mode 100644 play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.java create mode 100644 play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/SignInAccount.java create mode 100644 play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/internal/SignInConfiguration.java create mode 100644 play-services-base/src/main/aidl/com/google/android/gms/signin/internal/AuthAccountResult.aidl create mode 100644 play-services-base/src/main/aidl/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.aidl create mode 100644 play-services-base/src/main/java/com/google/android/gms/auth/api/signin/internal/GoogleSignInOptionsExtensionParcelable.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/signin/SignIn.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/signin/SignInClient.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/signin/SignInOptions.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/signin/internal/AuthAccountResult.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.java create mode 100644 play-services-base/src/main/java/org/microg/gms/signin/SignInClientImpl.java create mode 100644 play-services-base/src/main/java/org/microg/gms/signin/Storage.java create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/GoogleAuthService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInActivity.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/signin/SignInDefaultService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/signin/extensions.kt create mode 100644 play-services-core/src/main/res/drawable/ic_add_account_alt.xml create mode 100644 play-services-core/src/main/res/layout/signin_account_row.xml create mode 100644 play-services-core/src/main/res/layout/signin_confirm.xml create mode 100644 play-services-core/src/main/res/layout/signin_picker.xml 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 index 4a200050e..c7c9b1c09 100644 --- 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 @@ -25,16 +25,24 @@ import org.microg.safeparcel.SafeParceled; // TODO public class TokenRequest extends AutoSafeParcelable{ - @SafeParceled(1) - private int versionCode = 4; - @SafeParceled(3) + @Field(1) + private int versionCode = 8; + @Field(2) + private String service; + @Field(3) public String accountName; - @SafeParceled(4) + @Field(4) public Bundle extras; - @SafeParceled(9) + @Field(7) + public boolean signingIn; + @Field(9) public String consent; - @SafeParceled(15) + @Field(15) public String accountType; + @Field(16) + public int delegationType; + @Field(17) + public String delegateeUserId; public Account getAccount() { return new Account(accountName, accountType); diff --git a/play-services-auth-base/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl b/play-services-auth-base/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl index ae33a3349..d6afdf805 100644 --- a/play-services-auth-base/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl +++ b/play-services-auth-base/src/main/aidl/com/google/android/auth/IAuthManagerService.aidl @@ -7,7 +7,7 @@ import com.google.android.gms.auth.AccountChangeEventsResponse; import com.google.android.gms.auth.AccountChangeEventsRequest; import com.google.android.gms.auth.GetHubTokenRequest; import com.google.android.gms.auth.GetHubTokenInternalResponse; -import com.google.android.gms.auth.HasCababilitiesRequest; +import com.google.android.gms.auth.HasCapabilitiesRequest; interface IAuthManagerService { Bundle getToken(String accountName, String scope, in Bundle extras) = 0; @@ -18,6 +18,6 @@ interface IAuthManagerService { Bundle getAccounts(in Bundle extras) = 5; Bundle removeAccount(in Account account) = 6; Bundle requestGoogleAccountsAccess(String packageName) = 7; - int hasCapabilities(in HasCababilitiesRequest request) = 8; + int hasCapabilities(in HasCapabilitiesRequest request) = 8; GetHubTokenInternalResponse getHubToken(in GetHubTokenRequest request, in Bundle extras) = 9; } diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/GetAccountsRequest.aidl similarity index 52% rename from play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl rename to play-services-auth-base/src/main/aidl/com/google/android/gms/auth/GetAccountsRequest.aidl index c0c080cd1..db379cee6 100644 --- a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCababilitiesRequest.aidl +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/GetAccountsRequest.aidl @@ -1,3 +1,3 @@ package com.google.android.gms.auth; -parcelable HasCababilitiesRequest; +parcelable GetAccountsRequest; diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCapabilitiesRequest.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCapabilitiesRequest.aidl new file mode 100644 index 000000000..b6cb6b9db --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/HasCapabilitiesRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth; + +parcelable HasCapabilitiesRequest; diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IBundleCallback.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IBundleCallback.aidl new file mode 100644 index 000000000..9061e5e9f --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IBundleCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.common.api.Status; + +interface IBundleCallback { + void onBundle(in Status status, in Bundle bundle) = 1; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountChangeEventsCallback.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountChangeEventsCallback.aidl new file mode 100644 index 000000000..2be6e623d --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountChangeEventsCallback.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.auth.AccountChangeEventsResponse; + +interface IGetAccountChangeEventsCallback { + void onAccountChangeEventsResponse(in Status status, in AccountChangeEventsResponse response) = 1; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountsCallback.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountsCallback.aidl new file mode 100644 index 000000000..d291dfe01 --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetAccountsCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.common.api.Status; + +interface IGetAccountsCallback { + void onBundle(in Status status, in List bundle) = 1; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetHubTokenCallback.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetHubTokenCallback.aidl new file mode 100644 index 000000000..3c98b32dc --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetHubTokenCallback.aidl @@ -0,0 +1,8 @@ +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.auth.GetHubTokenInternalResponse; + +interface IGetHubTokenCallback { + void onGetHubTokenResponse(in Status status, in GetHubTokenInternalResponse bundle) = 1; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetTokenWithDetailsCallback.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetTokenWithDetailsCallback.aidl new file mode 100644 index 000000000..a5c4aadb1 --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGetTokenWithDetailsCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.common.api.Status; + +interface IGetTokenWithDetailsCallback { + void onTokenResults(in Status status, in Bundle bundle) = 1; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGoogleAuthService.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGoogleAuthService.aidl new file mode 100644 index 000000000..883a6f062 --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IGoogleAuthService.aidl @@ -0,0 +1,27 @@ +package com.google.android.gms.auth.account.data; + +import android.accounts.Account; + +import com.google.android.gms.auth.AccountChangeEventsRequest; +import com.google.android.gms.auth.GetAccountsRequest; +import com.google.android.gms.auth.GetHubTokenRequest; +import com.google.android.gms.auth.HasCapabilitiesRequest; +import com.google.android.gms.auth.account.data.IBundleCallback; +import com.google.android.gms.auth.account.data.IGetAccountChangeEventsCallback; +import com.google.android.gms.auth.account.data.IGetAccountsCallback; +import com.google.android.gms.auth.account.data.IGetHubTokenCallback; +import com.google.android.gms.auth.account.data.IGetTokenWithDetailsCallback; +import com.google.android.gms.auth.account.data.IHasCapabilitiesCallback; +import com.google.android.gms.auth.firstparty.dataservice.ClearTokenRequest; +import com.google.android.gms.common.api.internal.IStatusCallback; + +interface IGoogleAuthService { + void getTokenWithDetails(IGetTokenWithDetailsCallback callback, in Account account, String service, in Bundle extras) = 0; + void clearToken(IStatusCallback callback, in ClearTokenRequest request) = 1; + void requestAccountsAccess(IBundleCallback callback, String str) = 2; + void getAccountChangeEvents(IGetAccountChangeEventsCallback callback, in AccountChangeEventsRequest request) = 3; + void getAccounts(IGetAccountsCallback callback, in GetAccountsRequest request) = 4; + void removeAccount(IBundleCallback callback, in Account account) = 5; + void hasCapabilities(IHasCapabilitiesCallback callback, in HasCapabilitiesRequest request) = 6; + void getHubToken(IGetHubTokenCallback callback, in GetHubTokenRequest request) = 7; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IHasCapabilitiesCallback.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IHasCapabilitiesCallback.aidl new file mode 100644 index 000000000..8af4438b8 --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/account/data/IHasCapabilitiesCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.common.api.Status; + +interface IHasCapabilitiesCallback { + void onHasCapabilities(in Status status, int mode) = 1; +} \ No newline at end of file diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.aidl new file mode 100644 index 000000000..2c5cf58b4 --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.auth.firstparty.dataservice; + +parcelable ClearTokenRequest; diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetAccountsRequest.java similarity index 68% rename from play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java rename to play-services-auth-base/src/main/java/com/google/android/gms/auth/GetAccountsRequest.java index 602e150e2..e39f58dd0 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetAccountsRequest.java @@ -14,14 +14,12 @@ * limitations under the License. */ -package org.microg.gms.common.api; +package com.google.android.gms.auth; -public interface ApiClient { - void connect(); +import org.microg.gms.common.Hide; +import org.microg.safeparcel.AutoSafeParcelable; - void disconnect(); - - boolean isConnected(); - - boolean isConnecting(); +@Hide +public class GetAccountsRequest extends AutoSafeParcelable { + public static Creator CREATOR = findCreator(GetAccountsRequest.class); } diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java index ccecf2914..b7e0726f8 100644 --- a/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenInternalResponse.java @@ -8,8 +8,10 @@ package com.google.android.gms.auth; import android.accounts.Account; import android.content.Intent; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class GetHubTokenInternalResponse extends AutoSafeParcelable { @Field(1) public TokenData tokenData; diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java index cdaffb519..9310153c6 100644 --- a/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/GetHubTokenRequest.java @@ -5,8 +5,10 @@ package com.google.android.gms.auth; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class GetHubTokenRequest extends AutoSafeParcelable { @Field(1) public String accountName; diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/HasCapabilitiesRequest.java similarity index 58% rename from play-services-auth-base/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java rename to play-services-auth-base/src/main/java/com/google/android/gms/auth/HasCapabilitiesRequest.java index 27b94732c..2b9307f64 100644 --- a/play-services-auth-base/src/main/java/com/google/android/gms/auth/HasCababilitiesRequest.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/HasCapabilitiesRequest.java @@ -7,12 +7,14 @@ package com.google.android.gms.auth; import android.accounts.Account; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; -public class HasCababilitiesRequest extends AutoSafeParcelable { +@Hide +public class HasCapabilitiesRequest extends AutoSafeParcelable { @Field(1) public Account account; @Field(2) public String[] capabilities; - public static final Creator CREATOR = new AutoCreator<>(HasCababilitiesRequest.class); + public static final Creator CREATOR = new AutoCreator<>(HasCapabilitiesRequest.class); } diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/TokenData.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/TokenData.java index 3b7d45f53..ac08cfbae 100644 --- a/play-services-auth-base/src/main/java/com/google/android/gms/auth/TokenData.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/TokenData.java @@ -18,26 +18,29 @@ package com.google.android.gms.auth; import com.google.android.gms.common.api.Scope; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; import org.microg.safeparcel.SafeParceled; +import java.util.ArrayList; import java.util.List; +@Hide public class TokenData extends AutoSafeParcelable { - @SafeParceled(1) + @Field(value = 1, versionCode = 1) private int versionCode = 1; - @SafeParceled(2) + @Field(2) public final String token; - @SafeParceled(3) + @Field(3) public final Long expiry; - @SafeParceled(5) + @Field(5) public final boolean isOAuth; - @SafeParceled(value = 6, subClass = Scope.class) - public final List scopes; + @Field(6) + public final List scopes; public TokenData() { token = null; @@ -50,7 +53,12 @@ public class TokenData extends AutoSafeParcelable { this.token = token; this.expiry = expiry; this.isOAuth = isOAuth; - this.scopes = scopes; + this.scopes = new ArrayList<>(); + if (scopes != null) { + for (Scope scope : scopes) { + this.scopes.add(scope.getScopeUri()); + } + } } public TokenData(String token, Long expiry) { diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java index 71270ebb1..f3a6d9809 100644 --- a/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyRequest.java @@ -7,8 +7,10 @@ package com.google.android.gms.auth.api.proxy; import android.os.Bundle; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class ProxyRequest extends AutoSafeParcelable { public static final int HTTP_METHOD_GET = 0; public static final int HTTP_METHOD_POST = 1; diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java index 951210cbb..d7a7b9ff8 100644 --- a/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/api/proxy/ProxyResponse.java @@ -8,8 +8,10 @@ package com.google.android.gms.auth.api.proxy; import android.app.PendingIntent; import android.os.Bundle; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class ProxyResponse extends AutoSafeParcelable { public static final int STATUS_CODE_NO_CONNECTION = -1; diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.java new file mode 100644 index 000000000..3e13e2aeb --- /dev/null +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/ClearTokenRequest.java @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import org.microg.gms.common.Hide; +import org.microg.safeparcel.AutoSafeParcelable; + +@Hide +public class ClearTokenRequest extends AutoSafeParcelable { + @Field(1) + private int versionCode = 1; + @Field(2) + public String token; + + public static final Creator CREATOR = findCreator(ClearTokenRequest.class); +} diff --git a/play-services-auth/src/main/aidl/com/google/android/gms/auth/api/signin/internal/ISignInCallbacks.aidl b/play-services-auth/src/main/aidl/com/google/android/gms/auth/api/signin/internal/ISignInCallbacks.aidl index 9884a73af..1b54d1f17 100644 --- a/play-services-auth/src/main/aidl/com/google/android/gms/auth/api/signin/internal/ISignInCallbacks.aidl +++ b/play-services-auth/src/main/aidl/com/google/android/gms/auth/api/signin/internal/ISignInCallbacks.aidl @@ -4,7 +4,7 @@ import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.common.api.Status; interface ISignInCallbacks { - void onSignIn(in GoogleSignInAccount callbacks, in Status status) = 100; + void onSignIn(in GoogleSignInAccount account, in Status status) = 100; void onSignOut(in Status status) = 101; void onRevokeAccess(in Status status) = 102; } \ No newline at end of file diff --git a/play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/SignInAccount.java b/play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/SignInAccount.java new file mode 100644 index 000000000..2de3a3fad --- /dev/null +++ b/play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/SignInAccount.java @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.signin; + +import org.microg.gms.common.Hide; +import org.microg.safeparcel.AutoSafeParcelable; + +@Hide +public class SignInAccount extends AutoSafeParcelable { + @Field(4) + public String email; + @Field(7) + public GoogleSignInAccount googleSignInAccount; + @Field(8) + public String userId; + + public static final Creator CREATOR = findCreator(SignInAccount.class); +} diff --git a/play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/internal/SignInConfiguration.java b/play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/internal/SignInConfiguration.java new file mode 100644 index 000000000..98a2f6c0e --- /dev/null +++ b/play-services-auth/src/main/java/com/google/android/gms/auth/api/signin/internal/SignInConfiguration.java @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.signin.internal; + +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import org.microg.gms.common.Hide; +import org.microg.safeparcel.AutoSafeParcelable; + +@Hide +public class SignInConfiguration extends AutoSafeParcelable { + @Field(2) + public String packageName; + @Field(5) + public GoogleSignInOptions options; + + public static final Creator CREATOR = findCreator(SignInConfiguration.class); +} diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index fa6e4744f..dc7a3f4d5 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -55,4 +55,6 @@ dependencies { api "androidx.fragment:fragment:1.0.0" api project(':play-services-basement') api project(':play-services-tasks') + + annotationProcessor project(':safe-parcel-processor') } diff --git a/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/AuthAccountResult.aidl b/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/AuthAccountResult.aidl new file mode 100644 index 000000000..77d8774b6 --- /dev/null +++ b/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/AuthAccountResult.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.signin.internal; + +parcelable AuthAccountResult; \ No newline at end of file diff --git a/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/ISignInCallbacks.aidl b/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/ISignInCallbacks.aidl index 16d2471f1..0eff34074 100644 --- a/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/ISignInCallbacks.aidl +++ b/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/ISignInCallbacks.aidl @@ -1,7 +1,17 @@ package com.google.android.gms.signin.internal; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.signin.internal.AuthAccountResult; +import com.google.android.gms.signin.internal.RecordConsentByConsentResultResponse; import com.google.android.gms.signin.internal.SignInResponse; interface ISignInCallbacks { + void onAuthAccount(in ConnectionResult connectionResult, in AuthAccountResult result) = 2; + void onPutAccount(in Status status) = 3; + void onRecordConsent(in Status status) = 5; + void onCurrentAccount(in Status status, in GoogleSignInAccount account) = 6; void onSignIn(in SignInResponse response) = 7; + void onRecrodConsentByConsent(in RecordConsentByConsentResultResponse response) = 8; } \ No newline at end of file diff --git a/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.aidl b/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.aidl new file mode 100644 index 000000000..097df34b8 --- /dev/null +++ b/play-services-base/src/main/aidl/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.signin.internal; + +parcelable RecordConsentByConsentResultResponse; \ No newline at end of file diff --git a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java index fb3a505ea..54cb3e8e4 100644 --- a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java +++ b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInAccount.java @@ -13,6 +13,9 @@ import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.gms.common.api.Scope; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.microg.gms.auth.AuthConstants; import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; @@ -61,11 +64,18 @@ public class GoogleSignInAccount extends AutoSafeParcelable { } @Hide - public GoogleSignInAccount(Account account, Set grantedScopes) { - this.email = account.name; - this.obfuscatedIdentifier = account.name; - this.expirationTime = 0; + public GoogleSignInAccount(@Nullable String id, @Nullable String tokenId, @Nullable String email, @Nullable String displayName, @Nullable Uri photoUrl, String serverAuthCode, long expirationTime, String obfuscatedIdentifier, Set grantedScopes, @Nullable String givenName, @Nullable String familyName) { + this.id = id; + this.tokenId = tokenId; + this.email = email; + this.displayName = displayName; + this.photoUrl = photoUrl; + this.serverAuthCode = serverAuthCode; + this.expirationTime = expirationTime; + this.obfuscatedIdentifier = obfuscatedIdentifier; this.grantedScopes = new ArrayList<>(grantedScopes); + this.givenName = givenName; + this.familyName = familyName; } /** @@ -179,6 +189,72 @@ public class GoogleSignInAccount extends AutoSafeParcelable { return ((GoogleSignInAccount) obj).obfuscatedIdentifier.equals(obfuscatedIdentifier) && ((GoogleSignInAccount) obj).getGrantedScopes().equals(getGrantedScopes()); } + @Hide + public String getObfuscatedIdentifier() { + return obfuscatedIdentifier; + } + + private static final String JSON_ID = "id"; + private static final String JSON_TOKEN_ID = "tokenId"; + private static final String JSON_EMAIL = "email"; + private static final String JSON_DISPLAY_NAME = "displayName"; + private static final String JSON_GIVEN_NAME = "givenName"; + private static final String JSON_FAMILY_NAME = "familyName"; + private static final String JSON_PHOTO_URL = "photoUrl"; + private static final String JSON_SERVER_AUTH_CODE = "serverAuthCode"; + private static final String JSON_EXPIRATION_TIME = "expirationTime"; + private static final String JSON_OBFUSCATED_IDENTIFIER = "obfuscatedIdentifier"; + private static final String JSON_GRANTED_SCOPES = "grantedScopes"; + + @Hide + public static GoogleSignInAccount fromJson(@Nullable String jsonString) throws JSONException { + if (jsonString == null) return null; + JSONObject json = new JSONObject(jsonString); + GoogleSignInAccount account = new GoogleSignInAccount(); + account.id = json.optString(JSON_ID); + account.tokenId = json.has(JSON_TOKEN_ID) ? json.optString(JSON_TOKEN_ID) : null; + account.email = json.has(JSON_EMAIL) ? json.optString(JSON_EMAIL) : null; + account.displayName = json.has(JSON_DISPLAY_NAME) ? json.optString(JSON_DISPLAY_NAME) : null; + account.givenName = json.has(JSON_GIVEN_NAME) ? json.optString(JSON_GIVEN_NAME) : null; + account.familyName = json.has(JSON_FAMILY_NAME) ? json.optString(JSON_FAMILY_NAME) : null; + account.photoUrl = json.has(JSON_PHOTO_URL) ? Uri.parse(json.optString(JSON_PHOTO_URL)) : null; + account.serverAuthCode = json.has(JSON_SERVER_AUTH_CODE) ? json.optString(JSON_SERVER_AUTH_CODE) : null; + account.expirationTime = Long.parseLong(json.getString(JSON_EXPIRATION_TIME)); + account.obfuscatedIdentifier = json.getString(JSON_OBFUSCATED_IDENTIFIER); + account.grantedScopes = new ArrayList<>(); + JSONArray jsonGrantedScopes = json.getJSONArray(JSON_GRANTED_SCOPES); + for (int i = 0; i < jsonGrantedScopes.length(); i++) { + account.grantedScopes.add(new Scope(jsonGrantedScopes.getString(i))); + } + return account; + } + + @Hide + @NonNull + public String toJson() { + JSONObject json = new JSONObject(); + try { + if (id != null) json.put(JSON_ID, id); + if (tokenId != null) json.put(JSON_TOKEN_ID, tokenId); + if (email != null) json.put(JSON_EMAIL, email); + if (displayName != null) json.put(JSON_DISPLAY_NAME, displayName); + if (givenName != null) json.put(JSON_GIVEN_NAME, givenName); + if (familyName != null) json.put(JSON_FAMILY_NAME, familyName); + if (photoUrl != null) json.put(JSON_PHOTO_URL, photoUrl.toString()); + if (serverAuthCode != null) json.put(JSON_SERVER_AUTH_CODE, serverAuthCode); + json.put(JSON_EXPIRATION_TIME, expirationTime); + json.put(JSON_OBFUSCATED_IDENTIFIER, obfuscatedIdentifier); + JSONArray jsonGrantedScopes = new JSONArray(); + for (Scope grantedScope : grantedScopes) { + jsonGrantedScopes.put(grantedScope.getScopeUri()); + } + json.put(JSON_GRANTED_SCOPES, jsonGrantedScopes); + return json.toString(); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + @Override public int hashCode() { return (obfuscatedIdentifier.hashCode() + 527) * 31 + getGrantedScopes().hashCode(); diff --git a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptions.java b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptions.java index a16f14123..4d2ed31a9 100644 --- a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptions.java +++ b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptions.java @@ -11,15 +11,17 @@ package com.google.android.gms.auth.api.signin; import android.accounts.Account; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.android.gms.auth.api.signin.internal.GoogleSignInOptionsExtensionParcelable; import com.google.android.gms.common.Scopes; import com.google.android.gms.common.api.Scope; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.microg.gms.auth.AuthConstants; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * {@code GoogleSignInOptions} contains options used to configure the {@link Auth#GOOGLE_SIGN_IN_API}. @@ -37,7 +39,7 @@ public class GoogleSignInOptions extends AutoSafeParcelable { * NOT use {@link GoogleSignInOptions.Builder#requestIdToken(String)} to request user's real Google identity assertion. */ @NonNull - public static final GoogleSignInOptions DEFAULT_GAMES_SIGN_IN = null; + public static final GoogleSignInOptions DEFAULT_GAMES_SIGN_IN = new Builder().requestScopes(new Scope(Scopes.GAMES_LITE)).build(); /** * Default configuration for Google Sign In. You can get a stable user ID and basic profile info back via {@link GoogleSignInAccount#getId()} after you @@ -45,7 +47,7 @@ public class GoogleSignInOptions extends AutoSafeParcelable { * sign in result, please build a configuration via {@code new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)}. */ @NonNull - public static final GoogleSignInOptions DEFAULT_SIGN_IN = null; + public static final GoogleSignInOptions DEFAULT_SIGN_IN = new Builder().requestId().requestProfile().build(); @Field(1) private int versionCode = 3; @@ -63,8 +65,8 @@ public class GoogleSignInOptions extends AutoSafeParcelable { private String serverClientId; @Field(8) private String hostedDomain; - // @Field(9) -// private ArrayList extensions; + @Field(9) + private ArrayList extensions; @Field(10) private String logSessionId; @@ -78,7 +80,52 @@ public class GoogleSignInOptions extends AutoSafeParcelable { */ @NonNull public Scope[] getScopeArray() { - return null; + return scopes.toArray(new Scope[0]); + } + + @Hide + public List getScopes() { + return Collections.unmodifiableList(scopes); + } + + @Hide + public Account getAccount() { + return account; + } + + @Hide + public boolean isIdTokenRequested() { + return idTokenRequested; + } + + @Hide + public boolean isServerAuthCodeRequested() { + return serverAuthCodeRequested; + } + + @Hide + public boolean isForceCodeForRefreshToken() { + return forceCodeForRefreshToken; + } + + @Hide + public String getServerClientId() { + return serverClientId; + } + + @Hide + public String getHostedDomain() { + return hostedDomain; + } + + @Hide + public List getExtensions() { + return Collections.unmodifiableList(extensions); + } + + @Hide + public String getLogSessionId() { + return logSessionId; } /** @@ -95,6 +142,7 @@ public class GoogleSignInOptions extends AutoSafeParcelable { private Account account; @Nullable private String hostedDomain; + private final Map extensionMap = new HashMap<>(); public Builder() { this.scopes = new HashSet<>(); @@ -108,6 +156,9 @@ public class GoogleSignInOptions extends AutoSafeParcelable { this.serverClientId = options.serverClientId; this.account = options.account; this.hostedDomain = options.hostedDomain; + for (GoogleSignInOptionsExtensionParcelable extension : options.extensions) { + extensionMap.put(extension.type, extension); + } } /** @@ -117,6 +168,14 @@ public class GoogleSignInOptions extends AutoSafeParcelable { */ @NonNull public Builder addExtension(GoogleSignInOptionsExtension extension) { + if (this.extensionMap.containsKey(extension.getExtensionType())) { + throw new IllegalStateException("Only one extension per type may be added"); + } + List scopes = extension.getImpliedScopes(); + if (scopes != null) { + this.scopes.addAll(scopes); + } + this.extensionMap.put(extension.getExtensionType(), new GoogleSignInOptionsExtensionParcelable(extension)); return this; } @@ -247,9 +306,56 @@ public class GoogleSignInOptions extends AutoSafeParcelable { options.serverClientId = serverClientId; options.account = account; options.hostedDomain = hostedDomain; + options.extensions = new ArrayList<>(extensionMap.values()); return options; } } - public static final Creator CREATOR = new AutoCreator<>(GoogleSignInOptions.class); + private static final String JSON_SCOPES = "scopes"; + private static final String JSON_ACCOUNT_NAME = "accountName"; + private static final String JSON_ID_TOKEN_REQUESTED = "idTokenRequested"; + private static final String JSON_FORCE_CODE_FOR_REFRESH_TOKEN = "forceCodeForRefreshToken"; + private static final String JSON_SERVER_AUTH_REQUESTED = "serverAuthRequested"; + private static final String JSON_SERVER_CLIENT_ID = "serverClientId"; + private static final String JSON_HOSTED_DOMAIN = "hostedDomain"; + + public static GoogleSignInOptions fromJson(String jsonString) throws JSONException { + if (jsonString == null) return null; + JSONObject json = new JSONObject(jsonString); + GoogleSignInOptions options = new GoogleSignInOptions(); + JSONArray jsonScopes = json.getJSONArray(JSON_SCOPES); + for (int i = 0; i < jsonScopes.length(); i++) { + options.scopes.add(new Scope(jsonScopes.getString(i))); + } + options.account = json.has(JSON_ACCOUNT_NAME) ? new Account(json.optString(JSON_ACCOUNT_NAME), AuthConstants.DEFAULT_ACCOUNT_TYPE) : null; + options.idTokenRequested = json.getBoolean(JSON_ID_TOKEN_REQUESTED); + options.forceCodeForRefreshToken = json.getBoolean(JSON_FORCE_CODE_FOR_REFRESH_TOKEN); + options.serverAuthCodeRequested = json.getBoolean(JSON_SERVER_AUTH_REQUESTED); + options.serverClientId = json.has(JSON_SERVER_CLIENT_ID) ? json.optString(JSON_SERVER_CLIENT_ID) : null; + options.hostedDomain = json.has(JSON_HOSTED_DOMAIN) ? json.optString(JSON_HOSTED_DOMAIN) : null; + return options; + } + + @NonNull + public String toJson() { + JSONObject json = new JSONObject(); + try { + JSONArray jsonScopes = new JSONArray(); + for (Scope scope : scopes) { + jsonScopes.put(scope.getScopeUri()); + } + json.put(JSON_SCOPES, jsonScopes); + if (account != null) json.put(JSON_ACCOUNT_NAME, account.name); + json.put(JSON_ID_TOKEN_REQUESTED, idTokenRequested); + json.put(JSON_FORCE_CODE_FOR_REFRESH_TOKEN, forceCodeForRefreshToken); + json.put(JSON_SERVER_AUTH_REQUESTED, serverAuthCodeRequested); + if (serverClientId != null) json.put(JSON_SERVER_CLIENT_ID, serverClientId); + if (hostedDomain != null) json.put(JSON_HOSTED_DOMAIN, hostedDomain); + return json.toString(); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + public static final Creator CREATOR = findCreator(GoogleSignInOptions.class); } diff --git a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptionsExtension.java b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptionsExtension.java index 82a648ba3..6f8d661e3 100644 --- a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptionsExtension.java +++ b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/GoogleSignInOptionsExtension.java @@ -8,10 +8,29 @@ package com.google.android.gms.auth.api.signin; +import android.os.Bundle; +import com.google.android.gms.common.api.Scope; +import org.microg.gms.common.Hide; + +import java.util.List; + /** * An interface for API specific extension for {@link GoogleSignInOptions}. * * @see GoogleSignInOptions.Builder#addExtension(GoogleSignInOptionsExtension). */ public interface GoogleSignInOptionsExtension { + @Hide + int GAMES = 1; + @Hide + int FITNESS = 3; + + @Hide + int getExtensionType(); + + @Hide + Bundle toBundle(); + + @Hide + List getImpliedScopes(); } diff --git a/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/internal/GoogleSignInOptionsExtensionParcelable.java b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/internal/GoogleSignInOptionsExtensionParcelable.java new file mode 100644 index 000000000..ead1e437f --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/auth/api/signin/internal/GoogleSignInOptionsExtensionParcelable.java @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.api.signin.internal; + +import android.os.Bundle; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.auth.api.signin.GoogleSignInOptionsExtension; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import org.microg.gms.common.Hide; + +@Hide +@SafeParcelable.Class +public class GoogleSignInOptionsExtensionParcelable extends AbstractSafeParcelable { + @Field(1) + public final int versionCode; + @Field(2) + public final int type; + @Field(3) + public final Bundle bundle; + + public GoogleSignInOptionsExtensionParcelable(GoogleSignInOptionsExtension extension) { + this(extension.getExtensionType(), extension.toBundle()); + } + + public GoogleSignInOptionsExtensionParcelable(int type, Bundle bundle) { + this(1, type, bundle); + } + + @Constructor + public GoogleSignInOptionsExtensionParcelable(@Param(1) int versionCode, @Param(2) int type, @Param(3) Bundle bundle) { + this.versionCode = versionCode; + this.type = type; + this.bundle = bundle; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GoogleSignInOptionsExtensionParcelable.class); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java index 065fff8ae..fd91a375d 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java @@ -20,6 +20,7 @@ import android.accounts.Account; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.ApiClientBuilder; @@ -100,4 +101,14 @@ public final class Api { } } + @Hide + public interface Client { + void connect(); + + void disconnect(); + + boolean isConnected(); + + boolean isConnecting(); + } } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java index 2cb5e7834..4a33737cf 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java @@ -13,7 +13,6 @@ import com.google.android.gms.tasks.TaskCompletionSource; import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.GoogleApiManager; import org.microg.gms.common.api.PendingGoogleApiCall; @@ -39,7 +38,7 @@ public abstract class GoogleApi implements HasApiKey Task scheduleTask(PendingGoogleApiCall apiCall) { + protected Task scheduleTask(PendingGoogleApiCall apiCall) { TaskCompletionSource completionSource = new TaskCompletionSource<>(); manager.scheduleTask(this, apiCall, completionSource); return completionSource.getTask(); 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 index f74e53045..62e52ba36 100644 --- 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 @@ -364,7 +364,12 @@ public interface GoogleApiClient { } private ApiClientSettings getClientSettings() { - return null; + ApiClientSettings clientSettings = new ApiClientSettings(); + clientSettings.accountName = accountName; + clientSettings.scopes = new HashSet<>(scopes); + clientSettings.gravityForPopups = gravityForPopups; + clientSettings.viewForPopups = viewForPopups; + return clientSettings; } public Builder enableAutoManage(FragmentActivity fragmentActivity, int cliendId, diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/SignIn.java b/play-services-base/src/main/java/com/google/android/gms/signin/SignIn.java new file mode 100644 index 000000000..8aca936ca --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/signin/SignIn.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.signin; + +import com.google.android.gms.common.api.Api; +import org.microg.gms.common.Hide; +import org.microg.gms.signin.SignInClientImpl; + +@Hide +public class SignIn { + public static final Api API = new Api<>((options, context, looper, clientSettings, callbacks, connectionFailedListener) -> new SignInClientImpl(context, clientSettings, callbacks, connectionFailedListener)); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/SignInClient.java b/play-services-base/src/main/java/com/google/android/gms/signin/SignInClient.java new file mode 100644 index 000000000..53f85c89b --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/signin/SignInClient.java @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.signin; + +import androidx.annotation.NonNull; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.internal.IAccountAccessor; +import com.google.android.gms.signin.internal.ISignInCallbacks; + +public interface SignInClient extends Api.Client { + void clearAccountFromSessionStore(); + void saveDefaultAccount(@NonNull IAccountAccessor accountAccessor, boolean crossClient); + void signIn(@NonNull ISignInCallbacks callbacks); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/SignInOptions.java b/play-services-base/src/main/java/com/google/android/gms/signin/SignInOptions.java new file mode 100644 index 000000000..be607901a --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/signin/SignInOptions.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.signin; + +import com.google.android.gms.common.api.Api; + +public class SignInOptions implements Api.ApiOptions.Optional { +} diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/internal/AuthAccountResult.java b/play-services-base/src/main/java/com/google/android/gms/signin/internal/AuthAccountResult.java new file mode 100644 index 000000000..e880bb3f1 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/signin/internal/AuthAccountResult.java @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.signin.internal; + +import android.content.Intent; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.Status; +import org.microg.gms.common.Hide; +import org.microg.safeparcel.AutoSafeParcelable; + +@Hide +public class AuthAccountResult extends AutoSafeParcelable implements Result { + @Field(1) + private int versionCode = 2; + @Field(2) + public int connectionResultCode; + @Field(3) + public Intent rawAuthResolutionIntent; + + @Override + public Status getStatus() { + return connectionResultCode == ConnectionResult.SUCCESS ? Status.SUCCESS : Status.CANCELED; + } + + public static final Creator CREATOR = findCreator(AuthAccountResult.class); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/internal/CheckServerAuthResult.java b/play-services-base/src/main/java/com/google/android/gms/signin/internal/CheckServerAuthResult.java index 39b4b762d..543e2de7e 100644 --- a/play-services-base/src/main/java/com/google/android/gms/signin/internal/CheckServerAuthResult.java +++ b/play-services-base/src/main/java/com/google/android/gms/signin/internal/CheckServerAuthResult.java @@ -5,8 +5,20 @@ package com.google.android.gms.signin.internal; +import com.google.android.gms.common.api.Scope; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +import java.util.List; + +@Hide public class CheckServerAuthResult extends AutoSafeParcelable { - public static final Creator CREATOR = new AutoCreator<>(CheckServerAuthResult.class); + @Field(1) + private int versionCode = 1; + @Field(2) + public boolean newAuthCodeRequired; + @Field(3) + public List additionalScopes; + + public static final Creator CREATOR = findCreator(CheckServerAuthResult.class); } diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.java b/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.java new file mode 100644 index 000000000..269c5d681 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentByConsentResultResponse.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.signin.internal; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class RecordConsentByConsentResultResponse extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator<>(RecordConsentByConsentResultResponse.class); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentRequest.java b/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentRequest.java index 39d650927..ebc8e55f2 100644 --- a/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentRequest.java +++ b/play-services-base/src/main/java/com/google/android/gms/signin/internal/RecordConsentRequest.java @@ -5,8 +5,21 @@ package com.google.android.gms.signin.internal; +import android.accounts.Account; +import com.google.android.gms.common.api.Scope; +import org.microg.gms.common.Hide; import org.microg.safeparcel.AutoSafeParcelable; +@Hide public class RecordConsentRequest extends AutoSafeParcelable { - public static final Creator CREATOR = new AutoCreator<>(RecordConsentRequest.class); + @Field(1) + private int versionCode = 1; + @Field(2) + public Account account; + @Field(3) + public Scope[] scopesToConsent; + @Field(4) + public String serverClientId; + + public static final Creator CREATOR = findCreator(RecordConsentRequest.class); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java b/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java index 132a6148f..c527866f8 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java @@ -16,9 +16,9 @@ package org.microg.gms.common; -import org.microg.gms.common.api.ApiClient; +import com.google.android.gms.common.api.Api; -public class DummyApiClient implements ApiClient { +public class DummyApiClient implements Api.Client { private boolean connected = false; @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java index 25ebe6cd3..e5b717175 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java @@ -27,17 +27,17 @@ import android.os.RemoteException; import android.util.Log; import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.CommonStatusCodes; import com.google.android.gms.common.internal.ConnectionInfo; import com.google.android.gms.common.internal.GetServiceRequest; import com.google.android.gms.common.internal.IGmsCallbacks; import com.google.android.gms.common.internal.IGmsServiceBroker; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.ConnectionCallbacks; import org.microg.gms.common.api.OnConnectionFailedListener; -public abstract class GmsClient implements ApiClient { +public abstract class GmsClient implements Api.Client { private static final String TAG = "GmsClient"; private final Context context; @@ -69,8 +69,7 @@ public abstract class GmsClient implements ApiClient { throw new IllegalStateException("Service ID not set in constructor and onConnectedToBroker not implemented"); } GetServiceRequest request = new GetServiceRequest(serviceId); - request.extras = new Bundle(); - request.packageName = context.getPackageName(); + request.packageName = packageName; request.account = account; request.extras = extras; broker.getService(callbacks, request); diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java index 8013d70f0..b77c6d2c0 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java @@ -27,10 +27,9 @@ import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.Result; import org.microg.gms.common.api.AbstractPendingResult; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.GoogleApiClientImpl; -public class GmsConnector { +public class GmsConnector { private static final String TAG = "GmsConnector"; private final GoogleApiClientImpl apiClient; @@ -43,7 +42,7 @@ public class GmsConnector { this.callback = callback; } - public static PendingResult call(GoogleApiClient client, Api api, GmsConnector.Callback callback) { + public static PendingResult call(GoogleApiClient client, Api api, GmsConnector.Callback callback) { return new GmsConnector(client, api, callback).connect(); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java index b5aa386fb..18336fa54 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java @@ -22,5 +22,5 @@ import android.os.Looper; import com.google.android.gms.common.api.Api; public interface ApiClientBuilder { - ApiClient build(O options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener); + Api.Client build(O options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java index d1e314a74..af67e7998 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java @@ -5,5 +5,15 @@ package org.microg.gms.common.api; +import android.view.View; + +import java.util.Set; + public class ApiClientSettings { + public String accountName; + public String packageName; + public Integer sessionId; + public Set scopes; + public int gravityForPopups; + public View viewForPopups; } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java index 112336970..2dffa374f 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -19,7 +19,6 @@ package org.microg.gms.common.api; import android.content.Context; import android.os.Bundle; import android.os.Looper; -import android.os.Message; import androidx.fragment.app.FragmentActivity; @@ -44,7 +43,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { private final Looper looper; private final ApiClientSettings clientSettings; private final Map apis = new HashMap(); - private final Map apiConnections = new HashMap(); + private final Map apiConnections = new HashMap(); private final Set connectionCallbacks = new HashSet(); private final Set connectionFailedListeners = new HashSet(); private final int clientId; @@ -90,6 +89,10 @@ public class GoogleApiClientImpl implements GoogleApiClient { this.connectionFailedListeners.addAll(connectionFailedListeners); this.clientId = clientId; + if (this.clientSettings.sessionId == null) { + this.clientSettings.sessionId = hashCode(); + } + for (Api api : apis.keySet()) { apiConnections.put(api, api.getBuilder().build(apis.get(api), context, looper, clientSettings, baseConnectionCallbacks, baseConnectionFailedListener)); } @@ -108,7 +111,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { return looper; } - public ApiClient getApiConnection(Api api) { + public Api.Client getApiConnection(Api api) { return apiConnections.get(api); } @@ -138,7 +141,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { Log.d(TAG, "Already connected/connecting, nothing to do"); return; } - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (!connection.isConnected()) { connection.connect(); } @@ -151,7 +154,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { shouldDisconnect = true; } else { Log.d(TAG, "disconnect()"); - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (connection.isConnected()) { connection.disconnect(); } @@ -161,7 +164,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public synchronized boolean isConnected() { - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (!connection.isConnected()) return false; } return true; @@ -169,7 +172,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public synchronized boolean isConnecting() { - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (connection.isConnecting()) return true; } return false; diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java index 0f68b7c32..5cf7933ed 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java @@ -7,11 +7,11 @@ package org.microg.gms.common.api; import android.content.Context; import android.os.Bundle; -import android.os.DeadObjectException; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApi; +import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.TaskCompletionSource; import java.util.ArrayList; @@ -22,7 +22,7 @@ import java.util.Map; public class GoogleApiManager { private static GoogleApiManager instance; private Context context; - private Map clientMap = new HashMap<>(); + private Map clientMap = new HashMap<>(); private Map>> waitingApiCallMap = new HashMap<>(); private GoogleApiManager(Context context) { @@ -34,19 +34,19 @@ public class GoogleApiManager { return instance; } - private synchronized A clientForApi(GoogleApi api) { + private synchronized A clientForApi(GoogleApi api) { ApiInstance apiInstance = new ApiInstance(api); if (clientMap.containsKey(apiInstance)) { return (A) clientMap.get(apiInstance); } else { - ApiClient client = api.api.getBuilder().build(api.getOptions(), context, context.getMainLooper(), null, new ConnectionCallback(apiInstance), new ConnectionFailedListener(apiInstance)); + Api.Client client = api.api.getBuilder().build(api.getOptions(), context, context.getMainLooper(), null, new ConnectionCallback(apiInstance), new ConnectionFailedListener(apiInstance)); clientMap.put(apiInstance, client); waitingApiCallMap.put(apiInstance, new ArrayList<>()); return (A) client; } } - public synchronized void scheduleTask(GoogleApi api, PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { + public synchronized void scheduleTask(GoogleApi api, PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { A client = clientForApi(api); boolean connecting = client.isConnecting(); boolean connected = client.isConnected(); @@ -57,7 +57,7 @@ public class GoogleApiManager { completionSource.setException(e); } } else { - waitingApiCallMap.get(new ApiInstance(api)).add(new WaitingApiCall((PendingGoogleApiCall) apiCall, completionSource)); + waitingApiCallMap.get(new ApiInstance(api)).add(new WaitingApiCall((PendingGoogleApiCall) apiCall, completionSource)); if (!connecting) { client.connect(); } @@ -120,15 +120,15 @@ public class GoogleApiManager { } private static class WaitingApiCall { - private PendingGoogleApiCall apiCall; + private PendingGoogleApiCall apiCall; private TaskCompletionSource completionSource; - public WaitingApiCall(PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { + public WaitingApiCall(PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { this.apiCall = apiCall; this.completionSource = completionSource; } - public void execute(ApiClient client) throws Exception { + public void execute(Api.Client client) throws Exception { apiCall.execute(client, completionSource); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java index cffea8328..578d02b99 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java @@ -5,8 +5,9 @@ package org.microg.gms.common.api; +import com.google.android.gms.common.api.Api; import com.google.android.gms.tasks.TaskCompletionSource; -public interface PendingGoogleApiCall { +public interface PendingGoogleApiCall { void execute(A client, TaskCompletionSource completionSource) throws Exception; } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java index 601f3576f..f836b9d42 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java @@ -5,9 +5,10 @@ package org.microg.gms.common.api; +import com.google.android.gms.common.api.Api; import com.google.android.gms.tasks.TaskCompletionSource; -public interface ReturningGoogleApiCall extends PendingGoogleApiCall { +public interface ReturningGoogleApiCall extends PendingGoogleApiCall { R execute(A client) throws Exception; @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java index 79778e1ab..ee8cbf660 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java @@ -5,9 +5,10 @@ package org.microg.gms.common.api; +import com.google.android.gms.common.api.Api; import com.google.android.gms.tasks.TaskCompletionSource; -public interface VoidReturningGoogleApiCall extends PendingGoogleApiCall{ +public interface VoidReturningGoogleApiCall extends PendingGoogleApiCall{ void execute(A client) throws Exception; @Override diff --git a/play-services-base/src/main/java/org/microg/gms/signin/SignInClientImpl.java b/play-services-base/src/main/java/org/microg/gms/signin/SignInClientImpl.java new file mode 100644 index 000000000..7793d8dc2 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/signin/SignInClientImpl.java @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.signin; + +import android.accounts.Account; +import android.content.Context; +import android.os.IBinder; +import android.util.Log; +import androidx.annotation.NonNull; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.internal.IAccountAccessor; +import com.google.android.gms.common.internal.ResolveAccountRequest; +import com.google.android.gms.signin.SignInClient; +import com.google.android.gms.signin.internal.ISignInCallbacks; +import com.google.android.gms.signin.internal.ISignInService; +import com.google.android.gms.signin.internal.SignInRequest; +import com.google.android.gms.signin.internal.SignInResponse; +import org.microg.gms.auth.AuthConstants; +import org.microg.gms.common.GmsClient; +import org.microg.gms.common.GmsService; +import org.microg.gms.common.api.ApiClientSettings; +import org.microg.gms.common.api.ConnectionCallbacks; +import org.microg.gms.common.api.OnConnectionFailedListener; + +public class SignInClientImpl extends GmsClient implements SignInClient { + private static final String TAG = "SignInClientImpl"; + private final int sessionId; + private final Account account; + + public SignInClientImpl(Context context, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) { + super(context, callbacks, connectionFailedListener, GmsService.SIGN_IN.ACTION); + serviceId = GmsService.SIGN_IN.SERVICE_ID; + + account = new Account(clientSettings.accountName != null ? clientSettings.accountName : AuthConstants.DEFAULT_ACCOUNT, AuthConstants.DEFAULT_ACCOUNT_TYPE); + extras.putParcelable("com.google.android.gms.signin.internal.clientRequestedAccount", account); + + sessionId = clientSettings.sessionId; + extras.putInt("com.google.android.gms.common.internal.ClientSettings.sessionId", sessionId); + + extras.putBoolean("com.google.android.gms.signin.internal.offlineAccessRequested", false); + extras.putBoolean("com.google.android.gms.signin.internal.idTokenRequested", false); + extras.putString("com.google.android.gms.signin.internal.serverClientId", null); + extras.putBoolean("com.google.android.gms.signin.internal.usePromptModeForAuthCode", true); + extras.putBoolean("com.google.android.gms.signin.internal.forceCodeForRefreshToken", false); + extras.putString("com.google.android.gms.signin.internal.hostedDomain", null); + extras.putString("com.google.android.gms.signin.internal.logSessionId", null); + extras.putBoolean("com.google.android.gms.signin.internal.waitForAccessTokenRefresh", false); + + if (clientSettings.packageName != null && !context.getPackageName().equals(clientSettings.packageName)) { + extras.putString("com.google.android.gms.signin.internal.realClientPackageName", clientSettings.packageName); + } + } + + @Override + protected ISignInService interfaceFromBinder(IBinder binder) { + return ISignInService.Stub.asInterface(binder); + } + + @Override + public void clearAccountFromSessionStore() { + try { + getServiceInterface().clearAccountFromSessionStore(sessionId); + } catch (Exception e) { + Log.w(TAG, e); + } + } + + @Override + public void saveDefaultAccount(@NonNull IAccountAccessor accountAccessor, boolean crossClient) { + try { + getServiceInterface().saveDefaultAccount(accountAccessor, sessionId, crossClient); + } catch (Exception e) { + Log.w(TAG, e); + } + } + + @Override + public void signIn(@NonNull ISignInCallbacks callbacks) { + try { + SignInRequest request = new SignInRequest(); + request.request = new ResolveAccountRequest(); + request.request.account = account; + request.request.sessionId = sessionId; + if (account.name.equals(AuthConstants.DEFAULT_ACCOUNT)) { + request.request.signInAccountHint = Storage.getInstance(getContext()).getSavedDefaultGoogleSignInAccount(); + } + getServiceInterface().signIn(request, callbacks); + } catch (Exception e) { + Log.w(TAG, e); + try { + SignInResponse response = new SignInResponse(); + response.connectionResult = new ConnectionResult(ConnectionResult.INTERNAL_ERROR); + callbacks.onSignIn(response); + } catch (Exception ignored) { + } + } + } +} diff --git a/play-services-base/src/main/java/org/microg/gms/signin/Storage.java b/play-services-base/src/main/java/org/microg/gms/signin/Storage.java new file mode 100644 index 000000000..a12d23956 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/signin/Storage.java @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.signin; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import org.microg.gms.common.Hide; + +@Hide +@SuppressLint("StaticFieldLeak") +public class Storage { + private static Object LOCK = new Object(); + private static Storage INSTANCE; + + public static Storage getInstance(Context context) { + synchronized (LOCK) { + if (INSTANCE == null) { + INSTANCE = new Storage(context.getApplicationContext()); + } + } + return INSTANCE; + } + + private static final String PREF_DEFAULT_ACCOUNT = "defaultGoogleSignInAccount"; + private static final String PREF_PREFIX_ACCOUNT = "googleSignInAccount:"; + private static final String PREF_PREFIX_OPTIONS = "googleSignInOptions:"; + private final SharedPreferences sharedPreferences; + + public Storage(Context context) { + this.sharedPreferences = context.getSharedPreferences("com.google.android.gms.signin", Context.MODE_PRIVATE); + } + + @Nullable + public GoogleSignInAccount getSavedDefaultGoogleSignInAccount() { + synchronized (sharedPreferences) { + String defaultGoogleSignInAccountName = sharedPreferences.getString(PREF_DEFAULT_ACCOUNT, null); + if (defaultGoogleSignInAccountName == null) return null; + String googleSignInAccountJson = sharedPreferences.getString(PREF_PREFIX_ACCOUNT + defaultGoogleSignInAccountName, null); + if (googleSignInAccountJson == null) return null; + try { + return GoogleSignInAccount.fromJson(googleSignInAccountJson); + } catch (Exception e) { + return null; + } + } + } + + @Nullable + public GoogleSignInOptions getSavedDefaultGoogleSignInOptions() { + synchronized (sharedPreferences) { + String defaultGoogleSignInAccountName = sharedPreferences.getString(PREF_DEFAULT_ACCOUNT, null); + if (defaultGoogleSignInAccountName == null) return null; + String googleSignInOptionsJson = sharedPreferences.getString(PREF_PREFIX_OPTIONS + defaultGoogleSignInAccountName, null); + if (googleSignInOptionsJson == null) return null; + try { + return GoogleSignInOptions.fromJson(googleSignInOptionsJson); + } catch (Exception e) { + return null; + } + } + } + + public void saveDefaultGoogleSignInAccount(@NonNull GoogleSignInAccount googleSignInAccount, @NonNull GoogleSignInOptions googleSignInOptions) { + synchronized (sharedPreferences) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(PREF_DEFAULT_ACCOUNT, googleSignInAccount.getObfuscatedIdentifier()); + editor.putString(PREF_PREFIX_ACCOUNT + googleSignInAccount.getObfuscatedIdentifier(), googleSignInAccount.toJson()); + editor.putString(PREF_PREFIX_OPTIONS + googleSignInAccount.getObfuscatedIdentifier(), googleSignInOptions.toJson()); + editor.apply(); + } + } +} diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle index 02f91adcf..9419ad2b8 100644 --- a/play-services-basement/build.gradle +++ b/play-services-basement/build.gradle @@ -34,6 +34,7 @@ android { buildToolsVersion "$androidBuildVersionTools" aidlPackagedList "com/google/android/gms/common/api/Status.aidl" + aidlPackagedList "com/google/android/gms/common/ConnectionResult.aidl" aidlPackagedList "com/google/android/gms/common/internal/IAccountAccessor.aidl" aidlPackagedList "com/google/android/gms/common/internal/ICancelToken.aidl" aidlPackagedList "com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl" diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl new file mode 100644 index 000000000..e46e6d5d6 --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/ConnectionResult.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.common; + +parcelable ConnectionResult; \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/Scopes.java b/play-services-basement/src/main/java/com/google/android/gms/common/Scopes.java index 76a4cd291..d8526bb1e 100644 --- a/play-services-basement/src/main/java/com/google/android/gms/common/Scopes.java +++ b/play-services-basement/src/main/java/com/google/android/gms/common/Scopes.java @@ -88,4 +88,8 @@ public class Scopes { public static final String FITNESS_BODY_READ = "https://www.googleapis.com/auth/fitness.body.read"; @Hide public static final String FITNESS_BODY_READ_WRITE = "https://www.googleapis.com/auth/fitness.body.write"; + @Hide + public static final String USERINFO_EMAIL = "https://www.googleapis.com/auth/userinfo.email"; + @Hide + public static final String USERINFO_PROFILE = "https://www.googleapis.com/auth/userinfo.profile"; } diff --git a/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java index 3f6541b3f..e57822a9e 100644 --- a/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java +++ b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java @@ -165,8 +165,10 @@ public enum GmsService { SECOND_DEVICE_AUTH(275, "com.google.android.gms.setup.auth.SecondDeviceAuth.START"), LOCATION_SHARING_REPORTER(277, "com.google.android.gms.locationsharingreporter.service.START"), OCR(279, "com.google.android.gms.ocr.service.START"), + POTOKENS(285, "com.google.android.gms.potokens.service.START"), OCR_INTERNAL(281, "com.google.android.gms.ocr.service.internal.START"), - IN_APP_REACH(315, "com.google.android.gms.inappreach.service.START") + IN_APP_REACH(315, "com.google.android.gms.inappreach.service.START"), + APP_ERRORS(334, "com.google.android.gms.apperrors.service.START_APP_ERROR"), ; public int SERVICE_ID; diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastApiClientBuilder.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastApiClientBuilder.java index aba489712..a7d9247f9 100644 --- a/play-services-cast/src/main/java/org/microg/gms/cast/CastApiClientBuilder.java +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastApiClientBuilder.java @@ -21,15 +21,15 @@ import android.os.Looper; import com.google.android.gms.cast.Cast; +import com.google.android.gms.common.api.Api; import org.microg.gms.common.api.ApiClientBuilder; import org.microg.gms.common.api.ApiClientSettings; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.ConnectionCallbacks; import org.microg.gms.common.api.OnConnectionFailedListener; public class CastApiClientBuilder implements ApiClientBuilder { @Override - public ApiClient build(Cast.CastOptions options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) { + public Api.Client build(Cast.CastOptions options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) { return new CastClientImpl(context, options, callbacks, connectionFailedListener); } } diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiClientBuilder.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiClientBuilder.java index 588d25c3e..db53af366 100644 --- a/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiClientBuilder.java +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiClientBuilder.java @@ -21,16 +21,16 @@ import android.os.Looper; import com.google.android.gms.cast.CastRemoteDisplay; +import com.google.android.gms.common.api.Api; import org.microg.gms.common.DummyApiClient; import org.microg.gms.common.api.ApiClientBuilder; import org.microg.gms.common.api.ApiClientSettings; -import org.microg.gms.common.api.ApiClient; import org.microg.gms.common.api.ConnectionCallbacks; import org.microg.gms.common.api.OnConnectionFailedListener; public class CastRemoteDisplayApiClientBuilder implements ApiClientBuilder { @Override - public ApiClient build(CastRemoteDisplay.CastRemoteDisplayOptions options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) { + public Api.Client build(CastRemoteDisplay.CastRemoteDisplayOptions options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener) { return new DummyApiClient(); } } diff --git a/play-services-clearcut/src/main/java/com/google/android/gms/clearcut/LogEventParcelable.java b/play-services-clearcut/src/main/java/com/google/android/gms/clearcut/LogEventParcelable.java index 152d3b1d9..64aee6dde 100644 --- a/play-services-clearcut/src/main/java/com/google/android/gms/clearcut/LogEventParcelable.java +++ b/play-services-clearcut/src/main/java/com/google/android/gms/clearcut/LogEventParcelable.java @@ -19,7 +19,7 @@ package com.google.android.gms.clearcut; import android.util.Base64; import com.google.android.gms.clearcut.internal.LogVerifierResultParcelable; -import com.google.android.gms.phenotype.ExperimentToken; +import com.google.android.gms.phenotype.ExperimentTokens; import com.google.android.gms.phenotype.GenericDimension; import com.google.android.gms.clearcut.internal.PlayLoggerContext; @@ -57,7 +57,7 @@ public class LogEventParcelable extends AutoSafeParcelable { public final boolean addPhenotypeExperimentTokens; @Field(9) - public final ExperimentToken[] experimentTokenParcelables; + public final ExperimentTokens[] experimentTokenParcelables; @Field(10) public final GenericDimension[] genericDimensions; diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index ece083c1f..e2d0f728e 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -69,7 +69,9 @@ android:protectionLevel="normal" /> - + + + + + + + + + + + + + + + + + + + + + + + + @@ -492,6 +523,7 @@ + @@ -721,18 +753,6 @@ - - - - - - - - - - - - diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java index 036bc8e58..c358c6c96 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java @@ -59,7 +59,6 @@ public class AccountContentProvider extends ContentProvider { suggestedPackageName = getCallingPackage(); } String packageName = PackageUtils.getAndCheckCallingPackage(getContext(), suggestedPackageName); - Log.d(TAG, "Call " + method + " from " + packageName + " with arg " + arg); if (!PackageUtils.callerHasExtendedAccess(getContext())) { String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid()); if (packagesForUid != null && packagesForUid.length != 0) @@ -93,7 +92,6 @@ public class AccountContentProvider extends ContentProvider { } result.putParcelableArray(PROVIDER_EXTRA_ACCOUNTS, accounts); - Log.d(TAG, "get_accounts returns: " + Arrays.toString(accounts)); return result; } else if (PROVIDER_METHOD_CLEAR_PASSWORD.equals(method) && PackageUtils.callerHasExtendedAccess(getContext())) { Account a = extras.getParcelable(PROVIDER_EXTRA_CLEAR_PASSWORD); diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java index 8ba6c6a93..ad1746f46 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManager.java @@ -20,6 +20,7 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.content.pm.PackageManager; +import android.net.Uri; import android.util.Log; import org.microg.gms.common.PackageUtils; @@ -48,6 +49,16 @@ public class AuthManager { private String packageSignature; private String accountType; + + private int delegationType; + private String delegateeUserId; + private String oauth2Foreground; + private String oauth2Prompt; + private String itCaveatTypes; + private String tokenRequestOptions; + public String includeEmail; + public String includeProfile; + public AuthManager(Context context, String accountName, String packageName, String service) { this.context = context; this.accountName = accountName; @@ -80,7 +91,15 @@ public class AuthManager { } public String buildTokenKey(String service) { - return packageName + ":" + getPackageSignature() + ":" + service; + Uri.Builder builder = Uri.EMPTY.buildUpon(); + if (delegationType != 0 && delegateeUserId != null) + builder.appendQueryParameter("delegation_type", Integer.toString(delegationType)) + .appendQueryParameter("delegatee_user_id", delegateeUserId); + if (tokenRequestOptions != null) builder.appendQueryParameter("token_request_options", tokenRequestOptions); + if (includeEmail != null) builder.appendQueryParameter("include_email", includeEmail); + if (includeProfile != null) builder.appendQueryParameter("include_profile", includeEmail); + String query = builder.build().getEncodedQuery(); + return packageName + ":" + getPackageSignature() + ":" + service + (query != null ? ("?" + query) : ""); } public String buildTokenKey() { @@ -124,6 +143,32 @@ public class AuthManager { getAccountManager().setUserData(getAccount(), key, value); } + public void setDelegation(int delegationType, String delegateeUserId) { + if (delegationType != 0 && delegateeUserId != null) { + this.delegationType = delegationType; + this.delegateeUserId = delegateeUserId; + } else { + this.delegationType = 0; + this.delegateeUserId = null; + } + } + + public void setOauth2Foreground(String oauth2Foreground) { + this.oauth2Foreground = oauth2Foreground; + } + + public void setOauth2Prompt(String oauth2Prompt) { + this.oauth2Prompt = oauth2Prompt; + } + + public void setItCaveatTypes(String itCaveatTypes) { + this.itCaveatTypes = itCaveatTypes; + } + + public void setTokenRequestOptions(String tokenRequestOptions) { + this.tokenRequestOptions = tokenRequestOptions; + } + public boolean accountExists() { for (Account refAccount : getAccountManager().getAccountsByType(accountType)) { if (refAccount.name.equalsIgnoreCase(accountName)) return true; @@ -138,7 +183,7 @@ public class AuthManager { public String getAuthToken() { if (service.startsWith("weblogin:")) return null; - if (getExpiry() < System.currentTimeMillis() / 1000L) { + if (System.currentTimeMillis() / 1000L >= getExpiry() - 300L) { Log.d(TAG, "token present, but expired"); return null; } @@ -167,6 +212,14 @@ public class AuthManager { } } + public void invalidateAuthToken() { + invalidateAuthToken(peekAuthToken()); + } + + public void invalidateAuthToken(String auth) { + getAccountManager().invalidateAuthToken(accountType, auth); + } + public void storeResponse(AuthResponse response) { if (service.startsWith("weblogin:")) return; if (response.accountId != null) @@ -206,6 +259,10 @@ public class AuthManager { AuthResponse response = new AuthResponse(); response.issueAdvice = "stored"; response.auth = token; + if (service.startsWith("oauth2:")) { + response.grantedScopes = service.substring(7); + } + response.expiry = getExpiry(); return response; } } @@ -214,9 +271,16 @@ public class AuthManager { .app(packageName, getPackageSignature()) .email(accountName) .token(getAccountManager().getPassword(account)) - .service(service); - if (isSystemApp()) request.systemPartition(); - if (isPermitted()) request.hasPermission(); + .service(service) + .delegation(delegationType, delegateeUserId) + .oauth2Foreground(oauth2Foreground) + .oauth2Prompt(oauth2Prompt) + .oauth2IncludeProfile(includeProfile) + .oauth2IncludeEmail(includeEmail) + .itCaveatTypes(itCaveatTypes) + .tokenRequestOptions(tokenRequestOptions) + .systemPartition(isSystemApp()) + .hasPermission(isPermitted()); if (legacy) { request.callerIsGms().calledFromAccountManager(); } else { diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java index 2df0a4628..896149467 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java @@ -18,8 +18,6 @@ package org.microg.gms.auth; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.annotation.SuppressLint; import android.app.NotificationManager; import android.app.PendingIntent; @@ -40,7 +38,7 @@ import com.google.android.gms.auth.AccountChangeEventsRequest; import com.google.android.gms.auth.AccountChangeEventsResponse; import com.google.android.gms.auth.GetHubTokenInternalResponse; import com.google.android.gms.auth.GetHubTokenRequest; -import com.google.android.gms.auth.HasCababilitiesRequest; +import com.google.android.gms.auth.HasCapabilitiesRequest; import com.google.android.gms.auth.TokenData; import com.google.android.gms.common.api.Scope; @@ -70,6 +68,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { public static final String KEY_REQUEST_VISIBLE_ACTIVITIES = "request_visible_actions"; public static final String KEY_SUPPRESS_PROGRESS_SCREEN = "suppressProgressScreen"; public static final String KEY_SYNC_EXTRAS = "sync_extras"; + public static final String KEY_DELEGATION_TYPE = "delegation_type"; + public static final String KEY_DELEGATEE_USER_ID = "delegatee_user_id"; public static final String KEY_ERROR = "Error"; public static final String KEY_USER_RECOVERY_INTENT = "userRecoveryIntent"; @@ -116,7 +116,8 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false); - Log.d(TAG, "getToken: account:" + account.name + " scope:" + scope + " extras:" + extras + ", notify: " + notify); + if (!AuthConstants.SCOPE_GET_ACCOUNT_ID.equals(scope)) + Log.d(TAG, "getToken: account:" + account.name + " scope:" + scope + " extras:" + extras + ", notify: " + notify); /* * TODO: This scope seems to be invalid (according to https://developers.google.com/oauthplayground/), @@ -125,6 +126,10 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { scope = scope.replace("https://www.googleapis.com/auth/identity.plus.page.impersonation ", ""); AuthManager authManager = new AuthManager(context, account.name, packageName, scope); + if (extras.containsKey(KEY_DELEGATION_TYPE) && extras.getInt(KEY_DELEGATION_TYPE) != 0 ) { + authManager.setDelegation(extras.getInt(KEY_DELEGATION_TYPE), extras.getString("delegatee_user_id")); + } + authManager.setOauth2Foreground(notify ? "0" : "1"); Bundle result = new Bundle(); result.putString(KEY_ACCOUNT_NAME, account.name); result.putString(KEY_ACCOUNT_TYPE, authManager.getAccountType()); @@ -135,10 +140,11 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { try { AuthResponse res = authManager.requestAuth(false); if (res.auth != null) { - Log.d(TAG, "getToken: " + res); + if (!AuthConstants.SCOPE_GET_ACCOUNT_ID.equals(scope)) + Log.d(TAG, "getToken: " + res); result.putString(KEY_AUTHTOKEN, res.auth); Bundle details = new Bundle(); - details.putParcelable("TokenData", new TokenData(res.auth, res.expiry, scope.startsWith("oauth2:"), getScopes(scope))); + details.putParcelable("TokenData", new TokenData(res.auth, res.expiry, scope.startsWith("oauth2:"), getScopes(res.grantedScopes != null ? res.grantedScopes : scope))); result.putBundle("tokenDetails", details); result.putString(KEY_ERROR, "OK"); } else { @@ -217,7 +223,7 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { } @Override - public int hasCapabilities(HasCababilitiesRequest request) throws RemoteException { + public int hasCapabilities(HasCapabilitiesRequest request) throws RemoteException { Log.w(TAG, "Not implemented: hasCapabilities(" + request.account + ", " + Arrays.toString(request.capabilities) + ")"); return 1; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java index d0bb11443..b1f5d0905 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java @@ -84,6 +84,26 @@ public class AuthRequest extends HttpFormClient.Request { public boolean hasPermission; @RequestContent("add_account") public boolean addAccount; + @RequestContent("delegation_type") + public String delegationType; + @RequestContent("delegatee_user_id") + public String delegateeUserId; + @RequestContent("oauth2_foreground") + public String oauth2Foreground; + @RequestContent("token_request_options") + public String tokenRequestOptions; + @RequestContent("it_caveat_types") + public String itCaveatTypes; + @RequestContent("check_email") + public boolean checkEmail; + @RequestContent("request_visible_actions") + public String requestVisibleActions; + @RequestContent("oauth2_prompt") + public String oauth2Prompt; + @RequestContent("oauth2_include_profile") + public String oauth2IncludeProfile; + @RequestContent("oauth2_include_email") + public String oauth2IncludeEmail; public String deviceName; public String buildVersion; @@ -168,13 +188,13 @@ public class AuthRequest extends HttpFormClient.Request { return this; } - public AuthRequest systemPartition() { - systemPartition = true; + public AuthRequest systemPartition(boolean systemPartition) { + this.systemPartition = systemPartition; return this; } - public AuthRequest hasPermission() { - hasPermission = true; + public AuthRequest hasPermission(boolean hasPermission) { + this.hasPermission = hasPermission; return this; } @@ -193,6 +213,42 @@ public class AuthRequest extends HttpFormClient.Request { return this; } + public AuthRequest delegation(int delegationType, String delegateeUserId) { + this.delegationType = delegationType == 0 ? null : Integer.toString(delegationType); + this.delegateeUserId = delegateeUserId; + return this; + } + + public AuthRequest oauth2Foreground(String oauth2Foreground) { + this.oauth2Foreground = oauth2Foreground; + return this; + } + + public AuthRequest tokenRequestOptions(String tokenRequestOptions) { + this.tokenRequestOptions = tokenRequestOptions; + return this; + } + + public AuthRequest oauth2IncludeProfile(String oauth2IncludeProfile) { + this.oauth2IncludeProfile = oauth2IncludeProfile; + return this; + } + + public AuthRequest oauth2IncludeEmail(String oauth2IncludeProfile) { + this.oauth2IncludeEmail = oauth2IncludeEmail; + return this; + } + + public AuthRequest oauth2Prompt(String oauth2Prompt) { + this.oauth2Prompt = oauth2Prompt; + return this; + } + + public AuthRequest itCaveatTypes(String itCaveatTypes) { + this.itCaveatTypes = itCaveatTypes; + return this; + } + public AuthResponse getResponse() throws IOException { return HttpFormClient.request(SERVICE_URL, this, AuthResponse.class); } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthResponse.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthResponse.java index f94eacef5..338099103 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthResponse.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthResponse.java @@ -63,6 +63,14 @@ public class AuthResponse { public String scopeConsentDetails; @ResponseField("ConsentDataBase64") public String consentDataBase64; + @ResponseField("grantedScopes") + public String grantedScopes; + @ResponseField("itMetadata") + public String itMetadata; + @ResponseField("ResolutionDataBase64") + public String resolutionDataBase64; + @ResponseField("it") + public String auths; public static AuthResponse parse(String result) { AuthResponse response = new AuthResponse(); @@ -115,6 +123,7 @@ public class AuthResponse { if (permission != null) sb.append(", permission='").append(permission).append('\''); if (scopeConsentDetails != null) sb.append(", scopeConsentDetails='").append(scopeConsentDetails).append('\''); if (consentDataBase64 != null) sb.append(", consentDataBase64='").append(consentDataBase64).append('\''); + if (auths != null) sb.append(", auths='").append(auths).append('\''); sb.append('}'); return sb.toString(); } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java index 76c273abe..76814302b 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java @@ -342,8 +342,8 @@ public class LoginActivity extends AssistantActivity { .service(authManager.getService()) .email(account.name) .token(AccountManager.get(this).getPassword(account)) - .systemPartition() - .hasPermission() + .systemPartition(true) + .hasPermission(true) .addAccount() .getAccountId() .getResponseAsync(new HttpFormClient.Callback() { diff --git a/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java b/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java index 5b4118480..7891ddfbd 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/loginservice/AccountAuthenticator.java @@ -140,7 +140,6 @@ class AccountAuthenticator extends AbstractAccountAuthenticator { @Override public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { - Log.d(TAG, "hasFeatures: " + account + ", " + Arrays.toString(features)); AccountManager accountManager = AccountManager.get(context); String services = accountManager.getUserData(account, "services"); boolean res = true; diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java index 22211e160..1604ed256 100644 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinManager.java @@ -48,7 +48,7 @@ public class CheckinManager { for (Account account : accountManager.getAccountsByType(accountType)) { String token = new AuthRequest() .email(account.name).token(accountManager.getPassword(account)) - .hasPermission().service("ac2dm") + .hasPermission(true).service("ac2dm") .app("com.google.android.gsf", Constants.GMS_PACKAGE_SIGNATURE_SHA1) .getResponse().LSid; if (token != null) { diff --git a/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java b/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java index b0cb1e049..dd90304b7 100644 --- a/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java +++ b/play-services-core/src/main/java/org/microg/gms/gservices/GServicesProvider.java @@ -77,7 +77,6 @@ public class GServicesProvider extends ContentProvider { for (String name : cache.keySet()) { if (name.startsWith(prefix)) { String value = cache.get(name); - Log.d(TAG, "query caller=" + getCallingPackageName() + " prefix=" + prefix + " name=" + name + " value=" + value); cursor.addRow(new String[]{name, value}); } } @@ -91,7 +90,6 @@ public class GServicesProvider extends ContentProvider { value = databaseHelper.get(name); cache.put(name, value); } - Log.d(TAG, "query caller=" + getCallingPackageName() + " name=" + name + " value=" + value); if (value != null) { cursor.addRow(new String[]{name, value}); } diff --git a/play-services-core/src/main/java/org/microg/gms/people/DatabaseHelper.java b/play-services-core/src/main/java/org/microg/gms/people/DatabaseHelper.java index 29527e38d..498941438 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/DatabaseHelper.java +++ b/play-services-core/src/main/java/org/microg/gms/people/DatabaseHelper.java @@ -113,6 +113,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { public void putOwner(ContentValues contentValues) { getWritableDatabase().insertWithOnConflict(OWNERS_TABLE, null, contentValues, SQLiteDatabase.CONFLICT_REPLACE); + close(); } public Cursor getOwner(String accountName) { diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java index ccc2442a2..79331073b 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java @@ -25,6 +25,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; +import com.google.android.gms.common.Scopes; import org.json.JSONException; import org.json.JSONObject; import org.microg.gms.auth.AuthManager; @@ -41,7 +42,7 @@ import java.net.URLConnection; public class PeopleManager { private static final String TAG = "GmsPeopleManager"; - public static final String USERINFO_SCOPE = "oauth2:https://www.googleapis.com/auth/userinfo.profile"; + public static final String USERINFO_SCOPE = "oauth2:" + Scopes.USERINFO_PROFILE; public static final String USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; public static final String REGEX_SEARCH_USER_PHOTO = "https?\\:\\/\\/lh([0-9]*)\\.googleusercontent\\.com/"; diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/GoogleAuthService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/GoogleAuthService.kt new file mode 100644 index 000000000..1bc2f2d84 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/GoogleAuthService.kt @@ -0,0 +1,97 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.auth.account.data + +import android.accounts.Account +import android.os.Bundle +import android.os.Parcel +import android.util.Log +import com.google.android.gms.auth.* +import com.google.android.gms.auth.account.data.* +import com.google.android.gms.auth.firstparty.dataservice.ClearTokenRequest +import com.google.android.gms.common.Feature +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.common.api.Status +import com.google.android.gms.common.api.internal.IStatusCallback +import com.google.android.gms.common.internal.ConnectionInfo +import com.google.android.gms.common.internal.GetServiceRequest +import com.google.android.gms.common.internal.IGmsCallbacks +import org.microg.gms.BaseService +import org.microg.gms.common.GmsService +import org.microg.gms.utils.warnOnTransactionIssues + +private const val TAG = "GoogleAuthService" + +val FEATURES = arrayOf( + Feature("auth_suw", 224516000), + Feature("account_capability_api", 1), + Feature("account_data_service", 6), + Feature("account_data_service_legacy", 1), + Feature("account_data_service_token", 8), + Feature("account_data_service_visibility", 1), + Feature("config_sync", 1), + Feature("device_account_api", 1), + Feature("device_account_jwt_creation", 1), + Feature("gaiaid_primary_email_api", 1), + Feature("google_auth_service_accounts", 2), + Feature("google_auth_service_token", 3), + Feature("hub_mode_api", 1), + Feature("user_service_account_management", 1), + Feature("work_account_client_is_whitelisted", 1), +) + +class GoogleAuthService : BaseService(TAG, GmsService.GOOGLE_AUTH){ + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + val binder = GoogleAuthServiceImpl().asBinder() + callback.onPostInitCompleteWithConnectionInfo(CommonStatusCodes.SUCCESS, binder, ConnectionInfo().apply { features = FEATURES }) + } +} + +class GoogleAuthServiceImpl : IGoogleAuthService.Stub() { + override fun getTokenWithDetails(callback: IGetTokenWithDetailsCallback?, account: Account?, service: String?, extras: Bundle?) { + Log.d(TAG, "Not yet implemented: getTokenWithDetails($account, $service, $extras)") + callback?.onTokenResults(Status.INTERNAL_ERROR, Bundle.EMPTY) + } + + override fun clearToken(callback: IStatusCallback?, request: ClearTokenRequest?) { + Log.d(TAG, "Not yet implemented: clearToken($request)") + callback?.onResult(Status.INTERNAL_ERROR) + } + + override fun requestAccountsAccess(callback: IBundleCallback?, str: String?) { + Log.d(TAG, "Not yet implemented: requestAccountsAccess($str)") + callback?.onBundle(Status.INTERNAL_ERROR, Bundle.EMPTY) + } + + override fun getAccountChangeEvents(callback: IGetAccountChangeEventsCallback?, request: AccountChangeEventsRequest?) { + Log.d(TAG, "Not yet implemented: getAccountChangeEvents($request)") + callback?.onAccountChangeEventsResponse(Status.INTERNAL_ERROR, AccountChangeEventsResponse()) + } + + override fun getAccounts(callback: IGetAccountsCallback?, request: GetAccountsRequest?) { + Log.d(TAG, "Not yet implemented: getAccounts($request)") + callback?.onBundle(Status.INTERNAL_ERROR, emptyList()) + } + + override fun removeAccount(callback: IBundleCallback?, account: Account?) { + Log.d(TAG, "Not yet implemented: removeAccount($account)") + callback?.onBundle(Status.INTERNAL_ERROR, Bundle.EMPTY) + } + + override fun hasCapabilities(callback: IHasCapabilitiesCallback?, request: HasCapabilitiesRequest?) { + Log.d(TAG, "Not yet implemented: hasCapabilities($request)") + callback?.onHasCapabilities(Status.INTERNAL_ERROR, 1) + } + + override fun getHubToken(callback: IGetHubTokenCallback?, request: GetHubTokenRequest?) { + Log.d(TAG, "Not yet implemented: getHubToken($request)") + callback?.onGetHubTokenResponse(Status.INTERNAL_ERROR, GetHubTokenInternalResponse()) + } + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags, TAG) { + super.onTransact(code, data, reply, flags) + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInActivity.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInActivity.kt new file mode 100644 index 000000000..1e1d2b2fa --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInActivity.kt @@ -0,0 +1,217 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.auth.signin + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.Intent +import android.graphics.Bitmap +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.getSystemService +import androidx.lifecycle.lifecycleScope +import com.google.android.gms.R +import com.google.android.gms.auth.api.signin.GoogleSignInAccount +import com.google.android.gms.auth.api.signin.SignInAccount +import com.google.android.gms.auth.api.signin.internal.SignInConfiguration +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.common.api.Status +import com.google.android.gms.databinding.SigninConfirmBinding +import com.google.android.gms.databinding.SigninPickerBinding +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT +import org.microg.gms.auth.AuthConstants.DEFAULT_ACCOUNT_TYPE +import org.microg.gms.auth.login.LoginActivity +import org.microg.gms.people.DatabaseHelper +import org.microg.gms.people.PeopleManager +import org.microg.gms.utils.getApplicationLabel + +const val REQUEST_CODE_SIGN_IN = 100 +const val REQUEST_CODE_PICK_ACCOUNT = 101 + +/** + * TODO: Get privacy policy / terms of service links via + * https://clientauthconfig.googleapis.com/google.identity.clientauthconfig.v1.ClientAuthConfig/GetDisplayBrand + */ +class AuthSignInActivity : AppCompatActivity() { + private val config: SignInConfiguration? + get() = runCatching { + intent?.extras?.also { it.classLoader = SignInConfiguration::class.java.classLoader }?.getParcelable("config") + }.getOrNull() + + private val Int.px: Int get() = (this * resources.displayMetrics.density).toInt() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val packageName = config?.packageName + if (packageName == null || packageName != callingActivity?.packageName) return finishResult(CommonStatusCodes.DEVELOPER_ERROR) + val accountManager = getSystemService() ?: return finishResult(CommonStatusCodes.DEVELOPER_ERROR) + + val accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE) + if (accounts.isNotEmpty()) { + val account = config?.options?.account + if (account != null) { + if (account in accounts) { + showSignInConfirm(packageName, account) + } else { + finishResult(CommonStatusCodes.INVALID_ACCOUNT) + } + } else { + openAccountPicker(packageName) + } + } else { + openAddAccount() + } + } + + private fun openAddAccount() { + startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_CODE_SIGN_IN) + } + + private fun getDisplayName(account: Account): String? { + val cursor = DatabaseHelper(this).getOwner(account.name) + return try { + if (cursor.moveToNext()) { + cursor.getColumnIndex("display_name").takeIf { it >= 0 }?.let { cursor.getString(it) }.takeIf { !it.isNullOrBlank() } + } else null + } finally { + cursor.close() + } + } + + private fun bindAccountRow(root: View, account: Account, updateAction: (ImageView, Bitmap) -> Unit) { + val photoView = root.findViewById(R.id.account_photo) + val displayNameView = root.findViewById(R.id.account_display_name) + val emailView = root.findViewById(R.id.account_email) + if (account.name != DEFAULT_ACCOUNT) { + val photo = PeopleManager.getOwnerAvatarBitmap(this@AuthSignInActivity, account.name, false) + if (photo == null) { + lifecycleScope.launchWhenStarted { + val bitmap = withContext(Dispatchers.IO) { + PeopleManager.getOwnerAvatarBitmap(this@AuthSignInActivity, account.name, true) + } + updateAction(photoView, bitmap) + } + } + val displayName = getDisplayName(account) + photoView.setImageBitmap(photo) + if (displayName != null) { + displayNameView.text = displayName + emailView.text = account.name + emailView.visibility = View.VISIBLE + } else { + displayNameView.text = account.name + emailView.visibility = View.GONE + } + } else { + photoView.setImageResource(R.drawable.ic_add_account_alt) + displayNameView.setText(R.string.signin_picker_add_account_label) + emailView.visibility = View.GONE + } + } + + private fun openAccountPicker(packageName: String) { + val binding = SigninPickerBinding.inflate(layoutInflater) + binding.appName = packageManager.getApplicationLabel(packageName).toString() + binding.appIcon = packageManager.getApplicationIcon(packageName) + val accounts = getSystemService()!!.getAccountsByType(DEFAULT_ACCOUNT_TYPE) + Account(DEFAULT_ACCOUNT, DEFAULT_ACCOUNT_TYPE) + binding.pickerList.adapter = object : ArrayAdapter(this, 0, accounts) { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val v = convertView ?: layoutInflater.inflate(R.layout.signin_account_row, parent, false) + getItem(position)?.let { bindAccountRow(v, it) { _, _ -> notifyDataSetChanged() } } + return v + } + } + binding.pickerList.setOnItemClickListener { parent, view, position, id -> + binding.listProgressSpinner = true + if (accounts[position].name == DEFAULT_ACCOUNT) { + openAddAccount() + } else { + lifecycleScope.launchWhenStarted { + signIn(accounts[position]) + } + } + } + setContentView(binding.root) + } + + private fun showSignInConfirm(packageName: String, account: Account) { + val binding = SigninConfirmBinding.inflate(layoutInflater) + binding.appName = packageManager.getApplicationLabel(packageName).toString() + binding.appIcon = packageManager.getApplicationIcon(packageName) + bindAccountRow(binding.root, account) { view, bitmap -> view.setImageBitmap(bitmap) } + binding.button2.setOnClickListener { + finishResult(CommonStatusCodes.CANCELED) + } + binding.button1.setOnClickListener { + binding.button1.isEnabled = false + binding.button2.isEnabled = false + lifecycleScope.launchWhenStarted { + signIn(account) + } + } + setContentView(binding.root) + } + + private suspend fun signIn(account: Account) { + val googleSignInAccount = performSignIn(this, config?.packageName!!, config?.options, account, true) + if (googleSignInAccount != null) { + finishResult(CommonStatusCodes.SUCCESS, googleSignInAccount) + } else { + finishResult(CommonStatusCodes.CANCELED) + } + } + + private fun finishResult(statusCode: Int? = null, account: GoogleSignInAccount? = null) { + val data = Intent() + data.putExtra("googleSignInStatus", statusCode?.let { Status(it) }) + data.putExtra("googleSignInAccount", account) + if (account != null) { + data.putExtra("signInAccount", SignInAccount().apply { + email = account.email + googleSignInAccount = account + userId = account.id + }) + } + setResult(RESULT_OK, data) + val extras = data.extras + extras?.keySet() + finish() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + REQUEST_CODE_SIGN_IN -> { + val accountManager = getSystemService() ?: return finish() + val accounts = accountManager.getAccountsByType(DEFAULT_ACCOUNT_TYPE) + if (accounts.isNotEmpty()) { + openAccountPicker(config?.packageName!!) + } else { + finishResult(CommonStatusCodes.CANCELED) + } + } + + REQUEST_CODE_PICK_ACCOUNT -> { + val accountName = data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME) + if (resultCode == RESULT_OK && accountName != null) { + val account = Account(accountName, DEFAULT_ACCOUNT_TYPE) + lifecycleScope.launchWhenStarted { + signIn(account) + } + } else { + finishResult(CommonStatusCodes.CANCELED) + } + } + } + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInService.kt index a11badabf..6d3fd86b5 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/AuthSignInService.kt @@ -15,9 +15,16 @@ */ package org.microg.gms.auth.signin +import android.accounts.Account +import android.content.Context import android.os.Bundle import android.os.Parcel import android.util.Log +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.android.volley.toolbox.JsonObjectRequest +import com.android.volley.toolbox.Volley import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.auth.api.signin.internal.ISignInCallbacks import com.google.android.gms.auth.api.signin.internal.ISignInService @@ -30,6 +37,9 @@ import org.microg.gms.BaseService import org.microg.gms.common.GmsService import org.microg.gms.common.PackageUtils import org.microg.gms.utils.warnOnTransactionIssues +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine private const val TAG = "AuthSignInService" @@ -37,26 +47,90 @@ class AuthSignInService : BaseService(TAG, GmsService.AUTH_SIGN_IN) { override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { val packageName = PackageUtils.getAndCheckCallingPackage(this, request.packageName) ?: throw IllegalArgumentException("Missing package name") - val binder = SignInServiceImpl(packageName, request.scopes.asList(), request.extras).asBinder() + val binder = AuthSignInServiceImpl(this, lifecycle, packageName, request.account, request.scopes.asList(), request.extras).asBinder() callback.onPostInitComplete(CommonStatusCodes.SUCCESS, binder, Bundle()) } } -class SignInServiceImpl(private val packageName: String, private val scopes: List, private val extras: Bundle) : ISignInService.Stub() { - override fun silentSignIn(callbacks: ISignInCallbacks?, options: GoogleSignInOptions?) { - Log.d(TAG, "Not yet implemented: signIn: $options") - callbacks?.onSignIn(null, Status.INTERNAL_ERROR) +class AuthSignInServiceImpl( + private val context: Context, + private val lifecycle: Lifecycle, + private val packageName: String, + private val account: Account?, + private val scopes: List, + private val extras: Bundle +) : ISignInService.Stub(), LifecycleOwner { + private val queue = Volley.newRequestQueue(context) + override fun getLifecycle(): Lifecycle = lifecycle + + override fun silentSignIn(callbacks: ISignInCallbacks, options: GoogleSignInOptions?) { + lifecycleScope.launchWhenStarted { + try { + val account = account ?: options?.account ?: SignInDefaultService.getDefaultAccount(context, packageName) + if (account != null && getOAuthManager(context, packageName, options, account).isPermitted && options?.isForceCodeForRefreshToken != true) { + val googleSignInAccount = performSignIn(context, packageName, options, account) + if (googleSignInAccount != null) { + runCatching { callbacks.onSignIn(googleSignInAccount, Status(CommonStatusCodes.SUCCESS)) } + } else { + runCatching { callbacks.onSignIn(null, Status(CommonStatusCodes.DEVELOPER_ERROR)) } + } + } else { + runCatching { callbacks.onSignIn(null, Status(CommonStatusCodes.SIGN_IN_REQUIRED)) } + } + } catch (e: Exception) { + Log.w(TAG, e) + runCatching { callbacks.onSignIn(null, Status.INTERNAL_ERROR) } + } + } } - override fun signOut(callbacks: ISignInCallbacks?, options: GoogleSignInOptions?) { - Log.d(TAG, "Not yet implemented: signOut: $options") - callbacks?.onSignOut(Status.INTERNAL_ERROR) + override fun signOut(callbacks: ISignInCallbacks, options: GoogleSignInOptions?) { + lifecycleScope.launchWhenStarted { + try { + SignInDefaultService.setDefaultAccount(context, packageName, null) + runCatching { callbacks.onSignOut(Status.SUCCESS) } + } catch (e: Exception) { + Log.w(TAG, e) + runCatching { callbacks.onSignIn(null, Status.INTERNAL_ERROR) } + } + } } - override fun revokeAccess(callbacks: ISignInCallbacks?, options: GoogleSignInOptions?) { - Log.d(TAG, "Not yet implemented: revokeAccess: $options") - callbacks?.onRevokeAccess(Status.INTERNAL_ERROR) + override fun revokeAccess(callbacks: ISignInCallbacks, options: GoogleSignInOptions?) { + lifecycleScope.launchWhenStarted { + val account = account ?: options?.account ?: SignInDefaultService.getDefaultAccount(context, packageName) + if (account != null) { + try { + val authManager = getOAuthManager(context, packageName, options, account) + val token = authManager.peekAuthToken() + if (token != null) { + suspendCoroutine { continuation -> + queue.add(object : JsonObjectRequest( + "https://accounts.google.com/o/oauth2/revoke?token=$token", + { continuation.resume(it) }, + { continuation.resumeWithException(it) }) { + override fun getHeaders(): MutableMap { + return hashMapOf( + "Authorization" to "OAuth $token" + ) + } + }) + } + authManager.invalidateAuthToken(token) + authManager.isPermitted = false + } + SignInDefaultService.setDefaultAccount(context, packageName, account) + runCatching { callbacks.onRevokeAccess(Status.SUCCESS) } + } catch (e: Exception) { + Log.w(TAG, e) + runCatching { callbacks.onRevokeAccess(Status.INTERNAL_ERROR) } + } + } else { + runCatching { callbacks.onRevokeAccess(Status.SUCCESS) } + } + } } - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = + warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } } \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/SignInDefaultService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/SignInDefaultService.kt new file mode 100644 index 000000000..f39009d97 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/SignInDefaultService.kt @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.auth.signin + +import android.accounts.Account +import android.accounts.AccountManager +import android.app.Service +import android.content.* +import android.content.Context.BIND_ABOVE_CLIENT +import android.content.Context.BIND_AUTO_CREATE +import android.os.* +import androidx.core.content.getSystemService +import androidx.core.os.bundleOf +import org.microg.gms.auth.AuthConstants +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +private const val PREFERENCES_NAME = "google_account_cache" +private const val DEFAULT_ACCOUNT_PREFIX = "default_google_account_" + +private const val MSG_GET_DEFAULT_ACCOUNT = 1 +private const val MSG_SET_DEFAULT_ACCOUNT = 2 + +private const val MSG_DATA_PACKAGE_NAME = "package_name" +private const val MSG_DATA_ACCOUNT = "account" + +class SignInDefaultService : Service() { + private val preferences: SharedPreferences + get() = getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + private val accountManager: AccountManager + get() = getSystemService()!! + + override fun onBind(intent: Intent?): IBinder { + return Messenger(object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + val data = when (msg.what) { + MSG_GET_DEFAULT_ACCOUNT -> { + val packageName = msg.data?.getString(MSG_DATA_PACKAGE_NAME) + val account = packageName?.let { getDefaultAccount(it) } + bundleOf( + MSG_DATA_PACKAGE_NAME to packageName, + MSG_DATA_ACCOUNT to account + ) + } + + MSG_SET_DEFAULT_ACCOUNT -> { + val packageName = msg.data?.getString(MSG_DATA_PACKAGE_NAME) + val account = msg.data?.getParcelable(MSG_DATA_ACCOUNT) + packageName?.let { setDefaultAccount(it, account) } + bundleOf( + MSG_DATA_PACKAGE_NAME to packageName, + MSG_DATA_ACCOUNT to account + ) + } + + else -> Bundle.EMPTY + } + msg.replyTo?.send(Message.obtain().also { + it.what = msg.what + it.data = data + }) + } + }).binder + } + + private fun getDefaultAccount(packageName: String): Account? { + val name = preferences.getString(DEFAULT_ACCOUNT_PREFIX + packageName, null) + if (name.isNullOrBlank()) return null + val accounts: Array = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE) + for (account in accounts) { + if (account.name.equals(name)) return account + } + return null + } + + private fun setDefaultAccount(packageName: String, account: Account?) { + val editor: SharedPreferences.Editor = preferences.edit() + if (account == null || account.name == AuthConstants.DEFAULT_ACCOUNT) { + editor.remove(DEFAULT_ACCOUNT_PREFIX + packageName) + } else { + editor.putString(DEFAULT_ACCOUNT_PREFIX + packageName, account.name) + } + editor.apply() + } + + companion object { + + private suspend fun singleRequest(context: Context, message: Message) = suspendCoroutine { continuation -> + val connection = object : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + val connection = this + message.replyTo = Messenger(object : Handler(Looper.myLooper() ?: Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + runCatching { continuation.resume(msg) } + runCatching { context.unbindService(connection) } + } + }) + try { + Messenger(service).send(message) + } catch (e: Exception) { + runCatching { continuation.resumeWithException(e) } + runCatching { context.unbindService(connection) } + } + } + + override fun onServiceDisconnected(name: ComponentName?) { + runCatching { continuation.resumeWithException(RuntimeException("Disconnected")) } + } + } + val connected = context.bindService(Intent(context, SignInDefaultService::class.java), connection, BIND_AUTO_CREATE or BIND_ABOVE_CLIENT) + if (!connected) { + runCatching { continuation.resumeWithException(RuntimeException("Connection failed")) } + runCatching { context.unbindService(connection) } + } + } + + suspend fun getDefaultAccount(context: Context, packageName: String): Account? { + return singleRequest(context, Message.obtain().apply { + what = MSG_GET_DEFAULT_ACCOUNT + data = bundleOf( + MSG_DATA_PACKAGE_NAME to packageName + ) + }).data?.getParcelable(MSG_DATA_ACCOUNT) + } + + suspend fun setDefaultAccount(context: Context, packageName: String, account: Account?) { + singleRequest(context, Message.obtain().apply { + what = MSG_SET_DEFAULT_ACCOUNT + data = bundleOf( + MSG_DATA_PACKAGE_NAME to packageName, + MSG_DATA_ACCOUNT to account + ) + }) + } + + } +} \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/extensions.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/extensions.kt new file mode 100644 index 000000000..b3eee9066 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/signin/extensions.kt @@ -0,0 +1,97 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.auth.signin + +import android.accounts.Account +import android.content.Context +import android.net.Uri +import android.util.Log +import com.google.android.gms.auth.api.signin.GoogleSignInAccount +import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import com.google.android.gms.common.Scopes +import com.google.android.gms.common.api.Scope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.microg.gms.auth.AuthManager +import org.microg.gms.people.DatabaseHelper +import org.microg.gms.utils.toHexString +import java.security.MessageDigest +import kotlin.math.min + +private fun Long?.orMaxIfNegative() = this?.takeIf { it >= 0L } ?: Long.MAX_VALUE + +fun getOAuthManager(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account): AuthManager { + val scopes = options?.scopes.orEmpty() + return AuthManager(context, account.name, packageName, "oauth2:${scopes.joinToString(" ")}") +} + +suspend fun performSignIn(context: Context, packageName: String, options: GoogleSignInOptions?, account: Account, permitted: Boolean = false): GoogleSignInAccount? { + val authManager = getOAuthManager(context, packageName, options, account) + val authResponse = withContext(Dispatchers.IO) { + if (permitted) authManager.isPermitted = true + authManager.requestAuth(true) + } + if (authResponse.auth == null) return null + + val scopes = options?.scopes.orEmpty() + val includeId = scopes.any { it.scopeUri == Scopes.OPENID } + val includeEmail = scopes.any { it.scopeUri == Scopes.EMAIL } + val includeProfile = scopes.any { it.scopeUri == Scopes.PROFILE } + Log.d("AuthSignIn", "id token requested: ${options?.isIdTokenRequested == true}, serverClientId = ${options?.serverClientId}") + val idTokenResponse = if (options?.isIdTokenRequested == true && options.serverClientId != null) withContext(Dispatchers.IO) { + val idTokenManager = AuthManager(context, account.name, packageName, "audience:server:client_id:${options.serverClientId}") + idTokenManager.includeEmail = if (includeEmail) "1" else "0" + idTokenManager.includeProfile = if (includeProfile) "1" else "0" + idTokenManager.isPermitted = authManager.isPermitted + idTokenManager.requestAuth(true) + } else null + val serverAuthTokenResponse = if (options?.isServerAuthCodeRequested == true && options.serverClientId != null) withContext(Dispatchers.IO) { + val serverAuthTokenManager = AuthManager(context, account.name, packageName, "oauth2:server:client_id:${options.serverClientId}:api_scope:${scopes.joinToString(" ")}") + serverAuthTokenManager.includeEmail = if (includeEmail) "1" else "0" + serverAuthTokenManager.includeProfile = if (includeProfile) "1" else "0" + serverAuthTokenManager.setOauth2Prompt(if (options.isForceCodeForRefreshToken) "consent" else "auto") + serverAuthTokenManager.setItCaveatTypes("2") + serverAuthTokenManager.isPermitted = authManager.isPermitted + serverAuthTokenManager.requestAuth(true) + } else null + val googleUserId = authManager.getUserData("GoogleUserId") + val id = if (includeId) googleUserId else null + val tokenId = if (options?.isIdTokenRequested == true) idTokenResponse?.auth else null + val email = if (includeEmail) account.name else null + val serverAuthCode: String? = if (options?.isServerAuthCodeRequested == true) serverAuthTokenResponse?.auth else null + val expirationTime = min(authResponse.expiry.orMaxIfNegative(), idTokenResponse?.expiry.orMaxIfNegative()) + val obfuscatedIdentifier: String = MessageDigest.getInstance("MD5").digest("$googleUserId:$packageName".encodeToByteArray()).toHexString().uppercase() + val grantedScopes = authResponse.grantedScopes?.split(" ").orEmpty().map { Scope(it) }.toSet() + val (givenName, familyName, displayName, photoUrl) = if (includeProfile) { + val cursor = DatabaseHelper(context).getOwner(account.name) + try { + if (cursor.moveToNext()) { + listOf( + cursor.getColumnIndex("given_name").takeIf { it >= 0 }?.let { cursor.getString(it) }, + cursor.getColumnIndex("family_name").takeIf { it >= 0 }?.let { cursor.getString(it) }, + cursor.getColumnIndex("display_name").takeIf { it >= 0 }?.let { cursor.getString(it) }, + cursor.getColumnIndex("avatar").takeIf { it >= 0 }?.let { cursor.getString(it) }, + ) + } else listOf(null, null, null, null) + } finally { + cursor.close() + } + } else listOf(null, null, null, null) + SignInDefaultService.setDefaultAccount(context, packageName, account) + return GoogleSignInAccount( + id, + tokenId, + email, + displayName, + photoUrl?.let { Uri.parse(it) }, + serverAuthCode, + expirationTime, + obfuscatedIdentifier, + grantedScopes, + givenName, + familyName + ) +} \ No newline at end of file diff --git a/play-services-core/src/main/res/drawable/ic_add_account_alt.xml b/play-services-core/src/main/res/drawable/ic_add_account_alt.xml new file mode 100644 index 000000000..e9eef3944 --- /dev/null +++ b/play-services-core/src/main/res/drawable/ic_add_account_alt.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/play-services-core/src/main/res/layout/signin_account_row.xml b/play-services-core/src/main/res/layout/signin_account_row.xml new file mode 100644 index 000000000..c5e563e42 --- /dev/null +++ b/play-services-core/src/main/res/layout/signin_account_row.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/res/layout/signin_confirm.xml b/play-services-core/src/main/res/layout/signin_confirm.xml new file mode 100644 index 000000000..9e9003a60 --- /dev/null +++ b/play-services-core/src/main/res/layout/signin_confirm.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +