Repo Created

This commit is contained in:
Fr4nz D13trich 2025-11-15 17:44:12 +01:00
parent eb305e2886
commit a8c22c65db
4784 changed files with 329907 additions and 2 deletions

View file

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2015 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'signing'
android {
namespace "com.google.android.gms.common"
compileSdkVersion androidCompileSdk
buildToolsVersion "$androidBuildVersionTools"
aidlPackagedList "com/google/android/gms/common/api/Status.aidl"
aidlPackagedList "com/google/android/gms/common/ConnectionResult.aidl"
aidlPackagedList "com/google/android/gms/common/internal/IAccountAccessor.aidl"
aidlPackagedList "com/google/android/gms/common/internal/ICancelToken.aidl"
aidlPackagedList "com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl"
aidlPackagedList "com/google/android/gms/dynamic/IObjectWrapper.aidl"
buildFeatures {
aidl = true
buildConfig = true
}
defaultConfig {
versionName version
minSdkVersion androidMinSdk
targetSdkVersion androidTargetSdk
buildConfigField "int", "VERSION_CODE", "$appVersionCode"
consumerProguardFile 'consumer-rules.pro'
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
apply from: '../gradle/publish-android.gradle'
description = 'microG implementation of play-services-basement'
dependencies {
// Dependencies from play-services-basement:18.8.0
api 'androidx.collection:collection:1.0.0'
api 'androidx.core:core:1.2.0'
api 'androidx.fragment:fragment:1.1.0'
// TODO: Dependencies from play-services-stats:17.1.0
api 'androidx.legacy:legacy-support-core-utils:1.0.0'
annotationProcessor project(':safe-parcel-processor')
}

View file

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2023 microG Project Team
# SPDX-License-Identifier: CC0-1.0
# Keep AutoSafeParcelables
-keep public class * extends org.microg.safeparcel.AutoSafeParcelable {
@com.google.android.gms.common.internal.safeparcel.SafeParcelable$Field *;
@org.microg.safeparcel.SafeParceled *;
}
# Keep asInterface method cause it's accessed from SafeParcel
-keepattributes InnerClasses
-keep public class * extends android.os.IInterface {
public static * asInterface(android.os.IBinder);
}
-keep public class * extends android.os.Binder { public static *; }
# Keep name of SafeParcelables and their creators
-keepnames public class * implements com.google.android.gms.common.internal.safeparcel.SafeParcelable
-keepnames public class * implements com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2021, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'
apply plugin: 'signing'
dependencies {
api project(":play-services-basement")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
}
android {
namespace "org.microg.gms.basement.ktx"
compileSdkVersion androidCompileSdk
buildToolsVersion "$androidBuildVersionTools"
defaultConfig {
versionName version
minSdkVersion androidMinSdk
targetSdkVersion androidTargetSdk
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
kotlinOptions {
jvmTarget = 1.8
}
}
apply from: '../../gradle/publish-android.gradle'
description = 'microG kotlin extensions for play-services-basement'

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2021, microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<manifest />

View file

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.dynamic
inline fun <reified T> IObjectWrapper?.unwrap(): T? = ObjectWrapper.unwrapTyped(this, T::class.java)

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2016 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
</application>
</manifest>

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common;
parcelable ConnectionResult;

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common;
parcelable GoogleCertificatesLookupQuery;

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common;
parcelable GoogleCertificatesLookupResponse;

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common;
parcelable GoogleCertificatesQuery;

View file

@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.api;
parcelable ApiMetadata;

View file

@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.api;
parcelable ComplianceOptions;

View file

@ -0,0 +1,8 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.api;
parcelable Scope;

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common.api;
parcelable Status;

View file

@ -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;

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common.internal;
parcelable GetServiceRequest;

View file

@ -0,0 +1,5 @@
package com.google.android.gms.common.internal;
interface IAccountAccessor {
Account getAccount() = 1;
}

View file

@ -0,0 +1,5 @@
package com.google.android.gms.common.internal;
interface ICancelToken {
void cancel();
}

View file

@ -0,0 +1,8 @@
package com.google.android.gms.common.internal;
import com.google.android.gms.dynamic.IObjectWrapper;
interface ICertData {
IObjectWrapper getWrappedBytes();
int remoteHashCode();
}

View file

@ -0,0 +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);
}

View file

@ -0,0 +1,39 @@
package com.google.android.gms.common.internal;
import android.os.Bundle;
import com.google.android.gms.common.internal.IGmsCallbacks;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.ValidateAccountRequest;
interface IGmsServiceBroker {
void getPlusService(IGmsCallbacks callback, int code, String packageName, String authPackage, in String[] scopes, String accountName, in Bundle params) = 0;
void getPanoramaService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 1;
void getAppDataSearchService(IGmsCallbacks callback, int code, String packageName) = 2;
void getWalletService(IGmsCallbacks callback, int code) = 3;
void getPeopleService(IGmsCallbacks callback, int code, String str, in Bundle params) = 4;
void getReportingService(IGmsCallbacks callback, int code, String str, in Bundle params) = 5;
void getLocationService(IGmsCallbacks callback, int code, String str, in Bundle params) = 6;
void getGoogleLocationManagerService(IGmsCallbacks callback, int code, String str, in Bundle params) = 7;
void getGamesService(IGmsCallbacks callback, int code, String packageName, String accountName, in String[] scopes, String gamePackageName, IBinder popupWindowToken, String desiredLocale, in Bundle params) = 8;
void getAppStateService(IGmsCallbacks callback, int code, String packageName, String accountName, in String[] scopes) = 9;
void getPlayLogService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 10;
void getAdMobService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 11;
void getDroidGuardService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 12;
void getLockboxService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 13;
void getCastMirroringService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 14;
void getNetworkQualityService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 15;
void getGoogleIdentityService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 16;
void getGoogleFeedbackService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 17;
void getCastService(IGmsCallbacks callback, int code, String packageName, IBinder binder, in Bundle params) = 18;
void getDriveService(IGmsCallbacks callback, int code, String packageName, in String[] args, String str2, in Bundle params) = 19;
void getLightweightAppDataSearchService(IGmsCallbacks callback, int code, String packageName) = 20;
void getSearchAdministrationService(IGmsCallbacks callback, int code, String packageName) = 21;
void getAutoBackupService(IGmsCallbacks callback, int code, String packageName, in Bundle params) = 22;
void getAddressService(IGmsCallbacks callback, int code, String packageName) = 23;
void getWalletServiceWithPackageName(IGmsCallbacks callback, int code, String packageName) = 41;
void getService(IGmsCallbacks callback, in GetServiceRequest request) = 45;
void validateAccount(IGmsCallbacks callback, in ValidateAccountRequest request) = 46;
}

View file

@ -0,0 +1,18 @@
package com.google.android.gms.common.internal;
import com.google.android.gms.common.GoogleCertificatesLookupQuery;
import com.google.android.gms.common.GoogleCertificatesLookupResponse;
import com.google.android.gms.common.GoogleCertificatesQuery;
import com.google.android.gms.dynamic.IObjectWrapper;
interface IGoogleCertificatesApi {
IObjectWrapper getGoogleCertificates() = 0;
IObjectWrapper getGoogleReleaseCertificates() = 1;
boolean isGoogleReleaseSigned(String packageName, IObjectWrapper certData) = 2;
boolean isGoogleSigned(String packageName, IObjectWrapper certData) = 3;
boolean isGoogleOrPlatformSigned(in GoogleCertificatesQuery query, IObjectWrapper packageManager) = 4;
GoogleCertificatesLookupResponse isPackageGoogleOrPlatformSigned(in GoogleCertificatesLookupQuery query) = 5;
boolean isPackageGoogleOrPlatformSignedAvailable() = 6;
GoogleCertificatesLookupResponse queryPackageSigned(in GoogleCertificatesLookupQuery query) = 7;
boolean isFineGrainedPackageVerificationAvailable() = 8;
}

View file

@ -0,0 +1,3 @@
package com.google.android.gms.common.internal;
parcelable ValidateAccountRequest;

View file

@ -0,0 +1,8 @@
package com.google.android.gms.dynamic;
/**
* The concrete class implementing IObjectWrapper must have exactly one declared private field
* for the wrapped object. Preferably, this is an instance of the ObjectWrapper<T> class.
*/
interface IObjectWrapper {
}

View file

@ -0,0 +1,16 @@
package com.google.android.gms.dynamite;
import com.google.android.gms.dynamic.IObjectWrapper;
interface IDynamiteLoader {
int getModuleVersion(IObjectWrapper wrappedContext, String moduleId) = 0;
int getModuleVersion2(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) = 2;
int getModuleVersionV2(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) = 4;
IObjectWrapper getModuleVersionV3(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired, long requestStartTime) = 6;
IObjectWrapper createModuleContext(IObjectWrapper wrappedContext, String moduleId, int minVersion) = 1;
IObjectWrapper createModuleContextV2(IObjectWrapper wrappedContext, String moduleId, int minVersion) = 3;
IObjectWrapper createModuleContextV3(IObjectWrapper wrappedContext, String moduleId, int minVersion, IObjectWrapper cursorWrapped) = 7;
int getIDynamiteLoaderVersion() = 5;
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: CC-BY-4.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
/**
* Contains classes for Google Search Actions.
*/
package com.google.android.gms.actions;

View file

@ -0,0 +1,357 @@
/*
* SPDX-FileCopyrightText: 2016 microG Project Team
* SPDX-License-Identifier: Apache-2.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
package com.google.android.gms.common;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Parcel;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import java.util.Arrays;
/**
* Contains all possible error codes for when a client fails to connect to Google Play services.
* These error codes are used by {@link GoogleApiClient.OnConnectionFailedListener}.
*/
@SafeParcelable.Class
public class ConnectionResult extends AbstractSafeParcelable {
/**
* The connection was successful.
*/
public static final int SUCCESS = 0;
/**
* Google Play services is missing on this device. The calling activity should pass this error
* code to {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to get a localized
* error dialog that will resolve the error when shown.
*/
public static final int SERVICE_MISSING = 1;
/**
* The installed version of Google Play services is out of date. The calling activity should
* pass this error code to {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to
* get a localized error dialog that will resolve the error when shown.
*/
public static final int SERVICE_VERSION_UPDATE_REQUIRED = 2;
/**
* The installed version of Google Play services has been disabled on this device. The calling
* activity should pass this error code to
* {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to get a localized error
* dialog that will resolve the error when shown.
*/
public static final int SERVICE_DISABLED = 3;
/**
* The client attempted to connect to the service but the user is not signed in. The client may
* choose to continue without using the API or it may call
* {@link #startResolutionForResult(Activity, int)} to prompt the user to sign in. After the
* sign in activity returns with {@link Activity#RESULT_OK} further attempts to connect should
* succeed.
*/
public static final int SIGN_IN_REQUIRED = 4;
/**
* The client attempted to connect to the service with an invalid account name specified.
*/
public static final int INVALID_ACCOUNT = 5;
/**
* Completing the connection requires some form of resolution. A resolution will be available
* to be started with {@link #startResolutionForResult(Activity, int)}. If the result returned
* is {@link Activity#RESULT_OK}, then further attempts to connect should either complete or
* continue on to the next issue that needs to be resolved.
*/
public static final int RESOLUTION_REQUIRED = 6;
/**
* A network error occurred. Retrying should resolve the problem.
*/
public static final int NETWORK_ERROR = 7;
/**
* An internal error occurred. Retrying should resolve the problem.
*/
public static final int INTERNAL_ERROR = 8;
/**
* The version of the Google Play services installed on this device is not authentic.
*/
public static final int SERVICE_INVALID = 9;
/**
* The application is misconfigured. This error is not recoverable and will be treated as
* fatal. The developer should look at the logs after this to determine more actionable
* information.
*/
public static final int DEVELOPER_ERROR = 10;
/**
* The application is not licensed to the user. This error is not recoverable and will be
* treated as fatal.
*/
public static final int LICENSE_CHECK_FAILED = 11;
/**
* The client canceled the connection by calling {@link GoogleApiClient#disconnect()}.
* Only returned by {@link GoogleApiClient#blockingConnect()}.
*/
public static final int CANCELED = 13;
/**
* The timeout was exceeded while waiting for the connection to complete. Only returned by
* {@link GoogleApiClient#blockingConnect()}.
*/
public static final int TIMEOUT = 14;
/**
* An interrupt occurred while waiting for the connection complete. Only returned by
* {@link GoogleApiClient#blockingConnect()}.
*/
public static final int INTERRUPTED = 15;
/**
* One of the API components you attempted to connect to is not available. The API will not
* work on this device, and updating Google Play services will not likely solve the problem.
* Using the API on the device should be avoided.
*/
public static final int API_UNAVAILABLE = 16;
/**
* The client attempted to connect to the service but the user is not signed in. An error may have occurred when signing in the user and the error can not
* be recovered with any user interaction. Alternately, the API may have been requested with {@link GoogleApiClient.Builder#addApiIfAvailable(Api, Scope...)}
* and it may be the case that no required APIs needed authentication, so authentication did not occur.
* <p>
* When seeing this error code, there is no resolution for the sign-in failure.
*/
public static final int SIGN_IN_FAILED = 17;
/**
* Google Play service is currently being updated on this device.
*/
public static final int SERVICE_UPDATING = 18;
/**
* Service doesn't have one or more required permissions.
*/
public static final int SERVICE_MISSING_PERMISSION = 19;
/**
* The current user profile is restricted and cannot use authenticated features. (Jelly Bean MR2+ Restricted Profiles for Android tablets)
*/
public static final int RESTRICTED_PROFILE = 20;
/**
* There was a user-resolvable issue connecting to Google Play services, but when attempting to start the resolution, the activity was not found.
* <p>
* This can occur when attempting to resolve issues connecting to Google Play services on emulators with Google APIs but not Google Play Store.
*/
public static final int RESOLUTION_ACTIVITY_NOT_FOUND = 22;
/**
* The API being requested is disabled on this device for this application. Trying again at a later time may succeed.
*/
public static final int API_DISABLED = 23;
/**
* The API being requested is disabled for this connection attempt, but may work for other connections.
*/
public static final int API_DISABLED_FOR_CONNECTION = 24;
/**
* The Drive API requires external storage (such as an SD card), but no external storage is
* mounted. This error is recoverable if the user installs external storage (if none is
* present) and ensures that it is mounted (which may involve disabling USB storage mode,
* formatting the storage, or other initialization as required by the device).
* <p/>
* This error should never be returned on a device with emulated external storage. On devices
* with emulated external storage, the emulated "external storage" is always present regardless
* of whether the device also has removable storage.
*/
@Deprecated
public static final int DRIVE_EXTERNAL_STORAGE_REQUIRED = 1500;
@Field(1)
int versionCode = 1;
@Field(value = 2, getterName = "getErrorCode")
private int statusCode;
@Field(value = 3, getterName = "getResolution")
private PendingIntent resolution;
@Field(value = 4, getterName = "getErrorMessage")
private String message;
private ConnectionResult() {
}
/**
* Creates a connection result.
*
* @param statusCode The status code.
*/
public ConnectionResult(int statusCode) {
this(statusCode, null);
}
/**
* Creates a connection result.
*
* @param statusCode The status code.
* @param resolution A pending intent that will resolve the issue when started, or null.
*/
public ConnectionResult(int statusCode, PendingIntent resolution) {
this(statusCode, resolution, getStatusString(statusCode));
}
/**
* Creates a connection result.
*
* @param statusCode The status code.
* @param resolution A pending intent that will resolve the issue when started, or null.
* @param message An additional error message for the connection result, or null.
*/
@Constructor
public ConnectionResult(@Param(2) int statusCode, @Param(3) PendingIntent resolution, @Param(4) String message) {
this.statusCode = statusCode;
this.resolution = resolution;
this.message = message;
}
static String getStatusString(int statusCode) {
switch (statusCode) {
case -1:
return "UNKNOWN";
case 0:
return "SUCCESS";
case 1:
return "SERVICE_MISSING";
case 2:
return "SERVICE_VERSION_UPDATE_REQUIRED";
case 3:
return "SERVICE_DISABLED";
case 4:
return "SIGN_IN_REQUIRED";
case 5:
return "INVALID_ACCOUNT";
case 6:
return "RESOLUTION_REQUIRED";
case 7:
return "NETWORK_ERROR";
case 8:
return "INTERNAL_ERROR";
case 9:
return "SERVICE_INVALID";
case 10:
return "DEVELOPER_ERROR";
case 11:
return "LICENSE_CHECK_FAILED";
case 13:
return "CANCELED";
case 14:
return "TIMEOUT";
case 15:
return "INTERRUPTED";
case 16:
return "API_UNAVAILABLE";
case 17:
return "SIGN_IN_FAILED";
case 18:
return "SERVICE_UPDATING";
case 19:
return "SERVICE_MISSING_PERMISSION";
case 20:
return "RESTRICTED_PROFILE";
case 21:
return "API_VERSION_UPDATE_REQUIRED";
case 42:
return "UPDATE_ANDROID_WEAR";
case 99:
return "UNFINISHED";
case 1500:
return "DRIVE_EXTERNAL_STORAGE_REQUIRED";
default:
return "UNKNOWN_ERROR_CODE(" + statusCode + ")";
}
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof ConnectionResult)) {
return false;
} else {
ConnectionResult r = (ConnectionResult) o;
return statusCode == r.statusCode && resolution == null ? r.resolution == null : resolution.equals(r.resolution) && TextUtils.equals(message, r.message);
}
}
/**
* Indicates the type of error that interrupted connection.
*
* @return the error code, or {@link #SUCCESS} if no error occurred.
*/
public int getErrorCode() {
return statusCode;
}
/**
* Returns an error message for connection result.
*
* @return the message
*/
public String getErrorMessage() {
return message;
}
/**
* A pending intent to resolve the connection failure. This intent can be started with
* {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)} to
* present UI to solve the issue.
*
* @return The pending intent to resolve the connection failure.
*/
public PendingIntent getResolution() {
return resolution;
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[]{statusCode, resolution, message});
}
/**
* Returns {@code true} if calling {@link #startResolutionForResult(Activity, int)} will start
* any intents requiring user interaction.
*
* @return {@code true} if there is a resolution that can be started.
*/
public boolean hasResolution() {
return statusCode != 0 && resolution != null;
}
/**
* Returns {@code true} if the connection was successful.
*
* @return {@code true} if the connection was successful, {@code false} if there was an error.
*/
public boolean isSuccess() {
return statusCode == 0;
}
/**
* Resolves an error by starting any intents requiring user interaction. See
* {@link #SIGN_IN_REQUIRED}, and {@link #RESOLUTION_REQUIRED}.
*
* @param activity An Activity context to use to resolve the issue. The activity's
* {@link Activity#onActivityResult} method will be invoked after the user
* is done. If the resultCode is {@link Activity#RESULT_OK}, the application
* should try to connect again.
* @param requestCode The request code to pass to {@link Activity#onActivityResult}.
* @throws IntentSender.SendIntentException If the resolution intent has been canceled or is no
* longer able to execute the request.
*/
public void startResolutionForResult(Activity activity, int requestCode) throws
IntentSender.SendIntentException {
if (hasResolution()) {
activity.startIntentSenderForResult(resolution.getIntentSender(), requestCode, null, 0, 0, 0);
}
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<ConnectionResult> CREATOR = findCreator(ConnectionResult.class);
}

View file

@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.utils.ToStringHelper;
@SafeParcelable.Class
public class Feature extends AbstractSafeParcelable {
@Field(value = 1, getterName = "getName")
private String name;
@Field(2)
int oldVersion;
@Field(value = 3, getterName = "getVersion", defaultValue = "-1")
private long version = -1;
private Feature() {
}
@Constructor
public Feature(@Param(1) String name, @Param(3) long version) {
this.name = name;
this.version = version;
}
public String getName() {
return name;
}
public long getVersion() {
if (version == -1) return oldVersion;
return version;
}
@NonNull
@Override
public String toString() {
return ToStringHelper.name("Feature").value(name).value(getVersion()).end();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<Feature> CREATOR = findCreator(Feature.class);
}

View file

@ -0,0 +1,50 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper;
@SafeParcelable.Class
public class GoogleCertificatesLookupQuery extends AbstractSafeParcelable {
@Field(value = 1, getterName = "getCallingPackage")
String callingPackage;
@Field(2)
boolean allowTestKeys;
@Field(3)
boolean ignoreTestKeysOverride;
@Field(4)
IObjectWrapper contextWrapper;
private Context context;
@Field(5)
boolean isChimeraPackage;
@Field(6)
boolean includeHashesInErrorMessage;
public String getCallingPackage() {
return callingPackage;
}
public Context getContext() {
if (context == null && contextWrapper != null) {
context = ObjectWrapper.unwrapTyped(contextWrapper, Context.class);
}
return context;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<GoogleCertificatesLookupQuery> CREATOR = findCreator(GoogleCertificatesLookupQuery.class);
}

View file

@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common;
import android.os.Parcel;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
@SafeParcelable.Class
public class GoogleCertificatesLookupResponse extends AbstractSafeParcelable {
@Field(1)
public final boolean result;
@Field(2)
public final String errorMessage;
@Field(3)
public final int statusValue;
@Field(4)
public final int firstPartyStatusValue;
@Constructor
public GoogleCertificatesLookupResponse(@Param(1) boolean result, @Param(2) String errorMessage, @Param(3) int statusValue, @Param(4) int firstPartyStatusValue) {
this.result = result;
this.errorMessage = errorMessage;
this.statusValue = statusValue;
this.firstPartyStatusValue = firstPartyStatusValue;
}
@Override
public void writeToParcel(Parcel out, int flags) {
CREATOR.writeToParcel(this, out, flags);
}
public static final SafeParcelableCreatorAndWriter<GoogleCertificatesLookupResponse> CREATOR = findCreator(GoogleCertificatesLookupResponse.class);
}

View file

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.CertData;
import com.google.android.gms.common.internal.ICertData;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper;
import org.microg.gms.common.Hide;
@Hide
@SafeParcelable.Class
public class GoogleCertificatesQuery extends AbstractSafeParcelable {
@Field(value = 1, getterName = "getCallingPackage")
String callingPackage;
@Field(2)
IBinder certDataBinder;
private CertData certData;
@Field(3)
boolean allowTestKeys;
@Field(4)
boolean ignoreTestKeysOverride;
public String getCallingPackage() {
return callingPackage;
}
public CertData getCertData() {
if (certData == null && certDataBinder != null) {
certData = CertData.unwrap(certDataBinder);
}
return certData;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<GoogleCertificatesQuery> CREATOR = findCreator(GoogleCertificatesQuery.class);
}

View file

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2023 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;
/**
* Indicates Google Play services is not available.
*/
public class GooglePlayServicesNotAvailableException extends Exception {
/**
* The error code returned by {@link GoogleApiAvailability#isGooglePlayServicesAvailable(Context)} call.
*/
public final int errorCode;
public GooglePlayServicesNotAvailableException(int errorCode) {
this.errorCode = errorCode;
}
}

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 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;
import android.app.Dialog;
import android.content.Intent;
/**
* {@code GooglePlayServicesRepairableException}s are special instances of {@link UserRecoverableException}s which are
* thrown when Google Play services is not installed, up-to-date, or enabled. In these cases, client code can use
* {@link #getConnectionStatusCode()} in conjunction with {@link GoogleApiAvailability#getErrorDialog(android.app.Activity, int, int)}
* to provide users with a localized {@link Dialog} that will allow users to install, update, or otherwise enable Google Play services.
*/
public class GooglePlayServicesRepairableException extends UserRecoverableException {
private final int connectionStatusCode;
/**
* Creates a {@link GooglePlayServicesRepairableException}.
*
* @param connectionStatusCode a code for the {@link ConnectionResult} {@code statusCode} of the exception
* @param message a string message for the exception
* @param intent an intent that may be started to resolve the connection issue with Google Play services
*/
public GooglePlayServicesRepairableException(int connectionStatusCode, String message, Intent intent) {
super(message, intent);
this.connectionStatusCode = connectionStatusCode;
}
/**
* Returns the {@link ConnectionResult} {@code statusCode} of the exception.
* <p>
* This value may be passed in to {@link GoogleApiAvailability#getErrorDialog(android.app.Activity, int, int)} to
* provide users with a localized {@link Dialog} that will allow users to install, update, or otherwise enable Google Play services.
*/
public int getConnectionStatusCode() {
return connectionStatusCode;
}
}

View file

@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: 2015 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;
import org.microg.gms.common.Hide;
/**
* OAuth 2.0 scopes for use with Google Play services. See the specific client methods for details on which scopes are required.
*/
public class Scopes {
/**
* OAuth 2.0 scope for viewing a user's basic profile information.
*/
public static final String PROFILE = "profile";
@Hide
public static final String OPENID = "openid";
/**
* OAuth 2.0 scope for accessing user's Google account email address.
*/
public static final String EMAIL = "email";
/**
* OAuth 2.0 scope for accessing the user's name, basic profile info and Google+ profile info.
* <p>
* When using this scope, your app will have access to:
* <ul>
* <li>the user's full name, profile picture, Google+ profile ID, age range, and language</li>
* <li>any other publicly available information on the user's Google+ profile</li>
* </ul>
*
* @deprecated We recommend switching to {@link #PROFILE} scope to get the one-tap sign-in experience. Your app will get much higher sign-in completion
* rate by switching to profile scopes because of the streamlined user experience. And your existing users with PLUS_LOGIN grant will not be asked to
* sign-in again.
* If you really need user's age range and locale information (which is the only additional information you can get from PLUS_LOGIN as of
* September 2016), use below scopes in addition to PROFILE:<ul>
* <li>www.googleapis.com/auth/profile.agerange.read</li>
* <li>www.googleapis.com/auth/profile.language.read</li>
* </ul>
*/
@Deprecated
public static final String PLUS_LOGIN = "https://www.googleapis.com/auth/plus.login";
/**
* This scope was previously named PLUS_PROFILE.
* <p>
* When using this scope, it does the following:
* <ul>
* <li>It lets you know who the currently authenticated user is by letting you replace a Google+ user ID with "me", which represents the authenticated
* user, in any call to the Google+ API.</li>
* </ul>
*/
public static final String PLUS_ME = "https://www.googleapis.com/auth/plus.me";
/**
* Scope for accessing data from Google Play Games.
*/
public static final String GAMES = "https://www.googleapis.com/auth/games";
@Hide
public static final String GAMES_LITE = "https://www.googleapis.com/auth/games_lite";
@Hide
public static final String GAMES_FIRSTPARTY = "https://www.googleapis.com/auth/games.firstparty";
/**
* Scope for using the CloudSave service.
*/
public static final String CLOUD_SAVE = "https://www.googleapis.com/auth/datastoremobile";
/**
* Scope for using the App State service.
*/
public static final String APP_STATE = "https://www.googleapis.com/auth/appstate";
/**
* Scope for access user-authorized files from Google Drive.
*/
public static final String DRIVE_FILE = "https://www.googleapis.com/auth/drive.file";
/**
* Scope for accessing appfolder files from Google Drive.
*/
public static final String DRIVE_APPFOLDER = "https://www.googleapis.com/auth/drive.appdata";
@Hide
public static final String FITNESS_ACTIVITY_READ = "https://www.googleapis.com/auth/fitness.activity.read";
@Hide
public static final String FITNESS_ACTIVITY_READ_WRITE = "https://www.googleapis.com/auth/fitness.activity.write";
@Hide
public static final String FITNESS_LOCATION_READ = "https://www.googleapis.com/auth/fitness.location.read";
@Hide
public static final String FITNESS_LOCATION_READ_WRITE = "https://www.googleapis.com/auth/fitness.location.write";
@Hide
public static final String FITNESS_BODY_READ = "https://www.googleapis.com/auth/fitness.body.read";
@Hide
public static final String FITNESS_BODY_READ_WRITE = "https://www.googleapis.com/auth/fitness.body.write";
@Hide
public static final String USERINFO_EMAIL = "https://www.googleapis.com/auth/userinfo.email";
@Hide
public static final String USERINFO_PROFILE = "https://www.googleapis.com/auth/userinfo.profile";
@Hide
public static final String USER_BIRTHDAY_READ = "https://www.googleapis.com/auth/user.birthday.read";
@Hide
public static final String GMAIL_READONLY = "https://www.googleapis.com/auth/gmail.readonly";
/**
* Scope for cryptauthenrollment.googleapis.com (required for certain Google Workspace accounts)
*/
@Hide
public static final String CRYPTAUTH = "https://www.googleapis.com/auth/cryptauth";
}

View file

@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2023 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;
import android.app.Activity;
import android.content.Intent;
/**
* UserRecoverableExceptions signal errors that can be recovered with user action, such as a user login.
*/
public class UserRecoverableException extends Exception {
private final Intent intent;
public UserRecoverableException(String message, Intent intent) {
super(message);
this.intent = intent;
}
/**
* Getter for an {@link Intent} that when supplied to {@link Activity#startActivityForResult(Intent, int)}, will allow user intervention.
* @return Intent representing the ameliorating user action.
*/
public Intent getIntent() {
return intent;
}
}

View file

@ -0,0 +1,63 @@
/*
* 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 org.microg.gms.common.PublicApi;
/**
* Exception to be returned by a Task when a call to Google Play services has failed.
*/
@PublicApi
public class ApiException extends Exception {
/**
* @deprecated use {@link #getStatus()} instead
*/
@PublicApi
protected final Status mStatus;
/**
* Create an ApiException from a {@link com.google.android.gms.common.api.Status}.
* @param status the Status instance containing a message and code.
*/
@PublicApi
public ApiException(Status status) {
mStatus = status;
}
/**
* Returns the status of the operation.
*/
@PublicApi
public Status getStatus() {
return mStatus;
}
/**
* Indicates the status of the operation.
* @return Status code resulting from the operation.
* The value is one of the constants in {@link com.google.android.gms.common.api.CommonStatusCodes} or specific to the API in use.
*/
@PublicApi
public int getStatusCode() {
return mStatus.getStatusCode();
}
/**
* @deprecated use {@link #getMessage()} for a summary of the cause.
*/
@PublicApi
public String getStatusMessage() {
return getMessage();
}
@Override
public String getMessage() {
return mStatus.getStatusMessage();
}
}

View file

@ -0,0 +1,85 @@
/**
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.api;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.common.Hide;
import org.microg.gms.utils.ToStringHelper;
@Hide
@SafeParcelable.Class
public class ApiMetadata extends AbstractSafeParcelable {
public static final ApiMetadata DEFAULT = new ApiMetadata(null);
public static final ApiMetadata SKIP = new ApiMetadata(true);
@Field(1)
public final ComplianceOptions complianceOptions;
public final boolean skip;
@Constructor
public ApiMetadata(@Param(1) ComplianceOptions complianceOptions) {
this.complianceOptions = complianceOptions;
this.skip = false;
}
private ApiMetadata(boolean skip) {
this.complianceOptions = null;
this.skip = skip;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<ApiMetadata> CREATOR = new ApiMetadataCreator();
private static final SafeParcelableCreatorAndWriter<ApiMetadata> ORIGINAL_CREATOR = findCreator(ApiMetadata.class);
@NonNull
@Override
public String toString() {
return ToStringHelper.name("ApiMetadata").field("complianceOptions", complianceOptions).end();
}
private static class ApiMetadataCreator implements SafeParcelableCreatorAndWriter<ApiMetadata> {
private static final int METADATA_PRESENT_MAGIC = -204102970;
@Override
public ApiMetadata createFromParcel(Parcel parcel) {
int dataPosition = parcel.dataPosition();
if (parcel.readInt() != METADATA_PRESENT_MAGIC) {
parcel.setDataPosition(dataPosition - 4);
return ApiMetadata.DEFAULT;
}
return ORIGINAL_CREATOR.createFromParcel(parcel);
}
@Override
public ApiMetadata[] newArray(int size) {
return new ApiMetadata[size];
}
@Override
public void writeToParcel(ApiMetadata object, Parcel parcel, int flags) {
if (object.skip) {
parcel.setDataPosition(parcel.dataPosition() - 4);
parcel.setDataSize(parcel.dataPosition() - 4);
return;
}
parcel.writeInt(METADATA_PRESENT_MAGIC);
ORIGINAL_CREATOR.writeToParcel(object, parcel, flags);
}
}
}

View file

@ -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 com.google.android.gms.common.api;
import androidx.annotation.NonNull;
import org.microg.gms.common.PublicApi;
@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
@PublicApi
public class CommonStatusCodes {
public static final int SUCCESS_CACHE = -1;
public static final int SUCCESS = 0;
@Deprecated
public static final int SERVICE_MISSING = 1;
@Deprecated
public static final int SERVICE_VERSION_UPDATE_REQUIRED = 2;
@Deprecated
public static final int SERVICE_DISABLED = 3;
public static final int SIGN_IN_REQUIRED = 4;
public static final int INVALID_ACCOUNT = 5;
public static final int RESOLUTION_REQUIRED = 6;
public static final int NETWORK_ERROR = 7;
public static final int INTERNAL_ERROR = 8;
public static final int SERVICE_INVALID = 9;
public static final int DEVELOPER_ERROR = 10;
public static final int LICENSE_CHECK_FAILED = 11;
public static final int ERROR = 13;
public static final int INTERRUPTED = 14;
public static final int TIMEOUT = 15;
public static final int CANCELED = 16;
public static final int API_NOT_CONNECTED = 17;
public static final int DEAD_CLIENT = 18;
@NonNull
public static String getStatusCodeString(int statusCode) {
switch (statusCode) {
case SUCCESS_CACHE:
return "SUCCESS_CACHE";
case SUCCESS:
return "SUCCESS";
case SERVICE_VERSION_UPDATE_REQUIRED:
return "SERVICE_VERSION_UPDATE_REQUIRED";
case SERVICE_DISABLED:
return "SERVICE_DISABLED";
case SIGN_IN_REQUIRED:
return "SIGN_IN_REQUIRED";
case INVALID_ACCOUNT:
return "INVALID_ACCOUNT";
case RESOLUTION_REQUIRED:
return "RESOLUTION_REQUIRED";
case NETWORK_ERROR:
return "NETWORK_ERROR";
case INTERNAL_ERROR:
return "INTERNAL_ERROR";
case SERVICE_INVALID:
return "SERVICE_INVALID";
case DEVELOPER_ERROR:
return "DEVELOPER_ERROR";
case LICENSE_CHECK_FAILED:
return "LICENSE_CHECK_FAILED";
case ERROR:
return "ERROR";
case INTERRUPTED:
return "INTERRUPTED";
case TIMEOUT:
return "TIMEOUT";
case CANCELED:
return "CANCELED";
case API_NOT_CONNECTED:
return "API_NOT_CONNECTED";
case DEAD_CLIENT:
return "DEAD_CLIENT";
default:
return "unknown status code: " + statusCode;
}
}
}

View file

@ -0,0 +1,52 @@
/**
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.api;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.utils.ToStringHelper;
@SafeParcelable.Class
public class ComplianceOptions extends AbstractSafeParcelable {
@Field(1)
public int callerProductId;
@Field(2)
public int dataOwnerProductId;
@Field(3)
public int processingReason;
@Field(4)
public boolean isUserData;
public ComplianceOptions() {
}
@Constructor
public ComplianceOptions(@Param(1) int callerProductId, @Param(2) int dataOwnerProductId, @Param(3) int processingReason, @Param(4) boolean isUserData) {
this.callerProductId = callerProductId;
this.dataOwnerProductId = dataOwnerProductId;
this.processingReason = processingReason;
this.isUserData = isUserData;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<ComplianceOptions> CREATOR = findCreator(ComplianceOptions.class);
@NonNull
@Override
public String toString() {
return ToStringHelper.name("ComplianceOptions").field("callerProductId", callerProductId).field("dataOwnerProductId", dataOwnerProductId).field("processingReason", processingReason).field("isUserData", isUserData).end();
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.api;
/**
* Represents a resource, or a holder of resources, which may be released once they are no longer needed.
*/
public interface Releasable {
void release();
}

View file

@ -0,0 +1,54 @@
/*
* 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.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import org.microg.gms.common.PublicApi;
/**
* Exception to be returned by a Task when a call to Google Play services has failed with a
* possible resolution.
*/
@PublicApi
public class ResolvableApiException extends ApiException {
@PublicApi
public ResolvableApiException(Status status) {
super(status);
}
/**
* A pending intent to resolve the failure. This intent can be started with
* {@link android.app.Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
* to present UI to solve the issue.
* @return The pending intent to resolve the failure.
*/
@PublicApi
public PendingIntent getResolution() {
return mStatus.getResolution();
}
/**
* Resolves an error by starting any intents requiring user interaction.
* See {@link com.google.android.gms.common.api.CommonStatusCodes#SIGN_IN_REQUIRED}, and
* {@link com.google.android.gms.common.api.CommonStatusCodes#RESOLUTION_REQUIRED}.
* @param activity An Activity context to use to resolve the issue. The activity's
* onActivityResult method will be invoked after the user is done.
* If the resultCode is {@link android.app.Activity#RESULT_OK},
* the application should try to connect again.
* @param requestCode The request code to pass to onActivityResult.
*/
@PublicApi
public void startResolutionForResult(Activity activity, int requestCode) throws IntentSender.SendIntentException {
mStatus.startResolutionForResult(activity, requestCode);
}
}

View file

@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2022 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 org.microg.gms.common.PublicApi;
/**
* Represents the successful result of invoking an API method in Google Play services using a subclass of GoogleApi.
* Wraps a instance of a {@link Result}.
*/
@PublicApi
public class Response<T extends Result> {
private T result;
public Response() {
}
protected Response(T result) {
this.result = result;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.api;
import org.microg.gms.common.PublicApi;
/**
* Represents the final result of invoking an API method in Google Play Services.
*/
@PublicApi
public interface Result {
public Status getStatus();
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.api;
/**
* An interface for receiving a {@link Result} from a {@link PendingResult} as an asynchronous
* callback.
*/
public interface ResultCallback<R extends Result> {
/**
* Called when the {@link Result} is ready. It is the responsibility of each callback to
* release any resources associated with the result. Some result types may implement
* {@link Releasable}, in which case {@link Releasable#release()} should be used to free the
* associated resources.
*
* @param result The result from the API call. May not be null.
*/
public void onResult(R result);
}

View file

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2025 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.os.Handler;
import androidx.annotation.NonNull;
/**
* Callbacks for receiving a {@link Result} from a as an asynchronous callback. Contains separate callbacks for success and failure.
* These methods are called on the main thread, unless overridden by {@link GoogleApiClient.Builder#setHandler(Handler)}.
*/
public abstract class ResultCallbacks<R extends Result> implements ResultCallback<R> {
/**
* Called when the {@link Result} is ready and a failure occurred.
*
* @param result Status resulting from the API call. Guaranteed to be non-null and unsuccessful.
*/
public abstract void onFailure(@NonNull Status result);
/**
* Called when the {@link Result} is ready and was successful.
* <p>
* It is the responsibility of the callback to release any resources associated with the result if {@link #onSuccess(Result)} is called. Some result types may
* implement {@link Releasable}, in which case {@link Releasable#release()} should be used to free the associated resources. If a failure occurs the result will be
* released automatically.
*
* @param result The result from the API call. Never null.
*/
public abstract void onSuccess(@NonNull R result);
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.api;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.common.PublicApi;
/**
* Describes an OAuth 2.0 scope to request. This has security implications for the user, and
* requesting additional scopes will result in authorization dialogs.
*/
@PublicApi
@SafeParcelable.Class
public class Scope extends AbstractSafeParcelable {
@Field(1)
int versionCode = 1;
@Field(value = 2, getterName = "getScopeUri")
private final String scopeUri;
private Scope() {
scopeUri = null;
}
/**
* Creates a new scope with the given URI.
*/
@Constructor
public Scope(@Param(2) String scopeUri) {
this.scopeUri = scopeUri;
}
@Override
public boolean equals(Object o) {
return this == o || o instanceof Scope && scopeUri.equals(((Scope) o).scopeUri);
}
public String getScopeUri() {
return scopeUri;
}
@Override
public int hashCode() {
return scopeUri.hashCode();
}
@Override
public String toString() {
return scopeUri;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<Scope> CREATOR = findCreator(Scope.class);
}

View file

@ -0,0 +1,198 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.api;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.common.PublicApi;
import org.microg.gms.utils.ToStringHelper;
/**
* Represents the results of work.
*/
@PublicApi
@SafeParcelable.Class
public final class Status extends AbstractSafeParcelable implements Result {
@PublicApi(exclude = true)
public static final Status INTERNAL_ERROR = new Status(CommonStatusCodes.INTERNAL_ERROR, "Internal error");
@PublicApi(exclude = true)
public static final Status CANCELED = new Status(CommonStatusCodes.CANCELED, "Cancelled");
@PublicApi(exclude = true)
public static final Status SUCCESS_CACHE = new Status(CommonStatusCodes.SUCCESS_CACHE, "Success");
@PublicApi(exclude = true)
public static final Status SUCCESS = new Status(CommonStatusCodes.SUCCESS, "Success");
@Field(1000)
int versionCode = 1;
@Field(value = 1, getterName = "getStatusCode")
private final int statusCode;
@Field(value = 2, getterName = "getStatusMessage")
private final String statusMessage;
@Field(value = 3, getterName = "getResolution")
private final PendingIntent resolution;
private Status() {
statusCode = 0;
statusMessage = null;
resolution = null;
}
/**
* Creates a representation of the status resulting from a GoogleApiClient operation.
*
* @param statusCode The status code.
*/
public Status(int statusCode) {
this(statusCode, null);
}
/**
* Creates a representation of the status resulting from a GoogleApiClient operation.
*
* @param statusCode The status code.
* @param statusMessage The message associated with this status, or null.
*/
public Status(int statusCode, String statusMessage) {
this(statusCode, statusMessage, null);
}
/**
* Creates a representation of the status resulting from a GoogleApiClient operation.
*
* @param statusCode The status code.
* @param statusMessage The message associated with this status, or null.
* @param resolution A pending intent that will resolve the issue when started, or null.
*/
@Constructor
public Status(@Param(1) int statusCode, @Param(2) String statusMessage, @Param(3) PendingIntent resolution) {
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.resolution = resolution;
}
/**
* A pending intent to resolve the failure. This intent can be started with
* {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)} to
* present UI to solve the issue.
*
* @return The pending intent to resolve the failure.
*/
public PendingIntent getResolution() {
return resolution;
}
/**
* Returns the status of this result. Use {@link #isSuccess()} to determine whether the call
* was successful, and {@link #getStatusCode()} to determine what the error cause was.
* <p>
* Certain errors are due to failures that can be resolved by launching a particular intent.
* The resolution intent is available via {@link #getResolution()}.
*/
@Override
public Status getStatus() {
return this;
}
/**
* Indicates the status of the operation.
*
* @return Status code resulting from the operation. The value is one of the constants in
* {@link CommonStatusCodes} or specific to the APIs added to the GoogleApiClient.
*/
public int getStatusCode() {
return statusCode;
}
public String getStatusMessage() {
return statusMessage;
}
/**
* Returns true if calling {@link #startResolutionForResult(Activity, int)} will start any
* intents requiring user interaction.
*
* @return true if there is a resolution that can be started.
*/
public boolean hasResolution() {
return resolution != null;
}
/**
* Returns true if the operation was canceled.
*/
public boolean isCanceled() {
return statusCode == CommonStatusCodes.CANCELED;
}
/**
* Returns true if the operation was interrupted.
*/
public boolean isInterrupted() {
return statusCode == CommonStatusCodes.INTERRUPTED;
}
/**
* Returns true if the operation was successful.
*
* @return true if the operation was successful, false if there was an error.
*/
public boolean isSuccess() {
return statusCode <= 0;
}
/**
* Resolves an error by starting any intents requiring user interaction. See
* {@link CommonStatusCodes#SIGN_IN_REQUIRED}, and {@link CommonStatusCodes#RESOLUTION_REQUIRED}.
*
* @param activity An Activity context to use to resolve the issue. The activity's
* onActivityResult method will be invoked after the user is done. If the
* resultCode is {@link Activity#RESULT_OK}, the application should try to
* connect again.
* @param requestCode The request code to pass to onActivityResult.
* @throws SendIntentException If the resolution intent has been canceled or is no longer able
* to execute the request.
*/
public void startResolutionForResult(Activity activity, int requestCode) throws SendIntentException {
if (hasResolution()) {
activity.startIntentSenderForResult(resolution.getIntentSender(), requestCode, null, 0, 0, 0);
}
}
@NonNull
@Override
public String toString() {
return ToStringHelper.name("Status").field("code", statusCode).field("message", statusMessage).field("resolution", resolution).end();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static final SafeParcelableCreatorAndWriter<Status> CREATOR = findCreator(Status.class);
}

View file

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.api.internal;
import androidx.annotation.NonNull;
import com.google.android.gms.common.api.Status;
import org.microg.gms.common.Hide;
@Hide
public interface StatusExceptionMapper {
@NonNull
Exception getException(@NonNull Status status);
}

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.common.internal;
import android.os.IInterface;
import androidx.annotation.NonNull;
import com.google.android.gms.common.ConnectionResult;
import org.microg.gms.common.Hide;
@Hide
public abstract class BaseGmsClient<T extends IInterface> {
public interface ConnectionProgressReportCallbacks {
void onReportServiceBinding(@NonNull ConnectionResult connectionResult);
}
public interface SignOutCallbacks {
void onSignOutComplete();
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
public class BinderWrapper implements Parcelable {
public IBinder binder;
public BinderWrapper(IBinder binder) {
this.binder = binder;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(binder);
}
public static final Creator<BinderWrapper> CREATOR = new Creator<BinderWrapper>() {
@Override
public BinderWrapper createFromParcel(Parcel source) {
return new BinderWrapper(source.readStrongBinder());
}
@Override
public BinderWrapper[] newArray(int size) {
return new BinderWrapper[size];
}
};
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper;
import java.util.Arrays;
public class CertData extends ICertData.Stub {
private final byte[] bytes;
private final int hashCode;
public CertData(byte[] bytes) {
this.bytes = bytes;
if (bytes.length < 25) throw new RuntimeException("CertData to small");
hashCode = Arrays.hashCode(Arrays.copyOfRange(bytes, 0, 25));
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ICertData)) return false;
ICertData cert = (ICertData) obj;
try {
if (cert.remoteHashCode() != hashCode()) return false;
return Arrays.equals(ObjectWrapper.unwrapTyped(cert.getWrappedBytes(), byte[].class), getBytes());
} catch (RemoteException e) {
return false;
}
}
public byte[] getBytes() {
return bytes;
}
@Override
public IObjectWrapper getWrappedBytes() throws RemoteException {
return ObjectWrapper.wrap(getBytes());
}
@Override
public int remoteHashCode() throws RemoteException {
return hashCode();
}
@Nullable
public static CertData unwrap(IBinder certDataBinder) {
if (certDataBinder instanceof CertData) {
return (CertData) certDataBinder;
} else if (certDataBinder instanceof IObjectWrapper) {
return unwrap((IObjectWrapper) certDataBinder);
} else if (certDataBinder instanceof ICertData) {
return unwrap((ICertData) certDataBinder);
}
return null;
}
public static CertData unwrap(IObjectWrapper certDataWrapper) {
CertData certData = ObjectWrapper.unwrapTyped(certDataWrapper, CertData.class);
if (certData != null) return certData;
byte[] bytes = ObjectWrapper.unwrapTyped(certDataWrapper, byte[].class);
if (bytes != null) return new CertData(bytes);
ICertData iCertData = ObjectWrapper.unwrapTyped(certDataWrapper, ICertData.class);
if (iCertData != null) return unwrap(iCertData);
return null;
}
public static CertData unwrap(ICertData iCertData) {
if (iCertData == null) return null;
if (iCertData instanceof CertData) return (CertData) iCertData;
try {
byte[] bytes = ObjectWrapper.unwrapTyped(iCertData.getWrappedBytes(), byte[].class);
if (bytes != null) {
return new CertData(bytes);
}
} catch (RemoteException e) {
// Ignore
}
return null;
}
}

View file

@ -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<ConnectionInfo> CREATOR = findCreator(ConnectionInfo.class);
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal;
import android.accounts.Account;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.Feature;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import org.microg.gms.common.Constants;
import org.microg.gms.common.GmsService;
import org.microg.gms.utils.ToStringHelper;
import java.util.Arrays;
@SafeParcelable.Class
public class GetServiceRequest extends AbstractSafeParcelable {
@Field(1)
int versionCode = 6;
@Field(2)
public final int serviceId;
@Field(3)
public int gmsVersion;
@Field(4)
public String packageName;
@Field(5)
public IBinder accountAccessor;
@Field(6)
public Scope[] scopes;
@Field(7)
public Bundle extras;
@Field(8)
public Account account;
@Field(9)
@Deprecated
long field9;
@Field(10)
public Feature[] defaultFeatures;
@Field(11)
public Feature[] apiFeatures;
@Field(12)
public boolean supportsConnectionInfo;
@Field(13)
int field13;
@Field(14)
boolean field14;
@Field(15)
public String attributionTag;
private GetServiceRequest() {
serviceId = -1;
gmsVersion = Constants.GMS_VERSION_CODE;
}
@Constructor
public GetServiceRequest(@Param(2) int serviceId) {
this.serviceId = serviceId;
this.gmsVersion = Constants.GMS_VERSION_CODE;
this.supportsConnectionInfo = true;
}
@Override
public String toString() {
return ToStringHelper.name("GetServiceRequest")
.value(GmsService.nameFromServiceId(serviceId))
.field("packageName", packageName)
.field("gmsVersion", gmsVersion)
.field("scopes", scopes)
.field("extras", extras)
.field("account", account)
.field("defaultFeatures", defaultFeatures)
.field("apiFeatures", apiFeatures)
.field("supportsConnectionInfo", supportsConnectionInfo)
.field("attributionTag", attributionTag)
.end();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static SafeParcelableCreatorAndWriter<GetServiceRequest> CREATOR = findCreator(GetServiceRequest.class);
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.common.internal;
import android.os.Parcel;
import androidx.annotation.NonNull;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
@SafeParcelable.Class
public class ValidateAccountRequest extends AbstractSafeParcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
CREATOR.writeToParcel(this, dest, flags);
}
public static SafeParcelableCreatorAndWriter<ValidateAccountRequest> CREATOR = findCreator(ValidateAccountRequest.class);
}

View file

@ -0,0 +1,30 @@
/*
* 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 <T extends AbstractSafeParcelable> SafeParcelableCreatorAndWriter<T> findCreator(java.lang.Class<T> tClass) {
java.lang.Class<?> upmostClass = tClass;
while (upmostClass.getEnclosingClass() != null) upmostClass = upmostClass.getEnclosingClass();
String upmostClassName = upmostClass.getName();
int idx = upmostClassName.lastIndexOf('.');
String packagePrefix = idx > 0 ? upmostClassName.substring(0, idx + 1) : "";
String creatorClassName = packagePrefix + tClass.getSimpleName() + "$000Creator";
try {
return (SafeParcelableCreatorAndWriter<T>) 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;
}
}

View file

@ -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 extends Parcelable> T readParcelable(Parcel parcel, int header, Parcelable.Creator<T> 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 <T extends Parcelable> ArrayList<T> readParcelableList(Parcel parcel, int header, Parcelable.Creator<T> creator) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
ArrayList<T> list = parcel.createTypedArrayList(creator);
parcel.setDataPosition(start + size);
return list;
}
public static ArrayList<String> readStringList(Parcel parcel, int header) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
ArrayList<String> list = parcel.createStringArrayList();
parcel.setDataPosition(start + size);
return list;
}
public static ArrayList<Integer> readIntegerList(Parcel parcel, int header) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
int length = parcel.readInt();
ArrayList<Integer> list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
list.add(parcel.readInt());
}
parcel.setDataPosition(start + size);
return list;
}
public static ArrayList<Long> readLongList(Parcel parcel, int header) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
int length = parcel.readInt();
ArrayList<Long> list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
list.add(parcel.readLong());
}
parcel.setDataPosition(start + size);
return list;
}
public static ArrayList<Float> readFloatList(Parcel parcel, int header) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
int length = parcel.readInt();
ArrayList<Float> list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
list.add(parcel.readFloat());
}
parcel.setDataPosition(start + size);
return list;
}
public static ArrayList<Double> readDoubleList(Parcel parcel, int header) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
int length = parcel.readInt();
ArrayList<Double> list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
list.add(parcel.readDouble());
}
parcel.setDataPosition(start + size);
return list;
}
public static ArrayList<Boolean> readBooleanList(Parcel parcel, int header) {
int size = readSize(parcel, header);
if (size == 0)
return null;
int start = parcel.dataPosition();
int length = parcel.readInt();
ArrayList<Boolean> 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 extends Parcelable> T[] readParcelableArray(Parcel parcel, int header, Parcelable.Creator<T> 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);
}
}
}

View file

@ -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<String> 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<Integer> 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<Long> 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<Float> 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<Double> 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<Boolean> 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 <T extends Parcelable> 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 <T extends Parcelable> 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 <T extends Parcelable> void write(Parcel parcel, int fieldId, List<T> 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);
}
}
}

View file

@ -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 "";
}
}

View file

@ -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<T extends SafeParcelable> extends Parcelable.Creator<T> {
void writeToParcel(T object, Parcel parcel, int flags);
}

View file

@ -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 extends SafeParcelable> T deserializeFromBytes(byte[] bytes, Parcelable.Creator<T> 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 <T extends SafeParcelable> 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;
}
}

View file

@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: CC-BY-4.0
* Notice: Portions of this file are reproduced from work created and shared by Google and used
* according to terms described in the Creative Commons 4.0 Attribution License.
* See https://developers.google.com/readme/policies for details.
*/
/**
* Contains utility classes for Google Play services.
*/
package com.google.android.gms.common;

View file

@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.dynamic;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public interface LifecycleDelegate {
View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState);
void onCreate(@Nullable Bundle savedInstanceState);
void onDestroy();
void onDestroyView();
void onInflate(@NonNull Activity activity, @NonNull Bundle options, @Nullable Bundle onInflate);
void onLowMemory();
void onPause();
void onResume();
void onSaveInstanceState(@NonNull Bundle outState);
void onStart();
void onStop();
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.dynamic;
import android.os.IBinder;
import androidx.annotation.Nullable;
import java.lang.reflect.Field;
public class ObjectWrapper<T> extends IObjectWrapper.Stub {
private final T t;
public ObjectWrapper(T t) {
this.t = t;
}
@Nullable
public static Object unwrap(IObjectWrapper obj) {
if (obj == null) {
return null;
}
if (obj instanceof ObjectWrapper) {
return ((ObjectWrapper) obj).t;
}
IBinder binder = obj.asBinder();
Field[] fields = binder.getClass().getDeclaredFields();
if (fields.length < 1) {
throw new IllegalArgumentException("No fields were found");
}
// Ignore synthetic field(s) from JaCoCo or elsewhere
// https://www.jacoco.org/jacoco/trunk/doc/faq.html
@Nullable
Field field = null;
for (Field currentField : fields) {
if (currentField.isSynthetic()) {
continue;
}
if (field == null) {
field = currentField;
} else {
throw new IllegalArgumentException("Too many non-synthetic fields were found");
}
}
if (field == null) {
throw new IllegalArgumentException("No non-synthetic fields were found");
}
if (!field.isAccessible()) {
field.setAccessible(true);
try {
return field.get(binder);
} catch (NullPointerException localNullPointerException) {
throw new IllegalArgumentException("Binder object is null.",
localNullPointerException);
} catch (IllegalArgumentException localIllegalArgumentException) {
throw new IllegalArgumentException("remoteBinder is the wrong class.",
localIllegalArgumentException);
} catch (IllegalAccessException localIllegalAccessException) {
throw new IllegalArgumentException("Could not access the field in remoteBinder.",
localIllegalAccessException);
}
} else {
throw new IllegalArgumentException();
}
}
@Nullable
public static <T> T unwrapTyped(IObjectWrapper obj, Class<T> clazz) {
try {
return clazz.cast(unwrap(obj));
} catch (ClassCastException e) {
return null;
}
}
public static <T> ObjectWrapper<T> wrap(T t) {
return new ObjectWrapper<T>(t);
}
}

View file

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.dynamic;
public interface OnDelegateCreatedListener<T extends LifecycleDelegate> {
void onDelegateCreated(T delegate);
}

View file

@ -0,0 +1,163 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.dynamite;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.NonNull;
import java.lang.reflect.Field;
import java.util.Objects;
public class DynamiteModule {
private static final String TAG = "DynamiteModule";
public static final int NONE = 0;
public static final int LOCAL = -1;
public static final int REMOTE = 1;
@NonNull
public static final VersionPolicy PREFER_REMOTE = (context, moduleId, versions) -> {
VersionPolicy.SelectionResult result = new VersionPolicy.SelectionResult();
result.remoteVersion = versions.getRemoteVersion(context, moduleId, true);
if (result.remoteVersion != 0) {
result.selection = REMOTE;
} else {
result.localVersion = versions.getLocalVersion(context, moduleId);
if (result.localVersion != 0) {
result.selection = LOCAL;
}
}
return result;
};
@NonNull
public static final VersionPolicy PREFER_LOCAL = (context, moduleId, versions) -> {
VersionPolicy.SelectionResult result = new VersionPolicy.SelectionResult();
result.localVersion = versions.getLocalVersion(context, moduleId);
if (result.localVersion != 0) {
result.selection = LOCAL;
} else {
result.remoteVersion = versions.getRemoteVersion(context, moduleId, true);
if (result.remoteVersion != 0) {
result.selection = REMOTE;
}
}
return result;
};
public interface VersionPolicy {
interface IVersions {
int getLocalVersion(@NonNull Context context, @NonNull String moduleId);
int getRemoteVersion(@NonNull Context context, @NonNull String moduleId, boolean forceStaging) throws LoadingException;
IVersions Default = new IVersions() {
@Override
public int getLocalVersion(@NonNull Context context, @NonNull String moduleId) {
return DynamiteModule.getLocalVersion(context, moduleId);
}
@Override
public int getRemoteVersion(@NonNull Context context, @NonNull String moduleId, boolean forceStaging) throws LoadingException {
return DynamiteModule.getRemoteVersion(context, moduleId, forceStaging);
}
};
}
class SelectionResult {
public int localVersion = 0;
public int remoteVersion = 0;
public int selection = NONE;
}
SelectionResult selectModule(@NonNull Context context, @NonNull String moduleId, @NonNull IVersions versions) throws LoadingException;
}
public static class LoadingException extends Exception {
public LoadingException(String message) {
super(message);
}
public LoadingException(String message, Throwable cause) {
super(message, cause);
}
}
private Context moduleContext;
private DynamiteModule(Context moduleContext) {
this.moduleContext = moduleContext;
}
public Context getModuleContext() {
return moduleContext;
}
public static int getLocalVersion(@NonNull Context context, @NonNull String moduleId) {
try {
ClassLoader classLoader = context.getApplicationContext().getClassLoader();
Class<?> clazz = classLoader.loadClass("com.google.android.gms.dynamite.descriptors." + moduleId + ".ModuleDescriptor");
Field moduleIdField = clazz.getDeclaredField("MODULE_ID");
Field moduleVersionField = clazz.getDeclaredField("MODULE_VERSION");
if (!Objects.equals(moduleIdField.get(null), moduleId)) {
Log.e(TAG, "Module descriptor id '" + moduleIdField.get(null) + "' didn't match expected id '" + moduleId + "'");
return 0;
}
return moduleVersionField.getInt(null);
} catch (ClassNotFoundException e) {
Log.w(TAG, "Local module descriptor class for" + moduleId + " not found.");
return 0;
} catch (Exception e) {
Log.e(TAG, "Failed to load module descriptor class.", e);
return 0;
}
}
public static int getRemoteVersion(@NonNull Context context, @NonNull String moduleId) {
return getRemoteVersion(context, moduleId, false);
}
public static int getRemoteVersion(@NonNull Context context, @NonNull String moduleId, boolean forceStaging) {
Log.e(TAG, "Remote modules not yet supported");
return 0;
}
@NonNull
public static DynamiteModule load(@NonNull Context context, @NonNull VersionPolicy policy, @NonNull String moduleId) throws LoadingException {
Context applicationContext = context.getApplicationContext();
if (applicationContext == null) throw new LoadingException("null application Context", null);
try {
VersionPolicy.SelectionResult result = policy.selectModule(context, moduleId, VersionPolicy.IVersions.Default);
Log.i(TAG, "Considering local module " + moduleId + ":" + result.localVersion + " and remote module " + moduleId + ":" + result.remoteVersion);
switch (result.selection) {
case NONE:
throw new LoadingException("No acceptable module " + moduleId + " found. Local version is " + result.localVersion + " and remote version is " + result.remoteVersion + ".");
case LOCAL:
Log.i(TAG, "Selected local version of " + moduleId);
return new DynamiteModule(context);
case REMOTE:
throw new UnsupportedOperationException();
default:
throw new LoadingException("VersionPolicy returned invalid code:" + result.selection);
}
} catch (LoadingException loadingException) {
throw loadingException;
} catch (Throwable e) {
throw new LoadingException("Failed to load remote module.", e);
}
}
@NonNull
public IBinder instantiate(@NonNull String className) throws LoadingException {
try {
return (IBinder) this.moduleContext.getClassLoader().loadClass(className).newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | RuntimeException e) {
throw new LoadingException("Failed to instantiate module class: " + className, e);
}
}
}

View file

@ -0,0 +1,61 @@
/*
* 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.security;
import android.content.Context;
import android.content.Intent;
import java.security.Provider;
/**
* A utility class for installing a dynamically updatable {@link Provider} to replace the platform default provider.
*/
public class ProviderInstaller {
public static final String PROVIDER_NAME = "GmsCore_OpenSSL";
/**
* Installs the dynamically updatable security provider, if it's not already installed.
*
* @throws GooglePlayServicesRepairableException
* @throws GooglePlayServicesNotAvailableException
*/
public static void installIfNeeded(Context context) {
}
/**
* Asynchronously installs the dynamically updatable security provider, if it's not already installed. This method must be called on the UI thread.
*
* @param context
* @param listener called when the installation completes
*/
public static void installIfNeededAsync(Context context, ProviderInstallListener listener) {
if (listener != null) listener.onProviderInstalled();
}
/**
* Callback for notification of the result of provider installation.
*/
public interface ProviderInstallListener {
/**
* Called when installing the provider fails. This method is always called on the UI thread.
* <p>
* Implementers may use {@code errorCode} with the standard UI elements provided by {@link GoogleApiAvailability}; or {@code recoveryIntent} to implement custom UI.
*
* @param errorCode error code for the failure, for use with {@link GoogleApiAvailability#showErrorDialogFragment(Activity, int, int)} or {@link GoogleApiAvailability#showErrorNotification(Context, ConnectionResult)}
* @param recoveryIntent if non-null, an intent that can be used to install or update Google Play Services such that the provider can be installed
*/
void onProviderInstallFailed(int errorCode, Intent recoveryIntent);
/**
* Called when installing the provider succeeds. This method is always called on the UI thread.
*/
void onProviderInstalled();
}
}

View file

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2025 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package com.google.android.gms.stats;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.legacy.content.WakefulBroadcastReceiver;
import org.microg.gms.common.Hide;
/**
* TODO: This should end up in play-services-stats eventually
*/
@Hide
public abstract class GCoreWakefulBroadcastReceiver extends WakefulBroadcastReceiver {
public static boolean completeWakefulIntent(@NonNull Context context, @Nullable Intent intent) {
if (intent == null) return false;
return WakefulBroadcastReceiver.completeWakefulIntent(intent);
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.auth;
public class AuthConstants {
public static final String DEFAULT_ACCOUNT = "<<default account>>";
public static final String SCOPE_GET_ACCOUNT_ID = "^^_account_id_^^";
public static final String PROVIDER_METHOD_GET_ACCOUNTS = "get_accounts";
public static final String PROVIDER_METHOD_CLEAR_PASSWORD = "clear_password";
public static final String PROVIDER_EXTRA_CLEAR_PASSWORD = "clear_password";
public static final String PROVIDER_EXTRA_ACCOUNTS = "accounts";
public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
public static final String WORK_ACCOUNT_TYPE = "com.google.work";
public static final String KEY_ACCOUNT_SERVICES = "services";
public static final String KEY_ACCOUNT_CAPABILITIES = "capabilities";
public static final String GOOGLE_USER_ID = "GoogleUserId";
public static final String GOOGLE_SIGN_IN_STATUS = "googleSignInStatus";
public static final String GOOGLE_SIGN_IN_ACCOUNT = "googleSignInAccount";
public static final String SIGN_IN_ACCOUNT = "signInAccount";
public static final String ERROR_CODE = "errorCode";
public static final String SIGN_IN_CREDENTIAL = "sign_in_credential";
public static final String STATUS = "status";
public static final String SCOPE_OAUTH2 = "oauth2:";
public static final String SCOPE_EM_OP_PRO = "oauth2:email openid profile";
public static final String GOOGLE_SIGN_IN_AUTHORIZATION_RESULT = "authorization_result";
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2013-2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.common;
import com.google.android.gms.common.BuildConfig;
public class Constants {
public static final int GMS_VERSION_CODE = (BuildConfig.VERSION_CODE / 1000) * 1000;
public static final String GMS_PACKAGE_NAME = "com.google.android.gms";
public static final String USER_MICROG_PACKAGE_NAME = "org.microg.gms";
public static final String GSF_PACKAGE_NAME = "com.google.android.gsf";
public static final String GMS_PACKAGE_SIGNATURE_SHA1 = "38918a453d07199354f8b19af05ec6562ced5788";
public static final String GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1 = "bd32424203e0fb25f36b57e5aa356f9bdd1da998";
public static final String MICROG_PACKAGE_SIGNATURE_SHA1 = "10321bd893f69af97f7573aafe9de1dc0901f3a1";
@Deprecated
public static final int MAX_REFERENCE_VERSION = GMS_VERSION_CODE;
public static final String VENDING_PACKAGE_NAME = "com.android.vending";
}

View file

@ -0,0 +1,220 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.common;
public enum GmsService {
UNKNOWN(-2),
ANY(-1),
GAMES(1, "com.google.android.gms.games.service.START", "com.google.android.gms.games.internal.connect.service.START"),
PLUS(2, "com.google.android.gms.plus.service.START", "com.google.android.gms.plus.service.internal.START"),
PANORAMA(3, "com.google.android.gms.panorama.service.START"),
WALLET(4, "com.google.android.gms.wallet.service.BIND"),
PEOPLE(5, "com.google.android.gms.people.service.START"),
LOCATION(6),
APPSTATE(7, "com.google.android.gms.appstate.service.START"),
ADREQUEST(8, "com.google.android.gms.ads.service.START"),
ACCOUNT(9, "com.google.android.gms.accounts.ACCOUNT_SERVICE"),
CAST(10, "com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE"),
DRIVE(11, "com.google.android.gms.drive.ApiService.START"),
ADDRESS(12, "com.google.android.gms.identity.service.BIND"),
CAR(13, "com.google.android.gms.car.service.START"),
WEARABLE(14, "com.google.android.gms.wearable.BIND"),
AUTH_PROXY(16, "com.google.android.gms.auth.service.START"),
FITNESS(17, "com.google.android.gms.fitness.GoogleFitnessService.START"),
REMINDERS(18, "com.google.android.gms.reminders.service.START"),
LIGHTWEIGHT_INDEX(19, "com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE"),
DEVICE_CONNECTION(20, "com.google.android.gms.deviceconnection.service.START"),
INDEX(21, "com.google.android.gms.icing.INDEX_SERVICE"),
LOCATION_REPORTING(22, "com.google.android.gms.location.reporting.service.START", "com.google.android.location.reporting.service.START"),
LOCATION_MANAGER(23, "com.google.android.location.internal.GoogleLocationManagerService.START"),
PLAY_LOG(24, "com.google.android.gms.playlog.service.START"),
DROIDGUARD(25, "com.google.android.gms.droidguard.service.START"),
LOCKBOX(26, "com.google.android.gms.lockbox.service.START"),
CAST_MIRRORING(27, "com.google.android.gms.cast_mirroring.service.START"),
NETWORK_QUALITY(28, "com.google.android.gms.mdm.services.START"),
FEEDBACK(29, "com.google.android.gms.feedback.internal.IFeedbackService"),
SEARCH_ADMINISTRATION(30),
PHOTO_AUTO_BACKUP(31, "com.google.android.gms.photos.autobackup.service.START"),
SEARCH_QUERIES(32),
SEARCH_GLOBAL(33),
UDC(35, "com.google.android.gms.udc.service.START"),
SEARCH_CORPORA(36),
DEVICE_MANAGER(37, "com.google.android.gms.mdm.services.DeviceManagerApiService.START"),
PSEUDONYMOUS_ID(38, "com.google.android.gms.pseudonymous.service.START"),
COMMON(39, "com.google.android.gms.common.service.START"),
CLEARCUT_LOGGER(40, "com.google.android.gms.clearcut.service.START"),
USAGE_REPORTING(41, "com.google.android.gms.usagereporting.service.START"),
KIDS(42, "com.google.android.gms.kids.service.START"),
DOWNLOAD(43, "com.google.android.gms.common.download.START"),
SIGN_IN(44, "com.google.android.gms.signin.service.START"),
SAFETY_NET_CLIENT(45, "com.google.android.gms.safetynet.service.START"),
GSERVICES(46, "com.google.android.gms.ads.gservice.START"),
CONTEXT_MANAGER(47, "com.google.android.contextmanager.service.ContextManagerService.START"),
AUDIO_MODEM(48, "com.google.android.gms.audiomodem.service.AudioModemService.START"),
NEARBY_SHARING(49, "com.google.android.gms.nearby.sharing.service.NearbySharingService.START"),
LIGHTWEIGHT_NETWORK_QUALITY(50, "com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START"),
PHENOTYPE(51, "com.google.android.gms.phenotype.service.START"),
VOICE_UNLOCK(52, "com.google.android.gms.speech.service.START"),
NEARBY_CONNECTIONS(54, "com.google.android.gms.nearby.connection.service.START"),
FITNESS_SENSORS(55, "com.google.android.gms.fitness.SensorsApi"),
FITNESS_RECORDING(56, "com.google.android.gms.fitness.RecordingApi"),
FITNESS_HISTORY(57, "com.google.android.gms.fitness.HistoryApi"),
FITNESS_SESSIONS(58, "com.google.android.gms.fitness.SessionsApi"),
FITNESS_BLE(59, "com.google.android.gms.fitness.BleApi"),
FITNESS_CONFIG(60, "com.google.android.gms.fitness.ConfigApi"),
FITNESS_INTERNAL(61, "com.google.android.gms.fitness.InternalApi"),
NEARBY_MESSAGES(62, "com.google.android.gms.nearby.messages.service.NearbyMessagesService.START"),
HELP(63, "com.google.android.gms.googlehelp.service.GoogleHelpService.START"),
CONFIG(64, "com.google.android.gms.config.START"),
GEODATA(65, "com.google.android.gms.location.places.GeoDataApi"),
SEARCH_IME(66),
PLACE_DETECTION(67, "com.google.android.gms.location.places.PlaceDetectionApi"),
CREDENTIALS(68, "com.google.android.gms.auth.api.credentials.service.START"),
NEARBY_BOOTSTRAP(69, "com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START"),
PLUS_INTERNAL(70),
SOURCE_DEVICE(75, "com.google.android.gms.smartdevice.d2d.SourceDeviceService.START"),
TARGET_DEVICE(76, "com.google.android.gms.smartdevice.d2d.TargetDeviceService.START"),
APP_INVITE(77, "com.google.android.gms.appinvite.service.START"),
TAP_AND_PAY(79, "com.google.android.gms.tapandpay.service.BIND"),
CHROME_SYNC(80, "com.google.android.gms.chromesync.service.START"),
ACCOUNTS(81, "com.google.android.gms.smartdevice.setup.accounts.AccountsService.START"),
CAST_REMOTE_DISPLAY(83, "com.google.android.gms.cast.remote_display.service.START"),
TRUST_AGENT(85, "com.google.android.gms.trustagent.StateApi.START"),
AUTH_SIGN_IN(91, "com.google.android.gms.auth.api.signin.service.START"),
MEASUREMENT(93, "com.google.android.gms.measurement.START"),
FREIGHTER(98, "com.google.android.gms.freighter.service.START"),
GUNS(110, "com.google.android.gms.notifications.service.START"),
BLE(111, "com.google.android.gms.beacon.internal.IBleService.START"),
FIREBASE_AUTH(112, "com.google.firebase.auth.api.gms.service.START"),
APP_INDEXING(113),
GASS(116, "com.google.android.gms.gass.START"),
WORK_ACCOUNT(120, "com.google.android.gms.auth.account.workaccount.START"),
INSTANT_APPS(121, "com.google.android.gms.instantapps.START"),
CAST_FIRSTPATY(122, "com.google.android.gms.cast.firstparty.START"),
AD_CACHE(123, "com.google.android.gms.ads.service.CACHE"),
SMS_RETRIEVER(126, "com.google.android.gms.auth.api.phone.service.SmsRetrieverApiService.START"),
CRYPT_AUTH(129, "com.google.android.gms.auth.cryptauth.cryptauthservice.START"),
DYNAMIC_LINKS(131, "com.google.firebase.dynamiclinks.service.START"),
FONTS(132, "com.google.android.gms.fonts.service.START"),
ROMANESCO(135, "com.google.android.gms.romanesco.service.START"),
TRAINER(139, "com.google.android.gms.learning.trainer.START"),
FIDO2_REGULAR(148, "com.google.android.gms.fido.fido2.regular.START"),
FIDO2_PRIVILEGED(149, "com.google.android.gms.fido.fido2.privileged.START"),
DATA_DOWNLOAD(152, "com.google.android.mdd.service.START"),
ACCOUNT_DATA(153, "com.google.android.gms.auth.account.data.service.START"),
CONSTELLATION(155, "com.google.android.gms.constellation.service.START"),
AUDIT(154, "com.google.android.gms.audit.service.START"),
SYSTEM_UPDATE(157, "com.google.android.gms.update.START_API_SERVICE"),
MOBSTORE(160, "com.google.android.mobstore.service.START"),
USER_LOCATION(163, "com.google.android.gms.userlocation.service.START"),
AD_HTTP(166, "com.google.android.gms.ads.service.HTTP"),
LANGUAGE_PROFILE(167, "com.google.android.gms.languageprofile.service.START"),
MDNS(168, "com.google.android.gms.mdns.service.START"),
FOLSOM(172, "com.google.android.gms.auth.key.retrieval.service.START"),
SEMANTIC_LOCATION(173, "com.google.android.gms.semanticlocation.service.START_ODLH"),
FIDO2_ZEROPARTY(180, "com.google.android.gms.fido.fido2.zeroparty.START"),
G1_RESTORE(181, "com.google.android.gms.backup.G1_RESTORE"),
G1_BACKUP(182, "com.google.android.gms.backup.G1_BACKUP"),
OSS_LICENSES(185, "com.google.android.gms.oss.licenses.service.START"),
PAYSE(188, "com.google.android.gms.payse.service.BIND"),
RCS(189, "com.google.android.gms.rcs.START"),
CARRIER_AUTH(191, "com.google.android.gms.carrierauth.service.START"),
SYSTEM_UPDATE_SINGLE_UESR(192, "com.google.android.gms.update.START_SINGLE_USER_API_SERVICE"),
APP_USAGE(193, "com.google.android.gms.appusage.service.START"),
NEARBY_SHARING_2(194, "com.google.android.gms.nearby.sharing.START_SERVICE"),
AD_CONSENT_LOOKUP(195, "com.google.android.gms.ads.service.CONSENT_LOOKUP"),
CREDENTIAL_MANAGER(196, "com.google.android.gms.credential.manager.service.firstparty.START"),
PHONE_INTERNAL(197, "com.google.android.gms.auth.api.phone.service.InternalService.START"),
PAY(198, "com.google.android.gms.pay.service.BIND", "com.google.android.gms.pay.service.THIRD_PARTY"),
ASTERISM(199, "com.google.android.gms.asterism.service.START"),
MODULE_RESTORE(201, "com.google.android.gms.backup.GMS_MODULE_RESTORE"),
FACS_CACHE(202, "com.google.android.gms.facs.cache.service.START"),
RECAPTCHA(205, "com.google.android.gms.recaptcha.service.START"),
CONTACT_SYNC(208, "com.google.android.gms.people.contactssync.service.START"),
IDENTITY_SIGN_IN(212, "com.google.android.gms.auth.api.identity.service.signin.START"),
CREDENTIAL_STORE(214, "com.google.android.gms.fido.credentialstore.internal_service.START"),
MDI_SYNC(215, "com.google.android.gms.mdisync.service.START"),
EVENT_ATTESTATION(216, "com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION"),
SCHEDULER(218, "com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE"),
AUTHORIZATION(219, "com.google.android.gms.auth.api.identity.service.authorization.START"),
FACS_SYNC(220, "com.google.android.gms.facs.internal.service.START"),
AUTH_CONFIG_SYNC(221, "com.google.android.gms.auth.config.service.START"),
CREDENTIAL_SAVING(223, "com.google.android.gms.auth.api.identity.service.credentialsaving.START"),
GOOGLE_AUTH(224, "com.google.android.gms.auth.account.authapi.START"),
ENTERPRISE_LOADER(225, "com.google.android.gms.enterprise.loader.service.START"),
THUNDERBIRD(226, "com.google.android.gms.thunderbird.service.START"),
NEARBY_EXPOSURE(236, "com.google.android.gms.nearby.exposurenotification.START"),
GMS_COMPLIANCE(257, "com.google.android.gms.gmscompliance.service.START"),
BLOCK_STORE(258, "com.google.android.gms.auth.blockstore.service.START"),
FIDO_SOURCE_DEVICE(262, "com.google.android.gms.fido.sourcedevice.service.START"),
FAST_PAIR(265, "com.google.android.gms.nearby.fastpair.START"),
MATCHSTICK_LIGHTER(268, "com.google.android.gms.matchstick.lighter.service.START"),
FIDO_TARGET_DEVICE_INTERNAL(269, "com.google.android.gms.fido.targetdevice.internal_service.START"),
TELEMETRY(270, "com.google.android.gms.common.telemetry.service.START"),
SECOND_DEVICE_AUTH(275, "com.google.android.gms.setup.auth.SecondDeviceAuth.START"),
LOCATION_SHARING_REPORTER(277, "com.google.android.gms.locationsharingreporter.service.START"),
OCR(279, "com.google.android.gms.ocr.service.START"),
POTOKENS(285, "com.google.android.gms.potokens.service.START"),
OCR_INTERNAL(281, "com.google.android.gms.ocr.service.internal.START"),
APP_SET(300, "com.google.android.gms.appset.service.START"),
THREAD_NETWORK(305, "com.google.android.gms.threadnetwork.service.START"),
MODULE_INSTALL(308, "com.google.android.gms.chimera.container.moduleinstall.ModuleInstallService.START"),
SEMANTIC_LOCATION_HISTORY(314, "com.google.android.gms.semanticlocationhistory.service.START", "com.google.android.gms.semanticlocationhistory.zeroparty.service.START"),
IN_APP_REACH(315, "com.google.android.gms.inappreach.service.START"),
APP_ERRORS(334, "com.google.android.gms.apperrors.service.START_APP_ERROR"),
;
public int SERVICE_ID;
public String ACTION;
public String[] SECONDARY_ACTIONS;
GmsService(int serviceId, String... actions) {
this.SERVICE_ID = serviceId;
this.ACTION = actions.length > 0 ? actions[0] : null;
this.SECONDARY_ACTIONS = actions;
}
public interface ADVERTISING_ID {
// Has no service id
String ACTION = "com.google.android.gms.ads.identifier.service.START";
}
public static GmsService byServiceId(int serviceId) {
for (GmsService service : values()) {
if (service.SERVICE_ID == serviceId) return service;
}
return UNKNOWN;
}
public static GmsService byAction(String action) {
for (GmsService service : values()) {
for (String serviceAction : service.SECONDARY_ACTIONS) {
if (serviceAction.equals(action)) return service;
}
}
return UNKNOWN;
}
public static String nameFromServiceId(int serviceId) {
return byServiceId(serviceId).toString(serviceId);
}
public String toString(int serviceId) {
if (this != UNKNOWN) return toString();
return "UNKNOWN(" + serviceId + ")";
}
}

View file

@ -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 {
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An class, method or field is named public, if it can be used with the original play services
* client library.
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface PublicApi {
/**
* @return the first version that contains the given class, method or field
*/
String since() default "0";
/**
* @return the last version that contains the given class, method or field
*/
String until() default "latest";
/**
* Used on a method or field to exclude it from the public api if the corresponding class was
* marked as public api.
*
* @return true if the method or field is not part of the public api
* @deprecated use {@link Hide} instead
*/
@Deprecated
boolean exclude() default false;
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.gcm;
public final class GcmConstants {
public static final String ACTION_C2DM_RECEIVE = "com.google.android.c2dm.intent.RECEIVE";
public static final String ACTION_C2DM_REGISTER = "com.google.android.c2dm.intent.REGISTER";
public static final String ACTION_C2DM_REGISTRATION = "com.google.android.c2dm.intent.REGISTRATION";
public static final String ACTION_C2DM_UNREGISTER = "com.google.android.c2dm.intent.UNREGISTER";
public static final String ACTION_GCM_SEND = "com.google.android.gcm.intent.SEND";
public static final String ACTION_NOTIFICATION_OPEN = "com.google.android.gms.gcm.NOTIFICATION_OPEN";
public static final String ACTION_NOTIFICATION_DISMISS = "com.google.android.gms.gcm.NOTIFICATION_DISMISS";
public static final String ACTION_SCHEDULE = "com.google.android.gms.gcm.ACTION_SCHEDULE";
public static final String ACTION_TASK_READY = "com.google.android.gms.gcm.ACTION_TASK_READY";
public static final String ACTION_TASK_INITIALZE = "com.google.android.gms.gcm.SERVICE_ACTION_INITIALIZE";
public static final String ACTION_INSTANCE_ID = "com.google.android.gms.iid.InstanceID";
public static final String EXTRA_APP = "app";
public static final String EXTRA_APP_OVERRIDE = "org.microg.gms.gcm.APP_OVERRIDE";
public static final String EXTRA_APP_ID = "appid";
public static final String EXTRA_APP_VERSION_CODE = "app_ver";
public static final String EXTRA_APP_VERSION_NAME = "app_ver_name";
public static final String EXTRA_CLIENT_VERSION = "cliv";
public static final String EXTRA_COMPONENT = "component";
public static final String EXTRA_COLLAPSE_KEY = "collapse_key";
public static final String EXTRA_DELAY = "google.delay";
public static final String EXTRA_DELETE = "delete";
public static final String EXTRA_ERROR = "error";
public static final String EXTRA_FROM = "from";
public static final String EXTRA_GSF_INTENT = "GSF";
public static final String EXTRA_GMS_VERSION = "gmsv";
public static final String EXTRA_IS_MESSENGER2 = "messenger2";
public static final String EXTRA_KID = "kid";
public static final String EXTRA_MESSENGER = "google.messenger";
public static final String EXTRA_MESSAGE_TYPE = "message_type";
public static final String EXTRA_MESSAGE_ID = "google.message_id";
public static final String EXTRA_OS_VERSION = "osv";
public static final String EXTRA_PENDING_INTENT = "com.google.android.gms.gcm.PENDING_INTENT";
public static final String EXTRA_PUBLIC_KEY = "pub2";
public static final String EXTRA_RAWDATA = "rawData";
public static final String EXTRA_RAWDATA_BASE64 = "gcm.rawData64";
public static final String EXTRA_REGISTRATION_ID = "registration_id";
public static final String EXTRA_RETRY_AFTER = "Retry-After";
public static final String EXTRA_SCHEDULER_ACTION = "scheduler_action";
public static final String EXTRA_SCOPE = "scope";
public static final String EXTRA_SENDER = "sender";
public static final String EXTRA_SENDER_LEGACY = "legacy.sender";
public static final String EXTRA_SEND_TO = "google.to";
public static final String EXTRA_SEND_FROM = "google.from";
public static final String EXTRA_SENT_TIME = "google.sent_time";
public static final String EXTRA_SIGNATURE = "sig";
public static final String EXTRA_SUBSCIPTION = "subscription";
public static final String EXTRA_SUBTYPE = "subtype";
public static final String EXTRA_USE_GSF = "useGsf";
public static final String EXTRA_TAG = "tag";
public static final String EXTRA_TOPIC = "gcm.topic";
public static final String EXTRA_TTL = "google.ttl";
public static final String EXTRA_UNREGISTERED = "unregistered";
public static final String EXTRA_ACCOUNT_NAME = "a";
public static final String EXTRA_REG_ID = "id";
public static final String EXTRA_AUTHS_TOKEN = "t";
public static final String EXTRA_GCM_BODY = "gcmb";
public static final String EXTRA_GMS_GNOTS_PAYLOAD = "gms.gnots.payload";
public static final String MESSAGE_TYPE_GCM = "gcm";
public static final String MESSAGE_TYPE_DELETED_MESSAGE = "deleted_message";
public static final String MESSAGE_TYPE_SEND_ERROR = "send_error";
public static final String MESSAGE_TYPE_SEND_EVENT = "send_event";
public static final String SCHEDULER_ACTION_CANCEL = "CANCEL_TASK";
public static final String SCHEDULER_ACTION_CANCEL_ALL = "CANCEL_ALL";
public static final String SCHEDULER_ACTION_SCHEDULE = "SCHEDULE_TASK";
public static final String PERMISSION_GTALK = "com.google.android.gtalkservice.permission.GTALK_SERVICE";
public static final String PERMISSION_NETWORK_TASK = "com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE";
public static final String PERMISSION_RECEIVE = "com.google.android.c2dm.permission.RECEIVE";
public static final String PERMISSION_SEND = "com.google.android.c2dm.permission.SEND";
public static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
public static final String ERROR_INVALID_FID = "Invalid argument for the given fid";
public static final String INSTANCE_ID_SCOPE_GCM = "GCM";
public static final String GCMID_INSTANCE_ID = "google.com/iid";
public static final String GCMID_REFRESH = "gcm.googleapis.com/refresh";
}

View file

@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2022 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.utils;
import android.util.Base64;
import java.util.Arrays;
public class ToStringHelper {
private StringBuilder sb;
private boolean hasField;
private boolean hasValue;
private boolean hasEnd;
public ToStringHelper(String name) {
this.sb = new StringBuilder(name).append("[");
}
public static ToStringHelper name(String name) {
return new ToStringHelper(name);
}
public ToStringHelper value(String val) {
if (!hasField) {
if (hasValue) sb.append(',');
sb.append(val);
hasValue = true;
}
return this;
}
public ToStringHelper value(long val) {
return value(Long.toString(val));
}
public ToStringHelper value(double val) {
return value(Double.toString(val));
}
public ToStringHelper value(Object val) {
if (val instanceof Long) value((long) val);
if (val instanceof Double) value((double) val);
return value(val, false);
}
public ToStringHelper value(Object val, boolean forceNull) {
if (val == null && !forceNull) return this;
return value(val == null ? "null" : val.toString());
}
public ToStringHelper value(byte[] val) {
return value(val, false);
}
public ToStringHelper value(byte[] val, boolean forceNull) {
if (val == null && !forceNull) return this;
return value(val == null ? "null" : Base64.encodeToString(val, Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE));
}
private ToStringHelper fieldUnquoted(String name, String val) {
if (hasValue || hasField) sb.append(", ");
sb.append(name).append('=').append(val);
hasField = true;
return this;
}
public ToStringHelper field(String name, String val) {
return field(name, val, false);
}
public ToStringHelper field(String name, String val, boolean forceNull) {
if (val == null && !forceNull) return this;
if (val == null) return fieldUnquoted(name, "null");
if (hasValue || hasField) sb.append(", ");
sb.append(name).append("=\"").append(val.replace("\"", "\\\"")).append('"');
hasField = true;
return this;
}
public ToStringHelper field(String name, long val) {
return fieldUnquoted(name, Long.toString(val));
}
public ToStringHelper field(String name, double val) {
return fieldUnquoted(name, Double.toString(val));
}
public ToStringHelper field(String name, boolean val) {
return fieldUnquoted(name, Boolean.toString(val));
}
public ToStringHelper field(String name, Object val) {
if (val instanceof Long) return field(name, (long) val);
if (val instanceof Double) return field(name, (double) val);
if (val instanceof Boolean) return field(name, (boolean) val);
return field(name, val, false);
}
public ToStringHelper field(String name, Object val, boolean forceNull) {
if (val == null && !forceNull) return this;
return fieldUnquoted(name, val == null ? "null" : val.toString());
}
public ToStringHelper field(String name, byte[] val) {
return field(name, val, false);
}
public ToStringHelper field(String name, byte[] val, boolean forceNull) {
if (val == null && !forceNull) return this;
return fieldUnquoted(name, val == null ? "null" : Base64.encodeToString(val, Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE));
}
public ToStringHelper field(String name, Object[] val) {
return field(name, val, false);
}
public ToStringHelper field(String name, Object[] val, boolean forceNull) {
if (val == null && !forceNull) return this;
return fieldUnquoted(name, val == null ? "null" : Arrays.toString(val));
}
public String end() {
if (!hasEnd) sb.append(']');
hasEnd = true;
return sb.toString();
}
}

View file

@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.utils;
import android.os.WorkSource;
import android.util.Log;
import java.lang.reflect.Method;
import static android.os.Build.VERSION.SDK_INT;
public class WorkSourceUtil {
private static final String TAG = "WorkSourceUtil";
private static Method getMethod(String name, Class<?>... parameterTypes) throws Exception {
Method method = WorkSource.class.getMethod(name, parameterTypes);
method.setAccessible(true);
return method;
}
private static <T> T invokeMethod(WorkSource workSource, Method method, Object... args) throws Exception {
return (T) method.invoke(workSource, args);
}
private static <T> T invokeMethod(WorkSource workSource, String name, Object... args) throws Exception {
return invokeMethod(workSource, getMethod(name), args);
}
public static void add(WorkSource workSource, int uid, String packageName) {
try {
invokeMethod(workSource, getMethod("add", Integer.TYPE, String.class), uid, packageName);
} catch (Exception e) {
try {
invokeMethod(workSource, getMethod("add", Integer.TYPE), uid);
} catch (Exception ex) {
// Ignore
}
}
}
public static int size(WorkSource workSource) {
try {
return invokeMethod(workSource, "size");
} catch (Exception e) {
return 0;
}
}
public static boolean isEmpty(WorkSource workSource) {
if (SDK_INT >= 28) {
try {
return invokeMethod(workSource, "isEmpty");
} catch (Exception e) {
// Ignore and fall-through to size()
}
}
return size(workSource) == 0;
}
}

View file

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2015, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.safeparcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
public abstract class AutoSafeParcelable extends AbstractSafeParcelable {
private static final String TAG = "SafeParcel";
@SuppressWarnings("unchecked")
@Override
public void writeToParcel(Parcel dest, int flags) {
Creator<Parcelable> creator = SafeParcelReflectionUtil.getCreator(this.getClass());
if (creator instanceof SafeParcelableCreatorAndWriter) {
((SafeParcelableCreatorAndWriter<AutoSafeParcelable>) (SafeParcelableCreatorAndWriter<?>) creator).writeToParcel(this, dest, flags);
} else {
Log.w(TAG, "AutoSafeParcelable is not using SafeParcelableCreatorAndWriter");
SafeParcelReflectionUtil.writeObject(this, dest, flags);
}
}
@SuppressWarnings("unchecked")
public static <T extends AbstractSafeParcelable> SafeParcelableCreatorAndWriter<T> findCreator(java.lang.Class<T> tClass) {
try {
return AbstractSafeParcelable.findCreator(tClass);
} catch (Exception e) {
if (AutoSafeParcelable.class.isAssignableFrom(tClass)) {
return (SafeParcelableCreatorAndWriter<T>) new AutoCreator<>((java.lang.Class<AutoSafeParcelable>) tClass);
} else {
throw new RuntimeException("AutoSafeParcelable.findCreator() invoked with non-AutoSafeParcelable");
}
}
}
@Deprecated
public static class AutoCreator<T extends AutoSafeParcelable> extends ReflectedSafeParcelableCreatorAndWriter<T> {
public AutoCreator(java.lang.Class<T> tClass) {
super(tClass);
}
}
}

View file

@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2023 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.safeparcel;
import android.os.Parcel;
import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter;
import java.lang.reflect.Array;
public class ReflectedSafeParcelableCreatorAndWriter<T extends AutoSafeParcelable> implements SafeParcelableCreatorAndWriter<T> {
private final SafeParcelReflectionUtil.ClassDescriptor<T> descriptor;
public ReflectedSafeParcelableCreatorAndWriter(Class<T> tClass) {
this.descriptor = new SafeParcelReflectionUtil.ClassDescriptor<>(tClass);
}
@Override
public T createFromParcel(Parcel parcel) {
return SafeParcelReflectionUtil.createObject(parcel, descriptor);
}
@Override
public void writeToParcel(T object, Parcel parcel, int flags) {
SafeParcelReflectionUtil.writeObject(object, parcel, flags, descriptor);
}
@SuppressWarnings("unchecked")
@Override
public T[] newArray(int i) {
return (T[]) Array.newInstance(descriptor.tClass, i);
}
}

View file

@ -0,0 +1,538 @@
/*
* SPDX-FileCopyrightText: 2015, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.safeparcel;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.SparseArray;
import com.google.android.gms.common.internal.safeparcel.SafeParcelReader;
import com.google.android.gms.common.internal.safeparcel.SafeParcelWriter;
import com.google.android.gms.common.internal.safeparcel.SafeParcelable;
import org.microg.gms.common.Hide;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Hide
public final class SafeParcelReflectionUtil {
private static final String TAG = "SafeParcel";
private SafeParcelReflectionUtil() {
}
@Deprecated
public static <T extends AutoSafeParcelable> T createObject(Class<T> tClass, Parcel in) {
ClassDescriptor<T> descriptor = new ClassDescriptor<>(tClass);
return createObject(in, descriptor);
}
public static <T extends AutoSafeParcelable> T createObject(Parcel in, ClassDescriptor<T> descriptor) {
try {
Constructor<T> constructor = descriptor.constructor;
T t = constructor.newInstance();
readObject(t, in, descriptor);
return t;
} catch (Exception e) {
throw new RuntimeException("Can't construct object", e);
}
}
@Deprecated
public static void writeObject(AutoSafeParcelable object, Parcel parcel, int flags) {
if (object == null)
throw new NullPointerException();
Class<?> clazz = object.getClass();
ClassDescriptor<?> descriptor = new ClassDescriptor<>(clazz);
writeObject(object, parcel, flags, descriptor);
}
public static <T extends AutoSafeParcelable> void writeObject(T object, Parcel parcel, int flags, ClassDescriptor<?> descriptor) {
int start = SafeParcelWriter.writeObjectHeader(parcel);
for (ClassDescriptor.FieldDescriptor fieldDescriptor : descriptor.fields.values()) {
try {
writeField(object, parcel, flags, fieldDescriptor);
} catch (Exception e) {
Log.w(TAG, "Error writing field: " + e);
}
}
SafeParcelWriter.finishObjectHeader(parcel, start);
}
@SuppressWarnings("unchecked")
@Deprecated
public static <T extends AutoSafeParcelable> void readObject(T object, Parcel parcel) {
if (object == null)
throw new NullPointerException();
Class<T> clazz = (Class<T>) object.getClass();
ClassDescriptor<T> descriptor = new ClassDescriptor<>(clazz);
readObject(object, parcel, descriptor);
}
public static <T extends AutoSafeParcelable> void readObject(T object, Parcel parcel, ClassDescriptor<T> descriptor) {
if (object == null)
throw new NullPointerException();
int end = SafeParcelReader.readObjectHeader(parcel);
while (parcel.dataPosition() < end) {
int header = SafeParcelReader.readHeader(parcel);
int fieldId = SafeParcelReader.getFieldId(header);
ClassDescriptor.FieldDescriptor fieldDescriptor = descriptor.fields.get(fieldId);
if (fieldDescriptor == null) {
Log.d(TAG, String.format("Unknown field id %d in %s, skipping.", fieldId, descriptor.tClass.getName()));
SafeParcelReader.skip(parcel, header);
} else {
try {
readField(object, parcel, header, fieldDescriptor);
} catch (Exception e) {
Log.w(TAG, String.format("Error reading field: %d of type %s in %s, skipping.", fieldId, fieldDescriptor.type, descriptor.tClass.getName()), e);
SafeParcelReader.skip(parcel, header);
}
}
}
if (parcel.dataPosition() > end) {
throw new RuntimeException("Overread allowed size end=" + end);
}
}
@SuppressWarnings("unchecked")
private static Parcelable.Creator<Parcelable> getCreator(Field field) {
Class<?> clazz = field.getType();
if (clazz.isArray()) {
clazz = clazz.getComponentType();
}
if (clazz != null && Parcelable.class.isAssignableFrom(clazz)) {
return getCreator((Class<? extends Parcelable>) clazz);
}
throw new RuntimeException(clazz + " is not an Parcelable");
}
@SuppressWarnings("unchecked")
public static Parcelable.Creator<Parcelable> getCreator(Class<? extends Parcelable> clazz) {
try {
Field creatorField = clazz.getDeclaredField("CREATOR");
creatorField.setAccessible(true);
return (Parcelable.Creator<Parcelable>) creatorField.get(null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(clazz + " is an Parcelable without CREATOR");
} catch (IllegalAccessException e) {
throw new RuntimeException("CREATOR in " + clazz + " is not accessible");
}
}
@SuppressWarnings("deprecation")
private static Class<?> getSubClass(Field field) {
SafeParceled safeParceled = field.getAnnotation(SafeParceled.class);
SafeParcelable.Field safeParcelableField = field.getAnnotation(SafeParcelable.Field.class);
if (safeParceled != null && safeParceled.subClass() != SafeParceled.class) {
return safeParceled.subClass();
} else if (safeParceled != null && !"undefined".equals(safeParceled.subType())) {
try {
return Class.forName(safeParceled.subType());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
} else if (safeParcelableField != null && safeParcelableField.subClass() != SafeParcelable.class) {
return safeParcelableField.subClass();
} else {
return null;
}
}
@SuppressWarnings("deprecation")
private static Class<?> getListItemClass(Field field) {
Class<?> subClass = getSubClass(field);
if (subClass != null || field.isAnnotationPresent(SafeParceled.class)) return subClass;
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
if (pt.getActualTypeArguments().length >= 1) {
Type t = pt.getActualTypeArguments()[0];
if (t instanceof Class) return (Class<?>) t;
}
}
return null;
}
private static ClassLoader getClassLoader(Class<?> clazz) {
return clazz == null || clazz.getClassLoader() == null ? ClassLoader.getSystemClassLoader() : clazz.getClassLoader();
}
@SuppressWarnings("deprecation")
private static boolean isSafeParceledField(Field field) {
return field.isAnnotationPresent(SafeParceled.class) || field.isAnnotationPresent(SafeParcelable.Field.class);
}
@SuppressWarnings("unchecked")
private static void writeField(AutoSafeParcelable object, Parcel parcel, int flags, ClassDescriptor.FieldDescriptor descriptor)
throws IllegalAccessException {
switch (descriptor.type) {
case Parcelable:
SafeParcelWriter.write(parcel, descriptor.id, (Parcelable) descriptor.field.get(object), flags, descriptor.mayNull);
break;
case Binder:
SafeParcelWriter.write(parcel, descriptor.id, (IBinder) descriptor.field.get(object), descriptor.mayNull);
break;
case Interface:
IInterface iInterface = ((IInterface) descriptor.field.get(object));
IBinder iBinder = iInterface != null ? iInterface.asBinder() : null;
SafeParcelWriter.write(parcel, descriptor.id, iBinder, descriptor.mayNull);
break;
case StringList:
SafeParcelWriter.writeStringList(parcel, descriptor.id, ((List<String>) descriptor.field.get(object)), descriptor.mayNull);
break;
case IntegerList:
SafeParcelWriter.writeIntegerList(parcel, descriptor.id, ((List<Integer>) descriptor.field.get(object)), descriptor.mayNull);
break;
case BooleanList:
SafeParcelWriter.writeBooleanList(parcel, descriptor.id, ((List<Boolean>) descriptor.field.get(object)), descriptor.mayNull);
break;
case LongList:
SafeParcelWriter.writeLongList(parcel, descriptor.id, ((List<Long>) descriptor.field.get(object)), descriptor.mayNull);
break;
case FloatList:
SafeParcelWriter.writeFloatList(parcel, descriptor.id, ((List<Float>) descriptor.field.get(object)), descriptor.mayNull);
break;
case DoubleList:
SafeParcelWriter.writeDoubleList(parcel, descriptor.id, ((List<Double>) descriptor.field.get(object)), descriptor.mayNull);
break;
case List: {
Class<?> clazz = descriptor.listItemClass;
if (clazz == null || !Parcelable.class.isAssignableFrom(clazz) || descriptor.useValueParcel) {
SafeParcelWriter.write(parcel, descriptor.id, (List<?>) descriptor.field.get(object), descriptor.mayNull);
} else {
SafeParcelWriter.write(parcel, descriptor.id, (List<Parcelable>) descriptor.field.get(object), flags, descriptor.mayNull);
}
break;
}
case Map:
SafeParcelWriter.write(parcel, descriptor.id, (Map) descriptor.field.get(object), descriptor.mayNull);
break;
case Bundle:
SafeParcelWriter.write(parcel, descriptor.id, (Bundle) descriptor.field.get(object), descriptor.mayNull);
break;
case ParcelableArray:
SafeParcelWriter.write(parcel, descriptor.id, (Parcelable[]) descriptor.field.get(object), flags, descriptor.mayNull);
break;
case StringArray:
SafeParcelWriter.write(parcel, descriptor.id, (String[]) descriptor.field.get(object), descriptor.mayNull);
break;
case ByteArray:
SafeParcelWriter.write(parcel, descriptor.id, (byte[]) descriptor.field.get(object), descriptor.mayNull);
break;
case ByteArrayArray:
SafeParcelWriter.write(parcel, descriptor.id, (byte[][]) descriptor.field.get(object), descriptor.mayNull);
break;
case FloatArray:
SafeParcelWriter.write(parcel, descriptor.id, (float[]) descriptor.field.get(object), descriptor.mayNull);
break;
case IntArray:
SafeParcelWriter.write(parcel, descriptor.id, (int[]) descriptor.field.get(object), descriptor.mayNull);
break;
case Integer:
SafeParcelWriter.write(parcel, descriptor.id, (Integer) descriptor.field.get(object));
break;
case Long:
SafeParcelWriter.write(parcel, descriptor.id, (Long) descriptor.field.get(object));
break;
case Short:
SafeParcelWriter.write(parcel, descriptor.id, (Short) descriptor.field.get(object));
break;
case Boolean:
SafeParcelWriter.write(parcel, descriptor.id, (Boolean) descriptor.field.get(object));
break;
case Float:
SafeParcelWriter.write(parcel, descriptor.id, (Float) descriptor.field.get(object));
break;
case Double:
SafeParcelWriter.write(parcel, descriptor.id, (Double) descriptor.field.get(object));
break;
case String:
SafeParcelWriter.write(parcel, descriptor.id, (String) descriptor.field.get(object), descriptor.mayNull);
break;
case Byte:
SafeParcelWriter.write(parcel, descriptor.id, (Byte) descriptor.field.get(object));
break;
}
}
private static void readField(AutoSafeParcelable object, Parcel parcel, int header, ClassDescriptor.FieldDescriptor descriptor)
throws IllegalAccessException {
switch (descriptor.type) {
case Parcelable:
descriptor.field.set(object, SafeParcelReader.readParcelable(parcel, header, descriptor.creator));
break;
case Binder:
descriptor.field.set(object, SafeParcelReader.readBinder(parcel, header));
break;
case Interface: {
boolean hasStub = false;
for (Class<?> aClass : descriptor.field.getType().getDeclaredClasses()) {
try {
descriptor.field.set(object, aClass.getDeclaredMethod("asInterface", IBinder.class)
.invoke(null, SafeParcelReader.readBinder(parcel, header)));
hasStub = true;
break;
} catch (Exception ignored) {
}
}
if (!hasStub) throw new RuntimeException("Field has broken interface: " + descriptor.field);
break;
}
case StringList:
descriptor.field.set(object, SafeParcelReader.readStringList(parcel, header));
break;
case IntegerList:
descriptor.field.set(object, SafeParcelReader.readIntegerList(parcel, header));
break;
case BooleanList:
descriptor.field.set(object, SafeParcelReader.readBooleanList(parcel, header));
break;
case LongList:
descriptor.field.set(object, SafeParcelReader.readLongList(parcel, header));
break;
case FloatList:
descriptor.field.set(object, SafeParcelReader.readFloatList(parcel, header));
break;
case DoubleList:
descriptor.field.set(object, SafeParcelReader.readDoubleList(parcel, header));
break;
case List: {
Class<?> clazz = descriptor.listItemClass;
Object val;
if (clazz == null || !Parcelable.class.isAssignableFrom(clazz) || descriptor.useValueParcel) {
val = SafeParcelReader.readList(parcel, header, getClassLoader(clazz));
} else {
val = SafeParcelReader.readParcelableList(parcel, header, descriptor.creator);
}
descriptor.field.set(object, val);
break;
}
case Map: {
Class<?> clazz = descriptor.subClass;
Object val = SafeParcelReader.readMap(parcel, header, getClassLoader(clazz));
descriptor.field.set(object, val);
break;
}
case Bundle: {
Class<?> clazz = descriptor.subClass;
Object val;
if (clazz == null || !Parcelable.class.isAssignableFrom(clazz) || descriptor.useValueParcel /* should not happen on Bundles */) {
val = SafeParcelReader.readBundle(parcel, header, getClassLoader(descriptor.field.getDeclaringClass()));
} else {
val = SafeParcelReader.readBundle(parcel, header, getClassLoader(clazz));
}
descriptor.field.set(object, val);
break;
}
case ParcelableArray:
descriptor.field.set(object, SafeParcelReader.readParcelableArray(parcel, header, descriptor.creator));
break;
case StringArray:
descriptor.field.set(object, SafeParcelReader.readStringArray(parcel, header));
break;
case ByteArray:
descriptor.field.set(object, SafeParcelReader.readByteArray(parcel, header));
break;
case ByteArrayArray:
descriptor.field.set(object, SafeParcelReader.readByteArrayArray(parcel, header));
break;
case FloatArray:
descriptor.field.set(object, SafeParcelReader.readFloatArray(parcel, header));
break;
case IntArray:
descriptor.field.set(object, SafeParcelReader.readIntArray(parcel, header));
break;
case Integer: {
int i = SafeParcelReader.readInt(parcel, header);
if (descriptor.versionCode != -1 && i > descriptor.versionCode) {
Log.d(TAG, String.format("Version code of %s (%d) is older than object read (%d).", descriptor.field.getDeclaringClass().getName(), descriptor.versionCode, i));
}
descriptor.field.set(object, i);
break;
}
case Long: {
long l = SafeParcelReader.readLong(parcel, header);
if (descriptor.versionCode != -1 && l > descriptor.versionCode) {
Log.d(TAG, String.format("Version code of %s (%d) is older than object read (%d).", descriptor.field.getDeclaringClass().getName(), descriptor.versionCode, l));
}
descriptor.field.set(object, l);
break;
}
case Short: {
short i = SafeParcelReader.readShort(parcel, header);
if (descriptor.versionCode != -1 && i > descriptor.versionCode) {
Log.d(TAG, String.format("Version code of %s (%d) is older than object read (%d).", descriptor.field.getDeclaringClass().getName(), descriptor.versionCode, i));
}
descriptor.field.set(object, i);
break;
}
case Boolean:
descriptor.field.set(object, SafeParcelReader.readBool(parcel, header));
break;
case Float:
descriptor.field.set(object, SafeParcelReader.readFloat(parcel, header));
break;
case Double:
descriptor.field.set(object, SafeParcelReader.readDouble(parcel, header));
break;
case String:
descriptor.field.set(object, SafeParcelReader.readString(parcel, header));
break;
case Byte:
descriptor.field.set(object, SafeParcelReader.readByte(parcel, header));
break;
default:
throw new IllegalStateException("Unexpected value: " + descriptor.type);
}
}
private enum SafeParcelType {
Parcelable, Binder, Interface, Bundle,
StringList, IntegerList, BooleanList, LongList, FloatList, DoubleList, List, Map,
ParcelableArray, StringArray, ByteArray, ByteArrayArray, FloatArray, IntArray,
Integer, Long, Short, Boolean, Float, Double, String, Byte;
}
public static class ClassDescriptor<T> {
Class<T> tClass;
Constructor<T> constructor;
Map<Integer, FieldDescriptor> fields = new HashMap<>();
public ClassDescriptor(Class<T> tClass) {
this.tClass = tClass;
try {
constructor = tClass.getDeclaredConstructor();
constructor.setAccessible(true);
} catch (Exception e) {
Log.w(TAG, tClass + " has no default constructor");
}
Class<?> clazz = tClass;
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (isSafeParceledField(field)) {
FieldDescriptor fieldDescriptor = new FieldDescriptor(field);
fields.put(fieldDescriptor.id, fieldDescriptor);
}
}
clazz = clazz.getSuperclass();
}
}
public static class FieldDescriptor {
Field field;
int id;
boolean mayNull;
SafeParcelable.Field annotation;
SafeParceled legacyAnnotation;
SafeParcelType type;
Parcelable.Creator<? extends Parcelable> creator;
long versionCode = -1;
Class<?> listItemClass;
boolean useValueParcel;
Class<?> subClass;
public FieldDescriptor(Field field) {
this.field = field;
field.setAccessible(true);
try {
Field accessFlagsField = Field.class.getDeclaredField("accessFlags");
accessFlagsField.setAccessible(true);
accessFlagsField.setInt(field, accessFlagsField.getInt(field) & ~Modifier.FINAL);
} catch (Exception e) {
// Ignored
}
this.annotation = field.getAnnotation(SafeParcelable.Field.class);
this.legacyAnnotation = field.getAnnotation(SafeParceled.class);
if (annotation != null) {
this.id = annotation.value();
this.mayNull = annotation.mayNull();
this.useValueParcel = annotation.useValueParcel();
this.versionCode = annotation.versionCode();
} else if (legacyAnnotation != null) {
this.id = legacyAnnotation.value();
this.mayNull = legacyAnnotation.mayNull();
this.useValueParcel = legacyAnnotation.useClassLoader();
} else {
throw new IllegalArgumentException();
}
this.type = getType();
switch (type) {
case Parcelable:
case ParcelableArray:
creator = getCreator(field);
break;
case List:
if (listItemClass != null && Parcelable.class.isAssignableFrom(listItemClass)) {
if (!this.useValueParcel) {
creator = getCreator((Class<? extends Parcelable>) listItemClass);
}
}
break;
case Map:
case Bundle:
subClass = getSubClass(field);
break;
}
}
private SafeParcelType getType() {
Class<?> clazz = field.getType();
Class<?> component = clazz.getComponentType();
if (clazz.isArray() && component != null) {
if (Parcelable.class.isAssignableFrom(component)) return SafeParcelType.ParcelableArray;
if (String.class.isAssignableFrom(component)) return SafeParcelType.StringArray;
if (byte.class.isAssignableFrom(component)) return SafeParcelType.ByteArray;
if (byte[].class.isAssignableFrom(component)) return SafeParcelType.ByteArrayArray;
if (float.class.isAssignableFrom(component)) return SafeParcelType.FloatArray;
if (int.class.isAssignableFrom(component)) return SafeParcelType.IntArray;
}
if (Bundle.class.isAssignableFrom(clazz))
return SafeParcelType.Bundle;
if (Parcelable.class.isAssignableFrom(clazz))
return SafeParcelType.Parcelable;
if (IBinder.class.isAssignableFrom(clazz))
return SafeParcelType.Binder;
if (IInterface.class.isAssignableFrom(clazz))
return SafeParcelType.Interface;
if (clazz == List.class || clazz == ArrayList.class) {
listItemClass = getListItemClass(field);
if (listItemClass == String.class && !useValueParcel) return SafeParcelType.StringList;
if (listItemClass == Integer.class && annotation.useDirectList()) return SafeParcelType.IntegerList;
if (listItemClass == Boolean.class && annotation.useDirectList()) return SafeParcelType.BooleanList;
if (listItemClass == Long.class && annotation.useDirectList()) return SafeParcelType.LongList;
if (listItemClass == Float.class && annotation.useDirectList()) return SafeParcelType.FloatList;
if (listItemClass == Double.class && annotation.useDirectList()) return SafeParcelType.DoubleList;
return SafeParcelType.List;
}
if (clazz == Map.class || clazz == HashMap.class)
return SafeParcelType.Map;
if (clazz == int.class || clazz == Integer.class)
return SafeParcelType.Integer;
if (clazz == short.class || clazz == Short.class)
return SafeParcelType.Short;
if (clazz == boolean.class || clazz == Boolean.class)
return SafeParcelType.Boolean;
if (clazz == long.class || clazz == Long.class)
return SafeParcelType.Long;
if (clazz == float.class || clazz == Float.class)
return SafeParcelType.Float;
if (clazz == double.class || clazz == Double.class)
return SafeParcelType.Double;
if (clazz == byte.class || clazz == Byte.class)
return SafeParcelType.Byte;
if (clazz == java.lang.String.class)
return SafeParcelType.String;
throw new RuntimeException("Type is not yet usable with SafeParcelReflectionUtil: " + clazz);
}
}
}
}

View file

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2015, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.safeparcel;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Deprecated
public @interface SafeParceled {
int value();
boolean mayNull() default false;
@Deprecated String subType() default "undefined";
Class subClass() default SafeParceled.class;
boolean useClassLoader() default false;
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ SPDX-FileCopyrightText: 2015 microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<resources>
<integer name="google_play_services_version">12451000</integer>
</resources>