Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 726f7787 authored by Fynn Godau's avatar Fynn Godau
Browse files

Merge branch 'epic71-licensing-profile' into 'master'

Use device profile for licensing

See merge request !93
parents 562a3b12 a84c2196
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ android {
dependencies {
    implementation project(':fake-signature')
    implementation project(':play-services-auth')
    implementation project(':play-services-base-core')

    implementation "com.squareup.wire:wire-runtime:$wireVersion"
    implementation "com.android.volley:volley:$volleyVersion"
+6 −5
Original line number Diff line number Diff line
@@ -79,10 +79,10 @@ public abstract class LicenseChecker<D, R> {

    static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay";

    public abstract Request<?> createRequest(String packageName, String auth, int versionCode, D data,
    public abstract LicenseRequest<?> createRequest(String packageName, String auth, int versionCode, D data,
                                             BiConsumer<Integer, R> then, Response.ErrorListener errorListener);

    public void checkLicense(Account account, AccountManager accountManager,
    public void checkLicense(Account account, AccountManager accountManager, String androidId,
                             String packageName, PackageManager packageManager,
                             RequestQueue queue, D queryData,
                             BiConsumerWithException<Integer, R, RemoteException> onResult)
@@ -118,8 +118,9 @@ public abstract class LicenseChecker<D, R> {
                    future -> {
                        try {
                            String auth = future.getResult().getString(KEY_AUTHTOKEN);
                            Request<?> request = createRequest(packageName, auth,
                            LicenseRequest<?> request = createRequest(packageName, auth,
                                versionCode, queryData, onRequestFinished, onRequestError);
                            request.ANDROID_ID = Long.decode("0x" + androidId);
                            request.setShouldCache(false);
                            queue.add(request);
                        } catch (AuthenticatorException | IOException | OperationCanceledException e) {
@@ -149,7 +150,7 @@ public abstract class LicenseChecker<D, R> {
    public static class V1 extends LicenseChecker<Long, Tuple<String, String>> {

        @Override
        public Request<V1Container> createRequest(String packageName, String auth, int versionCode, Long nonce, BiConsumer<Integer, Tuple<String, String>> then,
        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 -> {
@@ -170,7 +171,7 @@ public abstract class LicenseChecker<D, R> {

    public static class V2 extends LicenseChecker<Unit, String> {
        @Override
        public Request<String> createRequest(String packageName, String auth, int versionCode, Unit data,
        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 -> {
+33 −17
Original line number Diff line number Diff line
@@ -31,8 +31,13 @@ import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.google.android.gms.common.BuildConfig;

import org.microg.gms.profile.Build;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;

@@ -40,12 +45,12 @@ import okio.ByteString;

public abstract class LicenseRequest<T> extends Request<T> {

    private final String xPsRh;
    private final String auth;
    private static final String TAG = "FakeLicenseRequest";

    private static final int BASE64_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING;
    private static final long ANDROID_ID = 1;
    long ANDROID_ID = 1;
    private static final String FINSKY_VERSION = "Finsky/37.5.24-29%20%5B0%5D%20%5BPR%5D%20565477504";

    private final Response.Listener<T> successListener;

@@ -55,6 +60,10 @@ public abstract class LicenseRequest<T> extends Request<T> {
        this.auth = auth;

        this.successListener = successListener;
    }

    @Override
    public Map<String, String> getHeaders() {

        long millis = System.currentTimeMillis();
        TimestampContainer.Builder timestamp = new TimestampContainer.Builder()
@@ -107,9 +116,9 @@ public abstract class LicenseRequest<T> extends Request<T> {
            .deviceMeta(new DeviceMeta.Builder()
                .android(
                    new AndroidVersionMeta.Builder()
                        .androidSdk(0)
                        .buildNumber("")
                        .androidVersion("")
                        .androidSdk(Build.VERSION.SDK_INT)
                        .buildNumber(Build.ID)
                        .androidVersion(Build.VERSION.RELEASE)
                        .unknown(0)
                        .build()
                )
@@ -119,13 +128,13 @@ public abstract class LicenseRequest<T> extends Request<T> {
                .build()
            )
            .userAgent(new UserAgent.Builder()
                .deviceProductName("")
                .deviceSoc("")
                .deviceModelName("")
                .finskyVersion("")
                .deviceName("")
                .deviceName(Build.DEVICE)
                .deviceHardware(Build.HARDWARE)
                .deviceModelName(Build.MODEL)
                .finskyVersion(FINSKY_VERSION)
                .deviceProductName(Build.MODEL)
                .androidId(ANDROID_ID) // must not be 0
                .deviceSignature("")
                .buildFingerprint(Build.FINGERPRINT)
                .build()
            )
            .uuid(new Uuid.Builder()
@@ -134,22 +143,29 @@ public abstract class LicenseRequest<T> extends Request<T> {
                .build()
            )
            .build().encode();
        this.xPsRh = new String(Base64.encode(Util.encodeGzip(header), BASE64_FLAGS));

        //Log.d(TAG, "Product " + Build.PRODUCT + ", Board " + Build.BOARD + " Model " +  Build.MODEL + " Device " + Build.DEVICE);
        String xPsRh = new String(Base64.encode(Util.encodeGzip(header), BASE64_FLAGS));

        Log.v(TAG, "X-PS-RH: " + xPsRh);
    }

    @Override
    public Map<String, String> getHeaders() {
        String userAgent = FINSKY_VERSION + " (api=3,versionCode=" + BuildConfig.VERSION_CODE + ",sdk=" + Build.VERSION.SDK +
            ",device=" + encodeString(Build.DEVICE) + ",hardware=" + encodeString(Build.HARDWARE) + ",product=" + encodeString(Build.PRODUCT) +
            ",platformVersionRelease=" + encodeString(Build.VERSION.RELEASE) + ",model=" + encodeString(Build.MODEL) + ",buildId=" + encodeString(Build.ID) +
            ",isWideScreen=" + 0 + ",supportedAbis=" + String.join(";", Build.SUPPORTED_ABIS) + ")";
        Log.v(TAG, "User-Agent: " + userAgent);

        return Map.of(
            "X-PS-RH", xPsRh,
            "User-Agent", userAgent,
            "Authorization", "Bearer " + auth,
            "Accept-Language", "en-US",
            "Connection", "Keep-Alive"
        );
    }

    private static String encodeString(String s) {
        return URLEncoder.encode(s).replace("+", "%20");
    }

    @Override
    protected void deliverResponse(T response) {
        successListener.onResponse(response);
+39 −2
Original line number Diff line number Diff line
@@ -13,18 +13,25 @@ import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;

import org.microg.gms.auth.AuthConstants;
import org.microg.gms.profile.Build;
import org.microg.gms.profile.ProfileManager;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

import kotlin.Unit;
@@ -37,6 +44,10 @@ public class LicensingService extends Service {

    private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA";

    private static final Uri PROFILE_PROVIDER = Uri.parse("content://com.google.android.gms.microg.profile");

    private String androidId;


    private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() {

@@ -58,7 +69,7 @@ public class LicensingService extends Service {
        private void checkLicense(long nonce, String packageName, PackageManager packageManager,
                                  ILicenseResultListener listener, Queue<Account> remainingAccounts) throws RemoteException {
            new LicenseChecker.V1().checkLicense(
                remainingAccounts.poll(), accountManager, packageName, packageManager,
                remainingAccounts.poll(), accountManager, androidId, packageName, packageManager,
                queue, nonce,
                (responseCode, stringTuple) -> {
                    if (responseCode != LICENSED && !remainingAccounts.isEmpty()) {
@@ -88,7 +99,7 @@ public class LicensingService extends Service {
                                    ILicenseV2ResultListener listener, Bundle extraParams,
                                    Queue<Account> remainingAccounts) throws RemoteException {
            new LicenseChecker.V2().checkLicense(
                remainingAccounts.poll(), accountManager, packageName, packageManager, queue, Unit.INSTANCE,
                remainingAccounts.poll(), accountManager, androidId, packageName, packageManager, queue, Unit.INSTANCE,
                (responseCode, data) -> {
                    /*
                     * Suppress failures on V2. V2 is commonly used by free apps whose checker
@@ -127,6 +138,32 @@ public class LicensingService extends Service {
    };

    public IBinder onBind(Intent intent) {


        Cursor cursor = null;
        try {
            cursor = getContentResolver().query(
                PROFILE_PROVIDER, null, null, null, null
            );

            if (cursor == null || cursor.getColumnCount() != 2) {
                Log.e(TAG, "profile provider not available");
            } else {
                Map<String, String> profileData = new HashMap<>();
                while (cursor.moveToNext()) {
                    profileData.put(cursor.getString(0), cursor.getString(1));
                }
                ProfileManager.INSTANCE.applyProfileData(profileData);
            }

        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        androidId = String.valueOf(Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID));

        queue = Volley.newRequestQueue(this);
        accountManager = AccountManager.get(this);
        notificationRunnable = new LicenseServiceNotificationRunnable(this);
+4 −4
Original line number Diff line number Diff line
@@ -56,13 +56,13 @@ message UnknownByte12 {

message UserAgent {
  // The names of these attributes are vague guesses and should be adapted if needed.
  optional string deviceProductName = 1; // e.g. "OnePlusNord"
  optional string deviceSoc = 2; // e.g. "qcom"
  optional string deviceName = 1; // e.g. "OnePlusNord"
  optional string deviceHardware = 2; // e.g. "qcom"
  optional string deviceModelName = 3; // e.g. "OnePlus Nord"
  optional string finskyVersion = 4; // e.g. "Finsky/37.5.24-29%20%5B0%5D%20%5BPR%5D%20565477504"
  optional string deviceName = 5; // e.g. "OnePlusNord"; difference to 1 not yet clear
  optional string deviceProductName = 5; // e.g. "OnePlusNord"
  optional uint64 androidId = 6;
  optional string deviceSignature = 7; // e.g. "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys"
  optional string buildFingerprint = 7; // e.g. "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys"
}

message Uuid {