VancedxMicroG/vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java
Jonathan Klee b5b603a340 Use Google androidId for Licensing
Licensing app feature expects a Google AndroidId.
2024-03-28 20:55:46 +07:00

211 lines
8.2 KiB
Java

package com.android.vending.licensing;
import static android.accounts.AccountManager.KEY_AUTHTOKEN;
import static android.os.Binder.getCallingUid;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
import com.android.vending.V1Container;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import java.io.IOException;
import kotlin.Unit;
/**
* Performs license check including caller UID verification, using a given account, for which
* an auth token is fetched.
*
* @param <D> Request parameter data value type
* @param <R> Result type
*/
public abstract class LicenseChecker<D, R> {
private static final String TAG = "FakeLicenseChecker";
/* Possible response codes for checkLicense v1, from
* https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and
* the LVL library.
*/
/**
* The application is licensed to the user. The user has purchased the application, or is authorized to
* download and install the alpha or beta version of the application.
*/
static final int LICENSED = 0x0;
/**
* The application is licensed to the user, but there is an updated application version available that is
* signed with a different key.
*/
static final int NOT_LICENSED = 0x1;
/**
* The application is not licensed to the user.
*/
static final int LICENSED_OLD_KEY = 0x2;
/**
* Server error — the application (package name) was not recognized by Google Play.
*/
static final int ERROR_NOT_MARKET_MANAGED = 0x3;
/**
* Server error — the server could not load the application's key pair for licensing.
*/
static final int ERROR_SERVER_FAILURE = 0x4;
static final int ERROR_OVER_QUOTA = 0x5;
/**
* Local error — the Google Play application was not able to reach the licensing server, possibly because
* of network availability problems.
*/
static final int ERROR_CONTACTING_SERVER = 0x101;
/**
* Local error — the application requested a license check for a package that is not installed on the device.
*/
static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
/**
* Local error — the application requested a license check for a package whose UID (package, user ID pair)
* does not match that of the requesting application.
*/
static final int ERROR_NON_MATCHING_UID = 0x103;
static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay";
public abstract LicenseRequest<?> createRequest(String packageName, String auth, int versionCode, D data,
BiConsumer<Integer, R> then, Response.ErrorListener errorListener);
public void checkLicense(Account account, AccountManager accountManager, String androidId,
String packageName, PackageManager packageManager,
RequestQueue queue, D queryData,
BiConsumerWithException<Integer, R, RemoteException> onResult)
throws RemoteException {
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
int versionCode = packageInfo.versionCode;
// Verify caller identity
if (packageInfo.applicationInfo.uid != getCallingUid()) {
Log.e(TAG,
"an app illegally tried to request licenses for another app (caller: " + getCallingUid() + ")");
onResult.accept(ERROR_NON_MATCHING_UID, null);
} else {
BiConsumer<Integer, R> onRequestFinished = (Integer integer, R r) -> {
try {
onResult.accept(integer, r);
} catch (RemoteException e) {
Log.e(TAG,
"After telling it the license check result, remote threw an Exception.");
e.printStackTrace();
}
};
Response.ErrorListener onRequestError = error -> {
Log.e(TAG, "license request failed with " + error.toString());
safeSendResult(onResult, ERROR_CONTACTING_SERVER, null);
};
accountManager.getAuthToken(
account, AUTH_TOKEN_SCOPE, false,
future -> {
try {
String auth = future.getResult().getString(KEY_AUTHTOKEN);
LicenseRequest<?> request = createRequest(packageName, auth,
versionCode, queryData, onRequestFinished, onRequestError);
if (androidId != null) {
request.ANDROID_ID = Long.parseLong(androidId, 16);
}
request.setShouldCache(false);
queue.add(request);
} catch (AuthenticatorException | IOException | OperationCanceledException e) {
safeSendResult(onResult, ERROR_CONTACTING_SERVER, null);
}
}, null);
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "an app tried to request licenses for package " + packageName + ", which does not exist");
onResult.accept(ERROR_INVALID_PACKAGE_NAME, null);
}
}
private static <A, B, T extends Exception> void safeSendResult(
BiConsumerWithException<A, B, T> consumerWithException, A a, B b) {
try {
consumerWithException.accept(a, b);
} catch (Exception e) {
Log.e(TAG, "While sending result " + a + ", " + b + ", remote encountered an exception.");
e.printStackTrace();
}
}
// Implementations
public static class V1 extends LicenseChecker<Long, Tuple<String, String>> {
@Override
public LicenseRequest<V1Container> createRequest(String packageName, String auth, int versionCode, Long nonce, BiConsumer<Integer, Tuple<String, String>> then,
Response.ErrorListener errorListener) {
return new LicenseRequest.V1(
packageName, auth, versionCode, nonce, response -> {
if (response != null) {
Log.v(TAG, "licenseV1 result was " + response.result + " with signed data " +
response.signedData);
if (response.result != null) {
then.accept(response.result, new Tuple<>(response.signedData, response.signature));
} else {
then.accept(LICENSED, new Tuple<>(response.signedData, response.signature));
}
}
}, errorListener
);
}
}
public static class V2 extends LicenseChecker<Unit, String> {
@Override
public LicenseRequest<String> createRequest(String packageName, String auth, int versionCode, Unit data,
BiConsumer<Integer, String> then, Response.ErrorListener errorListener) {
return new LicenseRequest.V2(
packageName, auth, versionCode, response -> {
if (response != null) {
then.accept(LICENSED, response);
} else {
then.accept(NOT_LICENSED, null);
}
}, errorListener
);
}
}
// Functional interfaces
interface BiConsumerWithException<A, B, T extends Exception> {
void accept(A a, B b) throws T;
}
interface BiConsumer<A, B> {
void accept(A a, B b);
}
static class Tuple<A, B> {
public final A a;
public final B b;
public Tuple(A a, B b) {
this.a = a;
this.b = b;
}
}
}