Loading vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java 0 → 100644 +202 −0 Original line number Diff line number Diff line 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 void checkLicense(Account account, AccountManager accountManager, 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 { accountManager.getAuthToken( account, AUTH_TOKEN_SCOPE, false, future -> { try { String auth = future.getResult().getString(KEY_AUTHTOKEN); Request<?> request = createRequest(packageName, auth, versionCode, queryData, (Integer integer, R r) -> { try { onResult.accept(integer, r); } catch (RemoteException e) { Log.e(TAG, "After telling it the license check result, remote threw an Exception."); e.printStackTrace(); } }, error -> { Log.e(TAG, "license request failed with " + error.toString()); safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); }); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); } }, null); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request licenses for package " + packageName + ", which does not exist"); onResult.accept(ERROR_INVALID_PACKAGE_NAME, null); } } private static <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(); } } public abstract Request<?> createRequest(String packageName, String auth, int versionCode, D data, BiConsumer<Integer, R> then, Response.ErrorListener 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; } } // Implementations 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, 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 Request<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 ); } } } vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +69 −190 Original line number Diff line number Diff line Loading @@ -5,12 +5,10 @@ package com.android.vending.licensing; import static android.accounts.AccountManager.KEY_AUTHTOKEN; import static com.android.vending.licensing.LicenseChecker.LICENSED; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Service; import android.content.Intent; import android.content.pm.PackageInfo; Loading @@ -20,14 +18,16 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; import org.microg.gms.auth.AuthConstants; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import kotlin.Unit; public class LicensingService extends Service { private static final String TAG = "FakeLicenseService"; Loading @@ -37,51 +37,6 @@ public class LicensingService extends Service { private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; private static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; /* Possible response codes for checkLicense v1, from * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and * the LVL library. */ /** * The application is licensed to the user. The user has purchased the application, or is authorized to * download and install the alpha or beta version of the application. */ private static final int LICENSED = 0x0; /** * The application is licensed to the user, but there is an updated application version available that is * signed with a different key. */ private static final int NOT_LICENSED = 0x1; /** * The application is not licensed to the user. */ private static final int LICENSED_OLD_KEY = 0x2; /** * Server error — the application (package name) was not recognized by Google Play. */ private static final int ERROR_NOT_MARKET_MANAGED = 0x3; /** * Server error — the server could not load the application's key pair for licensing. */ private static final int ERROR_SERVER_FAILURE = 0x4; private static final int ERROR_OVER_QUOTA = 0x5; /** * Local error — the Google Play application was not able to reach the licensing server, possibly because * of network availability problems. */ private static final int ERROR_CONTACTING_SERVER = 0x101; /** * Local error — the application requested a license check for a package that is not installed on the device. */ private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; /** * Local error — the application requested a license check for a package whose UID (package, user ID pair) * does not match that of the requesting application. */ private static final int ERROR_NON_MATCHING_UID = 0x103; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { Loading @@ -89,110 +44,52 @@ public class LicensingService extends Service { @Override public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); try { PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); int versionCode = packageInfo.versionCode; // Verify caller identity if (packageInfo.applicationInfo.uid != getCallingUid()) { Log.e(TAG, "an app illegally tried to request v1 licenses for another app (caller: " + getCallingUid() + ")"); listener.verifyLicense(ERROR_NON_MATCHING_UID, null, null); } else { Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); PackageManager packageManager = getPackageManager(); if (accounts.length == 0) { Log.e(TAG, "not checking license, as user is not signed in"); notificationRunnable.callerPackageName = packageName; notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); notificationRunnable.run(); } else accountManager.getAuthToken( accounts[0], AUTH_TOKEN_SCOPE, false, future -> { Request request = null; try { request = new LicenseRequest.V1( packageName, future.getResult().getString(KEY_AUTHTOKEN), versionCode, nonce, data -> { if (data != null) { Log.v(TAG, "licenseV1 result was " + data.result + " with signed data " + data.signedData); try { if (data.result != null) { listener.verifyLicense(data.result, data.signedData, data.signature); handleNoAccounts(packageName, packageManager); } else { listener.verifyLicense(LICENSED, data.signedData, data.signature); checkLicense(nonce, packageName, packageManager, listener, new LinkedList<>(Arrays.asList(accounts))); } } catch (RemoteException e) { Log.e(TAG, "After telling it the licenseV1 result, remote threw an Exception."); e.printStackTrace(); } } else { Log.v(TAG, "licenseV1 result was that user has no license"); sendError(listener, NOT_LICENSED); } }, error -> { Log.e(TAG, "licenseV1 request failed with " + error.toString()); sendError(listener, ERROR_CONTACTING_SERVER); }); } catch (AuthenticatorException | IOException | OperationCanceledException e) { sendError(listener, ERROR_CONTACTING_SERVER); } request.setShouldCache(false); queue.add(request); }, null); 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, queue, nonce, (responseCode, stringTuple) -> { if (responseCode != LICENSED && !remainingAccounts.isEmpty()) { checkLicense(nonce, packageName, packageManager, listener, remainingAccounts); } else { listener.verifyLicense(responseCode, stringTuple.a, stringTuple.b); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, null, null); } ); } @Override public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener, Bundle extraParams) throws RemoteException { Log.v(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); try { PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); int versionCode = packageInfo.versionCode; // Verify caller identity if (packageInfo.applicationInfo.uid != getCallingUid()) { Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")"); /* This negative result is provided even if users are not signed in; we expect apps * will usually behave correctly in practise so this will not prevent users from * using the app. */ listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle()); } else { Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); PackageManager packageManager = getPackageManager(); if (accounts.length == 0) { Log.e(TAG, "not checking license, as user is not signed in"); notificationRunnable.callerPackageName = packageName; notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); notificationRunnable.run(); } else accountManager.getAuthToken( accounts[0], AUTH_TOKEN_SCOPE, false, future -> { try { Bundle result = future.getResult(10, TimeUnit.SECONDS); String auth = result.getString(KEY_AUTHTOKEN); handleNoAccounts(packageName, packageManager); } else { checkLicenseV2(packageName, packageManager, listener, extraParams, new LinkedList<>(Arrays.asList(accounts))); } } Request request = new LicenseRequest.V2(packageName, auth, versionCode, jwt -> { Log.v(TAG, "LicenseV2 returned JWT license value " + jwt); Bundle bundle = new Bundle(); bundle.putString(KEY_V2_RESULT_JWT, jwt); try { if (jwt == null) { private void checkLicenseV2(String packageName, PackageManager packageManager, ILicenseV2ResultListener listener, Bundle extraParams, Queue<Account> remainingAccounts) throws RemoteException { new LicenseChecker.V2().checkLicense( remainingAccounts.poll(), accountManager, packageName, packageManager, queue, Unit.INSTANCE, (responseCode, data) -> { /* * Suppress failures on V2. V2 is commonly used by free apps whose checker * will not throw users out of the app if it never receives a response. Loading @@ -200,52 +97,34 @@ public class LicensingService extends Service { * This means that users who are signed in to a Google account will not * get a worse experience in these apps than users that are not signed in. */ Log.i(TAG, "Suppressed negative result for package " + packageName); if (responseCode == LICENSED) { Bundle bundle = new Bundle(); bundle.putString(KEY_V2_RESULT_JWT, data); listener.verifyLicense(responseCode, bundle); } else if (!remainingAccounts.isEmpty()) { checkLicenseV2(packageName, packageManager, listener, extraParams, remainingAccounts); } else { listener.verifyLicense(jwt == null ? NOT_LICENSED : LICENSED, bundle); } } catch (RemoteException e) { Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); e.printStackTrace(); Log.i(TAG, "Suppressed negative license result for package " + packageName); } }, error -> { Log.e(TAG, "licenseV2 request failed with " + error.toString()); //sendError(listener, ERROR_CONTACTING_SERVER); – see above }); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { //sendError(listener, ERROR_CONTACTING_SERVER); – see above e.printStackTrace(); } }, null ); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, new Bundle()); } } }; private static void sendError(ILicenseResultListener listener, int error) { private void handleNoAccounts(String packageName, PackageManager packageManager) { try { listener.verifyLicense(error, null, null); } catch (RemoteException e) { Log.e(TAG, "After telling it that licenseV1 had an error (" + error + "), remote threw an Exception."); } } private static void sendError(ILicenseV2ResultListener listener, int error) { try { listener.verifyLicense(error, new Bundle()); } catch (RemoteException e) { Log.e(TAG, "After telling it that licenseV2 had an error (" + error + "), remote threw an Exception."); Log.e(TAG, "not checking license, as user is not signed in"); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "ignored license request, but package name " + packageName + " was not known!"); notificationRunnable.callerAppName = packageName; } notificationRunnable.run(); } }; public IBinder onBind(Intent intent) { queue = Volley.newRequestQueue(this); Loading Loading
vending-app/src/main/java/com/android/vending/licensing/LicenseChecker.java 0 → 100644 +202 −0 Original line number Diff line number Diff line 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 void checkLicense(Account account, AccountManager accountManager, 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 { accountManager.getAuthToken( account, AUTH_TOKEN_SCOPE, false, future -> { try { String auth = future.getResult().getString(KEY_AUTHTOKEN); Request<?> request = createRequest(packageName, auth, versionCode, queryData, (Integer integer, R r) -> { try { onResult.accept(integer, r); } catch (RemoteException e) { Log.e(TAG, "After telling it the license check result, remote threw an Exception."); e.printStackTrace(); } }, error -> { Log.e(TAG, "license request failed with " + error.toString()); safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); }); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { safeSendResult(onResult, ERROR_CONTACTING_SERVER, null); } }, null); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request licenses for package " + packageName + ", which does not exist"); onResult.accept(ERROR_INVALID_PACKAGE_NAME, null); } } private static <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(); } } public abstract Request<?> createRequest(String packageName, String auth, int versionCode, D data, BiConsumer<Integer, R> then, Response.ErrorListener 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; } } // Implementations 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, 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 Request<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 ); } } }
vending-app/src/main/java/com/android/vending/licensing/LicensingService.java +69 −190 Original line number Diff line number Diff line Loading @@ -5,12 +5,10 @@ package com.android.vending.licensing; import static android.accounts.AccountManager.KEY_AUTHTOKEN; import static com.android.vending.licensing.LicenseChecker.LICENSED; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Service; import android.content.Intent; import android.content.pm.PackageInfo; Loading @@ -20,14 +18,16 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; import org.microg.gms.auth.AuthConstants; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import kotlin.Unit; public class LicensingService extends Service { private static final String TAG = "FakeLicenseService"; Loading @@ -37,51 +37,6 @@ public class LicensingService extends Service { private static final String KEY_V2_RESULT_JWT = "LICENSE_DATA"; private static final String AUTH_TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/googleplay"; /* Possible response codes for checkLicense v1, from * https://developer.android.com/google/play/licensing/licensing-reference#server-response-codes and * the LVL library. */ /** * The application is licensed to the user. The user has purchased the application, or is authorized to * download and install the alpha or beta version of the application. */ private static final int LICENSED = 0x0; /** * The application is licensed to the user, but there is an updated application version available that is * signed with a different key. */ private static final int NOT_LICENSED = 0x1; /** * The application is not licensed to the user. */ private static final int LICENSED_OLD_KEY = 0x2; /** * Server error — the application (package name) was not recognized by Google Play. */ private static final int ERROR_NOT_MARKET_MANAGED = 0x3; /** * Server error — the server could not load the application's key pair for licensing. */ private static final int ERROR_SERVER_FAILURE = 0x4; private static final int ERROR_OVER_QUOTA = 0x5; /** * Local error — the Google Play application was not able to reach the licensing server, possibly because * of network availability problems. */ private static final int ERROR_CONTACTING_SERVER = 0x101; /** * Local error — the application requested a license check for a package that is not installed on the device. */ private static final int ERROR_INVALID_PACKAGE_NAME = 0x102; /** * Local error — the application requested a license check for a package whose UID (package, user ID pair) * does not match that of the requesting application. */ private static final int ERROR_NON_MATCHING_UID = 0x103; private final ILicensingService.Stub mLicenseService = new ILicensingService.Stub() { Loading @@ -89,110 +44,52 @@ public class LicensingService extends Service { @Override public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { Log.v(TAG, "checkLicense(" + nonce + ", " + packageName + ")"); try { PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); int versionCode = packageInfo.versionCode; // Verify caller identity if (packageInfo.applicationInfo.uid != getCallingUid()) { Log.e(TAG, "an app illegally tried to request v1 licenses for another app (caller: " + getCallingUid() + ")"); listener.verifyLicense(ERROR_NON_MATCHING_UID, null, null); } else { Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); PackageManager packageManager = getPackageManager(); if (accounts.length == 0) { Log.e(TAG, "not checking license, as user is not signed in"); notificationRunnable.callerPackageName = packageName; notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); notificationRunnable.run(); } else accountManager.getAuthToken( accounts[0], AUTH_TOKEN_SCOPE, false, future -> { Request request = null; try { request = new LicenseRequest.V1( packageName, future.getResult().getString(KEY_AUTHTOKEN), versionCode, nonce, data -> { if (data != null) { Log.v(TAG, "licenseV1 result was " + data.result + " with signed data " + data.signedData); try { if (data.result != null) { listener.verifyLicense(data.result, data.signedData, data.signature); handleNoAccounts(packageName, packageManager); } else { listener.verifyLicense(LICENSED, data.signedData, data.signature); checkLicense(nonce, packageName, packageManager, listener, new LinkedList<>(Arrays.asList(accounts))); } } catch (RemoteException e) { Log.e(TAG, "After telling it the licenseV1 result, remote threw an Exception."); e.printStackTrace(); } } else { Log.v(TAG, "licenseV1 result was that user has no license"); sendError(listener, NOT_LICENSED); } }, error -> { Log.e(TAG, "licenseV1 request failed with " + error.toString()); sendError(listener, ERROR_CONTACTING_SERVER); }); } catch (AuthenticatorException | IOException | OperationCanceledException e) { sendError(listener, ERROR_CONTACTING_SERVER); } request.setShouldCache(false); queue.add(request); }, null); 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, queue, nonce, (responseCode, stringTuple) -> { if (responseCode != LICENSED && !remainingAccounts.isEmpty()) { checkLicense(nonce, packageName, packageManager, listener, remainingAccounts); } else { listener.verifyLicense(responseCode, stringTuple.a, stringTuple.b); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, null, null); } ); } @Override public void checkLicenseV2(String packageName, ILicenseV2ResultListener listener, Bundle extraParams) throws RemoteException { Log.v(TAG, "checkLicenseV2(" + packageName + ", " + extraParams + ")"); try { PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); int versionCode = packageInfo.versionCode; // Verify caller identity if (packageInfo.applicationInfo.uid != getCallingUid()) { Log.e(TAG, "an app illegally tried to request v2 licenses for another app (caller: " + getCallingUid() + ")"); /* This negative result is provided even if users are not signed in; we expect apps * will usually behave correctly in practise so this will not prevent users from * using the app. */ listener.verifyLicense(ERROR_NON_MATCHING_UID, new Bundle()); } else { Account[] accounts = accountManager.getAccountsByType(AuthConstants.DEFAULT_ACCOUNT_TYPE); PackageManager packageManager = getPackageManager(); if (accounts.length == 0) { Log.e(TAG, "not checking license, as user is not signed in"); notificationRunnable.callerPackageName = packageName; notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); notificationRunnable.run(); } else accountManager.getAuthToken( accounts[0], AUTH_TOKEN_SCOPE, false, future -> { try { Bundle result = future.getResult(10, TimeUnit.SECONDS); String auth = result.getString(KEY_AUTHTOKEN); handleNoAccounts(packageName, packageManager); } else { checkLicenseV2(packageName, packageManager, listener, extraParams, new LinkedList<>(Arrays.asList(accounts))); } } Request request = new LicenseRequest.V2(packageName, auth, versionCode, jwt -> { Log.v(TAG, "LicenseV2 returned JWT license value " + jwt); Bundle bundle = new Bundle(); bundle.putString(KEY_V2_RESULT_JWT, jwt); try { if (jwt == null) { private void checkLicenseV2(String packageName, PackageManager packageManager, ILicenseV2ResultListener listener, Bundle extraParams, Queue<Account> remainingAccounts) throws RemoteException { new LicenseChecker.V2().checkLicense( remainingAccounts.poll(), accountManager, packageName, packageManager, queue, Unit.INSTANCE, (responseCode, data) -> { /* * Suppress failures on V2. V2 is commonly used by free apps whose checker * will not throw users out of the app if it never receives a response. Loading @@ -200,52 +97,34 @@ public class LicensingService extends Service { * This means that users who are signed in to a Google account will not * get a worse experience in these apps than users that are not signed in. */ Log.i(TAG, "Suppressed negative result for package " + packageName); if (responseCode == LICENSED) { Bundle bundle = new Bundle(); bundle.putString(KEY_V2_RESULT_JWT, data); listener.verifyLicense(responseCode, bundle); } else if (!remainingAccounts.isEmpty()) { checkLicenseV2(packageName, packageManager, listener, extraParams, remainingAccounts); } else { listener.verifyLicense(jwt == null ? NOT_LICENSED : LICENSED, bundle); } } catch (RemoteException e) { Log.e(TAG, "After returning licenseV2 result, remote threw an Exception."); e.printStackTrace(); Log.i(TAG, "Suppressed negative license result for package " + packageName); } }, error -> { Log.e(TAG, "licenseV2 request failed with " + error.toString()); //sendError(listener, ERROR_CONTACTING_SERVER); – see above }); request.setShouldCache(false); queue.add(request); } catch (AuthenticatorException | IOException | OperationCanceledException e) { //sendError(listener, ERROR_CONTACTING_SERVER); – see above e.printStackTrace(); } }, null ); } } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "an app tried to request v1 licenses for package " + packageName + ", which does not exist"); listener.verifyLicense(ERROR_INVALID_PACKAGE_NAME, new Bundle()); } } }; private static void sendError(ILicenseResultListener listener, int error) { private void handleNoAccounts(String packageName, PackageManager packageManager) { try { listener.verifyLicense(error, null, null); } catch (RemoteException e) { Log.e(TAG, "After telling it that licenseV1 had an error (" + error + "), remote threw an Exception."); } } private static void sendError(ILicenseV2ResultListener listener, int error) { try { listener.verifyLicense(error, new Bundle()); } catch (RemoteException e) { Log.e(TAG, "After telling it that licenseV2 had an error (" + error + "), remote threw an Exception."); Log.e(TAG, "not checking license, as user is not signed in"); PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0); notificationRunnable.callerUid = packageInfo.applicationInfo.uid; notificationRunnable.callerAppName = packageManager.getApplicationLabel(packageInfo.applicationInfo); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "ignored license request, but package name " + packageName + " was not known!"); notificationRunnable.callerAppName = packageName; } notificationRunnable.run(); } }; public IBinder onBind(Intent intent) { queue = Volley.newRequestQueue(this); Loading