A little thread necromancy, but since I had so much trouble getting the OBB downloading to work I wanted to put all the problems in one place. Quite how - or indeed why - Google managed to make downloading a file from t'internet quite this complicated is beyond me.
Adding the Libraries to Android Studio
The official instructions proudly brought to you by the Ministry of Misinformation (https://developer.android.com/google/play/expansion-files.html) are simply wrong.
Before importing the libraries, go to sdk\extras\google\market_apk_expansion\downloader_library and edit project.properties to remove the "android.library.0" line entirely. You may also need to update the android target version line in the same file to match your existing project's target version.
Following the installation instructions on the page linked above, "Preparing to use the Downloader Library"
Step 2 should be "File > New > Import Module" and not "File > New > New Module".
After importing both modules, go to "File > Project Structure" and add dependencies:
- Your app should have a "Module Dependency" on both the licensing and
downloader libraries
- The downloader library should have a "Module Dependency" on the licensing library
The step that says "You must update the BASE64_PUBLIC_KEY value to be the public key belonging to your publisher account." is also wrong. You need the public key for this specific app, not your account, which is in the online Developer Console under "Services & APIs" after you've selected the app.
Both the License Validation Library and the Downloader Library have dependencies on a now-removed package "org.apache.http"
Replace two instances of "decodeExtras" method (one in APKExpansionPolicy.java, one in ServerManagedPolicy.java) with:
private Map<String, String> decodeExtras(String extras) {
Map<String, String> results = new HashMap<String, String>();
UrlQuerySanitizer sanitizer = new UrlQuerySanitizer();
sanitizer.setAllowUnregisteredParamaters(true);
sanitizer.setUnregisteredParameterValueSanitizer(new UrlQuerySanitizer.IllegalCharacterValueSanitizer(
UrlQuerySanitizer.IllegalCharacterValueSanitizer.URL_LEGAL));
sanitizer.parseQuery(extras);
for (UrlQuerySanitizer.ParameterValuePair item : sanitizer.getParameterList()) {
String name = item.mParameter;
int i = 0;
while (results.containsKey(name)) {
name = item.mParameter + ++i;
}
results.put(name, item.mValue);
}
return results;
}
Crash at runtime, saying "Service Intent must be explicit"
See the answer at https://stackoverflow.com/questions/24480069.
W/LicenseValidator: Error contacting licensing server
The license validation library won't work on an emulator, and this is the error it throws. If you're on a real device, it may just be a genuine timeout.
Debug versions fail a signature check (Signature verification failed), so are denied access.
The binary that is calling the license verification service must be a correctly-signed, release version. If you're running a debug copy that won't be the case, so the license checks fail.
- Go to your online Developer Console, Settings, Account Details
- Change the "License Test Response" to "LICENSED" (or whatever you're testing)
- Enter your email address under "Gmail accounts with testing access".
Downloader returns "STATE_COMPLETED" but download is not done.
This is a side-effect of the previous change. You are licensed, but the APK extension files are still not provided because you're not really licensed. This step is what allows you to actually test the downloads as they happen after distribution, instead of manually placing files onto your test devices.
- Create a promo code for the app.
- Create a new user on your device, for a totally new GMail account.
- Log in to your device as the new user, redeem the promo code. You are now a genuine app owner.
- Add the new gmail account under "Gmail accounts with testing access", as in the previous section.
You can't do this with the developer's account because purchasing or redeeming will fail, which means you can never own a copy of your own product, which means that the OBB download will never work.
Now that you have a second account, with a genuine Play Store copy of the app and which is registered as a test account with a 'fake' LICENSED status, you can run a debug copy of your code, get the LICENSED status, and have the downloads work as you'd expect.
W/LVLDL: Aborting request for download whavever.obb: while writing destination file: java.io.FileNotFoundException:
Since Android v23 it's been necessary to explicitly request permissions to write to "external storage", whether it's an SD card or not, and on top of having the permission listed in your manifest. See https://developer.android.com/guide/topics/permissions/requesting.html .
The "File not found" is referring to the directory on the external storage that the downloader library wants to put the OBB file into, which it has been unable to create. An exception gets raised if it can't create the file, but it fails silently if it can't create the directory. See processResponseHeaders()
in DownloadThread.java .
W/LVLDL: Aborting request for download main.whatever.obb: http error 410
Your app's version number must match one from a current APK that Google knows about otherwise your download request will be rejected with error 410.
Library will not re-check the license if it's found to be valid once.
If you're testing, chances are you need to re-do things a few times. The License Verification Library caches it's state after it's been found to be LICENSED. You can clear this without totally uninstalling your app:
DownloadsDB db = DownloadsDB.getDB(my_app_context);
db.updateMetadata(0, 0);
It also keeps the downloaded OBB in place, which can be found and cleared using:
final String dlName = Helpers.getExpansionAPKFileName(my_app_context, true, expansionVersion);
final String dlFullPath = Helpers.generateSaveFileName(my_app_context, dlName);
File dlFile = new File(dlFullPath);
if (dlFile.exists())
{
dlFile.delete();
}
Now, if you'll excuse me, I'm off to a spiritual retreat for a month or so.