Auto-purchase free apps upon license verification
Summary
Upon license verification, if a negative response is obtained, automatically resolve this situation for free apps by purchasing them (marking them as downloaded).
Description
Google Play keeps track of which free apps have been downloaded, and answers that the user doesn't have a license to the app if they never downloaded it. Our proposed solution to this problem is automatically obtaining a license if
- user is signed in already (otherwise impossible)
- the returned response is negative for all accounts
- the app is a free app
The benefit is that some free apps don't work without license verification to prevent sideloading. Our intended use case is that after downloading an app from App Lounge using an anonymous account, the app may not marked as downloaded / purchased in the account that is used upon license verification.
Below shows exactly which implementation steps are necessary to be able to perform this action.
Proof of concept showcase
- Choose a package on Google Play that is free and had never previously been downloaded by you. For example,
com.farlightgames.igame.gp
. - Compile https://github.com/e-foundation/lvl-sample while setting this package name as the sample app's package name.
- Sign in with a Google account if necessary. The result shown in the sample app for v1 should be
ERROR_NOT_MARKET_MANAGED
. v2 should load indefinitely. - Use Proof-of-Concept Aurora Store branch at https://gitlab.e.foundation/squads/third-party-apps-support/aurora-store/-/tree/poc-purchase-free-apps/. Sign in with same Google account. Enter package name on main screen text box and click Purchase. Observe logcat for result (or error on failure). App is not downloaded.
- Restart
lvl-sample
app. Results should now beLICENSED
.
Code
How to implement
On a high level, given an account token, the following two steps are necessary:
- Fetch app data
- Purchase app
Using gplayapi
and a given auth data authData
, the following snippet is sufficient to purchase a free app:
val app = AppDetailsHelper(authData).getAppByPackageName(packageName)
Log.d(TAG, "PurchaseDemo: $packageName is free app is ${app.isFree}")
if (app.isFree) {
val files = PurchaseHelper(authData).purchase(packageName, app.versionCode, app.offerType)
Log.d(TAG, "PurchaseDemo: Purchase successful. Acquired access to ${files.size} files.")
}
I think microG would prefer if we re-implemented the relevant functionality from gplayapi
, which shouldn't be difficult given we have all the source code available.
Where to implement
Negative results are sent in
- v1: https://github.com/microg/GmsCore/blob/827a8b914cb3ef970ad81897672f4539a423fdcc/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java#L83-L87
- v2: https://github.com/microg/GmsCore/blob/827a8b914cb3ef970ad81897672f4539a423fdcc/vending-app/src/main/java/com/android/vending/licensing/LicensingService.java#L125-L134
One open question is for which account to purchase the app if multiple accounts are given. For pragmatic reasons, I would suggest the first, because it is always queried first and thus subsequent queries to the same app are quicker than when using an account later in the list. Update: this is the behavior that we now agreed on, with the rationale that In-App Purchasing with Google Play services also always works with the first device account.