From 7c2f0dfde59c33331285b2111f1e05162b237f68 Mon Sep 17 00:00:00 2001 From: davinci9196 Date: Mon, 29 Jan 2024 09:53:27 +0800 Subject: [PATCH 01/33] Fix the bug in the signature spoofing module. Binder.getCallingUid() should be used for permission judgment. --- .../huawei/java/com/huawei/signature/diff/SignatureService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fake-signature/src/huawei/java/com/huawei/signature/diff/SignatureService.java b/fake-signature/src/huawei/java/com/huawei/signature/diff/SignatureService.java index 43ca6723e3..ca6d6549e2 100644 --- a/fake-signature/src/huawei/java/com/huawei/signature/diff/SignatureService.java +++ b/fake-signature/src/huawei/java/com/huawei/signature/diff/SignatureService.java @@ -64,7 +64,7 @@ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { - if (Binder.getCallingPid() > 10000) { + if (Binder.getCallingUid() > 10000) { Log.w(TAG, "Illegal access from app"); reply.writeException(new UnsupportedOperationException("Illegal")); return true; From b62f08911190dc2886d07ad838d51b7f5a536184 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 6 Dec 2023 20:14:26 +0530 Subject: [PATCH 02/33] play-services-core: Theme improvements * Apply background color on status and navigation bar from API 23 * Drop ActionBar theme and switch to Toolbar which works better with theming Signed-off-by: Aayush Gupta Change-Id: I82a0b00cf4d50e2ddf6739c730814f4ab3ab91c2 --- .../org/microg/tools/ui/AbstractSettingsActivity.java | 3 +++ .../src/main/res/layout/settings_activity.xml | 7 +++++++ play-services-core/src/main/AndroidManifest.xml | 2 +- .../java/org/microg/gms/ui/MainSettingsActivity.java | 2 ++ .../src/main/res/layout/settings_root_activity.xml | 7 +++++++ .../src/main/res/values-night-v23/themes.xml | 10 ++++++++++ .../src/main/res/values-night-v27/themes.xml | 11 +++++++++++ play-services-core/src/main/res/values-v23/themes.xml | 10 ++++++++++ play-services-core/src/main/res/values-v27/themes.xml | 11 +++++++++++ play-services-core/src/main/res/values/themes.xml | 6 ++++++ 10 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 play-services-core/src/main/res/values-night-v23/themes.xml create mode 100644 play-services-core/src/main/res/values-night-v27/themes.xml create mode 100644 play-services-core/src/main/res/values-v23/themes.xml create mode 100644 play-services-core/src/main/res/values-v27/themes.xml diff --git a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java index a771a9ea36..5450a7e215 100644 --- a/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java +++ b/play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/AbstractSettingsActivity.java @@ -5,6 +5,7 @@ import android.view.ViewGroup; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; @@ -19,6 +20,8 @@ public abstract class AbstractSettingsActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.settings_activity); + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + if (showHomeAsUp) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml b/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml index a664de26af..acc78dc5e6 100644 --- a/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml +++ b/play-services-core/microg-ui-tools/src/main/res/layout/settings_activity.xml @@ -19,6 +19,13 @@ android:layout_height="match_parent" android:orientation="vertical"> + + + android:theme="@style/Theme.App"> diff --git a/play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java index af930b5f25..e9095e1165 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/MainSettingsActivity.java @@ -5,6 +5,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.AppBarConfiguration; @@ -32,6 +33,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } setContentView(R.layout.settings_root_activity); + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); for (SettingsProvider settingsProvider : getAllSettingsProviders(this)) { settingsProvider.extendNavigation(getNavController()); diff --git a/play-services-core/src/main/res/layout/settings_root_activity.xml b/play-services-core/src/main/res/layout/settings_root_activity.xml index a592e38d2f..5e1eaf3ed6 100644 --- a/play-services-core/src/main/res/layout/settings_root_activity.xml +++ b/play-services-core/src/main/res/layout/settings_root_activity.xml @@ -9,6 +9,13 @@ android:layout_height="match_parent" android:orientation="vertical"> + + + + + + + diff --git a/play-services-core/src/main/res/values-night-v27/themes.xml b/play-services-core/src/main/res/values-night-v27/themes.xml new file mode 100644 index 0000000000..31acb06cc9 --- /dev/null +++ b/play-services-core/src/main/res/values-night-v27/themes.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/play-services-core/src/main/res/values-v23/themes.xml b/play-services-core/src/main/res/values-v23/themes.xml new file mode 100644 index 0000000000..9c4f22b677 --- /dev/null +++ b/play-services-core/src/main/res/values-v23/themes.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/play-services-core/src/main/res/values-v27/themes.xml b/play-services-core/src/main/res/values-v27/themes.xml new file mode 100644 index 0000000000..bcc7971d06 --- /dev/null +++ b/play-services-core/src/main/res/values-v27/themes.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/play-services-core/src/main/res/values/themes.xml b/play-services-core/src/main/res/values/themes.xml index d767b2fefb..c2c8d4f978 100644 --- a/play-services-core/src/main/res/values/themes.xml +++ b/play-services-core/src/main/res/values/themes.xml @@ -16,6 +16,12 @@ + + + + diff --git a/play-services-core/src/main/res/values-v31/themes.xml b/play-services-core/src/main/res/values-v31/themes.xml new file mode 100644 index 0000000000..571910dc88 --- /dev/null +++ b/play-services-core/src/main/res/values-v31/themes.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/play-services-core/src/main/res/values/themes.xml b/play-services-core/src/main/res/values/themes.xml index c2c8d4f978..7da8c6e458 100644 --- a/play-services-core/src/main/res/values/themes.xml +++ b/play-services-core/src/main/res/values/themes.xml @@ -19,7 +19,11 @@ + + + + + + + From c28909aa2b35196918ac5c62bc714a77f7042ead Mon Sep 17 00:00:00 2001 From: inson1 <75314629+inson1@users.noreply.github.com> Date: Thu, 8 Feb 2024 02:32:26 +0100 Subject: [PATCH 04/33] Update Copyright --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80f880ce68..606564e1d5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ microG Services is a FLOSS (Free/Libre Open Source Software) framework to allow License ------- - Copyright 2013-2023 microG Project Team + Copyright 2013-2024 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. From e098483d655e01629da824b1ef906588bce4b3df Mon Sep 17 00:00:00 2001 From: DaVinci9196 <150454414+DaVinci9196@users.noreply.github.com> Date: Sat, 17 Feb 2024 04:09:08 +0800 Subject: [PATCH 05/33] Add dummy service for AUDIT(154) (#2156) Co-authored-by: Marvin W --- play-services-api/build.gradle | 2 + .../gms/audit/LogAuditRecordsRequest.aidl | 8 +++ .../gms/audit/internal/IAuditService.aidl | 13 +++++ .../gms/audit/LogAuditRecordsRequest.java | 53 +++++++++++++++++++ .../google/android/gms/common/api/Status.java | 2 + .../src/main/AndroidManifest.xml | 7 ++- .../gms/audit/internal/AuditApiService.kt | 44 +++++++++++++++ 7 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/audit/LogAuditRecordsRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/audit/internal/IAuditService.aidl create mode 100644 play-services-api/src/main/java/com/google/android/gms/audit/LogAuditRecordsRequest.java create mode 100644 play-services-core/src/main/kotlin/com/google/android/gms/audit/internal/AuditApiService.kt diff --git a/play-services-api/build.gradle b/play-services-api/build.gradle index 08fa064398..11c3911317 100644 --- a/play-services-api/build.gradle +++ b/play-services-api/build.gradle @@ -41,4 +41,6 @@ android { dependencies { api project(':play-services-base') api project(':play-services-phenotype') + + annotationProcessor project(':safe-parcel-processor') } diff --git a/play-services-api/src/main/aidl/com/google/android/gms/audit/LogAuditRecordsRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/audit/LogAuditRecordsRequest.aidl new file mode 100644 index 0000000000..e2a0155ac0 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/audit/LogAuditRecordsRequest.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.audit; + +parcelable LogAuditRecordsRequest; \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/audit/internal/IAuditService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/audit/internal/IAuditService.aidl new file mode 100644 index 0000000000..7e26825e5a --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/audit/internal/IAuditService.aidl @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.audit.internal; + +import com.google.android.gms.common.api.internal.IStatusCallback; +import com.google.android.gms.audit.LogAuditRecordsRequest; + +interface IAuditService { + void logAuditRecords(in LogAuditRecordsRequest request, IStatusCallback callback); +} \ No newline at end of file diff --git a/play-services-api/src/main/java/com/google/android/gms/audit/LogAuditRecordsRequest.java b/play-services-api/src/main/java/com/google/android/gms/audit/LogAuditRecordsRequest.java new file mode 100644 index 0000000000..8042154c6a --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/audit/LogAuditRecordsRequest.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.audit; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +import org.microg.gms.utils.ToStringHelper; + +@SafeParcelable.Class +public class LogAuditRecordsRequest extends AbstractSafeParcelable { + @Field(1) + public int writeMode; + @Field(2) + public int componentId; + @Field(3) + public String accountName; + @Field(4) + public byte[][] auditRecords; + @Field(5) + public byte[] traceToken; + @Field(6) + public byte[] auditToken; + + @NonNull + @Override + public String toString() { + return ToStringHelper.name("LogAuditRecordsRequest") + .field("writeMode", writeMode) + .field("componentId", componentId) + .field("accountName", accountName) + .field("auditRecords", auditRecords) + .field("traceToken", traceToken) + .field("auditToken", auditToken) + .end(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(LogAuditRecordsRequest.class); + +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java index af1c543ca2..b49098d9a5 100644 --- a/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java @@ -40,6 +40,8 @@ public final class Status extends AbstractSafeParcelable implements Result { @PublicApi(exclude = true) public static final Status CANCELED = new Status(CommonStatusCodes.CANCELED, "Cancelled"); @PublicApi(exclude = true) + public static final Status SUCCESS_CACHE = new Status(CommonStatusCodes.SUCCESS_CACHE, "Success"); + @PublicApi(exclude = true) public static final Status SUCCESS = new Status(CommonStatusCodes.SUCCESS, "Success"); @Field(1000) diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 0ec7172b3c..53cf29cb55 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -835,6 +835,12 @@ + + + + + + @@ -847,7 +853,6 @@ - diff --git a/play-services-core/src/main/kotlin/com/google/android/gms/audit/internal/AuditApiService.kt b/play-services-core/src/main/kotlin/com/google/android/gms/audit/internal/AuditApiService.kt new file mode 100644 index 0000000000..450c7e2277 --- /dev/null +++ b/play-services-core/src/main/kotlin/com/google/android/gms/audit/internal/AuditApiService.kt @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.audit.internal + +import android.util.Log +import com.google.android.gms.audit.LogAuditRecordsRequest +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.api.Status +import com.google.android.gms.common.api.internal.IStatusCallback +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 + +private const val TAG = "AuditApiService" + +class AuditApiService : BaseService(TAG, GmsService.AUDIT) { + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + callback.onPostInitComplete(ConnectionResult.SUCCESS, AuditApiServiceImpl().asBinder(), null) + } + +} + +class AuditApiServiceImpl : IAuditService.Stub() { + + override fun logAuditRecords(request: LogAuditRecordsRequest?, callback: IStatusCallback) { + Log.d(TAG, "method 'logAuditRecords' not fully implemented, only return Status.SUCCESS") + when (request?.writeMode) { + 1 -> { + callback.onResult(Status.SUCCESS) + } + 2 -> { + callback.onResult(Status.SUCCESS_CACHE) + } + else -> { + callback.onResult(Status.SUCCESS) + } + } + } + +} \ No newline at end of file From 05265c60cd07911ffba485f2da87986d69823661 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 26 Sep 2023 20:00:27 +0200 Subject: [PATCH 06/33] Licensing service --- vending-app/build.gradle | 24 +- vending-app/src/main/AndroidManifest.xml | 2 + .../main/java/com/android/vending/Util.java | 28 +++ .../vending/licensing/LicenseRequest.java | 215 ++++++++++++++++++ .../vending/licensing/LicensingService.java | 153 ++++++++++++- .../src/main/proto/LicenseRequest.proto | 71 ++++++ .../src/main/proto/LicenseResult.proto | 28 +++ vending-app/src/main/proto/Locality.proto | 21 ++ vending-app/src/main/proto/Timestamp.proto | 33 +++ 9 files changed, 569 insertions(+), 6 deletions(-) create mode 100644 vending-app/src/main/java/com/android/vending/Util.java create mode 100644 vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java create mode 100644 vending-app/src/main/proto/LicenseRequest.proto create mode 100644 vending-app/src/main/proto/LicenseResult.proto create mode 100644 vending-app/src/main/proto/Locality.proto create mode 100644 vending-app/src/main/proto/Timestamp.proto diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 38376b97cb..2ed0ca836a 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -4,6 +4,8 @@ */ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'com.squareup.wire' android { namespace "com.android.vending" @@ -34,6 +36,15 @@ android { } } + sourceSets { + main { + java { + srcDirs += "build/generated/source/proto/main/java" + } + } + } + + buildFeatures { aidl = true } @@ -43,13 +54,22 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } } dependencies { implementation project(':fake-signature') + + implementation "com.squareup.wire:wire-runtime:$wireVersion" + implementation "com.android.volley:volley:$volleyVersion" +} + +wire { + kotlin { + javaInterop = true + } } if (file('user.gradle').exists()) { diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index 6b255e3b4f..8f11d4d5c0 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ + + diff --git a/vending-app/src/main/java/com/android/vending/Util.java b/vending-app/src/main/java/com/android/vending/Util.java new file mode 100644 index 0000000000..0585a28e38 --- /dev/null +++ b/vending-app/src/main/java/com/android/vending/Util.java @@ -0,0 +1,28 @@ +package com.android.vending; + +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPOutputStream; + +public class Util { + + private static final String TAG = "FakeStoreUtil"; + + /** + * From StackOverflow, CC BY-SA 4.0 by Sergey Frolov, adapted. + */ + public static byte[] encodeGzip(final byte[] input) { + + try (final ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + final GZIPOutputStream gzipOutput = new GZIPOutputStream(byteOutput)) { + gzipOutput.write(input); + gzipOutput.finish(); + return byteOutput.toByteArray(); + } catch (IOException e) { + Log.e(TAG, "Failed to encode bytes as GZIP"); + return new byte[0]; + } + } +} diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java new file mode 100644 index 0000000000..60742a1d7d --- /dev/null +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java @@ -0,0 +1,215 @@ +package com.android.vending.licensing; + +import static com.android.volley.Request.Method.GET; + +import android.util.Base64; +import android.util.Log; + +import com.android.vending.AndroidVersionMeta; +import com.android.vending.DeviceMeta; +import com.android.vending.EncodedTriple; +import com.android.vending.EncodedTripleWrapper; +import com.android.vending.IntWrapper; +import com.android.vending.LicenseRequestHeader; +import com.android.vending.LicenseResult; +import com.android.vending.Locality; +import com.android.vending.LocalityWrapper; +import com.android.vending.StringWrapper; +import com.android.vending.Timestamp; +import com.android.vending.TimestampContainer; +import com.android.vending.TimestampContainer1; +import com.android.vending.TimestampContainer1Wrapper; +import com.android.vending.TimestampContainer2; +import com.android.vending.TimestampStringWrapper; +import com.android.vending.TimestampWrapper; +import com.android.vending.UnknownByte12; +import com.android.vending.UserAgent; +import com.android.vending.Util; +import com.android.vending.Uuid; +import com.android.vending.V1Container; +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.VolleyError; + +import java.io.IOException; +import java.util.Map; +import java.util.UUID; + +import okio.ByteString; + +public abstract class LicenseRequest extends Request { + + private final String xPsRh; + private static final String TAG = "FakeLicenseRequest"; + + private static final int BASE64_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING; + private static final long ANDROID_ID = 1; + + private final Response.Listener successListener; + + + protected LicenseRequest(String url, Response.Listener successListener, Response.ErrorListener errorListener) { + super(GET, url, errorListener); + + this.successListener = successListener; + + long millis = System.currentTimeMillis(); + TimestampContainer.Builder timestamp = new TimestampContainer.Builder() + .container2(new TimestampContainer2.Builder() + .wrapper(new TimestampWrapper.Builder().timestamp(makeTimestamp(millis)).build()) + .timestamp(makeTimestamp(millis)) + .build()); + millis = System.currentTimeMillis(); + timestamp + .container1Wrapper(new TimestampContainer1Wrapper.Builder() + .androidId(String.valueOf(ANDROID_ID)) + .container(new TimestampContainer1.Builder() + .timestamp(millis + "000") + .wrapper(makeTimestamp(millis)) + .build()) + .build() + ); + String encodedTimestamps = new String( + Base64.encode(Util.encodeGzip(timestamp.build().encode()), BASE64_FLAGS) + ); + + Locality locality = new Locality.Builder() + .unknown1(1) + .unknown2(2) + .countryCode("") + .region(new TimestampStringWrapper.Builder() + .string("").timestamp(makeTimestamp(System.currentTimeMillis())).build()) + .country(new TimestampStringWrapper.Builder() + .string("").timestamp(makeTimestamp(System.currentTimeMillis())).build()) + .unknown3(0) + .build(); + String encodedLocality = new String( + Base64.encode(locality.encode(), BASE64_FLAGS) + ); + + byte[] header = new LicenseRequestHeader.Builder() + .encodedTimestamps(new StringWrapper.Builder().string(encodedTimestamps).build()) + .triple( + new EncodedTripleWrapper.Builder().triple( + new EncodedTriple.Builder() + .encoded1("") + .encoded2("") + .empty("") + .build() + ).build() + ) + .locality(new LocalityWrapper.Builder().encodedLocalityProto(encodedLocality).build()) + .unknown(new IntWrapper.Builder().integer(5).build()) + .empty("") + .deviceMeta(new DeviceMeta.Builder() + .android( + new AndroidVersionMeta.Builder() + .androidSdk(0) + .buildNumber("") + .androidVersion("") + .unknown(0) + .build() + ) + .unknown1(new UnknownByte12.Builder().bytes(new ByteString(new byte[]{} + )).build()) + .unknown2(1) + .build() + ) + .userAgent(new UserAgent.Builder() + .deviceProductName("") + .deviceSoc("") + .deviceModelName("") + .finskyVersion("") + .deviceName("") + .androidId(ANDROID_ID) // must not be 0 + .deviceSignature("") + .build() + ) + .uuid(new Uuid.Builder() + .uuid(UUID.randomUUID().toString()) + .unknown(2) + .build() + ) + .build().encode(); + this.xPsRh = new String(Base64.encode(Util.encodeGzip(header), BASE64_FLAGS)); + + //Log.d(TAG, "Product " + Build.PRODUCT + ", Board " + Build.BOARD + " Model " + Build.MODEL + " Device " + Build.DEVICE); + + Log.v(TAG, "X-PS-RH: " + xPsRh); + } + + @Override + public Map getHeaders() { + return Map.of( + "X-PS-RH", xPsRh, + "Authorization", "Bearer ya29.[…]]", + "Connection", "Keep-Alive" + ); + } + + @Override + protected void deliverResponse(T response) { + successListener.onResponse(response); + } + + private static Timestamp makeTimestamp(long millis) { + return new Timestamp.Builder() + .seconds((int) (millis / 1000)) + .nanos(Math.floorMod(millis, 1000) * 1000000) + .build(); + } + + public static class V1 extends LicenseRequest { + + public V1(String packageName, int versionCode, long nonce, Response.Listener successListener, Response.ErrorListener errorListener) { + super("https://play-fe.googleapis.com/fdfe/apps/checkLicense?pkgn=" + packageName + "&vc=" + versionCode + "&nnc=" + nonce, + successListener, errorListener + ); + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + if (response != null && response.data != null) { + try { + LicenseResult result = LicenseResult.ADAPTER.decode(response.data); + return Response.success(result.information.v1, null); + } catch (IOException e) { + return Response.error(new VolleyError(e)); + } catch (NullPointerException e) { + // A field does not exist → user has no license + return Response.success(null, null); + } + } else { + return Response.error(new VolleyError("No response was returned")); + } + } + } + + public static class V2 extends LicenseRequest { + public V2(String packageName, int versionCode, Response.Listener successListener, + Response.ErrorListener errorListener) { + super( + "https://play-fe.googleapis.com/fdfe/apps/checkLicenseServerFallback?pkgn=" + packageName + "&vc=" + versionCode, + successListener, errorListener + ); + } + + @Override + protected Response parseNetworkResponse(NetworkResponse response) { + if (response != null && response.data != null) { + try { + LicenseResult result = LicenseResult.ADAPTER.decode(response.data); + return Response.success(result.information.v2.license.jwt, null); + } catch (IOException e) { + return Response.error(new VolleyError(e)); + } catch (NullPointerException e) { + // A field does not exist → user has no license + return Response.success(null, null); + } + } else { + return Response.error(new VolleyError("No response was returned")); + } + } + } +} diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index b479aab644..ba351bf8c4 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -7,30 +7,175 @@ import android.app.Service; import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.toolbox.Volley; + public class LicensingService extends Service { private static final String TAG = "FakeLicenseService"; + private RequestQueue queue; + + private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { + + + + /* Possible response codes for checkLicense v1, from + * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and + * the LVL library. + */ + + /** + * The application is licensed to the user. The user has purchased the application, or is authorized to + * download and install the alpha or beta version of the application. + */ + private static final int LICENSED = 0x0; + /** + * The application is licensed to the user, but there is an updated application version available that is + * signed with a different key. + */ + private static final int NOT_LICENSED = 0x1; + /** + * The application is not licensed to the user. + */ + private static final int LICENSED_OLD_KEY = 0x2; + /** + * Server error — the application (package name) was not recognized by Google Play. + */ + private static final int ERROR_NOT_MARKET_MANAGED = 0x3; + /** + * Server error — the server could not load the application's key pair for licensing. + */ + private static final int ERROR_SERVER_FAILURE = 0x4; + private static final int ERROR_OVER_QUOTA = 0x5; + + /** + * Local error — the Google Play application was not able to reach the licensing server, possibly because + * of network availability problems. + */ + private static final int ERROR_CONTACTING_SERVER = 0x101; + /** + * Local error — the application requested a license check for a package that is not installed on the device. + */ + private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; + /** + * Local error — the application requested a license check for a package whose UID (package, user ID pair) + * does not match that of the requesting application. + */ + private static final int ERROR_NON_MATCHING_UID = 0x103; + @Override public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { - Log.d(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); - // We don't return anything yet. Seems to work good for some checkers. + Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0); + int versionCode = packageInfo.versionCode; + + // Verify caller identity + if (packageInfo.applicationInfo.uid != getCallingUid()) { + Log.e(TAG, "an app illegally tried to request v1 licenses for another app (caller: " + getCallingUid() + ")"); + listener.verifyLicense(ERROR_NON_MATCHING_UID, null, null); + } else { + Request request = + new LicenseRequest.V1(packageName, versionCode, nonce, data -> { + try { + if (data != null) { + Log.v(TAG, "licenseV1 result was " + data.result + "with signed data " + data.signedData); + + if (data.result != null) { + listener.verifyLicense(data.result, data.signedData, data.signature); + } else { + listener.verifyLicense(LICENSED, data.signedData, data.signature); + } + } else { + Log.v(TAG, "licenseV1 result was that user has no license"); + listener.verifyLicense(NOT_LICENSED, null, null); + } + } catch (RemoteException e) { + Log.e(TAG, "After returning licenseV1 result, remote threw an Exception."); + e.printStackTrace(); + } + }, error -> { + Log.e(TAG, "licenseV1 request failed with " + error.toString()); + try { + listener.verifyLicense(ERROR_CONTACTING_SERVER, null, null); + } catch (RemoteException e) { + Log.e(TAG, "After telling it that licenseV1 had an error when contacting server, remote threw an Exception."); + e.printStackTrace(); + Log.e(TAG, "Caused after network error:"); + error.printStackTrace(); + } + }); + + request.setShouldCache(false); + queue.add(request); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); + listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, null, null); + } } @Override public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener, Bundle extraParams) throws RemoteException { - Log.d(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); - // We don't return anything yet. Seems to work good for some checkers. + Log.v(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); + + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0); + int versionCode = packageInfo.versionCode; + + // Verify caller identity + if (packageInfo.applicationInfo.uid != getCallingUid()) { + Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")"); + listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle()); + } else { + Request request = + new LicenseRequest.V2(packageName, versionCode, jwt -> { + Log.v(TAG, "LicenseV2 returned JWT license value " + jwt); + Bundle bundle = new Bundle(); + bundle.putString(KEY_V2_RESULT_JWT, jwt); + try { + listener.verifyLicense(jwt == null? NOT_LICENSED : LICENSED, bundle); + } catch (RemoteException e) { + Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); + e.printStackTrace(); + } + }, error -> { + Log.e(TAG, "licenseV2 request failed with " + error.toString()); + try { + listener.verifyLicense(ERROR_CONTACTING_SERVER, new Bundle()); + } catch (RemoteException e) { + Log.e(TAG, "After telling it that licenseV2 had an error when contacting server, remote threw an Exception."); + e.printStackTrace(); + Log.e(TAG, "Caused after network error:"); + error.printStackTrace(); + } + }); + + request.setShouldCache(false); + queue.add(request); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); + listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, new Bundle()); + } + } }; public IBinder onBind(Intent intent) { + queue = Volley.newRequestQueue(this); return mLicenseService; } + + } diff --git a/vending-app/src/main/proto/LicenseRequest.proto b/vending-app/src/main/proto/LicenseRequest.proto new file mode 100644 index 0000000000..da8747c2cd --- /dev/null +++ b/vending-app/src/main/proto/LicenseRequest.proto @@ -0,0 +1,71 @@ +syntax = "proto2"; + +option java_package = "com.android.vending"; +option java_multiple_files = true; + +message LicenseRequestHeader { + optional StringWrapper encodedTimestamps = 1; + optional EncodedTripleWrapper triple = 10; + optional LocalityWrapper locality = 11; + optional IntWrapper unknown = 12; + optional string empty = 14; + optional DeviceMeta deviceMeta = 20; + optional UserAgent userAgent = 21; + optional Uuid uuid = 27; +} + +message StringWrapper { + optional string string = 1; +} + +message EncodedTripleWrapper { + optional EncodedTriple triple = 1; +} + +message EncodedTriple { + optional string encoded1 = 1; + optional string encoded2 = 2; + optional string empty = 3; +} + +message LocalityWrapper { + optional string encodedLocalityProto = 1; +} + +message IntWrapper { + optional uint32 integer = 1; +} + +message DeviceMeta { + optional AndroidVersionMeta android = 1; + optional UnknownByte12 unknown1 = 2; + optional uint32 unknown2 = 3; // observed value: 1 + +} + +message AndroidVersionMeta { + optional uint32 androidSdk = 1; + optional string buildNumber = 2; + optional string androidVersion = 3; + optional uint32 unknown = 4; +} + +message UnknownByte12 { + optional bytes bytes = 12; +} + +message UserAgent { + // The names of these attributes are vague guesses and should be adapted if needed. + optional string deviceProductName = 1; // e.g. "OnePlusNord" + optional string deviceSoc = 2; // e.g. "qcom" + optional string deviceModelName = 3; // e.g. "OnePlus Nord" + optional string finskyVersion = 4; // e.g. "Finsky/37.5.24-29%20%5B0%5D%20%5BPR%5D%20565477504" + optional string deviceName = 5; // e.g. "OnePlusNord"; difference to 1 not yet clear + optional uint64 androidId = 6; + optional string deviceSignature = 7; // e.g. "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys" +} + +message Uuid { + optional string uuid = 1; + optional uint32 unknown = 2; // might be a constant, e.g. format ID. Observed value: 2. +} diff --git a/vending-app/src/main/proto/LicenseResult.proto b/vending-app/src/main/proto/LicenseResult.proto new file mode 100644 index 0000000000..238aa263f6 --- /dev/null +++ b/vending-app/src/main/proto/LicenseResult.proto @@ -0,0 +1,28 @@ +syntax = "proto2"; + +option java_package = "com.android.vending"; +option java_multiple_files = true; + +message LicenseResult { + optional LicenseInformation information = 1; +} + +message LicenseInformation { + optional V1Container v1 = 76; + optional V2Container v2 = 173; +} + +message V1Container { + optional uint32 result = 1; + optional string signedData = 2; + optional string signature = 3; + +} + +message V2Container { + optional AppLicense license = 1; +} + +message AppLicense { + optional string jwt = 1; +} \ No newline at end of file diff --git a/vending-app/src/main/proto/Locality.proto b/vending-app/src/main/proto/Locality.proto new file mode 100644 index 0000000000..8e74fbcf69 --- /dev/null +++ b/vending-app/src/main/proto/Locality.proto @@ -0,0 +1,21 @@ +syntax = "proto2"; + +option java_package = "com.android.vending"; +option java_multiple_files = true; + +import "Timestamp.proto"; + +message Locality { + optional uint32 unknown1 = 2; // value: 1 + optional uint32 unknown2 = 3; // value: 0 + optional string countryCode = 4; // e.g. "DE" + optional TimestampStringWrapper region = 8; // e.g. "DE-BY" and a timestamp + optional TimestampStringWrapper country = 9; // e.g. "DE" and a timestamp + optional uint32 unknown3 = 11; // value: 0 + +} + +message TimestampStringWrapper { + optional string string = 1; + optional Timestamp timestamp = 2; +} diff --git a/vending-app/src/main/proto/Timestamp.proto b/vending-app/src/main/proto/Timestamp.proto new file mode 100644 index 0000000000..9459ff8274 --- /dev/null +++ b/vending-app/src/main/proto/Timestamp.proto @@ -0,0 +1,33 @@ +syntax = "proto2"; + +option java_package = "com.android.vending"; +option java_multiple_files = true; + +message TimestampContainer { + optional TimestampContainer1Wrapper container1Wrapper = 3; + optional TimestampContainer2 container2 = 6; +} + +message TimestampContainer1Wrapper { + optional string androidId = 1; + optional TimestampContainer1 container = 2; +} + +message TimestampContainer1 { + optional string timestamp = 1; + optional Timestamp wrapper = 2; +} + +message Timestamp { + optional uint32 seconds = 1; + optional uint32 nanos = 2; +} + +message TimestampContainer2 { + optional TimestampWrapper wrapper = 1; + optional Timestamp timestamp = 2; +} + +message TimestampWrapper { + optional Timestamp timestamp = 1; +} \ No newline at end of file From c5deb9a57592d8938b938f3a78816876205708c2 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Sun, 1 Oct 2023 18:43:25 +0200 Subject: [PATCH 07/33] Replace hardcoded auth token with auth token fetched via account --- vending-app/build.gradle | 1 + vending-app/src/main/AndroidManifest.xml | 11 +- .../vending/licensing/LicenseRequest.java | 14 +- .../vending/licensing/LicensingService.java | 243 +++++++++++------- 4 files changed, 165 insertions(+), 104 deletions(-) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 2ed0ca836a..8031b91dd4 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -61,6 +61,7 @@ android { dependencies { implementation project(':fake-signature') + implementation project(':play-services-auth') implementation "com.squareup.wire:wire-runtime:$wireVersion" implementation "com.android.volley:volley:$volleyVersion" diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index 8f11d4d5c0..7156d709b1 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -5,12 +5,19 @@ - - + + + + + extends Request { private final String xPsRh; + private final String auth; private static final String TAG = "FakeLicenseRequest"; private static final int BASE64_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING; @@ -49,8 +50,9 @@ public abstract class LicenseRequest extends Request { private final Response.Listener successListener; - protected LicenseRequest(String url, Response.Listener successListener, Response.ErrorListener errorListener) { + protected LicenseRequest(String url, String auth, Response.Listener successListener, Response.ErrorListener errorListener) { super(GET, url, errorListener); + this.auth = auth; this.successListener = successListener; @@ -143,7 +145,7 @@ protected LicenseRequest(String url, Response.Listener successListener, Respo public Map getHeaders() { return Map.of( "X-PS-RH", xPsRh, - "Authorization", "Bearer ya29.[…]]", + "Authorization", "Bearer " + auth, "Connection", "Keep-Alive" ); } @@ -162,9 +164,9 @@ private static Timestamp makeTimestamp(long millis) { public static class V1 extends LicenseRequest { - public V1(String packageName, int versionCode, long nonce, Response.Listener successListener, Response.ErrorListener errorListener) { + public V1(String packageName, String auth, int versionCode, long nonce, Response.Listener successListener, Response.ErrorListener errorListener) { super("https://play-fe.googleapis.com/fdfe/apps/checkLicense?pkgn=" + packageName + "&vc=" + versionCode + "&nnc=" + nonce, - successListener, errorListener + auth, successListener, errorListener ); } @@ -187,11 +189,11 @@ protected Response parseNetworkResponse(NetworkResponse response) { } public static class V2 extends LicenseRequest { - public V2(String packageName, int versionCode, Response.Listener successListener, + public V2(String packageName, String auth, int versionCode, Response.Listener successListener, Response.ErrorListener errorListener) { super( "https://play-fe.googleapis.com/fdfe/apps/checkLicenseServerFallback?pkgn=" + packageName + "&vc=" + versionCode, - successListener, errorListener + auth, successListener, errorListener ); } diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index ba351bf8c4..267ddc84b8 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -5,6 +5,12 @@ package com.android.vending.licensing; +import static android.accounts.AccountManager.KEY_AUTHTOKEN; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.app.Service; import android.content.Intent; import android.content.pm.PackageInfo; @@ -18,60 +24,66 @@ import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; +import org.microg.gms.auth.AuthConstants; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + public class LicensingService extends Service { private static final String TAG = "FakeLicenseService"; private RequestQueue queue; + private AccountManager accountManager; private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; - private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { - - + private static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; + + /* Possible response codes for checkLicense v1, from + * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and + * the LVL library. + */ + + /** + * The application is licensed to the user. The user has purchased the application, or is authorized to + * download and install the alpha or beta version of the application. + */ + private static final int LICENSED = 0x0; + /** + * The application is licensed to the user, but there is an updated application version available that is + * signed with a different key. + */ + private static final int NOT_LICENSED = 0x1; + /** + * The application is not licensed to the user. + */ + private static final int LICENSED_OLD_KEY = 0x2; + /** + * Server error — the application (package name) was not recognized by Google Play. + */ + private static final int ERROR_NOT_MARKET_MANAGED = 0x3; + /** + * Server error — the server could not load the application's key pair for licensing. + */ + private static final int ERROR_SERVER_FAILURE = 0x4; + private static final int ERROR_OVER_QUOTA = 0x5; + + /** + * Local error — the Google Play application was not able to reach the licensing server, possibly because + * of network availability problems. + */ + private static final int ERROR_CONTACTING_SERVER = 0x101; + /** + * Local error — the application requested a license check for a package that is not installed on the device. + */ + private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; + /** + * Local error — the application requested a license check for a package whose UID (package, user ID pair) + * does not match that of the requesting application. + */ + private static final int ERROR_NON_MATCHING_UID = 0x103; + private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { - /* Possible response codes for checkLicense v1, from - * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and - * the LVL library. - */ - - /** - * The application is licensed to the user. The user has purchased the application, or is authorized to - * download and install the alpha or beta version of the application. - */ - private static final int LICENSED = 0x0; - /** - * The application is licensed to the user, but there is an updated application version available that is - * signed with a different key. - */ - private static final int NOT_LICENSED = 0x1; - /** - * The application is not licensed to the user. - */ - private static final int LICENSED_OLD_KEY = 0x2; - /** - * Server error — the application (package name) was not recognized by Google Play. - */ - private static final int ERROR_NOT_MARKET_MANAGED = 0x3; - /** - * Server error — the server could not load the application's key pair for licensing. - */ - private static final int ERROR_SERVER_FAILURE = 0x4; - private static final int ERROR_OVER_QUOTA = 0x5; - - /** - * Local error — the Google Play application was not able to reach the licensing server, possibly because - * of network availability problems. - */ - private static final int ERROR_CONTACTING_SERVER = 0x101; - /** - * Local error — the application requested a license check for a package that is not installed on the device. - */ - private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; - /** - * Local error — the application requested a license check for a package whose UID (package, user ID pair) - * does not match that of the requesting application. - */ - private static final int ERROR_NON_MATCHING_UID = 0x103; @Override public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { @@ -85,39 +97,50 @@ public void checkLicense(long nonce, String packageName, ILicenseResultListener Log.e(TAG, "an app illegally tried to request v1 licenses for another app (caller: " + getCallingUid() + ")"); listener.verifyLicense(ERROR_NON_MATCHING_UID, null, null); } else { - Request request = - new LicenseRequest.V1(packageName, versionCode, nonce, data -> { - try { - if (data != null) { - Log.v(TAG, "licenseV1 result was " + data.result + "with signed data " + data.signedData); - if (data.result != null) { - listener.verifyLicense(data.result, data.signedData, data.signature); + Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); + + if (accounts.length == 0) { + Log.e(TAG, "not checking license, as user is not signed in"); + } else accountManager.getAuthToken( + accounts[0], AUTH_TOKEN_SCOPE, false, + future -> { + Request request = null; + try { + request = new LicenseRequest.V1( + packageName, + future.getResult().getString(KEY_AUTHTOKEN), + versionCode, nonce, data -> { + if (data != null) { + Log.v(TAG, "licenseV1 result was " + data.result + " with signed data " + + data.signedData); + + try { + if (data.result != null) { + listener.verifyLicense(data.result, data.signedData, data.signature); + } else { + listener.verifyLicense(LICENSED, data.signedData, data.signature); + } + } catch (RemoteException e) { + Log.e(TAG, + "After telling it the licenseV1 result, remote threw an Exception."); + e.printStackTrace(); + } } else { - listener.verifyLicense(LICENSED, data.signedData, data.signature); + Log.v(TAG, "licenseV1 result was that user has no license"); + sendError(listener, NOT_LICENSED); } - } else { - Log.v(TAG, "licenseV1 result was that user has no license"); - listener.verifyLicense(NOT_LICENSED, null, null); - } - } catch (RemoteException e) { - Log.e(TAG, "After returning licenseV1 result, remote threw an Exception."); - e.printStackTrace(); + }, error -> { + Log.e(TAG, "licenseV1 request failed with " + error.toString()); + sendError(listener, ERROR_CONTACTING_SERVER); + }); + } catch (AuthenticatorException | IOException | OperationCanceledException e) { + sendError(listener, ERROR_CONTACTING_SERVER); } - }, error -> { - Log.e(TAG, "licenseV1 request failed with " + error.toString()); - try { - listener.verifyLicense(ERROR_CONTACTING_SERVER, null, null); - } catch (RemoteException e) { - Log.e(TAG, "After telling it that licenseV1 had an error when contacting server, remote threw an Exception."); - e.printStackTrace(); - Log.e(TAG, "Caused after network error:"); - error.printStackTrace(); - } - }); - request.setShouldCache(false); - queue.add(request); + request.setShouldCache(false); + queue.add(request); + }, null); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); @@ -138,31 +161,41 @@ public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")"); listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle()); } else { - Request request = - new LicenseRequest.V2(packageName, versionCode, jwt -> { - Log.v(TAG, "LicenseV2 returned JWT license value " + jwt); - Bundle bundle = new Bundle(); - bundle.putString(KEY_V2_RESULT_JWT, jwt); - try { - listener.verifyLicense(jwt == null? NOT_LICENSED : LICENSED, bundle); - } catch (RemoteException e) { - Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); - e.printStackTrace(); - } - }, error -> { - Log.e(TAG, "licenseV2 request failed with " + error.toString()); + Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); + + if (accounts.length == 0) { + Log.e(TAG, "not checking license, as user is not signed in"); + } else accountManager.getAuthToken( + accounts[0], AUTH_TOKEN_SCOPE, false, + future -> { try { - listener.verifyLicense(ERROR_CONTACTING_SERVER, new Bundle()); - } catch (RemoteException e) { - Log.e(TAG, "After telling it that licenseV2 had an error when contacting server, remote threw an Exception."); + Bundle result = future.getResult(10, TimeUnit.SECONDS); + String auth = result.getString(KEY_AUTHTOKEN); + + Request request = new LicenseRequest.V2(packageName, auth, versionCode, jwt -> { + Log.v(TAG, "LicenseV2 returned JWT license value " + jwt); + Bundle bundle = new Bundle(); + bundle.putString(KEY_V2_RESULT_JWT, jwt); + try { + listener.verifyLicense(jwt == null ? NOT_LICENSED : LICENSED, bundle); + } catch (RemoteException e) { + Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); + e.printStackTrace(); + } + }, error -> { + Log.e(TAG, "licenseV2 request failed with " + error.toString()); + sendError(listener, ERROR_CONTACTING_SERVER); + }); + + request.setShouldCache(false); + queue.add(request); + + } catch (AuthenticatorException | IOException | OperationCanceledException e) { + sendError(listener, ERROR_CONTACTING_SERVER); e.printStackTrace(); - Log.e(TAG, "Caused after network error:"); - error.printStackTrace(); } - }); - - request.setShouldCache(false); - queue.add(request); + }, null + ); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); @@ -172,8 +205,26 @@ public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener } }; + private static void sendError(ILicenseResultListener listener, int error) { + try { + listener.verifyLicense(error, null, null); + } catch (RemoteException e) { + Log.e(TAG, "After telling it that licenseV1 had an error (" + error + "), remote threw an Exception."); + } + } + + private static void sendError(ILicenseV2ResultListener listener, int error) { + try { + listener.verifyLicense(error, new Bundle()); + } catch (RemoteException e) { + Log.e(TAG, "After telling it that licenseV2 had an error (" + error + "), remote threw an Exception."); + } + } + public IBinder onBind(Intent intent) { queue = Volley.newRequestQueue(this); + accountManager = AccountManager.get(this); + return mLicenseService; } From 10c53aa6f40e2623a6c446c01da3115d0c40ed5f Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 16:37:23 +0200 Subject: [PATCH 08/33] Notify is user not signed in to any Google account --- vending-app/src/main/AndroidManifest.xml | 7 + .../LicenseServiceNotificationRunnable.java | 157 ++++++++++++++++++ .../vending/licensing/LicensingService.java | 16 +- vending-app/src/main/res/values/strings.xml | 8 + 4 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index 7156d709b1..36bf475c36 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ android:protectionLevel="normal" /> + + + + + diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java new file mode 100644 index 0000000000..f692c413fe --- /dev/null +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java @@ -0,0 +1,157 @@ +package com.android.vending.licensing; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.CallSuper; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import com.android.vending.R; + +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + +public class LicenseServiceNotificationRunnable implements Runnable { + + private final Context context; + + public String callerPackageName; + public CharSequence callerAppName; + public int callerUid; + + private static final String TAG = "FakeLicenseNotification"; + private static final String GMS_PACKAGE_NAME = "com.google.android.gms"; + private static final String GMS_AUTH_INTENT_ACTION = "com.google.android.gms.auth.login.LOGIN"; + + private static final String PREFERENCES_KEY_IGNORE_PACKAGES_LIST = "ignorePackages"; + private static final String PREFERENCES_FILE_NAME = "licensing"; + + private static final String INTENT_KEY_IGNORE_PACKAGE_NAME = "package"; + private static final String INTENT_KEY_NOTIFICATION_ID = "id"; + + + public LicenseServiceNotificationRunnable(Context context) { + this.context = context; + } + + private static final String CHANNEL_ID = "LicenseNotification"; + + @Override + public void run() { + registerNotificationChannel(); + + SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_FILE_NAME, Context.MODE_PRIVATE); + + Set ignoreList = preferences.getStringSet(PREFERENCES_KEY_IGNORE_PACKAGES_LIST, Collections.emptySet()); + for (String ignoredPackage : ignoreList) { + if (callerPackageName.equals(ignoredPackage)) { + Log.d(TAG, "Not notifying about license check, as user has ignored notifications for package " + ignoredPackage); + return; + } + } + + Intent authIntent = new Intent(context, LicenseServiceNotificationRunnable.SignInReceiver.class); + authIntent.putExtra(INTENT_KEY_NOTIFICATION_ID, callerUid); + PendingIntent authPendingIntent = PendingIntent.getBroadcast( + context, callerUid * 2, authIntent, PendingIntent.FLAG_IMMUTABLE + ); + + Intent ignoreIntent = new Intent(context, LicenseServiceNotificationRunnable.IgnoreReceiver.class); + ignoreIntent.putExtra(INTENT_KEY_IGNORE_PACKAGE_NAME, callerPackageName); + ignoreIntent.putExtra(INTENT_KEY_NOTIFICATION_ID, callerUid); + PendingIntent ignorePendingIntent = PendingIntent.getBroadcast( + context, callerUid * 2 + 1, ignoreIntent, PendingIntent.FLAG_IMMUTABLE + ); + + Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_app_foreground) + .setContentTitle(context.getString(R.string.license_notification_title, callerAppName)) + .setContentText(context.getString(R.string.license_notification_body)) + .addAction( + new NotificationCompat.Action.Builder( + null, context.getString(R.string.license_notification_sign_in), authPendingIntent + ).build() + ) + .addAction( + new NotificationCompat.Action.Builder( + null, context.getString(R.string.license_notification_ignore), ignorePendingIntent + ).build() + ) + .setAutoCancel(true) + .build(); + + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + notificationManager.notify(callerUid, notification); + + } + + private void registerNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + context.getString(R.string.license_notification_channel_name), + NotificationManager.IMPORTANCE_HIGH + ); + channel.setDescription(context.getString(R.string.license_notification_channel_description)); + + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + + } + + private static class Receiver extends BroadcastReceiver { + + @Override + @CallSuper + public void onReceive(Context context, Intent intent) { + // Dismiss notification + NotificationManagerCompat.from(context) + .cancel(intent.getIntExtra(INTENT_KEY_NOTIFICATION_ID, -1)); + } + } + + public static final class IgnoreReceiver extends Receiver { + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + + SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_FILE_NAME, Context.MODE_PRIVATE); + + Set ignoreList = new TreeSet<>( + preferences.getStringSet(PREFERENCES_KEY_IGNORE_PACKAGES_LIST, Collections.emptySet()) + ); + + String newIgnorePackage = intent.getStringExtra(INTENT_KEY_IGNORE_PACKAGE_NAME); + Log.d(TAG, "Adding package " + newIgnorePackage + " to ignore list"); + + ignoreList.add(newIgnorePackage); + preferences.edit().putStringSet(PREFERENCES_KEY_IGNORE_PACKAGES_LIST, ignoreList).apply(); + } + } + + public static final class SignInReceiver extends Receiver { + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + + Log.d(TAG, "Starting sign in activity"); + Intent authIntent = new Intent(GMS_AUTH_INTENT_ACTION); + authIntent.setPackage(GMS_PACKAGE_NAME); + authIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(authIntent); + } + } + +} diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index 267ddc84b8..f6df3b71ef 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -33,6 +33,7 @@ public class LicensingService extends Service { private static final String TAG = "FakeLicenseService"; private RequestQueue queue; private AccountManager accountManager; + private LicenseServiceNotificationRunnable notificationRunnable; private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; @@ -89,7 +90,8 @@ public class LicensingService extends Service { public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); try { - PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0); + PackageManager packageManager = getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); int versionCode = packageInfo.versionCode; // Verify caller identity @@ -102,6 +104,10 @@ public void checkLicense(long nonce, String packageName, ILicenseResultListener if (accounts.length == 0) { Log.e(TAG, "not checking license, as user is not signed in"); + notificationRunnable.callerPackageName = packageName; + notificationRunnable.callerUid = packageInfo.applicationInfo.uid; + notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); + notificationRunnable.run(); } else accountManager.getAuthToken( accounts[0], AUTH_TOKEN_SCOPE, false, future -> { @@ -153,7 +159,8 @@ public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener Log.v(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); try { - PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName, 0); + PackageManager packageManager = getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); int versionCode = packageInfo.versionCode; // Verify caller identity @@ -165,6 +172,10 @@ public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener if (accounts.length == 0) { Log.e(TAG, "not checking license, as user is not signed in"); + notificationRunnable.callerPackageName = packageName; + notificationRunnable.callerUid = packageInfo.applicationInfo.uid; + notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); + notificationRunnable.run(); } else accountManager.getAuthToken( accounts[0], AUTH_TOKEN_SCOPE, false, future -> { @@ -224,6 +235,7 @@ private static void sendError(ILicenseV2ResultListener listener, int error) { public IBinder onBind(Intent intent) { queue = Volley.newRequestQueue(this); accountManager = AccountManager.get(this); + notificationRunnable = new LicenseServiceNotificationRunnable(this); return mLicenseService; } diff --git a/vending-app/src/main/res/values/strings.xml b/vending-app/src/main/res/values/strings.xml index 5169119862..07c309f959 100644 --- a/vending-app/src/main/res/values/strings.xml +++ b/vending-app/src/main/res/values/strings.xml @@ -8,4 +8,12 @@ microG Companion microG Companion cannot be used standalone. Opened microG Services settings instead. microG Companion cannot be used standalone. Please install microG Services to use microG. + + License notifications + Notifies when an app tries to validate its license, but you are not signed in to any Google account. + %1$s could not verify license + If the app is misbehaving, sign in to a Google account with which you have bought the app. + + Sign In + Ignore \ No newline at end of file From 640745586e9ace1d5a200415b918f778cc767260 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 16:42:23 +0200 Subject: [PATCH 09/33] Don't provide most negative results in V2 licensing --- .../vending/licensing/LicensingService.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index f6df3b71ef..1324b491b2 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -166,6 +166,10 @@ public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener // Verify caller identity if (packageInfo.applicationInfo.uid != getCallingUid()) { Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")"); + /* This negative result is provided even if users are not signed in; we expect apps + * will usually behave correctly in practise so this will not prevent users from + * using the app. + */ listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle()); } else { Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); @@ -188,21 +192,32 @@ public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener Bundle bundle = new Bundle(); bundle.putString(KEY_V2_RESULT_JWT, jwt); try { - listener.verifyLicense(jwt == null ? NOT_LICENSED : LICENSED, bundle); + if (jwt == null) { + /* + * Suppress failures on V2. V2 is commonly used by free apps whose checker + * will not throw users out of the app if it never receives a response. + * + * This means that users who are signed in to a Google account will not + * get a worse experience in these apps than users that are not signed in. + */ + Log.i(TAG, "Suppressed negative result for package " + packageName); + } else { + listener.verifyLicense(jwt == null ? NOT_LICENSED : LICENSED, bundle); + } } catch (RemoteException e) { Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); e.printStackTrace(); } }, error -> { Log.e(TAG, "licenseV2 request failed with " + error.toString()); - sendError(listener, ERROR_CONTACTING_SERVER); + //sendError(listener, ERROR_CONTACTING_SERVER); – see above }); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { - sendError(listener, ERROR_CONTACTING_SERVER); + //sendError(listener, ERROR_CONTACTING_SERVER); – see above e.printStackTrace(); } }, null From 7034f62c0bcef96bcc20b8ec6ecf33df5e5cbedb Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 3 Oct 2023 16:56:30 +0200 Subject: [PATCH 10/33] Improve license notification icon & sound --- .../LicenseServiceNotificationRunnable.java | 4 +- .../src/main/res/drawable/ic_notification.xml | 42 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 vending-app/src/main/res/drawable/ic_notification.xml diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java index f692c413fe..193c755da7 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java @@ -74,7 +74,8 @@ public void run() { ); Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_app_foreground) + .setSmallIcon(R.drawable.ic_notification) + .setSound(null) .setContentTitle(context.getString(R.string.license_notification_title, callerAppName)) .setContentText(context.getString(R.string.license_notification_body)) .addAction( @@ -103,6 +104,7 @@ private void registerNotificationChannel() { NotificationManager.IMPORTANCE_HIGH ); channel.setDescription(context.getString(R.string.license_notification_channel_description)); + channel.setSound(null, null); NotificationManager notificationManager = context.getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); diff --git a/vending-app/src/main/res/drawable/ic_notification.xml b/vending-app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000000..a1fdf972e1 --- /dev/null +++ b/vending-app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + From fe9990639ead4273f208f1efdc6dd111ba04e131 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Wed, 4 Oct 2023 13:51:02 +0200 Subject: [PATCH 11/33] Query licenses from multiple Google accounts in a row --- .../vending/licensing/LicenseChecker.java | 202 ++++++++++++++ .../vending/licensing/LicensingService.java | 259 +++++------------- 2 files changed, 271 insertions(+), 190 deletions(-) create mode 100644 vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java new file mode 100644 index 0000000000..bb8ed02731 --- /dev/null +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java @@ -0,0 +1,202 @@ +package com.android.vending.licensing; + +import static android.accounts.AccountManager.KEY_AUTHTOKEN; +import static android.os.Binder.getCallingUid; + + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.util.Log; + +import com.android.vending.V1Container; +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.Response; + +import java.io.IOException; + +import kotlin.Unit; + +/** + * Performs license check including caller UID verification, using a given account, for which + * an auth token is fetched. + * + * @param Request parameter data value type + * @param Result type + */ +public abstract class LicenseChecker { + + private static final String TAG = "FakeLicenseChecker"; + + /* Possible response codes for checkLicense v1, from + * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and + * the LVL library. + */ + + /** + * The application is licensed to the user. The user has purchased the application, or is authorized to + * download and install the alpha or beta version of the application. + */ + static final int LICENSED = 0x0; + /** + * The application is licensed to the user, but there is an updated application version available that is + * signed with a different key. + */ + static final int NOT_LICENSED = 0x1; + /** + * The application is not licensed to the user. + */ + static final int LICENSED_OLD_KEY = 0x2; + /** + * Server error — the application (package name) was not recognized by Google Play. + */ + static final int ERROR_NOT_MARKET_MANAGED = 0x3; + /** + * Server error — the server could not load the application's key pair for licensing. + */ + static final int ERROR_SERVER_FAILURE = 0x4; + static final int ERROR_OVER_QUOTA = 0x5; + + /** + * Local error — the Google Play application was not able to reach the licensing server, possibly because + * of network availability problems. + */ + static final int ERROR_CONTACTING_SERVER = 0x101; + /** + * Local error — the application requested a license check for a package that is not installed on the device. + */ + static final int ERROR_INVALID_PACKAGE_NAME = 0x102; + /** + * Local error — the application requested a license check for a package whose UID (package, user ID pair) + * does not match that of the requesting application. + */ + static final int ERROR_NON_MATCHING_UID = 0x103; + + static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; + + public void checkLicense(Account account, AccountManager accountManager, + String packageName, PackageManager packageManager, + RequestQueue queue, D queryData, + BiConsumerWithException onResult) + throws RemoteException { + try { + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + int versionCode = packageInfo.versionCode; + + // Verify caller identity + if (packageInfo.applicationInfo.uid != getCallingUid()) { + Log.e(TAG, + "an app illegally tried to request licenses for another app (caller: " + getCallingUid() + ")"); + onResult.accept(ERROR_NON_MATCHING_UID, null); + } else { + + accountManager.getAuthToken( + account, AUTH_TOKEN_SCOPE, false, + future -> { + try { + String auth = future.getResult().getString(KEY_AUTHTOKEN); + Request request = createRequest(packageName, auth, + versionCode, queryData, (Integer integer, R r) -> { + + try { + onResult.accept(integer, r); + } catch (RemoteException e) { + Log.e(TAG, + "After telling it the license check result, remote threw an Exception."); + e.printStackTrace(); + } + }, error -> { + Log.e(TAG, "license request failed with " + error.toString()); + safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); + }); + request.setShouldCache(false); + queue.add(request); + } catch (AuthenticatorException | IOException | OperationCanceledException e) { + safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); + } + + }, null); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "an app tried to request licenses for package " + packageName + ", which does not exist"); + onResult.accept(ERROR_INVALID_PACKAGE_NAME, null); + } + } + + private static void safeSendResult( + BiConsumerWithException consumerWithException, A a, B b) { + try { + consumerWithException.accept(a, b); + } catch (Exception e) { + Log.e(TAG, "While sending result " + a + ", " + b + ", remote encountered an exception."); + e.printStackTrace(); + } + } + + public abstract Request createRequest(String packageName, String auth, int versionCode, D data, BiConsumer then, Response.ErrorListener errorListener); + + // Functional interfaces + + interface BiConsumerWithException { + void accept(A a, B b) throws T; + } + + interface BiConsumer { + void accept(A a, B b); + } + + static class Tuple { + public final A a; + public final B b; + + public Tuple(A a, B b) { + this.a = a; + this.b = b; + } + } + + // Implementations + + public static class V1 extends LicenseChecker> { + + @Override + public Request createRequest(String packageName, String auth, int versionCode, Long nonce, BiConsumer> then, + Response.ErrorListener errorListener) { + return new LicenseRequest.V1( + packageName, auth, versionCode, nonce, response -> { + if (response != null) { + Log.v(TAG, "licenseV1 result was " + response.result + " with signed data " + + response.signedData); + + if (response.result != null) { + then.accept(response.result, new Tuple<>(response.signedData, response.signature)); + } else { + then.accept(LICENSED, new Tuple<>(response.signedData, response.signature)); + } + } + }, errorListener + ); + } + } + + public static class V2 extends LicenseChecker { + @Override + public Request createRequest(String packageName, String auth, int versionCode, Unit data, + BiConsumer then, Response.ErrorListener errorListener) { + return new LicenseRequest.V2( + packageName, auth, versionCode, response -> { + if (response != null) { + then.accept(LICENSED, response); + } else { + then.accept(NOT_LICENSED, null); + } + }, errorListener + ); + } + } +} diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index 1324b491b2..8b58ca22ed 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -5,12 +5,10 @@ package com.android.vending.licensing; -import static android.accounts.AccountManager.KEY_AUTHTOKEN; +import static com.android.vending.licensing.LicenseChecker.LICENSED; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.app.Service; import android.content.Intent; import android.content.pm.PackageInfo; @@ -20,14 +18,16 @@ import android.os.RemoteException; import android.util.Log; -import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; import org.microg.gms.auth.AuthConstants; -import java.io.IOException; -import java.util.concurrent.TimeUnit; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +import kotlin.Unit; public class LicensingService extends Service { private static final String TAG = "FakeLicenseService"; @@ -37,51 +37,6 @@ public class LicensingService extends Service { private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; - private static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; - - /* Possible response codes for checkLicense v1, from - * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and - * the LVL library. - */ - - /** - * The application is licensed to the user. The user has purchased the application, or is authorized to - * download and install the alpha or beta version of the application. - */ - private static final int LICENSED = 0x0; - /** - * The application is licensed to the user, but there is an updated application version available that is - * signed with a different key. - */ - private static final int NOT_LICENSED = 0x1; - /** - * The application is not licensed to the user. - */ - private static final int LICENSED_OLD_KEY = 0x2; - /** - * Server error — the application (package name) was not recognized by Google Play. - */ - private static final int ERROR_NOT_MARKET_MANAGED = 0x3; - /** - * Server error — the server could not load the application's key pair for licensing. - */ - private static final int ERROR_SERVER_FAILURE = 0x4; - private static final int ERROR_OVER_QUOTA = 0x5; - - /** - * Local error — the Google Play application was not able to reach the licensing server, possibly because - * of network availability problems. - */ - private static final int ERROR_CONTACTING_SERVER = 0x101; - /** - * Local error — the application requested a license check for a package that is not installed on the device. - */ - private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; - /** - * Local error — the application requested a license check for a package whose UID (package, user ID pair) - * does not match that of the requesting application. - */ - private static final int ERROR_NON_MATCHING_UID = 0x103; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { @@ -89,164 +44,88 @@ public class LicensingService extends Service { @Override public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); - try { - PackageManager packageManager = getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); - int versionCode = packageInfo.versionCode; - - // Verify caller identity - if (packageInfo.applicationInfo.uid != getCallingUid()) { - Log.e(TAG, "an app illegally tried to request v1 licenses for another app (caller: " + getCallingUid() + ")"); - listener.verifyLicense(ERROR_NON_MATCHING_UID, null, null); - } else { - - Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); - if (accounts.length == 0) { - Log.e(TAG, "not checking license, as user is not signed in"); - notificationRunnable.callerPackageName = packageName; - notificationRunnable.callerUid = packageInfo.applicationInfo.uid; - notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); - notificationRunnable.run(); - } else accountManager.getAuthToken( - accounts[0], AUTH_TOKEN_SCOPE, false, - future -> { - Request request = null; - try { - request = new LicenseRequest.V1( - packageName, - future.getResult().getString(KEY_AUTHTOKEN), - versionCode, nonce, data -> { - if (data != null) { - Log.v(TAG, "licenseV1 result was " + data.result + " with signed data " + - data.signedData); + Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); + PackageManager packageManager = getPackageManager(); - try { - if (data.result != null) { - listener.verifyLicense(data.result, data.signedData, data.signature); - } else { - listener.verifyLicense(LICENSED, data.signedData, data.signature); - } - } catch (RemoteException e) { - Log.e(TAG, - "After telling it the licenseV1 result, remote threw an Exception."); - e.printStackTrace(); - } - } else { - Log.v(TAG, "licenseV1 result was that user has no license"); - sendError(listener, NOT_LICENSED); - } - }, error -> { - Log.e(TAG, "licenseV1 request failed with " + error.toString()); - sendError(listener, ERROR_CONTACTING_SERVER); - }); - } catch (AuthenticatorException | IOException | OperationCanceledException e) { - sendError(listener, ERROR_CONTACTING_SERVER); - } + if (accounts.length == 0) { + handleNoAccounts(packageName, packageManager); + } else { + checkLicense(nonce, packageName, packageManager, listener, new LinkedList<>(Arrays.asList(accounts))); + } + } - request.setShouldCache(false); - queue.add(request); - }, null); + private void checkLicense(long nonce, String packageName, PackageManager packageManager, + ILicenseResultListener listener, Queue remainingAccounts) throws RemoteException { + new LicenseChecker.V1().checkLicense( + remainingAccounts.poll(), accountManager, packageName, packageManager, + queue, nonce, + (responseCode, stringTuple) -> { + if (responseCode != LICENSED && !remainingAccounts.isEmpty()) { + checkLicense(nonce, packageName, packageManager, listener, remainingAccounts); + } else { + listener.verifyLicense(responseCode, stringTuple.a, stringTuple.b); + } } - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); - listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, null, null); - } + ); } @Override public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener, Bundle extraParams) throws RemoteException { Log.v(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); - try { - PackageManager packageManager = getPackageManager(); - PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); - int versionCode = packageInfo.versionCode; - - // Verify caller identity - if (packageInfo.applicationInfo.uid != getCallingUid()) { - Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")"); - /* This negative result is provided even if users are not signed in; we expect apps - * will usually behave correctly in practise so this will not prevent users from - * using the app. - */ - listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle()); - } else { - Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); + Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); + PackageManager packageManager = getPackageManager(); - if (accounts.length == 0) { - Log.e(TAG, "not checking license, as user is not signed in"); - notificationRunnable.callerPackageName = packageName; - notificationRunnable.callerUid = packageInfo.applicationInfo.uid; - notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); - notificationRunnable.run(); - } else accountManager.getAuthToken( - accounts[0], AUTH_TOKEN_SCOPE, false, - future -> { - try { - Bundle result = future.getResult(10, TimeUnit.SECONDS); - String auth = result.getString(KEY_AUTHTOKEN); + if (accounts.length == 0) { + handleNoAccounts(packageName, packageManager); + } else { + checkLicenseV2(packageName, packageManager, listener, extraParams, new LinkedList<>(Arrays.asList(accounts))); + } + } - Request request = new LicenseRequest.V2(packageName, auth, versionCode, jwt -> { - Log.v(TAG, "LicenseV2 returned JWT license value " + jwt); - Bundle bundle = new Bundle(); - bundle.putString(KEY_V2_RESULT_JWT, jwt); - try { - if (jwt == null) { - /* - * Suppress failures on V2. V2 is commonly used by free apps whose checker - * will not throw users out of the app if it never receives a response. - * - * This means that users who are signed in to a Google account will not - * get a worse experience in these apps than users that are not signed in. - */ - Log.i(TAG, "Suppressed negative result for package " + packageName); - } else { - listener.verifyLicense(jwt == null ? NOT_LICENSED : LICENSED, bundle); - } - } catch (RemoteException e) { - Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); - e.printStackTrace(); - } - }, error -> { - Log.e(TAG, "licenseV2 request failed with " + error.toString()); - //sendError(listener, ERROR_CONTACTING_SERVER); – see above - }); + private void checkLicenseV2(String packageName, PackageManager packageManager, + ILicenseV2ResultListener listener, Bundle extraParams, + Queue remainingAccounts) throws RemoteException { + new LicenseChecker.V2().checkLicense( + remainingAccounts.poll(), accountManager, packageName, packageManager, queue, Unit.INSTANCE, + (responseCode, data) -> { + /* + * Suppress failures on V2. V2 is commonly used by free apps whose checker + * will not throw users out of the app if it never receives a response. + * + * This means that users who are signed in to a Google account will not + * get a worse experience in these apps than users that are not signed in. + */ + if (responseCode == LICENSED) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_V2_RESULT_JWT, data); + + listener.verifyLicense(responseCode, bundle); + } else if (!remainingAccounts.isEmpty()) { + checkLicenseV2(packageName, packageManager, listener, extraParams, remainingAccounts); + } else { + Log.i(TAG, "Suppressed negative license result for package " + packageName); + } + } + ); - request.setShouldCache(false); - queue.add(request); + } - } catch (AuthenticatorException | IOException | OperationCanceledException e) { - //sendError(listener, ERROR_CONTACTING_SERVER); – see above - e.printStackTrace(); - } - }, null - ); - } + private void handleNoAccounts(String packageName, PackageManager packageManager) { + try { + Log.e(TAG, "not checking license, as user is not signed in"); + PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); + notificationRunnable.callerUid = packageInfo.applicationInfo.uid; + notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); - listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, new Bundle()); + Log.e(TAG, "ignored license request, but package name " + packageName + " was not known!"); + notificationRunnable.callerAppName = packageName; } - + notificationRunnable.run(); } }; - private static void sendError(ILicenseResultListener listener, int error) { - try { - listener.verifyLicense(error, null, null); - } catch (RemoteException e) { - Log.e(TAG, "After telling it that licenseV1 had an error (" + error + "), remote threw an Exception."); - } - } - - private static void sendError(ILicenseV2ResultListener listener, int error) { - try { - listener.verifyLicense(error, new Bundle()); - } catch (RemoteException e) { - Log.e(TAG, "After telling it that licenseV2 had an error (" + error + "), remote threw an Exception."); - } - } - public IBinder onBind(Intent intent) { queue = Volley.newRequestQueue(this); accountManager = AccountManager.get(this); From d378e7a4d37c2fb1e214d90f7ec53803f1472b78 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Wed, 4 Oct 2023 13:58:05 +0200 Subject: [PATCH 12/33] Dismiss all notifications if user wants to sign in --- .../LicenseServiceNotificationRunnable.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java index 193c755da7..7a82341ba2 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java @@ -112,22 +112,14 @@ private void registerNotificationChannel() { } - private static class Receiver extends BroadcastReceiver { + public static final class IgnoreReceiver extends BroadcastReceiver { @Override - @CallSuper public void onReceive(Context context, Intent intent) { - // Dismiss notification + + // Dismiss ignored notification NotificationManagerCompat.from(context) .cancel(intent.getIntExtra(INTENT_KEY_NOTIFICATION_ID, -1)); - } - } - - public static final class IgnoreReceiver extends Receiver { - - @Override - public void onReceive(Context context, Intent intent) { - super.onReceive(context, intent); SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_FILE_NAME, Context.MODE_PRIVATE); @@ -143,10 +135,12 @@ public void onReceive(Context context, Intent intent) { } } - public static final class SignInReceiver extends Receiver { + public static final class SignInReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - super.onReceive(context, intent); + + // Dismiss all notifications + NotificationManagerCompat.from(context).cancelAll(); Log.d(TAG, "Starting sign in activity"); Intent authIntent = new Intent(GMS_AUTH_INTENT_ACTION); From 9ba870c18cb20ae80898442bfc55db052d989a34 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Wed, 4 Oct 2023 15:16:08 +0200 Subject: [PATCH 13/33] Always request `android.permission.GET_ACCOUNTS` --- vending-app/src/main/AndroidManifest.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index 36bf475c36..2e4fec3e61 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -11,10 +11,8 @@ + - From 6799f808185f4e35590bdfc555001e6e69a72e20 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Wed, 4 Oct 2023 15:38:45 +0200 Subject: [PATCH 14/33] Target JVM 1.8 instead of 17 in fakestore --- vending-app/build.gradle | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 8031b91dd4..06c974c798 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -54,8 +54,12 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = 1.8 } } From 934a64178fee835787f5fb6102672e3eb82afb11 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 12 Oct 2023 13:34:17 +0200 Subject: [PATCH 15/33] Require permission to check license --- vending-app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index 2e4fec3e61..b0ec60f59a 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -38,6 +38,7 @@ From e5e0b761e4583c788d488ebafd7b8c57e31f35e7 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 26 Oct 2023 15:22:46 +0200 Subject: [PATCH 16/33] Apply review Reviewed-at: https://gitlab.e.foundation/e/os/GmsCore/-/merge_requests/91#note_458642 --- .../vending/licensing/LicenseChecker.java | 106 +++++++++--------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java index bb8ed02731..ff9992f7df 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java @@ -79,6 +79,9 @@ public abstract class LicenseChecker { static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; + public abstract Request createRequest(String packageName, String auth, int versionCode, D data, + BiConsumer then, Response.ErrorListener errorListener); + public void checkLicense(Account account, AccountManager accountManager, String packageName, PackageManager packageManager, RequestQueue queue, D queryData, @@ -95,25 +98,28 @@ public void checkLicense(Account account, AccountManager accountManager, onResult.accept(ERROR_NON_MATCHING_UID, null); } else { + BiConsumer onRequestFinished = (Integer integer, R r) -> { + try { + onResult.accept(integer, r); + } catch (RemoteException e) { + Log.e(TAG, + "After telling it the license check result, remote threw an Exception."); + e.printStackTrace(); + } + }; + + Response.ErrorListener onRequestError = error -> { + Log.e(TAG, "license request failed with " + error.toString()); + safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); + }; + accountManager.getAuthToken( account, AUTH_TOKEN_SCOPE, false, future -> { try { String auth = future.getResult().getString(KEY_AUTHTOKEN); Request request = createRequest(packageName, auth, - versionCode, queryData, (Integer integer, R r) -> { - - try { - onResult.accept(integer, r); - } catch (RemoteException e) { - Log.e(TAG, - "After telling it the license check result, remote threw an Exception."); - e.printStackTrace(); - } - }, error -> { - Log.e(TAG, "license request failed with " + error.toString()); - safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); - }); + versionCode, queryData, onRequestFinished, onRequestError); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { @@ -138,28 +144,6 @@ private static void safeSendResult( } } - public abstract Request createRequest(String packageName, String auth, int versionCode, D data, BiConsumer then, Response.ErrorListener errorListener); - - // Functional interfaces - - interface BiConsumerWithException { - void accept(A a, B b) throws T; - } - - interface BiConsumer { - void accept(A a, B b); - } - - static class Tuple { - public final A a; - public final B b; - - public Tuple(A a, B b) { - this.a = a; - this.b = b; - } - } - // Implementations public static class V1 extends LicenseChecker> { @@ -169,17 +153,17 @@ public Request createRequest(String packageName, String auth, int v Response.ErrorListener errorListener) { return new LicenseRequest.V1( packageName, auth, versionCode, nonce, response -> { - if (response != null) { - Log.v(TAG, "licenseV1 result was " + response.result + " with signed data " + - response.signedData); - - if (response.result != null) { - then.accept(response.result, new Tuple<>(response.signedData, response.signature)); - } else { - then.accept(LICENSED, new Tuple<>(response.signedData, response.signature)); - } + if (response != null) { + Log.v(TAG, "licenseV1 result was " + response.result + " with signed data " + + response.signedData); + + if (response.result != null) { + then.accept(response.result, new Tuple<>(response.signedData, response.signature)); + } else { + then.accept(LICENSED, new Tuple<>(response.signedData, response.signature)); } - }, errorListener + } + }, errorListener ); } } @@ -190,13 +174,33 @@ public Request createRequest(String packageName, String auth, int versio BiConsumer then, Response.ErrorListener errorListener) { return new LicenseRequest.V2( packageName, auth, versionCode, response -> { - if (response != null) { - then.accept(LICENSED, response); - } else { - then.accept(NOT_LICENSED, null); - } - }, errorListener + if (response != null) { + then.accept(LICENSED, response); + } else { + then.accept(NOT_LICENSED, null); + } + }, errorListener ); } } + + // Functional interfaces + + interface BiConsumerWithException { + void accept(A a, B b) throws T; + } + + interface BiConsumer { + void accept(A a, B b); + } + + static class Tuple { + public final A a; + public final B b; + + public Tuple(A a, B b) { + this.a = a; + this.b = b; + } + } } From 9f4ac5951e534f646d4dee88c401397553d761e6 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 24 Oct 2023 07:43:47 +0200 Subject: [PATCH 17/33] Check preference from within Licensing service --- vending-app/src/main/AndroidManifest.xml | 2 + .../vending/licensing/LicensingService.java | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index b0ec60f59a..bd61b73bad 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -13,6 +13,8 @@ + + diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index 8b58ca22ed..a193199bcd 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -13,9 +13,11 @@ import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.database.Cursor; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.net.Uri; import android.util.Log; import com.android.volley.RequestQueue; @@ -37,14 +39,47 @@ public class LicensingService extends Service { private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; + private static final Uri SETTINGS_PROVIDER = Uri.parse("content://com.google.android.gms.microg.settings/play"); + + private static final String PREFERENCE_LICENSING_ENABLED = "play_licensing"; + + private String androidId; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { + private boolean shouldCheckLicense() { + + Cursor cursor = null; + try { + cursor = getContentResolver().query( + SETTINGS_PROVIDER, new String[]{PREFERENCE_LICENSING_ENABLED}, null, null, null + ); + + if (cursor == null || cursor.getColumnCount() != 1) { + Log.e(TAG, "settings provider not available"); + return false; + } else { + cursor.moveToNext(); + return cursor.getInt(0) != 0; + } + + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + @Override public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); + if (!shouldCheckLicense()) { + Log.d(TAG, "not checking license, as it is disabled by user"); + return; + } + Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); PackageManager packageManager = getPackageManager(); @@ -74,6 +109,11 @@ private void checkLicense(long nonce, String packageName, PackageManager package public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener, Bundle extraParams) throws RemoteException { Log.v(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); + if (!shouldCheckLicense()) { + Log.d(TAG, "not checking license, as it is disabled by user"); + return; + } + Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); PackageManager packageManager = getPackageManager(); From 412507b975395548c428ab27740fa03271e8d272 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Sat, 11 Nov 2023 16:49:04 +0100 Subject: [PATCH 18/33] Load device profile data for licensing user agent --- vending-app/build.gradle | 1 + .../vending/licensing/LicenseChecker.java | 11 ++-- .../vending/licensing/LicenseRequest.java | 51 ++++++++++++------- .../vending/licensing/LicensingService.java | 41 ++++++++++++++- .../src/main/proto/LicenseRequest.proto | 8 +-- 5 files changed, 84 insertions(+), 28 deletions(-) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 06c974c798..5c15378b6a 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -66,6 +66,7 @@ android { dependencies { implementation project(':fake-signature') implementation project(':play-services-auth') + implementation project(':play-services-base-core') implementation "com.squareup.wire:wire-runtime:$wireVersion" implementation "com.android.volley:volley:$volleyVersion" diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java index ff9992f7df..6e4dcbb984 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java @@ -79,10 +79,10 @@ public abstract class LicenseChecker { static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; - public abstract Request createRequest(String packageName, String auth, int versionCode, D data, + public abstract LicenseRequest createRequest(String packageName, String auth, int versionCode, D data, BiConsumer then, Response.ErrorListener errorListener); - public void checkLicense(Account account, AccountManager accountManager, + public void checkLicense(Account account, AccountManager accountManager, String androidId, String packageName, PackageManager packageManager, RequestQueue queue, D queryData, BiConsumerWithException onResult) @@ -118,8 +118,9 @@ public void checkLicense(Account account, AccountManager accountManager, future -> { try { String auth = future.getResult().getString(KEY_AUTHTOKEN); - Request request = createRequest(packageName, auth, + LicenseRequest request = createRequest(packageName, auth, versionCode, queryData, onRequestFinished, onRequestError); + request.ANDROID_ID = Long.decode("0x" + androidId); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { @@ -149,7 +150,7 @@ private static void safeSendResult( public static class V1 extends LicenseChecker> { @Override - public Request createRequest(String packageName, String auth, int versionCode, Long nonce, BiConsumer> then, + public LicenseRequest createRequest(String packageName, String auth, int versionCode, Long nonce, BiConsumer> then, Response.ErrorListener errorListener) { return new LicenseRequest.V1( packageName, auth, versionCode, nonce, response -> { @@ -170,7 +171,7 @@ public Request createRequest(String packageName, String auth, int v public static class V2 extends LicenseChecker { @Override - public Request createRequest(String packageName, String auth, int versionCode, Unit data, + public LicenseRequest createRequest(String packageName, String auth, int versionCode, Unit data, BiConsumer then, Response.ErrorListener errorListener) { return new LicenseRequest.V2( packageName, auth, versionCode, response -> { diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java index 08d6e28ab3..71ec31cf26 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java @@ -31,21 +31,27 @@ import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.VolleyError; +import com.google.android.gms.common.BuildConfig; + +import org.microg.gms.profile.Build; import java.io.IOException; +import java.net.URLEncoder; +import java.util.Arrays; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import okio.ByteString; public abstract class LicenseRequest extends Request { - private final String xPsRh; private final String auth; private static final String TAG = "FakeLicenseRequest"; private static final int BASE64_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING; - private static final long ANDROID_ID = 1; + long ANDROID_ID = 1; + private static final String FINSKY_VERSION = "Finsky/37.5.24-29%20%5B0%5D%20%5BPR%5D%20565477504"; private final Response.Listener successListener; @@ -55,6 +61,10 @@ protected LicenseRequest(String url, String auth, Response.Listener successLi this.auth = auth; this.successListener = successListener; + } + + @Override + public Map getHeaders() { long millis = System.currentTimeMillis(); TimestampContainer.Builder timestamp = new TimestampContainer.Builder() @@ -107,9 +117,9 @@ protected LicenseRequest(String url, String auth, Response.Listener successLi .deviceMeta(new DeviceMeta.Builder() .android( new AndroidVersionMeta.Builder() - .androidSdk(0) - .buildNumber("") - .androidVersion("") + .androidSdk(Build.VERSION.SDK_INT) + .buildNumber(Build.ID) + .androidVersion(Build.VERSION.RELEASE) .unknown(0) .build() ) @@ -119,13 +129,13 @@ protected LicenseRequest(String url, String auth, Response.Listener successLi .build() ) .userAgent(new UserAgent.Builder() - .deviceProductName("") - .deviceSoc("") - .deviceModelName("") - .finskyVersion("") - .deviceName("") + .deviceName(Build.DEVICE) + .deviceHardware(Build.HARDWARE) + .deviceModelName(Build.MODEL) + .finskyVersion(FINSKY_VERSION) + .deviceProductName(Build.MODEL) .androidId(ANDROID_ID) // must not be 0 - .deviceSignature("") + .buildFingerprint(Build.FINGERPRINT) .build() ) .uuid(new Uuid.Builder() @@ -134,22 +144,29 @@ protected LicenseRequest(String url, String auth, Response.Listener successLi .build() ) .build().encode(); - this.xPsRh = new String(Base64.encode(Util.encodeGzip(header), BASE64_FLAGS)); - - //Log.d(TAG, "Product " + Build.PRODUCT + ", Board " + Build.BOARD + " Model " + Build.MODEL + " Device " + Build.DEVICE); + String xPsRh = new String(Base64.encode(Util.encodeGzip(header), BASE64_FLAGS)); Log.v(TAG, "X-PS-RH: " + xPsRh); - } - @Override - public Map getHeaders() { + String userAgent = FINSKY_VERSION + " (api=3,versionCode=" + BuildConfig.VERSION_CODE + ",sdk=" + Build.VERSION.SDK + + ",device=" + encodeString(Build.DEVICE) + ",hardware=" + encodeString(Build.HARDWARE) + ",product=" + encodeString(Build.PRODUCT) + + ",platformVersionRelease=" + encodeString(Build.VERSION.RELEASE) + ",model=" + encodeString(Build.MODEL) + ",buildId=" + encodeString(Build.ID) + + ",isWideScreen=" + 0 + ",supportedAbis=" + String.join(";", Build.SUPPORTED_ABIS) + ")"; + Log.v(TAG, "User-Agent: " + userAgent); + return Map.of( "X-PS-RH", xPsRh, + "User-Agent", userAgent, "Authorization", "Bearer " + auth, + "Accept-Language", "en-US", "Connection", "Keep-Alive" ); } + private static String encodeString(String s) { + return URLEncoder.encode(s).replace("+", "%20"); + } + @Override protected void deliverResponse(T response) { successListener.onResponse(response); diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index a193199bcd..9f0602261b 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -18,15 +18,24 @@ import android.os.IBinder; import android.os.RemoteException; import android.net.Uri; +import android.net.Uri; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.provider.Settings; import android.util.Log; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; import org.microg.gms.auth.AuthConstants; +import org.microg.gms.profile.Build; +import org.microg.gms.profile.ProfileManager; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedList; +import java.util.Map; import java.util.Queue; import kotlin.Unit; @@ -43,6 +52,8 @@ public class LicensingService extends Service { private static final String PREFERENCE_LICENSING_ENABLED = "play_licensing"; + private static final Uri PROFILE_PROVIDER = Uri.parse("content://com.google.android.gms.microg.profile"); + private String androidId; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { @@ -93,7 +104,7 @@ public void checkLicense(long nonce, String packageName, ILicenseResultListener private void checkLicense(long nonce, String packageName, PackageManager packageManager, ILicenseResultListener listener, Queue remainingAccounts) throws RemoteException { new LicenseChecker.V1().checkLicense( - remainingAccounts.poll(), accountManager, packageName, packageManager, + remainingAccounts.poll(), accountManager, androidId, packageName, packageManager, queue, nonce, (responseCode, stringTuple) -> { if (responseCode != LICENSED && !remainingAccounts.isEmpty()) { @@ -128,7 +139,7 @@ private void checkLicenseV2(String packageName, PackageManager packageManager, ILicenseV2ResultListener listener, Bundle extraParams, Queue remainingAccounts) throws RemoteException { new LicenseChecker.V2().checkLicense( - remainingAccounts.poll(), accountManager, packageName, packageManager, queue, Unit.INSTANCE, + remainingAccounts.poll(), accountManager, androidId, packageName, packageManager, queue, Unit.INSTANCE, (responseCode, data) -> { /* * Suppress failures on V2. V2 is commonly used by free apps whose checker @@ -167,6 +178,32 @@ private void handleNoAccounts(String packageName, PackageManager packageManager) }; public IBinder onBind(Intent intent) { + + + Cursor cursor = null; + try { + cursor = getContentResolver().query( + PROFILE_PROVIDER, null, null, null, null + ); + + if (cursor == null || cursor.getColumnCount() != 2) { + Log.e(TAG, "profile provider not available"); + } else { + Map profileData = new HashMap<>(); + while (cursor.moveToNext()) { + profileData.put(cursor.getString(0), cursor.getString(1)); + } + ProfileManager.INSTANCE.applyProfileData(profileData); + } + + } finally { + if (cursor != null) { + cursor.close(); + } + } + + androidId = String.valueOf(Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID)); + queue = Volley.newRequestQueue(this); accountManager = AccountManager.get(this); notificationRunnable = new LicenseServiceNotificationRunnable(this); diff --git a/vending-app/src/main/proto/LicenseRequest.proto b/vending-app/src/main/proto/LicenseRequest.proto index da8747c2cd..dc0b71339e 100644 --- a/vending-app/src/main/proto/LicenseRequest.proto +++ b/vending-app/src/main/proto/LicenseRequest.proto @@ -56,13 +56,13 @@ message UnknownByte12 { message UserAgent { // The names of these attributes are vague guesses and should be adapted if needed. - optional string deviceProductName = 1; // e.g. "OnePlusNord" - optional string deviceSoc = 2; // e.g. "qcom" + optional string deviceName = 1; // e.g. "OnePlusNord" + optional string deviceHardware = 2; // e.g. "qcom" optional string deviceModelName = 3; // e.g. "OnePlus Nord" optional string finskyVersion = 4; // e.g. "Finsky/37.5.24-29%20%5B0%5D%20%5BPR%5D%20565477504" - optional string deviceName = 5; // e.g. "OnePlusNord"; difference to 1 not yet clear + optional string deviceProductName = 5; // e.g. "OnePlusNord" optional uint64 androidId = 6; - optional string deviceSignature = 7; // e.g. "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys" + optional string buildFingerprint = 7; // e.g. "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys" } message Uuid { From 12cf1ea8046ca77cac4bfa5a505777d09ab6a129 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Sun, 10 Dec 2023 00:38:40 +0100 Subject: [PATCH 19/33] Apply review --- .../main/java/com/android/vending/licensing/LicenseRequest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java index 71ec31cf26..843716f086 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseRequest.java @@ -40,7 +40,6 @@ import java.util.Arrays; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; import okio.ByteString; From 0d410519f8bde8bb1d3bb1fe0b58472b538e8cfd Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Tue, 19 Dec 2023 09:32:25 +0100 Subject: [PATCH 20/33] Use Google androidId for Licensing Licensing app feature expects a Google AndroidId. --- .../vending/licensing/LicenseChecker.java | 6 ++++- .../vending/licensing/LicensingService.java | 25 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java index 6e4dcbb984..1b38e23c47 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java @@ -120,7 +120,11 @@ public void checkLicense(Account account, AccountManager accountManager, String String auth = future.getResult().getString(KEY_AUTHTOKEN); LicenseRequest request = createRequest(packageName, auth, versionCode, queryData, onRequestFinished, onRequestError); - request.ANDROID_ID = Long.decode("0x" + androidId); + + if (androidId != null) { + request.ANDROID_ID = Long.parseLong(androidId, 16); + } + request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index 9f0602261b..f821dd558c 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -22,7 +22,6 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.provider.Settings; import android.util.Log; import com.android.volley.RequestQueue; @@ -45,16 +44,17 @@ public class LicensingService extends Service { private RequestQueue queue; private AccountManager accountManager; private LicenseServiceNotificationRunnable notificationRunnable; + private String androidId; private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; - private static final Uri SETTINGS_PROVIDER = Uri.parse("content://com.google.android.gms.microg.settings/play"); + private static final Uri PROFILE_PROVIDER = Uri.parse("content://com.google.android.gms.microg.profile"); - private static final String PREFERENCE_LICENSING_ENABLED = "play_licensing"; + private static final Uri CHECKIN_SETTINGS_PROVIDER = Uri.parse("content://com.google.android.gms.microg.settings/check-in"); - private static final Uri PROFILE_PROVIDER = Uri.parse("content://com.google.android.gms.microg.profile"); + private static final Uri SETTINGS_PROVIDER = Uri.parse("content://com.google.android.gms.microg.settings/play"); - private String androidId; + private static final String PREFERENCE_LICENSING_ENABLED = "play_licensing"; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { @@ -202,7 +202,20 @@ public IBinder onBind(Intent intent) { } } - androidId = String.valueOf(Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID)); + try { + cursor = getContentResolver().query( + CHECKIN_SETTINGS_PROVIDER, new String[] { "androidId" }, null, null, null + ); + + if (cursor != null) { + cursor.moveToNext(); + androidId = Long.toHexString(cursor.getLong(0)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } queue = Volley.newRequestQueue(this); accountManager = AccountManager.get(this); From 413b067a984f274e771019ce02c109252cff3a21 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 24 Oct 2023 07:43:47 +0200 Subject: [PATCH 21/33] Check preference from within Licensing service --- vending-app/src/main/AndroidManifest.xml | 1 + .../java/com/android/vending/licensing/LicensingService.java | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index bd61b73bad..bfc542132b 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ + Date: Thu, 11 Jan 2024 23:15:09 +0100 Subject: [PATCH 22/33] Fix swapped docstrings --- .../java/com/android/vending/licensing/LicenseChecker.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java index 1b38e23c47..83e76c2f07 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java @@ -44,12 +44,12 @@ public abstract class LicenseChecker { */ static final int LICENSED = 0x0; /** - * The application is licensed to the user, but there is an updated application version available that is - * signed with a different key. + * The application is not licensed to the user. */ static final int NOT_LICENSED = 0x1; /** - * The application is not licensed to the user. + * The application is licensed to the user, but there is an updated application version available that is + * signed with a different key. */ static final int LICENSED_OLD_KEY = 0x2; /** From d99a1407090bbcf5950895c3814ff14c39d8a119 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 11 Jan 2024 23:38:34 +0100 Subject: [PATCH 23/33] Fix Ignore on notification not working --- .../licensing/LicenseServiceNotificationRunnable.java | 7 ++++++- .../com/android/vending/licensing/LicensingService.java | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java index 7a82341ba2..34567d1d9a 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java @@ -70,7 +70,7 @@ public void run() { ignoreIntent.putExtra(INTENT_KEY_IGNORE_PACKAGE_NAME, callerPackageName); ignoreIntent.putExtra(INTENT_KEY_NOTIFICATION_ID, callerUid); PendingIntent ignorePendingIntent = PendingIntent.getBroadcast( - context, callerUid * 2 + 1, ignoreIntent, PendingIntent.FLAG_IMMUTABLE + context, callerUid * 2 + 1, ignoreIntent, PendingIntent.FLAG_MUTABLE ); Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID) @@ -128,6 +128,11 @@ public void onReceive(Context context, Intent intent) { ); String newIgnorePackage = intent.getStringExtra(INTENT_KEY_IGNORE_PACKAGE_NAME); + if (newIgnorePackage == null) { + Log.e(TAG, "Received no ignore package; can't add to ignore list."); + return; + } + Log.d(TAG, "Adding package " + newIgnorePackage + " to ignore list"); ignoreList.add(newIgnorePackage); diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java index 95b4a02723..c66e9b7274 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java @@ -163,11 +163,15 @@ private void checkLicenseV2(String packageName, PackageManager packageManager, } private void handleNoAccounts(String packageName, PackageManager packageManager) { + notificationRunnable.callerPackageName = packageName; try { Log.e(TAG, "not checking license, as user is not signed in"); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); + if (notificationRunnable.callerAppName == null) { + notificationRunnable.callerAppName = packageName; + } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "ignored license request, but package name " + packageName + " was not known!"); notificationRunnable.callerAppName = packageName; From 3d48fcc3dbd805dc61aabda38ce882d3b3eba500 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 11 Jan 2024 23:49:35 +0100 Subject: [PATCH 24/33] Enable multidex --- vending-app/build.gradle | 3 +++ vending-app/src/main/AndroidManifest.xml | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 5c15378b6a..96dac40cc9 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -17,6 +17,8 @@ android { versionCode vendingAppVersionCode minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk + + multiDexEnabled true } flavorDimensions = ['target'] @@ -70,6 +72,7 @@ dependencies { implementation "com.squareup.wire:wire-runtime:$wireVersion" implementation "com.android.volley:volley:$volleyVersion" + implementation "androidx.multidex:multidex:$multidexVersion" } wire { diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index bfc542132b..6916b5df5a 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -24,7 +24,8 @@ android:forceQueryable="true" android:icon="@mipmap/ic_app" android:roundIcon="@mipmap/ic_app" - android:label="@string/app_name"> + android:label="@string/app_name" + android:name="androidx.multidex.MultiDexApplication"> Date: Mon, 22 Jan 2024 19:34:06 +0100 Subject: [PATCH 25/33] Check for `POST_NOTIFICATIONS` permission --- .../licensing/LicenseServiceNotificationRunnable.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java index 34567d1d9a..daec6121a3 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java @@ -1,5 +1,6 @@ package com.android.vending.licensing; +import android.Manifest; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -8,10 +9,11 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.os.Build; import android.util.Log; -import androidx.annotation.CallSuper; +import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; @@ -92,8 +94,10 @@ public void run() { .build(); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - notificationManager.notify(callerUid, notification); - + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED) { + notificationManager.notify(callerUid, notification); + } } private void registerNotificationChannel() { From e3f31bfdbd05542f829d4510397feeecc3839f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8Jonathan?= <¨jonathan.klee@e.email¨> Date: Wed, 17 Jan 2024 09:09:20 +0100 Subject: [PATCH 26/33] Use big text style so that the text is displayed fully --- .../vending/licensing/LicenseServiceNotificationRunnable.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java index daec6121a3..84a17dacc3 100644 --- a/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java +++ b/vending-app/src/main/java/com/android/vending/licensing/LicenseServiceNotificationRunnable.java @@ -75,11 +75,13 @@ public void run() { context, callerUid * 2 + 1, ignoreIntent, PendingIntent.FLAG_MUTABLE ); + String contentText = context.getString(R.string.license_notification_body); Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setSound(null) .setContentTitle(context.getString(R.string.license_notification_title, callerAppName)) - .setContentText(context.getString(R.string.license_notification_body)) + .setContentText(contentText) + .setStyle(new NotificationCompat.BigTextStyle().bigText(contentText)) .addAction( new NotificationCompat.Action.Builder( null, context.getString(R.string.license_notification_sign_in), authPendingIntent From c9f42f72e19a9f3cc2ccefa6e913af5f77d2ed52 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 22 Jan 2024 20:43:14 +0100 Subject: [PATCH 27/33] Disable `GetLocales` lint Like f0b15d275a0c4ec3625033d651414d396e368320 --- vending-app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 96dac40cc9..8cc4f93a12 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -52,7 +52,7 @@ android { } lintOptions { - disable 'MissingTranslation' + disable 'MissingTranslation', 'GetLocales' } compileOptions { From fdcf1745a5b034f5fb2da5a0ded7b382465e02a6 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 29 Jan 2024 13:40:06 +0100 Subject: [PATCH 28/33] Enable postprocessing in vending-app --- vending-app/build.gradle | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 8cc4f93a12..468e45c1ea 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -21,6 +21,25 @@ android { multiDexEnabled true } + buildTypes { + debug { + postprocessing { + removeUnusedCode true + removeUnusedResources true + obfuscate false + optimizeCode true + } + } + release { + postprocessing { + removeUnusedCode true + removeUnusedResources true + obfuscate false + optimizeCode true + } + } + } + flavorDimensions = ['target'] productFlavors { "default" { From 62081a823c16b49066d6b52660cfd5a9c6ed07b4 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 29 Jan 2024 13:40:24 +0100 Subject: [PATCH 29/33] Revert "Enable multidex" This reverts commit da7bb3d46bc25f8b53c68f3c56197cbf7b6993f5. --- vending-app/build.gradle | 3 --- vending-app/src/main/AndroidManifest.xml | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/vending-app/build.gradle b/vending-app/build.gradle index 468e45c1ea..f57b700b52 100644 --- a/vending-app/build.gradle +++ b/vending-app/build.gradle @@ -17,8 +17,6 @@ android { versionCode vendingAppVersionCode minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk - - multiDexEnabled true } buildTypes { @@ -91,7 +89,6 @@ dependencies { implementation "com.squareup.wire:wire-runtime:$wireVersion" implementation "com.android.volley:volley:$volleyVersion" - implementation "androidx.multidex:multidex:$multidexVersion" } wire { diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml index 6916b5df5a..bfc542132b 100644 --- a/vending-app/src/main/AndroidManifest.xml +++ b/vending-app/src/main/AndroidManifest.xml @@ -24,8 +24,7 @@ android:forceQueryable="true" android:icon="@mipmap/ic_app" android:roundIcon="@mipmap/ic_app" - android:label="@string/app_name" - android:name="androidx.multidex.MultiDexApplication"> + android:label="@string/app_name"> Date: Sat, 17 Feb 2024 04:41:08 +0800 Subject: [PATCH 30/33] Add dummy service for PANORAMA(3) (#2173) Co-authored-by: Marvin W --- play-services-core/build.gradle | 1 + .../src/main/AndroidManifest.xml | 1 - play-services-panorama/build.gradle | 41 ++++++++++++++ play-services-panorama/core/build.gradle | 56 +++++++++++++++++++ .../core/src/main/AndroidManifest.xml | 15 +++++ .../microg/gms/panorama/PanoramaService.kt | 31 ++++++++++ .../src/AndroidManifest.xml | 6 ++ .../google/android/gms/panorama/Panorama.java | 30 ++++++++++ .../android/gms/panorama/PanoramaApi.java | 50 +++++++++++++++++ .../panorama/internal/IPanoramaCallbacks.aidl | 11 ++++ .../panorama/internal/IPanoramaService.aidl | 13 +++++ settings.gradle | 2 + 12 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 play-services-panorama/build.gradle create mode 100644 play-services-panorama/core/build.gradle create mode 100644 play-services-panorama/core/src/main/AndroidManifest.xml create mode 100644 play-services-panorama/core/src/main/kotlin/org/microg/gms/panorama/PanoramaService.kt create mode 100644 play-services-panorama/src/AndroidManifest.xml create mode 100644 play-services-panorama/src/main/aidl/com/google/android/gms/panorama/Panorama.java create mode 100644 play-services-panorama/src/main/aidl/com/google/android/gms/panorama/PanoramaApi.java create mode 100644 play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaCallbacks.aidl create mode 100644 play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaService.aidl diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index 1c4a9df24a..424c785c47 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation project(':play-services-location-core') implementation project(':play-services-location-core-base') implementation project(':play-services-oss-licenses-core') + implementation project(':play-services-panorama-core') implementation project(':play-services-pay-core') implementation project(':play-services-recaptcha-core') implementation project(':play-services-safetynet-core') diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 53cf29cb55..b0ec393f6f 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -931,7 +931,6 @@ - diff --git a/play-services-panorama/build.gradle b/play-services-panorama/build.gradle new file mode 100644 index 0000000000..2c230a6777 --- /dev/null +++ b/play-services-panorama/build.gradle @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +android { + namespace "com.google.android.gms.panorama" + + 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-panorama' + +dependencies { + // Dependencies from play-services-panorama:17.1.0 + api project(":play-services-base") + api project(":play-services-basement") + api project(":play-services-tasks") +} diff --git a/play-services-panorama/core/build.gradle b/play-services-panorama/core/build.gradle new file mode 100644 index 0000000000..81ce810d7b --- /dev/null +++ b/play-services-panorama/core/build.gradle @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +dependencies { + api project(':play-services-panorama') + + implementation project(':play-services-base-core') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion" +} + +android { + namespace "org.microg.gms.panorama.core" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + sourceSets { + main { + java.srcDirs = ['src/main/kotlin'] + } + } + + lintOptions { + disable 'MissingTranslation' + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + + kotlinOptions { + jvmTarget = 1.8 + } +} + +// Nothing to publish yet +//apply from: '../gradle/publish-android.gradle' + +description = 'microG service implementation for play-services-panorama' diff --git a/play-services-panorama/core/src/main/AndroidManifest.xml b/play-services-panorama/core/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..fbe8d24413 --- /dev/null +++ b/play-services-panorama/core/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/play-services-panorama/core/src/main/kotlin/org/microg/gms/panorama/PanoramaService.kt b/play-services-panorama/core/src/main/kotlin/org/microg/gms/panorama/PanoramaService.kt new file mode 100644 index 0000000000..382a698cb6 --- /dev/null +++ b/play-services-panorama/core/src/main/kotlin/org/microg/gms/panorama/PanoramaService.kt @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package org.microg.gms.panorama + +import android.net.Uri +import android.os.Bundle +import android.util.Log +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.common.internal.GetServiceRequest +import com.google.android.gms.common.internal.IGmsCallbacks +import com.google.android.gms.panorama.internal.IPanoramaCallbacks +import com.google.android.gms.panorama.internal.IPanoramaService +import org.microg.gms.BaseService +import org.microg.gms.common.GmsService + +const val TAG = "PanoramaService" + +class PanoramaService : BaseService(TAG, GmsService.PANORAMA) { + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + callback.onPostInitComplete(CommonStatusCodes.SUCCESS, PanoramaServiceImpl().asBinder(), null) + } +} + +class PanoramaServiceImpl : IPanoramaService.Stub() { + override fun loadPanoramaInfo(callback: IPanoramaCallbacks?, uri: Uri, bundle: Bundle, needGrantReadUriPermissions: Boolean) { + Log.d(TAG, "Not implemented! $uri bundle:$bundle") + runCatching { callback?.onPanoramaResult(CommonStatusCodes.SUCCESS, null, 0, null) } + } +} diff --git a/play-services-panorama/src/AndroidManifest.xml b/play-services-panorama/src/AndroidManifest.xml new file mode 100644 index 0000000000..210a05c167 --- /dev/null +++ b/play-services-panorama/src/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + diff --git a/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/Panorama.java b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/Panorama.java new file mode 100644 index 0000000000..005b0f7609 --- /dev/null +++ b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/Panorama.java @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.panorama; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient; + +/** + * The main entry point for panorama integration. + */ +@Deprecated +public class Panorama { + /** + * Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Panorama features. + */ + @Deprecated + public static final Api API = null; + + /** + * The entry point for interacting with the Panorama API. + */ + @Deprecated + public static final PanoramaApi PanoramaApi = null; +} diff --git a/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/PanoramaApi.java b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/PanoramaApi.java new file mode 100644 index 0000000000..9c255f22fc --- /dev/null +++ b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/PanoramaApi.java @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.panorama; + +import android.content.Intent; +import android.net.Uri; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Result; + +/** + * The main entry point for interacting with Panorama viewer. This class provides methods for obtaining an Intent to view a Panorama. + */ +@Deprecated +public interface PanoramaApi { + /** + * Loads information about a panorama. + * + * @param uri the URI of the panorama to load info about. May be a file:, content:, or android_resource: scheme. + */ + @Deprecated + PendingResult loadPanoramaInfo(GoogleApiClient client, Uri uri); + + /** + * Loads information about a panorama from a content provider. This method will also explicitly grant and revoke access to + * the URI while the load is happening so images in content providers may be inspected without giving permission to an + * entire content provider. The returned viewer intent will also have the {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} set so + * the viewer has access. + */ + @Deprecated + PendingResult loadPanoramaInfoAndGrantAccess(GoogleApiClient client, Uri uri); + + /** + * Result interface for loading panorama info. + */ + @Deprecated + interface PanoramaResult extends Result { + /** + * Returns if the image is a panorama this is not null and will launch a viewer when started. If the image is not a panorama this will be null. + */ + @Deprecated + Intent getViewerIntent(); + } +} diff --git a/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaCallbacks.aidl b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaCallbacks.aidl new file mode 100644 index 0000000000..58b92427eb --- /dev/null +++ b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaCallbacks.aidl @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.panorama.internal; + +import android.content.Intent; + +interface IPanoramaCallbacks { + void onPanoramaResult(int statusCode, in Bundle statusExtras, int unknown, in Intent viewerIntent); +} \ No newline at end of file diff --git a/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaService.aidl b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaService.aidl new file mode 100644 index 0000000000..3bada8be22 --- /dev/null +++ b/play-services-panorama/src/main/aidl/com/google/android/gms/panorama/internal/IPanoramaService.aidl @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.panorama.internal; + +import com.google.android.gms.panorama.internal.IPanoramaCallbacks; +import android.os.Bundle; +import android.net.Uri; + +interface IPanoramaService { + void loadPanoramaInfo(IPanoramaCallbacks callback, in Uri uri, in Bundle bundle, boolean needGrantReadUriPermissions) = 0; +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7ad47bc2ea..e1831588e9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -46,6 +46,7 @@ include ':play-services-measurement-base' sublude ':play-services-mlkit:barcode-scanning' include ':play-services-nearby' include ':play-services-oss-licenses' +include ':play-services-panorama' include ':play-services-pay' include ':play-services-phenotype' include ':play-services-places' @@ -97,6 +98,7 @@ sublude ':play-services-maps:core:vtm:microg-theme' if (hasModule("nearby", true)) sublude ':play-services-nearby:core' if (hasModule("nearby", true)) sublude ':play-services-nearby:core:package' sublude ':play-services-oss-licenses:core' +sublude ':play-services-panorama:core' sublude ':play-services-pay:core' sublude ':play-services-safetynet:core' sublude ':play-services-recaptcha:core' From e4c1c5a67157f03b7ab8e2cd64801138c7e425c7 Mon Sep 17 00:00:00 2001 From: DaVinci9196 <150454414+DaVinci9196@users.noreply.github.com> Date: Sat, 17 Feb 2024 04:43:35 +0800 Subject: [PATCH 31/33] fixed & app map layer display issues (#2161) --- .../org/microg/gms/maps/hms/utils/typeConverter.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt index 64cb463f6b..d7c542b83d 100644 --- a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt +++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt @@ -107,10 +107,12 @@ fun GmsGroundOverlayOptions.toHms(): GroundOverlayOptions { .image(image.remoteObject.unwrap()) .visible(isVisible) .zIndex(zIndex) - if (height > 0) { - groundOverlayOptions.position(location.toHms(), width, height) - } else { - groundOverlayOptions.position(location.toHms(), width) + location?.let { + if (height > 0) { + groundOverlayOptions.position(it.toHms(), width, height) + } else { + groundOverlayOptions.position(it.toHms(), width) + } } bounds?.let { groundOverlayOptions.positionFromBounds(it.toHms()) } return groundOverlayOptions From 37dd2b59e4d14c75625a3b10c801115e08bcfbff Mon Sep 17 00:00:00 2001 From: DaVinci9196 <150454414+DaVinci9196@users.noreply.github.com> Date: Sat, 17 Feb 2024 22:23:56 +0800 Subject: [PATCH 32/33] Add dummy service for SEMANTIC_LOCATION(173) (#2174) Co-authored-by: Marvin W --- .../SemanticLocationEventRequest.aidl | 7 +++ .../internal/ISemanticLocationService.aidl | 18 ++++++ .../internal/SemanticLocationParameters.aidl | 7 +++ .../SemanticLocationEventRequest.java | 32 +++++++++++ .../internal/SemanticLocationParameters.java | 38 +++++++++++++ .../org/microg/gms/common/GmsService.java | 1 + .../src/main/AndroidManifest.xml | 7 ++- .../SemanticLocationService.kt | 56 +++++++++++++++++++ 8 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/ISemanticLocationService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.aidl create mode 100644 play-services-api/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.java create mode 100644 play-services-core/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationService.kt diff --git a/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.aidl b/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.aidl new file mode 100644 index 0000000000..715a75c020 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.aidl @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.semanticlocation; + +parcelable SemanticLocationEventRequest; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/ISemanticLocationService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/ISemanticLocationService.aidl new file mode 100644 index 0000000000..de75deffa5 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/ISemanticLocationService.aidl @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.semanticlocation.internal; + +import android.app.PendingIntent; +import android.os.IInterface; +import com.google.android.gms.semanticlocation.SemanticLocationEventRequest; +import com.google.android.gms.semanticlocation.internal.SemanticLocationParameters; +import com.google.android.gms.common.api.internal.IStatusCallback; + +interface ISemanticLocationService { + void registerSemanticLocationEvents(in SemanticLocationParameters params, IStatusCallback callback, in SemanticLocationEventRequest request, in PendingIntent pendingIntent) = 0; + void unregisterSemanticLocationEvents(in SemanticLocationParameters params, IStatusCallback callback, in PendingIntent pendingIntent) = 1; + + void setIncognitoMode(in SemanticLocationParameters params, IStatusCallback callback, boolean mode) = 4; +} \ No newline at end of file diff --git a/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.aidl b/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.aidl new file mode 100644 index 0000000000..1ac959ebfe --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.aidl @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.semanticlocation.internal; + +parcelable SemanticLocationParameters; diff --git a/play-services-api/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.java b/play-services-api/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.java new file mode 100644 index 0000000000..65ee1379c0 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationEventRequest.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.semanticlocation; + + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class SemanticLocationEventRequest extends AbstractSafeParcelable { + @Field(1) + public final float position; + + @Constructor + public SemanticLocationEventRequest(@Param(1) float position) { + this.position = position; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(SemanticLocationEventRequest.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.java b/play-services-api/src/main/java/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.java new file mode 100644 index 0000000000..3dc753ebc4 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/semanticlocation/internal/SemanticLocationParameters.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.semanticlocation.internal; + +import android.accounts.Account; +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class SemanticLocationParameters extends AbstractSafeParcelable { + @Field(1) + public final Account account; + @Field(2) + public final String clientIdentifier; + @Field(3) + public final String packageName; + + @Constructor + public SemanticLocationParameters(@Param(1) Account account, @Param(2) String clientIdentifier, @Param(3) String packageName) { + this.account = account; + this.clientIdentifier = clientIdentifier; + this.packageName = packageName; + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(SemanticLocationParameters.class); + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } +} 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 00cfbb8a76..17e4d6ef30 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 @@ -124,6 +124,7 @@ public enum GmsService { AD_HTTP(166, "com.google.android.gms.ads.service.HTTP"), LANGUAGE_PROFILE(167, "com.google.android.gms.languageprofile.service.START"), MDNS(168, "com.google.android.gms.mdns.service.START"), + SEMANTIC_LOCATION(173, "com.google.android.gms.semanticlocation.service.START_ODLH"), FIDO2_ZEROPARTY(180, "com.google.android.gms.fido.fido2.zeroparty.START"), G1_RESTORE(181, "com.google.android.gms.backup.G1_RESTORE"), G1_BACKUP(182, "com.google.android.gms.backup.G1_BACKUP"), diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index b0ec393f6f..ff1260caed 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -835,6 +835,12 @@ + + + + + + @@ -943,7 +949,6 @@ - diff --git a/play-services-core/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationService.kt b/play-services-core/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationService.kt new file mode 100644 index 0000000000..70b3653861 --- /dev/null +++ b/play-services-core/src/main/java/com/google/android/gms/semanticlocation/SemanticLocationService.kt @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package com.google.android.gms.semanticlocation + +import android.app.PendingIntent +import android.util.Log +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.Feature +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 com.google.android.gms.semanticlocation.internal.ISemanticLocationService +import com.google.android.gms.semanticlocation.internal.SemanticLocationParameters +import org.microg.gms.BaseService +import org.microg.gms.common.GmsService + +private const val TAG = "SemanticLocationService" + +private val FEATURES = arrayOf( + Feature("semanticlocation_events", 1L), +) + +class SemanticLocationService : BaseService(TAG, GmsService.SEMANTIC_LOCATION) { + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + val connectionInfo = ConnectionInfo().apply { + features = FEATURES + } + callback.onPostInitCompleteWithConnectionInfo( + ConnectionResult.SUCCESS, + SemanticLocationServiceImpl().asBinder(), + connectionInfo + ) + } +} + +class SemanticLocationServiceImpl : ISemanticLocationService.Stub() { + override fun registerSemanticLocationEvents( + params: SemanticLocationParameters, + callback: IStatusCallback, + request: SemanticLocationEventRequest, + pendingIntent: PendingIntent + ) { + Log.d(TAG, "registerSemanticLocationEvents: $params") + } + + override fun setIncognitoMode(params: SemanticLocationParameters, callback: IStatusCallback, mode: Boolean) { + Log.d(TAG, "setIncognitoMode: $params") + } + + override fun unregisterSemanticLocationEvents(params: SemanticLocationParameters, callback: IStatusCallback, pendingIntent: PendingIntent) { + Log.d(TAG, "unregisterSemanticLocationEvents: $params") + } +} From 9ee35e3c7c713b157f151e62408a88af85c1daa0 Mon Sep 17 00:00:00 2001 From: DaVinci9196 <150454414+DaVinci9196@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:21:54 +0800 Subject: [PATCH 33/33] Add dummy services for PSEUDONYMOUS_ID(52), GASS(116), ACCOUNT_DATA(153) and APP_SET(300) (#2175) Co-authored-by: Marvin W --- .../gms/gass/internal/GassRequestParcel.aidl | 8 ++ .../gms/gass/internal/GassResponseParcel.aidl | 8 ++ .../gms/gass/internal/IGassService.aidl | 16 ++++ .../gms/pseudonymous/PseudonymousIdToken.aidl | 8 ++ .../internal/IPseudonymousIdCallbacks.aidl | 15 ++++ .../internal/IPseudonymousIdService.aidl | 15 ++++ .../gms/gass/internal/GassRequestParcel.java | 32 ++++++++ .../gms/gass/internal/GassResponseParcel.java | 49 ++++++++++++ .../gms/pseudonymous/PseudonymousIdToken.java | 34 +++++++++ play-services-appset/build.gradle | 37 +++++++++ play-services-appset/core/build.gradle | 56 ++++++++++++++ .../core/src/main/AndroidManifest.xml | 16 ++++ .../org/microg/gms/appset/AppSetService.kt | 40 ++++++++++ .../src/main/AndroidManifest.xml | 4 + .../gms/appset/AppSetIdRequestParams.aidl | 8 ++ .../android/gms/appset/AppSetInfoParcel.aidl | 8 ++ .../appset/internal/IAppSetIdCallback.aidl | 13 ++++ .../gms/appset/internal/IAppSetService.aidl | 13 ++++ .../com/google/android/gms/appset/AppSet.java | 25 +++++++ .../android/gms/appset/AppSetIdClient.java | 25 +++++++ .../android/gms/appset/AppSetIdInfo.java | 75 +++++++++++++++++++ .../gms/appset/AppSetIdRequestParams.java | 45 +++++++++++ .../android/gms/appset/AppSetInfoParcel.java | 38 ++++++++++ .../android/gms/appset/package-info.java | 12 +++ .../DeviceManagementInfoResponse.aidl | 8 ++ .../DeviceManagementInfoResponse.java | 44 +++++++++++ .../account/data/IAccountDataService.aidl | 16 ++++ .../data/IDeviceManagementInfoCallback.aidl | 13 ++++ .../org/microg/gms/common/GmsService.java | 1 + play-services-core/build.gradle | 1 + .../src/main/AndroidManifest.xml | 21 +++++- .../auth/account/data/AccountDataService.kt | 52 +++++++++++++ .../kotlin/org/microg/gms/gass/GassService.kt | 39 ++++++++++ .../gms/pseudonymous/PseudonymousIdService.kt | 52 +++++++++++++ settings.gradle | 2 + 35 files changed, 846 insertions(+), 3 deletions(-) create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassRequestParcel.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassResponseParcel.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/gass/internal/IGassService.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/PseudonymousIdToken.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdCallbacks.aidl create mode 100644 play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdService.aidl create mode 100644 play-services-api/src/main/java/com/google/android/gms/gass/internal/GassRequestParcel.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/gass/internal/GassResponseParcel.java create mode 100644 play-services-api/src/main/java/com/google/android/gms/pseudonymous/PseudonymousIdToken.java create mode 100644 play-services-appset/build.gradle create mode 100644 play-services-appset/core/build.gradle create mode 100644 play-services-appset/core/src/main/AndroidManifest.xml create mode 100644 play-services-appset/core/src/main/kotlin/org/microg/gms/appset/AppSetService.kt create mode 100644 play-services-appset/src/main/AndroidManifest.xml create mode 100644 play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetIdRequestParams.aidl create mode 100644 play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetInfoParcel.aidl create mode 100644 play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetIdCallback.aidl create mode 100644 play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetService.aidl create mode 100644 play-services-appset/src/main/java/com/google/android/gms/appset/AppSet.java create mode 100644 play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdClient.java create mode 100644 play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdInfo.java create mode 100644 play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdRequestParams.java create mode 100644 play-services-appset/src/main/java/com/google/android/gms/appset/AppSetInfoParcel.java create mode 100644 play-services-appset/src/main/java/com/google/android/gms/appset/package-info.java create mode 100644 play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.aidl create mode 100644 play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.java create mode 100644 play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IAccountDataService.aidl create mode 100644 play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IDeviceManagementInfoCallback.aidl create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/AccountDataService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/gass/GassService.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/pseudonymous/PseudonymousIdService.kt diff --git a/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassRequestParcel.aidl b/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassRequestParcel.aidl new file mode 100644 index 0000000000..ffe2ae733b --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassRequestParcel.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.gass.internal; + +parcelable GassRequestParcel; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassResponseParcel.aidl b/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassResponseParcel.aidl new file mode 100644 index 0000000000..f8f40a73c9 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/GassResponseParcel.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.gass.internal; + +parcelable GassResponseParcel; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/IGassService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/IGassService.aidl new file mode 100644 index 0000000000..7fafe56aa0 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/gass/internal/IGassService.aidl @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.gass.internal; + +import android.os.Bundle; +import android.os.IInterface; +import com.google.android.gms.gass.internal.GassRequestParcel; +import com.google.android.gms.gass.internal.GassResponseParcel; + +interface IGassService { + GassResponseParcel getGassResponse(in GassRequestParcel gassRequestParcel) = 0; + Bundle getGassBundle(in Bundle bundle, int code) = 3; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/PseudonymousIdToken.aidl b/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/PseudonymousIdToken.aidl new file mode 100644 index 0000000000..b8d88c14dd --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/PseudonymousIdToken.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.pseudonymous; + +parcelable PseudonymousIdToken; diff --git a/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdCallbacks.aidl b/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdCallbacks.aidl new file mode 100644 index 0000000000..f6a47ded67 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdCallbacks.aidl @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.pseudonymous.internal; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.pseudonymous.PseudonymousIdToken; + +interface IPseudonymousIdCallbacks { + void onGetTokenResponse(in Status status, in PseudonymousIdToken token) = 0; + void onSetTokenResponse(in Status status) = 1; + void onGetLastResetWallTimeMsResponse(in Status status, long time) = 2; +} diff --git a/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdService.aidl b/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdService.aidl new file mode 100644 index 0000000000..e99f7df675 --- /dev/null +++ b/play-services-api/src/main/aidl/com/google/android/gms/pseudonymous/internal/IPseudonymousIdService.aidl @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.pseudonymous.internal; + +import com.google.android.gms.pseudonymous.internal.IPseudonymousIdCallbacks; +import com.google.android.gms.pseudonymous.PseudonymousIdToken; + +interface IPseudonymousIdService { + void getToken(IPseudonymousIdCallbacks call) = 0; + void setToken(IPseudonymousIdCallbacks call, in PseudonymousIdToken token) = 1; + void getLastResetWallTimeMs(IPseudonymousIdCallbacks callbacks) = 2; +} diff --git a/play-services-api/src/main/java/com/google/android/gms/gass/internal/GassRequestParcel.java b/play-services-api/src/main/java/com/google/android/gms/gass/internal/GassRequestParcel.java new file mode 100644 index 0000000000..666696835d --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/gass/internal/GassRequestParcel.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.gass.internal; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class GassRequestParcel extends AbstractSafeParcelable { + + @Field(1) + public int versionCode; + @Field(2) + public String packageName; + @Field(3) + public String appVersionCode; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GassRequestParcel.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/gass/internal/GassResponseParcel.java b/play-services-api/src/main/java/com/google/android/gms/gass/internal/GassResponseParcel.java new file mode 100644 index 0000000000..34e2c61745 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/gass/internal/GassResponseParcel.java @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.gass.internal; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import com.google.android.gms.feedback.ErrorReport; + +@SafeParcelable.Class +public class GassResponseParcel extends AbstractSafeParcelable { + + @Field(1) + public int versionCode; + + @Field(2) + public byte[] data; + + public ErrorReport report; + + public GassResponseParcel() { + } + + public GassResponseParcel(int i, byte[] bArr) { + this.versionCode = i; + this.report = null; + this.data = bArr; + } + + public GassResponseParcel(ErrorReport report) { + this.versionCode = 1; + this.report = report; + this.data = null; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GassResponseParcel.class); +} diff --git a/play-services-api/src/main/java/com/google/android/gms/pseudonymous/PseudonymousIdToken.java b/play-services-api/src/main/java/com/google/android/gms/pseudonymous/PseudonymousIdToken.java new file mode 100644 index 0000000000..18df15cb08 --- /dev/null +++ b/play-services-api/src/main/java/com/google/android/gms/pseudonymous/PseudonymousIdToken.java @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.pseudonymous; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class PseudonymousIdToken extends AbstractSafeParcelable { + + @Field(2) + public String name; + + @Constructor + public PseudonymousIdToken(@Param(2) String name) { + this.name = name; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(PseudonymousIdToken.class); + +} diff --git a/play-services-appset/build.gradle b/play-services-appset/build.gradle new file mode 100644 index 0000000000..cb838e7e5e --- /dev/null +++ b/play-services-appset/build.gradle @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' + +android { + namespace "com.google.android.gms.appset" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + buildFeatures { + aidl = true + } + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +description = 'microG implementation of play-services-appset' + +dependencies { + api project(':play-services-base') + api project(':play-services-basement') + + annotationProcessor project(':safe-parcel-processor') +} diff --git a/play-services-appset/core/build.gradle b/play-services-appset/core/build.gradle new file mode 100644 index 0000000000..c6017e1a20 --- /dev/null +++ b/play-services-appset/core/build.gradle @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +dependencies { + api project(':play-services-appset') + + implementation project(':play-services-base-core') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion" +} + +android { + namespace "org.microg.gms.appset.core" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + sourceSets { + main { + java.srcDirs = ['src/main/kotlin'] + } + } + + lintOptions { + disable 'MissingTranslation' + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + + kotlinOptions { + jvmTarget = 1.8 + } +} + +// Nothing to publish yet +//apply from: '../gradle/publish-android.gradle' + +description = 'microG service implementation for play-services-appset' diff --git a/play-services-appset/core/src/main/AndroidManifest.xml b/play-services-appset/core/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..0df43222f7 --- /dev/null +++ b/play-services-appset/core/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/play-services-appset/core/src/main/kotlin/org/microg/gms/appset/AppSetService.kt b/play-services-appset/core/src/main/kotlin/org/microg/gms/appset/AppSetService.kt new file mode 100644 index 0000000000..a024f498d4 --- /dev/null +++ b/play-services-appset/core/src/main/kotlin/org/microg/gms/appset/AppSetService.kt @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.appset + +import android.util.Log +import com.google.android.gms.appset.AppSetIdRequestParams +import com.google.android.gms.appset.internal.IAppSetIdCallback +import com.google.android.gms.appset.internal.IAppSetService +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.Feature +import com.google.android.gms.common.api.Status +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 + +private const val TAG = "AppSetService" +private val FEATURES = arrayOf(Feature("app_set_id", 1L)) + +class AppSetService : BaseService(TAG, GmsService.APP_SET) { + + override fun handleServiceRequest(callback: IGmsCallbacks?, request: GetServiceRequest?, service: GmsService?) { + callback?.onPostInitCompleteWithConnectionInfo( + ConnectionResult.SUCCESS, + AppSetServiceImpl().asBinder(), + ConnectionInfo().apply { features = FEATURES } + ) + } +} + +class AppSetServiceImpl : IAppSetService.Stub() { + override fun getAppSetIdInfo(params: AppSetIdRequestParams?, callback: IAppSetIdCallback?) { + Log.d(TAG, "AppSetServiceImp getAppSetIdInfo is called -> ${params?.toString()} ") + callback?.onAppSetInfo(Status.SUCCESS, null) + } +} diff --git a/play-services-appset/src/main/AndroidManifest.xml b/play-services-appset/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..8bdb7e14b3 --- /dev/null +++ b/play-services-appset/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetIdRequestParams.aidl b/play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetIdRequestParams.aidl new file mode 100644 index 0000000000..5beef70820 --- /dev/null +++ b/play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetIdRequestParams.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.appset; + +parcelable AppSetIdRequestParams; diff --git a/play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetInfoParcel.aidl b/play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetInfoParcel.aidl new file mode 100644 index 0000000000..c2d4818acf --- /dev/null +++ b/play-services-appset/src/main/aidl/com/google/android/gms/appset/AppSetInfoParcel.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.appset; + +parcelable AppSetInfoParcel; diff --git a/play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetIdCallback.aidl b/play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetIdCallback.aidl new file mode 100644 index 0000000000..1b453de3dd --- /dev/null +++ b/play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetIdCallback.aidl @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.appset.internal; + +import com.google.android.gms.appset.AppSetInfoParcel; +import com.google.android.gms.common.api.Status; + +interface IAppSetIdCallback { + void onAppSetInfo(in Status status, in AppSetInfoParcel info) = 0; +} \ No newline at end of file diff --git a/play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetService.aidl b/play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetService.aidl new file mode 100644 index 0000000000..59dbb6f926 --- /dev/null +++ b/play-services-appset/src/main/aidl/com/google/android/gms/appset/internal/IAppSetService.aidl @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.appset.internal; + +import com.google.android.gms.appset.AppSetIdRequestParams; +import com.google.android.gms.appset.internal.IAppSetIdCallback; + +interface IAppSetService { + void getAppSetIdInfo(in AppSetIdRequestParams params, in IAppSetIdCallback callback) = 0; +} diff --git a/play-services-appset/src/main/java/com/google/android/gms/appset/AppSet.java b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSet.java new file mode 100644 index 0000000000..f361652754 --- /dev/null +++ b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSet.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.appset; + +import android.content.Context; +import androidx.annotation.NonNull; + +/** + * Entry point of the app set APIs. + */ +public class AppSet { + /** + * Creates a new instance of {@link AppSetIdClient}. + */ + @NonNull + public static AppSetIdClient getClient (Context context) { + throw new UnsupportedOperationException(); + } +} diff --git a/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdClient.java b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdClient.java new file mode 100644 index 0000000000..91b6a671c4 --- /dev/null +++ b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdClient.java @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.appset; + +import androidx.annotation.NonNull; +import com.google.android.gms.tasks.Task; + +/** + * A client for interacting with the {@link AppSetIdInfo} API. + */ +public interface AppSetIdClient { + /** + * Gets the AppSetIdInfo asynchronously. + * + * @return a {@link Task} of the returned {@link AppSetIdInfo}. + */ + @NonNull + Task getAppSetIdInfo(); +} diff --git a/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdInfo.java b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdInfo.java new file mode 100644 index 0000000000..29608f5fb8 --- /dev/null +++ b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdInfo.java @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ + +package com.google.android.gms.appset; + +import androidx.annotation.IntDef; +import org.microg.gms.common.Hide; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Contains information about app set ID. + */ +public class AppSetIdInfo { + /** + * The app set ID is scoped to the app. + */ + public static final int SCOPE_APP = 1; + /** + * The app set ID is scoped to a developer account on an app store. All apps from the same developer on a device will have + * the same developer scoped app set ID. + */ + public static final int SCOPE_DEVELOPER = 2; + + private final String id; + private final @Scope int scope; + + @Hide + public AppSetIdInfo(String id, @Scope int scope) { + this.id = id; + this.scope = scope; + } + + /** + * Gets the app set ID. + * + * @return the app set ID. + */ + public String getId() { + return id; + } + + /** + * Returns the {@link AppSetIdInfo.Scope} of the app set ID. Possible values include {@link #SCOPE_APP} and {@link #SCOPE_DEVELOPER}. + * + * @return the app set ID's {@link AppSetIdInfo.Scope}. + */ + public @Scope int getScope() { + return scope; + } + + /** + * Allowed constants for {@link AppSetIdInfo#getScope()}. + *

+ * Supported constants: + *

    + *
  • {@link #SCOPE_APP}
  • + *
  • {@link #SCOPE_DEVELOPER}
  • + *
+ */ + @Target({ElementType.TYPE_USE}) + @Retention(RetentionPolicy.SOURCE) + @IntDef({SCOPE_APP, SCOPE_DEVELOPER}) + public @interface Scope { + + } +} diff --git a/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdRequestParams.java b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdRequestParams.java new file mode 100644 index 0000000000..2bc9b0d019 --- /dev/null +++ b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetIdRequestParams.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.appset; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +import org.microg.gms.common.Hide; +import org.microg.gms.utils.ToStringHelper; + +@SafeParcelable.Class +@Hide +public class AppSetIdRequestParams extends AbstractSafeParcelable { + @Field(1) + public final String version; + @Field(2) + public final String clientAppPackageName; + + @Constructor + public AppSetIdRequestParams(@Param(1) String version, @Param(2) String clientAppPackageName) { + this.version = version; + this.clientAppPackageName = clientAppPackageName; + } + + @NonNull + @Override + public String toString() { + return ToStringHelper.name("AppSetIdRequestParams").field("version", version).field("clientAppPackageName", clientAppPackageName).end(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(AppSetIdRequestParams.class); +} diff --git a/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetInfoParcel.java b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetInfoParcel.java new file mode 100644 index 0000000000..dd47b947df --- /dev/null +++ b/play-services-appset/src/main/java/com/google/android/gms/appset/AppSetInfoParcel.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.appset; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import org.microg.gms.common.Hide; + +@SafeParcelable.Class +@Hide +public class AppSetInfoParcel extends AbstractSafeParcelable { + @Field(1) + public final String id; + @Field(2) + public final int scope; + + @Constructor + public AppSetInfoParcel(@Param(1) String id, @Param(2) int scope) { + this.id = id; + this.scope = scope; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(AppSetInfoParcel.class); + +} diff --git a/play-services-appset/src/main/java/com/google/android/gms/appset/package-info.java b/play-services-appset/src/main/java/com/google/android/gms/appset/package-info.java new file mode 100644 index 0000000000..e9ab491d25 --- /dev/null +++ b/play-services-appset/src/main/java/com/google/android/gms/appset/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: CC-BY-4.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. + */ +/** + * For analytics or fraud prevention use cases, on a given device you may + need to correlate usage or actions across a set of apps owned by your organization. + */ +package com.google.android.gms.appset; diff --git a/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.aidl b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.aidl new file mode 100644 index 0000000000..950fc2fbb3 --- /dev/null +++ b/play-services-auth-base/src/main/aidl/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +parcelable DeviceManagementInfoResponse; diff --git a/play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.java b/play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.java new file mode 100644 index 0000000000..bb93f63a9a --- /dev/null +++ b/play-services-auth-base/src/main/java/com/google/android/gms/auth/firstparty/dataservice/DeviceManagementInfoResponse.java @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.firstparty.dataservice; + +import android.os.Parcel; + +import androidx.annotation.NonNull; + +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class DeviceManagementInfoResponse extends AbstractSafeParcelable { + @Field(1) + public final int versionCode; + @Field(2) + public final String info; + @Field(3) + public final boolean status; + + + @Constructor + public DeviceManagementInfoResponse(@Param(1) int versionCode, @Param(2) String info, @Param(3) boolean status) { + this.versionCode = versionCode; + this.info = info; + this.status = status; + } + + public DeviceManagementInfoResponse(String info, boolean status) { + this(1, info, status); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(DeviceManagementInfoResponse.class); + +} diff --git a/play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IAccountDataService.aidl b/play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IAccountDataService.aidl new file mode 100644 index 0000000000..2fd6c8540b --- /dev/null +++ b/play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IAccountDataService.aidl @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.auth.account.data.IDeviceManagementInfoCallback; +import android.accounts.Account; +import com.google.android.gms.common.api.internal.IStatusCallback; + +interface IAccountDataService { + void requestDeviceManagementInfo(in IDeviceManagementInfoCallback callback, in Account account) = 0; + void requestAccountInfo(in IStatusCallback callback, in Account account, boolean isPrimary) = 1; + void requestProfileInfo(in IStatusCallback callback, String profile) = 2; +} diff --git a/play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IDeviceManagementInfoCallback.aidl b/play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IDeviceManagementInfoCallback.aidl new file mode 100644 index 0000000000..801beac3ff --- /dev/null +++ b/play-services-auth/src/main/aidl/com/google/android/gms/auth/account/data/IDeviceManagementInfoCallback.aidl @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.auth.account.data; + +import com.google.android.gms.auth.firstparty.dataservice.DeviceManagementInfoResponse; +import com.google.android.gms.common.api.Status; + +interface IDeviceManagementInfoCallback { + void onResult(in Status status, in DeviceManagementInfoResponse response); +} 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 17e4d6ef30..b926f6f369 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 @@ -168,6 +168,7 @@ public enum GmsService { 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"), + APP_SET(300, "com.google.android.gms.appset.service.START"), MODULE_INSTALL(308, "com.google.android.gms.chimera.container.moduleinstall.ModuleInstallService.START"), IN_APP_REACH(315, "com.google.android.gms.inappreach.service.START"), APP_ERRORS(334, "com.google.android.gms.apperrors.service.START_APP_ERROR"), diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index 424c785c47..43f7ea95f3 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(':play-services-ads-identifier-core') implementation project(':play-services-ads-lite-core') implementation project(':play-services-appinvite-core') + implementation project(':play-services-appset-core') implementation project(':play-services-auth-api-phone-core') implementation project(':play-services-base-core') implementation project(':play-services-cast-core') diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index ff1260caed..de2e72a643 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -835,6 +835,24 @@
+ + + + + + + + + + + + + + + + + + @@ -863,7 +881,6 @@ - @@ -910,7 +927,6 @@ - @@ -944,7 +960,6 @@ - diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/AccountDataService.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/AccountDataService.kt new file mode 100644 index 0000000000..e81ae066c9 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/account/data/AccountDataService.kt @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.auth.account.data + +import android.accounts.Account +import android.util.Log +import com.google.android.gms.auth.account.data.IAccountDataService +import com.google.android.gms.auth.account.data.IDeviceManagementInfoCallback +import com.google.android.gms.auth.firstparty.dataservice.DeviceManagementInfoResponse +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.Feature +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 + +private const val TAG = "AccountDataService" + +class AccountDataService : BaseService(TAG, GmsService.ACCOUNT_DATA) { + + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + callback.onPostInitCompleteWithConnectionInfo( + ConnectionResult.SUCCESS, + AccountDataServiceImpl().asBinder(), + ConnectionInfo().apply { features = FEATURES } + ) + } + +} + +class AccountDataServiceImpl : IAccountDataService.Stub() { + override fun requestDeviceManagementInfo(callback: IDeviceManagementInfoCallback, account: Account?) { + Log.d(TAG, "requestDeviceManagementInfo is called ") + callback.onResult(Status.SUCCESS, DeviceManagementInfoResponse(null, false)) + } + + override fun requestAccountInfo(callback: IStatusCallback, account: Account?, isPrimary: Boolean) { + Log.d(TAG, "requestAccountInfo is called ") + callback.onResult(Status.SUCCESS) + } + + override fun requestProfileInfo(callback: IStatusCallback, profile: String?) { + Log.d(TAG, "requestProfileInfo is called ") + callback.onResult(Status.SUCCESS) + } +} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gass/GassService.kt b/play-services-core/src/main/kotlin/org/microg/gms/gass/GassService.kt new file mode 100644 index 0000000000..afff107fef --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/gass/GassService.kt @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ +package org.microg.gms.gass + +import android.os.Bundle +import android.util.Log +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.internal.GetServiceRequest +import com.google.android.gms.common.internal.IGmsCallbacks +import com.google.android.gms.gass.internal.GassRequestParcel +import com.google.android.gms.gass.internal.GassResponseParcel +import com.google.android.gms.gass.internal.IGassService +import org.microg.gms.BaseService +import org.microg.gms.common.GmsService + +private const val TAG = "GassService" + +class GassService : BaseService(TAG, GmsService.GASS) { + + override fun handleServiceRequest(callback: IGmsCallbacks?, request: GetServiceRequest?, service: GmsService?) { + callback?.onPostInitComplete(ConnectionResult.SUCCESS, GassServiceImpl().asBinder(), null) + } + +} + +class GassServiceImpl : IGassService.Stub() { + override fun getGassBundle(bundle: Bundle?, code: Int): Bundle? { + Log.d(TAG, "GassServiceImpl getGassBundle is Called") + return null + } + + override fun getGassResponse(gassRequestParcel: GassRequestParcel?): GassResponseParcel? { + Log.d(TAG, "GassServiceImpl getGassResponse is Called") + return null + } + +} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/pseudonymous/PseudonymousIdService.kt b/play-services-core/src/main/kotlin/org/microg/gms/pseudonymous/PseudonymousIdService.kt new file mode 100644 index 0000000000..7750ed3a4d --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/pseudonymous/PseudonymousIdService.kt @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.pseudonymous + +import android.util.Log +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.internal.ConnectionInfo +import com.google.android.gms.common.internal.GetServiceRequest +import com.google.android.gms.common.internal.IGmsCallbacks +import com.google.android.gms.pseudonymous.PseudonymousIdToken +import com.google.android.gms.pseudonymous.internal.IPseudonymousIdCallbacks +import com.google.android.gms.pseudonymous.internal.IPseudonymousIdService +import org.microg.gms.BaseService +import org.microg.gms.common.GmsService + +private const val TAG = "PseudonymousIdService" +private val FEATURES = arrayOf(Feature("get_last_reset_time_api", 1L)) + +class PseudonymousIdService : BaseService(TAG, GmsService.PSEUDONYMOUS_ID) { + + override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) { + Log.e(TAG, " PseudonymousIdService handleServiceRequest is start") + callback.onPostInitCompleteWithConnectionInfo( + CommonStatusCodes.SUCCESS, + PseudonymousIdServiceImpl().asBinder(), + ConnectionInfo().apply { features = FEATURES } + ) + } +} + +class PseudonymousIdServiceImpl : IPseudonymousIdService.Stub() { + + override fun getToken(call: IPseudonymousIdCallbacks) { + Log.d(TAG, "PseudonymousIdServiceImpl onResponse is called") + call.onGetTokenResponse(Status.SUCCESS, PseudonymousIdToken(null)) + } + + override fun setToken(call: IPseudonymousIdCallbacks, token: PseudonymousIdToken?) { + Log.d(TAG, "PseudonymousIdServiceImpl onResponseToken is called") + call.onSetTokenResponse(Status.SUCCESS) + } + + override fun getLastResetWallTimeMs(callbacks: IPseudonymousIdCallbacks?) { + Log.d(TAG, "Not yet implemented: getLastResetWallTimeMs") + } + +} diff --git a/settings.gradle b/settings.gradle index e1831588e9..8da636b3a1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,6 +25,7 @@ include ':play-services-ads-base' include ':play-services-ads-identifier' include ':play-services-ads-lite' include ':play-services-appinvite' +include ':play-services-appset' include ':play-services-auth' include ':play-services-auth-api-phone' include ':play-services-auth-base' @@ -76,6 +77,7 @@ sublude ':play-services-ads:core' sublude ':play-services-ads-identifier:core' sublude ':play-services-ads-lite:core' sublude ':play-services-appinvite:core' +sublude ':play-services-appset:core' sublude ':play-services-auth-api-phone:core' sublude ':play-services-base:core' sublude ':play-services-base:core:package'