From b19fad128147ebbffaac26bc436ea8cd4025e2cf Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:11:04 +0900 Subject: [PATCH] Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f --- build.gradle | 19 + .../gms/common/GoogleApiAvailability.java | 192 +++++++++ .../android/gms/common/api/AccountInfo.java | 2 +- .../google/android/gms/common/api/Api.java | 18 + .../android/gms/common/api/GoogleApi.java | 42 +- .../gms/common/api/GoogleApiActivity.java | 28 ++ .../gms/common/api/GoogleApiClient.java | 38 +- .../android/gms/common/api/PendingResult.java | 43 ++ .../ApiClient.java => DummyApiClient.java} | 28 +- .../java/org/microg/gms/common/GmsClient.java | 204 ++++++++++ .../org/microg/gms/common/GmsConnector.java | 92 +++++ .../gms/common/MultiConnectionKeeper.java | 229 +++++++++++ .../gms/common/api/AbstractPendingResult.java | 4 + .../gms/common/api/ApiClientBuilder.java | 2 +- .../gms/common/api/ApiClientSettings.java | 10 + .../gms/common/api/GoogleApiClientImpl.java | 102 ++++- .../gms/common/api/GoogleApiManager.java | 177 ++++++++ .../gms/common/api/PendingGoogleApiCall.java | 13 + .../gms/common/api/ResultCallbackHandler.java | 14 + .../common/api/ReturningGoogleApiCall.java | 22 + .../api/VoidReturningGoogleApiCall.java | 23 ++ play-services-basement/build.gradle | 2 +- .../gms/common/internal/ConnectionInfo.aidl | 7 + .../gms/common/internal/IGmsCallbacks.aidl | 8 + .../gms/common/internal/ConnectionInfo.java | 31 ++ .../internal/ValidateAccountRequest.java | 16 +- .../safeparcel/AbstractSafeParcelable.java | 24 ++ .../internal/safeparcel/SafeParcelReader.java | 328 +++++++++++++++ .../internal/safeparcel/SafeParcelWriter.java | 384 ++++++++++++++++++ .../internal/safeparcel/SafeParcelable.java | 55 +++ .../SafeParcelableCreatorAndWriter.java | 13 + .../safeparcel/SafeParcelableSerializer.java | 30 ++ .../main/java/org/microg/gms/common/Hide.java | 19 + .../java/org/microg/gms/common/PublicApi.java | 2 + .../gms/auth/login/AssistantActivity.java | 4 +- 35 files changed, 2204 insertions(+), 21 deletions(-) rename play-services-base/src/main/java/org/microg/gms/common/{api/ApiClient.java => DummyApiClient.java} (56%) create mode 100644 play-services-base/src/main/java/org/microg/gms/common/GmsClient.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java create mode 100644 play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java create mode 100644 play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java create mode 100644 play-services-basement/src/main/java/org/microg/gms/common/Hide.java diff --git a/build.gradle b/build.gradle index 2c713c7c..3a471c38 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,13 @@ */ buildscript { +<<<<<<< HEAD ext.kotlinVersion = '1.8.20' +======= + ext.safeParcelVersion = '1.7.1' + + ext.kotlinVersion = '1.7.21' +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) ext.coroutineVersion = '1.6.4' ext.annotationVersion = '1.6.0' @@ -65,4 +71,17 @@ subprojects { mavenCentral() google() } +<<<<<<< HEAD } +======= + afterEvaluate { + // Temporary hack for Android Studio + if (project.plugins.hasPlugin('com.android.base')) { + dependencies { + compileOnly "org.microg:safe-parcel:$safeParcelVersion" + testCompileOnly "org.microg:safe-parcel:$safeParcelVersion" + } + } + } +} +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java index 61ee1ec0..50ff0e23 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java @@ -50,4 +50,196 @@ public class GoogleApiAvailability { return null; } +<<<<<<< HEAD +======= + /** + * Returns a PendingIntent to address the provided connection failure. + *

+ * If {@link ConnectionResult#hasResolution()} is true, then {@link ConnectionResult#getResolution()} + * will be returned. Otherwise, the returned PendingIntent will direct the user to either the + * Play Store if Google Play services is out of date or missing, or system settings if Google + * Play services is disabled on the device. + * + * @param context parent context for creating the PendingIntent. + * @param result the connection failure. If successful or the error is not resolvable by the user, null is returned. + */ + public PendingIntent getErrorResolutionPendingIntent(Context context, ConnectionResult result) { + if (result.hasResolution()) { + return result.getResolution(); + } + return getErrorResolutionPendingIntent(context, result.getErrorCode(), 0); + } + + /** + * Returns a PendingIntent to address the provided errorCode. It will direct the user to either + * the Play Store if Google Play services is out of date or missing, or system settings if + * Google Play services is disabled on the device. + * + * @param context parent context for creating the PendingIntent. + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param requestCode The requestCode given when calling startActivityForResult. + */ + public PendingIntent getErrorResolutionPendingIntent(Context context, int errorCode, int requestCode) { + // TODO + return null; + } + + /** + * Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable(Context)}. + */ + public final String getErrorString(int errorCode) { + return ConnectionResult.getStatusString(errorCode); + } + + /** + * Verifies that Google Play services is installed and enabled on this device, and that the + * version installed on this device is no older than the one required by this client. + * + * @return status code indicating whether there was an error. Can be one of following in + * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING, + * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID + */ + public int isGooglePlayServicesAvailable(Context context) { + Log.d(TAG, "As we can't know right now if the later desired feature is available, " + + "we just pretend it to be."); + return SUCCESS; + } + + /** + * Verifies that Google Play services is installed and enabled on this device, and that the version installed on + * this device is no older than the one required by this client or the version is not older than the one specified + * in minApkVersion. + * + * @return status code indicating whether there was an error. Can be one of following in + * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING, + * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID + */ + public int isGooglePlayServicesAvailable(Context context, int minApkVersion) { + return isGooglePlayServicesAvailable(context); + } + + /** + * Determines whether an error can be resolved via user action. If true, proceed by calling + * {@link #getErrorDialog(Activity, int, int)} and showing the dialog. + * + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)}, or + * returned to your application via {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} + * @return true if the error is resolvable with {@link #getErrorDialog(Activity, int, int)} + */ + public final boolean isUserResolvableError(int errorCode) { + switch (errorCode) { + case SERVICE_MISSING: + case SERVICE_VERSION_UPDATE_REQUIRED: + case SERVICE_DISABLED: + case SERVICE_INVALID: + return true; + case SIGN_IN_REQUIRED: + case INVALID_ACCOUNT: + case RESOLUTION_REQUIRED: + case NETWORK_ERROR: + case INTERNAL_ERROR: + default: + return false; + } + } + + /** + * Attempts to make Google Play services available on this device. If Play Services is already + * available, the returned {@link Task} may complete immediately. + *

+ * If it is necessary to display UI in order to complete this request (e.g. sending the user + * to the Google Play store) the passed {@link Activity} will be used to display this UI. + *

+ * It is recommended to call this method from {@link Activity#onCreate(Bundle)}. + * If the passed {@link Activity} completes before the returned {@link Task} completes, the + * Task will fail with a {@link java.util.concurrent.CancellationException}. + *

+ * This method must be called from the main thread. + * + * @return A {@link Task}. If this Task completes without throwing an exception, Play Services + * is available on this device. + */ + public Task makeGooglePlayServicesAvailable(Activity activity) { + int status = isGooglePlayServicesAvailable(activity); + if (status == SUCCESS) { + return Tasks.forResult(null); + } + // TODO + return Tasks.forResult(null); + } + + /** + * Displays a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}. + * + * @param activity parent activity for creating the dialog, also used for identifying language to display dialog in. + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param requestCode The requestCode given when calling startActivityForResult. + * @return true if the dialog is shown, false otherwise + * @throws RuntimeException if API level is below 11 and activity is not a {@link FragmentActivity}. + * @see ErrorDialogFragment + * @see SupportErrorDialogFragmet + */ + public boolean showErrorDialogFragment(Activity activity, int errorCode, int requestCode) { + return showErrorDialogFragment(activity, errorCode, requestCode, null); + } + + /** + * Displays a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}. + * + * @param activity parent activity for creating the dialog, also used for identifying language to display dialog in. + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param requestCode The requestCode given when calling startActivityForResult. + * @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog is canceled. + * @return true if the dialog is shown, false otherwise + * @throws RuntimeException if API level is below 11 and activity is not a {@link FragmentActivity}. + */ + public boolean showErrorDialogFragment(Activity activity, int errorCode, int requestCode, DialogInterface.OnCancelListener cancelListener) { + Dialog dialog = getErrorDialog(activity, errorCode, requestCode, cancelListener); + if (dialog == null) { + return false; + } else { + // TODO + return false; + } + } + + /** + * Displays a notification for an error code returned from + * {@link #isGooglePlayServicesAvailable(Context)}, if it is resolvable by the user. + *

+ * This method is similar to {@link #getErrorDialog(int, android.app.Activity, int)}, but is + * provided for background tasks that cannot or should not display dialogs. + * + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param context used for identifying language to display dialog in as well as accessing the + * {@link android.app.NotificationManager}. + */ + public void showErrorNotification(Context context, int errorCode) { + if (errorCode == RESOLUTION_REQUIRED) { + Log.e(TAG, "showErrorNotification(context, errorCode) is called for RESOLUTION_REQUIRED when showErrorNotification(context, result) should be called"); + } + + if (isUserResolvableError(errorCode)) { + GooglePlayServicesUtil.showErrorNotification(errorCode, context); + } + } + + /** + * Displays a notification for a connection failure, if it is resolvable by the user. + * + * @param context The calling context used to display the notification. + * @param result The connection failure. If successful or the error is not resolvable by the + * user, no notification is shown. + */ + public void showErrorNotification(Context context, ConnectionResult result) { + PendingIntent pendingIntent = getErrorResolutionPendingIntent(context, result); + if (pendingIntent != null) { + // TODO + } + } +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java b/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java index fd400c45..30d6a169 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java @@ -20,5 +20,5 @@ import org.microg.safeparcel.AutoSafeParcelable; @Deprecated public class AccountInfo extends AutoSafeParcelable { - public static final Creator CREATOR = new AutoCreator<>(AccountInfo.class); + public static final Creator CREATOR = new AutoCreator(AccountInfo.class); } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java index 6b7f15e3..a6942637 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java @@ -16,6 +16,14 @@ package com.google.android.gms.common.api; +<<<<<<< HEAD +======= +import android.accounts.Account; + +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; + +import org.microg.gms.common.Hide; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.ApiClientBuilder; @@ -67,4 +75,14 @@ public final class Api { } + @Hide + public interface Client { + void connect(); + + void disconnect(); + + boolean isConnected(); + + boolean isConnecting(); + } } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java index f0adb967..ff165974 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApi.java @@ -7,22 +7,62 @@ package com.google.android.gms.common.api; import android.content.Context; +<<<<<<< HEAD +======= +import com.google.android.gms.common.api.internal.ApiKey; +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.TaskCompletionSource; + +import org.microg.gms.common.Hide; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.GoogleApiManager; @PublicApi +<<<<<<< HEAD public abstract class GoogleApi implements HasApiKey { @PublicApi(exclude = true) +======= +public abstract class GoogleApi implements HasApiKey { + private GoogleApiManager manager; + @Hide +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) public Api api; + @Hide + public O options; @PublicApi(exclude = true) protected GoogleApi(Context context, Api api) { this.api = api; +<<<<<<< HEAD GoogleApiManager.getInstance(); +======= + this.manager = GoogleApiManager.getInstance(context); + } + + @PublicApi(exclude = true) + protected GoogleApi(Context context, Api api, O options) { + this.api = api; + this.manager = GoogleApiManager.getInstance(context); + this.options = options; + } + + @PublicApi(exclude = true) + protected Task scheduleTask(PendingGoogleApiCall apiCall) { + TaskCompletionSource completionSource = new TaskCompletionSource<>(); + manager.scheduleTask(this, apiCall, completionSource); + return completionSource.getTask(); + } + + @Override + @PublicApi(exclude = true) + public ApiKey getApiKey() { + return null; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } @PublicApi(exclude = true) public O getOptions() { - return null; + return options; } } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java index 8a6b0690..e5524cb9 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiActivity.java @@ -1,6 +1,34 @@ +<<<<<<< HEAD package com.google.android.gms.common.api; import android.app.Activity; public class GoogleApiActivity extends Activity { +======= +/* + * SPDX-FileCopyrightText: 2020, 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.common.api; + +import android.app.Activity; +import android.content.DialogInterface; + +import org.microg.gms.common.PublicApi; + +/** + * Helper activity used by Google Play services APIs to display resolutions for connection errors. + */ +@PublicApi +public class GoogleApiActivity extends Activity implements DialogInterface.OnCancelListener { + @Override + @PublicApi(exclude = true) + public void onCancel(DialogInterface dialog) { + + } +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java index 0e20b6d5..a841fd98 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java @@ -111,7 +111,7 @@ public interface GoogleApiClient { * @see #registerConnectionFailedListener(OnConnectionFailedListener) * @see #unregisterConnectionFailedListener(OnConnectionFailedListener) */ - boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener); + public boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener); /** * Registers a listener to receive connection events from this {@link GoogleApiClient}. If the @@ -150,7 +150,7 @@ public interface GoogleApiClient { * @param listener the listener where the results of the asynchronous {@link #connect()} call * are delivered. */ - void registerConnectionFailedListener(OnConnectionFailedListener listener); + public void registerConnectionFailedListener(OnConnectionFailedListener listener); /** * Removes a connection listener from this {@link GoogleApiClient}. Note that removing a @@ -166,10 +166,24 @@ public interface GoogleApiClient { @PublicApi class Builder { private final Context context; +<<<<<<< HEAD private final Map apis = new HashMap<>(); private final Set connectionCallbacks = new HashSet<>(); private final Set connectionFailedListeners = new HashSet<>(); private final Looper looper; +======= + private final Map apis = new HashMap(); + private final Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); + private final Set scopes = new HashSet(); + private String accountName; + private int clientId = -1; + private FragmentActivity fragmentActivity; + private Looper looper; + private int gravityForPopups; + private OnConnectionFailedListener unresolvedConnectionFailedListener; + private View viewForPopups; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) public Builder(Context context) { this.context = context; @@ -198,7 +212,12 @@ public interface GoogleApiClient { } private ApiClientSettings getClientSettings() { - return null; + ApiClientSettings clientSettings = new ApiClientSettings(); + clientSettings.accountName = accountName; + clientSettings.scopes = new HashSet<>(scopes); + clientSettings.gravityForPopups = gravityForPopups; + clientSettings.viewForPopups = viewForPopups; + return clientSettings; } } @@ -210,6 +229,19 @@ public interface GoogleApiClient { @PublicApi @Deprecated interface ConnectionCallbacks extends org.microg.gms.common.api.ConnectionCallbacks { +<<<<<<< HEAD +======= + /** + * A suspension cause informing that the service has been killed. + */ + int CAUSE_SERVICE_DISCONNECTED = 1; + /** + * A suspension cause informing you that a peer device connection was lost. + */ + int CAUSE_NETWORK_LOST = 2; + + void onConnected(Bundle connectionHint); +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } @PublicApi diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java index e5f92160..1e5c462f 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java @@ -16,5 +16,48 @@ package com.google.android.gms.common.api; +<<<<<<< HEAD public interface PendingResult { +======= +import java.util.concurrent.TimeUnit; + +/** + * Represents a pending result from calling an API method in Google Play services. The final result + * object from a PendingResult is of type R, which can be retrieved in one of two ways. + *

+ *

+ * After the result has been retrieved using {@link #await()} or delivered to the result callback, + * it is an error to attempt to retrieve the result again. It is the responsibility of the caller + * or callback receiver to release any resources associated with the returned result. Some result + * types may implement {@link Releasable}, in which case {@link Releasable#release()} should be + * used to free the associated resources. + *

+ * TODO: Docs + */ +public interface PendingResult { + /** + * Blocks until the task is completed. This is not allowed on the UI thread. The returned + * result object can have an additional failure mode of INTERRUPTED. + */ + public R await(); + + /** + * Blocks until the task is completed or has timed out waiting for the result. This is not + * allowed on the UI thread. The returned result object can have an additional failure mode + * of either INTERRUPTED or TIMEOUT. + */ + public R await(long time, TimeUnit unit); + + public void cancel(); + + public boolean isCanceled(); + + public void setResultCallback(ResultCallback callback, long time, TimeUnit unit); + + public void setResultCallback(ResultCallback callback); +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java b/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java similarity index 56% rename from play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java rename to play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java index 602e150e..c527866f 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/DummyApiClient.java @@ -14,14 +14,30 @@ * limitations under the License. */ -package org.microg.gms.common.api; +package org.microg.gms.common; -public interface ApiClient { - void connect(); +import com.google.android.gms.common.api.Api; - void disconnect(); +public class DummyApiClient implements Api.Client { + private boolean connected = false; - boolean isConnected(); + @Override + public void connect() { + connected = true; + } - boolean isConnecting(); + @Override + public void disconnect() { + connected = false; + } + + @Override + public boolean isConnected() { + return connected; + } + + @Override + public boolean isConnecting() { + return false; + } } diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java new file mode 100644 index 00000000..e5b71717 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.common; + +import android.accounts.Account; +import android.content.ComponentName; +import android.content.Context; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.RemoteException; +import android.util.Log; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.common.internal.ConnectionInfo; +import com.google.android.gms.common.internal.GetServiceRequest; +import com.google.android.gms.common.internal.IGmsCallbacks; +import com.google.android.gms.common.internal.IGmsServiceBroker; + +import org.microg.gms.common.api.ConnectionCallbacks; +import org.microg.gms.common.api.OnConnectionFailedListener; + +public abstract class GmsClient implements Api.Client { + private static final String TAG = "GmsClient"; + + private final Context context; + protected final ConnectionCallbacks callbacks; + protected final OnConnectionFailedListener connectionFailedListener; + protected ConnectionState state = ConnectionState.NOT_CONNECTED; + private ServiceConnection serviceConnection; + private I serviceInterface; + private final String actionString; + + protected boolean requireMicrog; + protected String packageName; + + protected int serviceId = -1; + protected Account account = null; + protected Bundle extras = new Bundle(); + + public GmsClient(Context context, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener, String actionString) { + this.context = context; + this.callbacks = callbacks; + this.connectionFailedListener = connectionFailedListener; + this.actionString = actionString; + this.requireMicrog = false; + this.packageName = context.getPackageName(); + } + + protected void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks) throws RemoteException { + if (serviceId == -1) { + throw new IllegalStateException("Service ID not set in constructor and onConnectedToBroker not implemented"); + } + GetServiceRequest request = new GetServiceRequest(serviceId); + request.packageName = packageName; + request.account = account; + request.extras = extras; + broker.getService(callbacks, request); + } + + protected abstract I interfaceFromBinder(IBinder binder); + + @Override + public synchronized void connect() { + Log.d(TAG, "connect()"); + if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) { + Log.d(TAG, "Already connected/connecting - nothing to do"); + } + state = ConnectionState.CONNECTING; + if (serviceConnection != null) { + MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection); + } + serviceConnection = new GmsServiceConnection(); + if (!MultiConnectionKeeper.getInstance(context).bind(actionString, serviceConnection, requireMicrog)) { + state = ConnectionState.ERROR; + handleConnectionFailed(); + } + } + + public void handleConnectionFailed() { + connectionFailedListener.onConnectionFailed(new ConnectionResult(ConnectionResult.API_UNAVAILABLE, null)); + } + + @Override + public synchronized void disconnect() { + Log.d(TAG, "disconnect()"); + if (state == ConnectionState.DISCONNECTING) return; + if (state == ConnectionState.CONNECTING) { + state = ConnectionState.DISCONNECTING; + return; + } + serviceInterface = null; + if (serviceConnection != null) { + MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection); + serviceConnection = null; + } + state = ConnectionState.NOT_CONNECTED; + } + + @Override + public synchronized boolean isConnected() { + return (state == ConnectionState.CONNECTED && serviceInterface != null && serviceInterface.asBinder().isBinderAlive() ) || state == ConnectionState.PSEUDO_CONNECTED; + } + + @Override + public synchronized boolean isConnecting() { + return state == ConnectionState.CONNECTING; + } + + public synchronized boolean hasError() { + return state == ConnectionState.ERROR; + } + + public Context getContext() { + return context; + } + + public synchronized I getServiceInterface() { + if (isConnecting()) { + // TODO: wait for connection to be established and return afterwards. + throw new IllegalStateException("Waiting for connection"); + } else if (!isConnected()) { + throw new IllegalStateException("interface only available once connected!"); + } + return serviceInterface; + } + + protected enum ConnectionState { + NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR, PSEUDO_CONNECTED + } + + private class GmsServiceConnection implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + try { + Log.d(TAG, "ServiceConnection : onServiceConnected(" + componentName + ")"); + onConnectedToBroker(IGmsServiceBroker.Stub.asInterface(iBinder), new GmsCallbacks()); + } catch (RemoteException e) { + disconnect(); + } + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + synchronized (GmsClient.this) { + state = ConnectionState.NOT_CONNECTED; + } + } + } + + public class GmsCallbacks extends IGmsCallbacks.Stub { + + @Override + public void onPostInitComplete(int statusCode, IBinder binder, Bundle params) + throws RemoteException { + if (statusCode != CommonStatusCodes.SUCCESS) { + state = ConnectionState.CONNECTED; + disconnect(); + connectionFailedListener.onConnectionFailed(new ConnectionResult(statusCode)); + } else { + synchronized (GmsClient.this) { + if (state == ConnectionState.DISCONNECTING) { + state = ConnectionState.CONNECTED; + disconnect(); + return; + } + state = ConnectionState.CONNECTED; + serviceInterface = interfaceFromBinder(binder); + } + Log.d(TAG, "GmsCallbacks : onPostInitComplete(" + serviceInterface + ")"); + callbacks.onConnected(params); + } + } + + @Override + public void onAccountValidationComplete(int statusCode, Bundle params) throws RemoteException { + Log.d(TAG, "GmsCallbacks : onAccountValidationComplete"); + } + + @Override + public void onPostInitCompleteWithConnectionInfo(int statusCode, IBinder binder, ConnectionInfo info) throws RemoteException { + onPostInitComplete(statusCode, binder, info == null ? null : info.params); + } + } + +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java new file mode 100644 index 00000000..b77c6d2c --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.common; + +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Result; + +import org.microg.gms.common.api.AbstractPendingResult; +import org.microg.gms.common.api.GoogleApiClientImpl; + +public class GmsConnector { + private static final String TAG = "GmsConnector"; + + private final GoogleApiClientImpl apiClient; + private final Api api; + private final Callback callback; + + public GmsConnector(GoogleApiClient apiClient, Api api, Callback callback) { + this.apiClient = (GoogleApiClientImpl) apiClient; + this.api = api; + this.callback = callback; + } + + public static PendingResult call(GoogleApiClient client, Api api, GmsConnector.Callback callback) { + return new GmsConnector(client, api, callback).connect(); + } + + public AbstractPendingResult connect() { + Log.d(TAG, "connect()"); + apiClient.incrementUsageCounter(); + apiClient.getApiConnection(api); + Looper looper = apiClient.getLooper(); + final AbstractPendingResult result = new AbstractPendingResult(looper); + Message msg = new Message(); + msg.obj = result; + new Handler(looper).sendMessage(msg); + return result; + } + + public interface Callback { + void onClientAvailable(C client, ResultProvider resultProvider) throws RemoteException; + + interface ResultProvider { + void onResultAvailable(R result); + } + } + + private class Handler extends android.os.Handler { + private Handler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + Log.d(TAG, "Handler : handleMessage"); + final AbstractPendingResult result = (AbstractPendingResult) msg.obj; + try { + C connection = (C) apiClient.getApiConnection(api); + callback.onClientAvailable(connection, new GmsConnector.Callback.ResultProvider() { + @Override + public void onResultAvailable(R realResult) { + result.deliverResult(realResult); + apiClient.decrementUsageCounter(); + } + }); + } catch (RemoteException ignored) { + + } + } + } +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java new file mode 100644 index 00000000..30c1d61c --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2013-2017 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.common; + +import android.annotation.SuppressLint; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; +import android.content.pm.ResolveInfo; +import android.os.IBinder; +import android.util.Log; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; + +public class MultiConnectionKeeper { + private static final String TAG = "GmsMultiConKeeper"; + + private static MultiConnectionKeeper INSTANCE; + + private final Context context; + private final Map connections = new HashMap(); + + public MultiConnectionKeeper(Context context) { + this.context = context; + } + + public synchronized static MultiConnectionKeeper getInstance(Context context) { + if (INSTANCE == null) + INSTANCE = new MultiConnectionKeeper(context.getApplicationContext()); + return INSTANCE; + } + + public synchronized boolean bind(String action, ServiceConnection connection) { + return bind(action, connection, false); + } + + public synchronized boolean bind(String action, ServiceConnection connection, boolean requireMicrog) { + Connection con = connections.get(action); + Log.d(TAG, "bind(" + action + ", " + connection + ", " + requireMicrog + ") has=" + (con != null)); + if (con != null) { + if (!con.forwardsConnection(connection)) { + con.addConnectionForward(connection); + if (!con.isBound()) + con.bind(); + } + } else { + con = new Connection(action, requireMicrog); + con.addConnectionForward(connection); + con.bind(); + connections.put(action, con); + } + Log.d(TAG, "bind() : bound=" + con.isBound()); + return con.isBound(); + } + + public synchronized void unbind(String action, ServiceConnection connection) { + Log.d(TAG, "unbind(" + action + ", " + connection + ")"); + Connection con = connections.get(action); + if (con != null) { + con.removeConnectionForward(connection); + if (con.isBound()) { + if (!con.hasForwards()) { + con.unbind(); + connections.remove(action); + } else { + Log.d(TAG, "Not unbinding for " + connection + ": has pending other bindings on action " + action); + } + } + } + } + + public class Connection { + private final String actionString; + private final boolean requireMicrog; + private final Set connectionForwards = new HashSet(); + private boolean bound = false; + private boolean connected = false; + private IBinder binder; + private ComponentName component; + private ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + + "onServiceConnected(" + componentName + ")"); + binder = iBinder; + component = componentName; + for (ServiceConnection connection : connectionForwards) { + connection.onServiceConnected(componentName, iBinder); + } + connected = true; + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + + "onServiceDisconnected(" + componentName + ")"); + binder = null; + component = componentName; + for (ServiceConnection connection : connectionForwards) { + connection.onServiceDisconnected(componentName); + } + connected = false; + bound = false; + } + }; + + public Connection(String actionString) { + this(actionString, false); + } + + public Connection(String actionString, boolean requireMicrog) { + this.actionString = actionString; + this.requireMicrog = requireMicrog; + } + + @SuppressLint("InlinedApi") + public void bind() { + Log.d(TAG, "Connection(" + actionString + ") : bind()"); + Intent gmsIntent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); + Intent selfIntent = new Intent(actionString).setPackage(context.getPackageName()); + Intent intent; + ResolveInfo resolveInfo; + if ((resolveInfo = context.getPackageManager().resolveService(gmsIntent, 0)) == null) { + Log.w(TAG, "No GMS service found for " + actionString); + if (context.getPackageManager().resolveService(selfIntent, 0) != null) { + Log.d(TAG, "Found service for " + actionString + " in self package, using it instead"); + intent = selfIntent; + } else { + return; + } + } else if (requireMicrog && !isMicrog(resolveInfo)) { + Log.w(TAG, "GMS service found for " + actionString + " but looks not like microG"); + if (context.getPackageManager().resolveService(selfIntent, 0) != null) { + Log.d(TAG, "Found service for " + actionString + " in self package, using it instead"); + intent = selfIntent; + } else { + intent = gmsIntent; + } + } else { + intent = gmsIntent; + } + int flags = Context.BIND_AUTO_CREATE | Context.BIND_DEBUG_UNBIND; + if (SDK_INT >= ICE_CREAM_SANDWICH) { + flags |= Context.BIND_ADJUST_WITH_ACTIVITY; + } + bound = context.bindService(intent, serviceConnection, flags); + Log.d(TAG, "Connection(" + actionString + ") : bind() : bindService=" + bound); + if (!bound) { + context.unbindService(serviceConnection); + } + } + + public boolean isMicrog(ResolveInfo resolveInfo) { + if (resolveInfo == null || resolveInfo.serviceInfo == null) return false; + if (resolveInfo.serviceInfo.name.startsWith("org.microg.")) return true; + try { + PermissionInfo info = context.getPackageManager().getPermissionInfo("org.microg.gms.EXTENDED_ACCESS", 0); + return info.packageName.equals(resolveInfo.serviceInfo.packageName); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + public boolean isBound() { + return bound; + } + + public IBinder getBinder() { + return binder; + } + + public void unbind() { + Log.d(TAG, "Connection(" + actionString + ") : unbind()"); + try { + context.unbindService(serviceConnection); + } catch (IllegalArgumentException e) { // not bound (whatever reason) + Log.w(TAG, e); + } + bound = false; + } + + public void addConnectionForward(ServiceConnection connection) { + connectionForwards.add(connection); + if (connected) { + connection.onServiceConnected(component, binder); + } + } + + public void removeConnectionForward(ServiceConnection connection) { + connectionForwards.remove(connection); + if (connected) { + connection.onServiceDisconnected(component); + } + } + + public boolean forwardsConnection(ServiceConnection connection) { + return connectionForwards.contains(connection); + } + + public boolean hasForwards() { + return !connectionForwards.isEmpty(); + } + } +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java index cea4229b..df6b96e3 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java @@ -23,7 +23,11 @@ import com.google.android.gms.common.api.PendingResult; public class AbstractPendingResult implements PendingResult { public AbstractPendingResult(Looper looper) { +<<<<<<< HEAD new ResultCallbackHandler<>(looper); +======= + handler = new ResultCallbackHandler(looper); +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java index b5aa386f..18336fa5 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientBuilder.java @@ -22,5 +22,5 @@ import android.os.Looper; import com.google.android.gms.common.api.Api; public interface ApiClientBuilder { - ApiClient build(O options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener); + Api.Client build(O options, Context context, Looper looper, ApiClientSettings clientSettings, ConnectionCallbacks callbacks, OnConnectionFailedListener connectionFailedListener); } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java index d1e314a7..af67e799 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiClientSettings.java @@ -5,5 +5,15 @@ package org.microg.gms.common.api; +import android.view.View; + +import java.util.Set; + public class ApiClientSettings { + public String accountName; + public String packageName; + public Integer sessionId; + public Set scopes; + public int gravityForPopups; + public View viewForPopups; } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java index 371a6555..2e023fde 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -19,8 +19,15 @@ package org.microg.gms.common.api; import android.content.Context; import android.os.Bundle; import android.os.Looper; + +<<<<<<< HEAD +======= +import androidx.fragment.app.FragmentActivity; + import android.util.Log; +import com.google.android.gms.common.ConnectionResult; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApiClient; @@ -32,9 +39,48 @@ import java.util.Set; public class GoogleApiClientImpl implements GoogleApiClient { private static final String TAG = "GmsApiClientImpl"; +<<<<<<< HEAD private final Map apiConnections = new HashMap<>(); private final Set connectionCallbacks = new HashSet<>(); private final Set connectionFailedListeners = new HashSet<>(); +======= + private final Context context; + private final Looper looper; + private final ApiClientSettings clientSettings; + private final Map apis = new HashMap(); + private final Map apiConnections = new HashMap(); + private final Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); + private final int clientId; + private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { + @Override + public void onConnected(Bundle connectionHint) { + Log.d(TAG, "ConnectionCallbacks : onConnected()"); + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnected(connectionHint); + } + } + + @Override + public void onConnectionSuspended(int cause) { + Log.d(TAG, "ConnectionCallbacks : onConnectionSuspended()"); + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnectionSuspended(cause); + } + } + }; + private final OnConnectionFailedListener baseConnectionFailedListener = new + OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult result) { + Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()"); + for (OnConnectionFailedListener listener : connectionFailedListeners) { + listener.onConnectionFailed(result); + } + } + }; + private int usageCounter = 0; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) private boolean shouldDisconnect = false; public GoogleApiClientImpl(Context context, Looper looper, ApiClientSettings clientSettings, @@ -44,6 +90,10 @@ public class GoogleApiClientImpl implements GoogleApiClient { this.connectionCallbacks.addAll(connectionCallbacks); this.connectionFailedListeners.addAll(connectionFailedListeners); + if (this.clientSettings.sessionId == null) { + this.clientSettings.sessionId = hashCode(); + } + for (Api api : apis.keySet()) { ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { @Override @@ -72,6 +122,41 @@ public class GoogleApiClientImpl implements GoogleApiClient { } } +<<<<<<< HEAD +======= + public synchronized void incrementUsageCounter() { + usageCounter++; + } + + public synchronized void decrementUsageCounter() { + usageCounter--; + if (shouldDisconnect) disconnect(); + } + + public Looper getLooper() { + return looper; + } + + public Api.Client getApiConnection(Api api) { + return apiConnections.get(api); + } + + @Override + public ConnectionResult blockingConnect() { + return null; + } + + @Override + public ConnectionResult blockingConnect(long timeout, TimeUnit unit) { + return null; + } + + @Override + public PendingResult clearDefaultAccountAndReconnect() { + return null; + } + +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) @Override public synchronized void connect() { Log.d(TAG, "connect()"); @@ -83,7 +168,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { Log.d(TAG, "Already connected/connecting, nothing to do"); return; } - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (!connection.isConnected()) { connection.connect(); } @@ -92,17 +177,28 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public synchronized void disconnect() { +<<<<<<< HEAD Log.d(TAG, "disconnect()"); for (ApiClient connection : apiConnections.values()) { if (connection.isConnected()) { connection.disconnect(); +======= + if (usageCounter > 0) { + shouldDisconnect = true; + } else { + Log.d(TAG, "disconnect()"); + for (Api.Client connection : apiConnections.values()) { + if (connection.isConnected()) { + connection.disconnect(); + } +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } } } @Override public synchronized boolean isConnected() { - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (!connection.isConnected()) return false; } return true; @@ -110,7 +206,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public synchronized boolean isConnecting() { - for (ApiClient connection : apiConnections.values()) { + for (Api.Client connection : apiConnections.values()) { if (connection.isConnecting()) return true; } return false; diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java index a5fa2545..d30293ed 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiManager.java @@ -5,11 +5,33 @@ package org.microg.gms.common.api; +<<<<<<< HEAD import android.annotation.SuppressLint; +======= +import android.content.Context; +import android.os.Bundle; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApi; +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.TaskCompletionSource; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) public class GoogleApiManager { @SuppressLint("StaticFieldLeak") private static GoogleApiManager instance; +<<<<<<< HEAD +======= + private Context context; + private Map clientMap = new HashMap<>(); + private Map>> waitingApiCallMap = new HashMap<>(); +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) private GoogleApiManager() { } @@ -18,4 +40,159 @@ public class GoogleApiManager { if (instance == null) instance = new GoogleApiManager(); } +<<<<<<< HEAD +======= + private synchronized A clientForApi(GoogleApi api) { + ApiInstance apiInstance = new ApiInstance(api); + if (clientMap.containsKey(apiInstance)) { + return (A) clientMap.get(apiInstance); + } else { + Api.Client client = api.api.getBuilder().build(api.getOptions(), context, context.getMainLooper(), null, new ConnectionCallback(apiInstance), new ConnectionFailedListener(apiInstance)); + clientMap.put(apiInstance, client); + waitingApiCallMap.put(apiInstance, new ArrayList<>()); + return (A) client; + } + } + + public synchronized void scheduleTask(GoogleApi api, PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { + A client = clientForApi(api); + boolean connecting = client.isConnecting(); + boolean connected = client.isConnected(); + if (connected) { + try { + apiCall.execute(client, completionSource); + } catch (Exception e) { + completionSource.setException(e); + } + } else { + waitingApiCallMap.get(new ApiInstance(api)).add(new WaitingApiCall((PendingGoogleApiCall) apiCall, completionSource)); + if (!connecting) { + client.connect(); + } + } + } + + private synchronized void onInstanceConnected(ApiInstance apiInstance, Bundle connectionHint) { + List> waitingApiCalls = waitingApiCallMap.get(apiInstance); + for (WaitingApiCall waitingApiCall : waitingApiCalls) { + try { + waitingApiCall.execute(clientMap.get(apiInstance)); + } catch (Exception e) { + waitingApiCall.failed(e); + } + } + waitingApiCalls.clear(); + } + + private synchronized void onInstanceSuspended(ApiInstance apiInstance, int cause) { + + } + + private synchronized void onInstanceFailed(ApiInstance apiInstance, ConnectionResult result) { + List> waitingApiCalls = waitingApiCallMap.get(apiInstance); + for (WaitingApiCall waitingApiCall : waitingApiCalls) { + waitingApiCall.failed(new RuntimeException(result.getErrorMessage())); + } + waitingApiCalls.clear(); + } + + private class ConnectionCallback implements ConnectionCallbacks { + private ApiInstance apiInstance; + + public ConnectionCallback(ApiInstance apiInstance) { + this.apiInstance = apiInstance; + } + + @Override + public void onConnected(Bundle connectionHint) { + onInstanceConnected(apiInstance, connectionHint); + } + + @Override + public void onConnectionSuspended(int cause) { + onInstanceSuspended(apiInstance, cause); + } + } + + private class ConnectionFailedListener implements OnConnectionFailedListener { + private ApiInstance apiInstance; + + public ConnectionFailedListener(ApiInstance apiInstance) { + this.apiInstance = apiInstance; + } + + @Override + public void onConnectionFailed(ConnectionResult result) { + onInstanceFailed(apiInstance, result); + } + } + + private static class WaitingApiCall { + private PendingGoogleApiCall apiCall; + private TaskCompletionSource completionSource; + + public WaitingApiCall(PendingGoogleApiCall apiCall, TaskCompletionSource completionSource) { + this.apiCall = apiCall; + this.completionSource = completionSource; + } + + public void execute(Api.Client client) throws Exception { + apiCall.execute(client, completionSource); + } + + public void failed(Exception e) { + completionSource.setException(e); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + WaitingApiCall that = (WaitingApiCall) o; + + if (apiCall != null ? !apiCall.equals(that.apiCall) : that.apiCall != null) return false; + return completionSource != null ? completionSource.equals(that.completionSource) : that.completionSource == null; + } + + @Override + public int hashCode() { + int result = apiCall != null ? apiCall.hashCode() : 0; + result = 31 * result + (completionSource != null ? completionSource.hashCode() : 0); + return result; + } + } + + private static class ApiInstance { + private Class apiClass; + private Api.ApiOptions apiOptions; + + public ApiInstance(Class apiClass, Api.ApiOptions apiOptions) { + this.apiClass = apiClass; + this.apiOptions = apiOptions; + } + + public ApiInstance(GoogleApi api) { + this(api.getClass(), api.getOptions()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ApiInstance that = (ApiInstance) o; + + if (apiClass != null ? !apiClass.equals(that.apiClass) : that.apiClass != null) return false; + return apiOptions != null ? apiOptions.equals(that.apiOptions) : that.apiOptions == null; + } + + @Override + public int hashCode() { + int result = apiClass != null ? apiClass.hashCode() : 0; + result = 31 * result + (apiOptions != null ? apiOptions.hashCode() : 0); + return result; + } + } +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java new file mode 100644 index 00000000..578d02b9 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/PendingGoogleApiCall.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.tasks.TaskCompletionSource; + +public interface PendingGoogleApiCall { + void execute(A client, TaskCompletionSource completionSource) throws Exception; +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java index a737a076..3401f596 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java @@ -47,6 +47,20 @@ class ResultCallbackHandler extends Handler { } } +<<<<<<< HEAD +======= + public void sendResultCallback(ResultCallback callback, R result) { + Message message = new Message(); + message.what = CALLBACK_ON_COMPLETE; + message.obj = new OnCompleteObject(callback, result); + sendMessage(message); + } + + public void sendTimeoutResultCallback(AbstractPendingResult pendingResult, long millis) { + + } + +>>>>>>> 49892e88 (Restructure modules https://github.com/microg/GmsCore/commit/18c3a6154dc41cebe1d4aeda5592b8f23928356f) public static class OnCompleteObject { public ResultCallback callback; public R result; diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java new file mode 100644 index 00000000..f836b9d4 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ReturningGoogleApiCall.java @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.tasks.TaskCompletionSource; + +public interface ReturningGoogleApiCall extends PendingGoogleApiCall { + R execute(A client) throws Exception; + + @Override + default void execute(A client, TaskCompletionSource completionSource) { + try { + completionSource.setResult(execute(client)); + } catch (Exception e) { + completionSource.setException(e); + } + } +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java b/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java new file mode 100644 index 00000000..ee8cbf66 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/VoidReturningGoogleApiCall.java @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2022 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common.api; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.tasks.TaskCompletionSource; + +public interface VoidReturningGoogleApiCall extends PendingGoogleApiCall{ + void execute(A client) throws Exception; + + @Override + default void execute(A client, TaskCompletionSource completionSource) { + try { + execute(client); + completionSource.setResult(null); + } catch (Exception e) { + completionSource.setException(e); + } + } +} diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle index 8971fb98..bbc86b99 100644 --- a/play-services-basement/build.gradle +++ b/play-services-basement/build.gradle @@ -18,7 +18,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' dependencies { - api "org.microg:safe-parcel:1.7.0" + api "org.microg:safe-parcel:$safeParcelVersion" // Dependencies from play-services-basement:18.0.0 api "androidx.collection:collection:$collectionVersion" diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl new file mode 100644 index 00000000..b393f11d --- /dev/null +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ConnectionInfo.aidl @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal; +parcelable ConnectionInfo; diff --git a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl index b0210281..8fe63347 100644 --- a/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl +++ b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl @@ -1,7 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + package com.google.android.gms.common.internal; import android.os.Bundle; +import com.google.android.gms.common.internal.ConnectionInfo; interface IGmsCallbacks { void onPostInitComplete(int statusCode, IBinder binder, in Bundle params); + void onAccountValidationComplete(int statusCode, in Bundle params); + void onPostInitCompleteWithConnectionInfo(int statusCode, IBinder binder, in ConnectionInfo info); } diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java new file mode 100644 index 00000000..19326d08 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ConnectionInfo.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal; + +import android.os.Bundle; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.Feature; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class ConnectionInfo extends AbstractSafeParcelable { + @Field(1) + public Bundle params; + @Field(2) + public Feature[] features; + @Field(3) + public int unknown3; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ConnectionInfo.class); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java index 75e59d95..1cf0ecc1 100644 --- a/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java @@ -16,8 +16,18 @@ package com.google.android.gms.common.internal; -import org.microg.safeparcel.AutoSafeParcelable; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; -public class ValidateAccountRequest extends AutoSafeParcelable { - public static Creator CREATOR = new AutoCreator<>(ValidateAccountRequest.class); +@SafeParcelable.Class +public class ValidateAccountRequest extends AbstractSafeParcelable { + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static SafeParcelableCreatorAndWriter CREATOR = findCreator(ValidateAccountRequest.class); } diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java new file mode 100644 index 00000000..ec7d84d5 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/AbstractSafeParcelable.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +public abstract class AbstractSafeParcelable implements SafeParcelable { + + @SuppressWarnings("unchecked") + public static SafeParcelableCreatorAndWriter findCreator(java.lang.Class tClass) { + String creatorClassName = tClass.getName() + "$000Creator"; + try { + return (SafeParcelableCreatorAndWriter) java.lang.Class.forName(creatorClassName).newInstance(); + } catch (Exception e) { + throw new RuntimeException("No Creator found for " + tClass.getName(), e); + } + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java new file mode 100644 index 00000000..226f50e8 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelReader.java @@ -0,0 +1,328 @@ +/* + * SPDX-FileCopyrightText: 2015, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.HashMap; + +@SuppressWarnings("MagicNumber") +public final class SafeParcelReader { + + private SafeParcelReader() { + } + + @Deprecated + public static int halfOf(int i) { + return i & 0xFFFF; + } + + public static int getFieldId(int header) { + return header & 0xFFFF; + } + + @Deprecated + public static int readSingleInt(Parcel parcel) { + return parcel.readInt(); + } + + public static int readHeader(Parcel parcel) { + return parcel.readInt(); + } + + private static int readSize(Parcel parcel, int header) { + if ((header & 0xFFFF0000) != 0xFFFF0000) + return header >> 16 & 0xFFFF; + return parcel.readInt(); + } + + private static void readExpectedSize(Parcel parcel, int header, int expectedSize) { + int i = readSize(parcel, header); + if (i != expectedSize) + throw new ReadException("Expected size " + expectedSize + " got " + i + " (0x" + Integer.toHexString(i) + ")", parcel); + } + + @Deprecated + public static int readStart(Parcel parcel) { + return readObjectHeader(parcel); + } + + public static int readObjectHeader(Parcel parcel) { + int header = readHeader(parcel); + int size = readSize(parcel, header); + int start = parcel.dataPosition(); + if (getFieldId(header) != SafeParcelable.SAFE_PARCEL_OBJECT_MAGIC) + throw new ReadException("Expected object header. Got 0x" + Integer.toHexString(header), parcel); + int end = start + size; + if ((end < start) || (end > parcel.dataSize())) + throw new ReadException("Size read is invalid start=" + start + " end=" + end, parcel); + return end; + } + + public static int readInt(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return parcel.readInt(); + } + + public static byte readByte(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return (byte) parcel.readInt(); + } + + public static short readShort(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return (short) parcel.readInt(); + } + + public static boolean readBool(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return parcel.readInt() != 0; + } + + public static long readLong(Parcel parcel, int header) { + readExpectedSize(parcel, header, 8); + return parcel.readLong(); + } + + public static float readFloat(Parcel parcel, int header) { + readExpectedSize(parcel, header, 4); + return parcel.readFloat(); + } + + public static double readDouble(Parcel parcel, int header) { + readExpectedSize(parcel, header, 8); + return parcel.readDouble(); + } + + public static String readString(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + String string = parcel.readString(); + parcel.setDataPosition(start + size); + return string; + } + + public static IBinder readBinder(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + IBinder binder = parcel.readStrongBinder(); + parcel.setDataPosition(start + size); + return binder; + } + + public static T readParcelable(Parcel parcel, int header, Parcelable.Creator creator) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + T t = creator.createFromParcel(parcel); + parcel.setDataPosition(start + size); + return t; + } + + public static ArrayList readList(Parcel parcel, int header, ClassLoader classLoader) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + ArrayList list = parcel.readArrayList(classLoader); + parcel.setDataPosition(start + size); + return list; + } + + public static HashMap readMap(Parcel parcel, int header, ClassLoader classLoader) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + HashMap map = parcel.readHashMap(classLoader); + parcel.setDataPosition(start + size); + return map; + } + + public static ArrayList readParcelableList(Parcel parcel, int header, Parcelable.Creator creator) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + ArrayList list = parcel.createTypedArrayList(creator); + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readStringList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + ArrayList list = parcel.createStringArrayList(); + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readIntegerList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readInt()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readLongList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readLong()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readFloatList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readFloat()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readDoubleList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readDouble()); + } + parcel.setDataPosition(start + size); + return list; + } + + public static ArrayList readBooleanList(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + ArrayList list = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + list.add(parcel.readInt() != 0); + } + parcel.setDataPosition(start + size); + return list; + } + + public static T[] readParcelableArray(Parcel parcel, int header, Parcelable.Creator creator) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + T[] arr = parcel.createTypedArray(creator); + parcel.setDataPosition(start + size); + return arr; + } + + public static String[] readStringArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + String[] arr = parcel.createStringArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static byte[] readByteArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + byte[] arr = parcel.createByteArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static byte[][] readByteArrayArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int length = parcel.readInt(); + byte[][] arr = new byte[length][]; + for (int i = 0; i < length; i++) { + arr[i] = parcel.createByteArray(); + } + parcel.setDataPosition(start + size); + return arr; + } + + public static float[] readFloatArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + float[] arr = parcel.createFloatArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static int[] readIntArray(Parcel parcel, int header) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + int[] arr = parcel.createIntArray(); + parcel.setDataPosition(start + size); + return arr; + } + + public static Bundle readBundle(Parcel parcel, int header, ClassLoader classLoader) { + int size = readSize(parcel, header); + if (size == 0) + return null; + int start = parcel.dataPosition(); + Bundle bundle = parcel.readBundle(classLoader); + parcel.setDataPosition(start + size); + return bundle; + } + + public static void skip(Parcel parcel, int header) { + int size = readSize(parcel, header); + parcel.setDataPosition(parcel.dataPosition() + size); + } + + public static class ReadException extends RuntimeException { + public ReadException(String message, Parcel parcel) { + super(message); + } + } +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java new file mode 100644 index 00000000..b2f96116 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelWriter.java @@ -0,0 +1,384 @@ +/* + * SPDX-FileCopyrightText: 2015, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; +import java.util.Map; + +@SuppressWarnings("MagicNumber") +public final class SafeParcelWriter { + + private SafeParcelWriter() { + } + + private static void writeHeader(Parcel parcel, int fieldId, int size) { + if (size >= 0xFFFF) { + parcel.writeInt(0xFFFF0000 | fieldId); + parcel.writeInt(size); + } else { + parcel.writeInt(size << 16 | fieldId); + } + } + + @Deprecated + public static int writeStart(Parcel parcel) { + return writeObjectHeader(parcel); + } + + public static int writeObjectHeader(Parcel parcel) { + writeHeader(parcel, SafeParcelable.SAFE_PARCEL_OBJECT_MAGIC, 0xFFFF); + return parcel.dataPosition(); + } + + private static int writeObjectHeader(Parcel parcel, int fieldId) { + writeHeader(parcel, fieldId, 0xFFFF); + return parcel.dataPosition(); + } + + @Deprecated + public static void writeEnd(Parcel parcel, int start) { + finishObjectHeader(parcel, start); + } + + public static void finishObjectHeader(Parcel parcel, int start) { + int end = parcel.dataPosition(); + int length = end - start; + parcel.setDataPosition(start - 4); + parcel.writeInt(length); + parcel.setDataPosition(end); + } + + public static void write(Parcel parcel, int fieldId, Boolean val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val ? 1 : 0); + } + + public static void write(Parcel parcel, int fieldId, Byte val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val); + } + + public static void write(Parcel parcel, int fieldId, Short val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val); + } + + public static void write(Parcel parcel, int fieldId, Integer val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeInt(val); + } + + public static void write(Parcel parcel, int fieldId, Long val) { + if (val == null) return; + writeHeader(parcel, fieldId, 8); + parcel.writeLong(val); + } + + public static void write(Parcel parcel, int fieldId, Float val) { + if (val == null) return; + writeHeader(parcel, fieldId, 4); + parcel.writeFloat(val); + } + + public static void write(Parcel parcel, int fieldId, Double val) { + if (val == null) return; + writeHeader(parcel, fieldId, 8); + parcel.writeDouble(val); + } + + public static void write(Parcel parcel, int fieldId, String val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeString(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Parcelable val, int flags, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + val.writeToParcel(parcel, flags); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Bundle val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeBundle(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, byte[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeByteArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, byte[][] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.length); + for (byte[] arr : val) { + parcel.writeByteArray(arr); + } + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, float[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeFloatArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, int[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeIntArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, String[] val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeStringArray(val); + finishObjectHeader(parcel, start); + } + } + + public static void writeStringList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeStringList(val); + finishObjectHeader(parcel, start); + } + } + + public static void writeIntegerList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Integer i : val) { + parcel.writeInt(i); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeLongList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Long l : val) { + parcel.writeLong(l); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeFloatList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Float f : val) { + parcel.writeFloat(f); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeDoubleList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Double d : val) { + parcel.writeDouble(d); + } + finishObjectHeader(parcel, start); + } + } + + public static void writeBooleanList(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (Boolean b : val) { + parcel.writeInt(b ? 1 : 0); + } + finishObjectHeader(parcel, start); + } + } + + private static void writeArrayPart(Parcel parcel, T val, int flags) { + int before = parcel.dataPosition(); + parcel.writeInt(1); + int start = parcel.dataPosition(); + val.writeToParcel(parcel, flags); + int end = parcel.dataPosition(); + parcel.setDataPosition(before); + parcel.writeInt(end - start); + parcel.setDataPosition(end); + } + + public static void write(Parcel parcel, int fieldId, T[] val, int flags, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.length); + for (T t : val) { + if (t == null) { + parcel.writeInt(0); + } else { + writeArrayPart(parcel, t, flags); + } + } + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, List val, int flags, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeInt(val.size()); + for (T t : val) { + if (t == null) { + parcel.writeInt(0); + } else { + writeArrayPart(parcel, t, flags); + } + } + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Parcel val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.appendFrom(val, 0, val.dataSize()); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, List val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeList(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, Map val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeMap(val); + finishObjectHeader(parcel, start); + } + } + + public static void write(Parcel parcel, int fieldId, IBinder val, boolean mayNull) { + if (val == null) { + if (mayNull) { + writeHeader(parcel, fieldId, 0); + } + } else { + int start = writeObjectHeader(parcel, fieldId); + parcel.writeStrongBinder(val); + finishObjectHeader(parcel, start); + } + } + +} \ No newline at end of file diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java new file mode 100644 index 00000000..0d8d1cc0 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelable.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Parcelable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public interface SafeParcelable extends Parcelable { + int SAFE_PARCEL_OBJECT_MAGIC = 0x4F45; + + @Target(ElementType.TYPE) + @interface Class { + } + + @Target(ElementType.CONSTRUCTOR) + @interface Constructor { + + } + + @Target(ElementType.PARAMETER) + @interface Param { + int value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @interface Field { + int value(); + + boolean mayNull() default false; + + java.lang.Class subClass() default SafeParcelable.class; + + boolean useValueParcel() default false; + + boolean useDirectList() default false; + + long versionCode() default -1; + + String defaultValue() default ""; + + String type() default ""; + + String getterName() default ""; + + String getter() default ""; + } +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java new file mode 100644 index 00000000..6afaa9c6 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableCreatorAndWriter.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Parcel; +import android.os.Parcelable; + +public interface SafeParcelableCreatorAndWriter extends Parcelable.Creator { + void writeToParcel(T object, Parcel parcel, int flags); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java new file mode 100644 index 00000000..66b8b4d5 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/safeparcel/SafeParcelableSerializer.java @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.common.internal.safeparcel; + +import android.os.Parcel; +import android.os.Parcelable; + +public class SafeParcelableSerializer { + public static T deserializeFromBytes(byte[] bytes, Parcelable.Creator tCreator) { + if (bytes == null) return null; + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + T parcelable = tCreator.createFromParcel(parcel); + parcel.recycle(); + return parcelable; + } + + public static byte[] serializeToBytes(T parcelable) { + if (parcelable == null) return null; + Parcel parcel = Parcel.obtain(); + parcelable.writeToParcel(parcel, 0); + byte[] bytes = parcel.marshall(); + parcel.recycle(); + return bytes; + } +} diff --git a/play-services-basement/src/main/java/org/microg/gms/common/Hide.java b/play-services-basement/src/main/java/org/microg/gms/common/Hide.java new file mode 100644 index 00000000..e15414b5 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/Hide.java @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Hide the class, method or field from the public API. + */ +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface Hide { +} diff --git a/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java b/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java index 92663979..a7fb9b44 100644 --- a/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java +++ b/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java @@ -43,6 +43,8 @@ public @interface PublicApi { * marked as public api. * * @return true if the method or field is not part of the public api + * @deprecated use {@link Hide} instead */ + @Deprecated boolean exclude() default false; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/AssistantActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/AssistantActivity.java index d53f38dd..b4bea3f8 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/AssistantActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/AssistantActivity.java @@ -17,6 +17,7 @@ package org.microg.gms.auth.login; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.res.Configuration; import android.os.Bundle; import android.util.DisplayMetrics; @@ -25,11 +26,10 @@ import android.widget.Button; import android.widget.TextView; import androidx.annotation.StringRes; -import androidx.appcompat.app.AppCompatActivity; import com.mgoogle.android.gms.R; -public abstract class AssistantActivity extends AppCompatActivity { +public abstract class AssistantActivity extends Activity { private static final int TITLE_MIN_HEIGHT = 64; private static final double TITLE_WIDTH_FACTOR = (8.0 / 18.0);