Working with Lollipop, I have a device-owner app that is installed with NFC at provision time.
What I need now is to handle automatic updates for my App, from Google Play to rely on the standard Android App update system...
So far I can imagine 2 ways to get this done, but don't know how to handle any of them :
in my NFC install constant EXTRA PROVISIONING DEVICE ADMIN PACKAGE
DOWNLOAD LOCATION install the App directly from the Play Store instead of the url on my own dev server. However
this constant need to handle the url of an apk file, and I did not find any
official way to get apk install direct from Play Store ? (as it will
be a production App in the future I'm not interested in hacks)
keep installing the apk from the dev server, but then allow the App
to update itself with its little brother located on the Play Store
with the same package name. To say it an other way: Would this be possible to install a v1 apk from a custom location, then put a v2 on the PlayStore... and let the magic come true ?
I'd be glad to hear if anyone could share experience about such procedures. Thanks for reading!
EDIT after #Stephan Branczyk suggestion I could make some more testing, here is what I did and the results:
1 - In the NFC provisioning I replaced the apk url with
snep://my.app.packagename without luck ; it just gives an error
without much explanation.
2 - I replaced this url by such a PlayStore link :
https://play.google.com/store/apps/details?id=my.app.packagename but
it gives a checksum error whether I use the checksum locally
computed, or the checksum given on the GooglePlay apk details. It looks not so far from the goal but I could not make it work.
3 - Finally I came back on my first solution, a self-hosted apk
versioned 1... but this time I tried to put on the PlayStore a newer
version 2 of the app with the exact same packagename... That led me
to strange things:
At first my App did not appear anywhere in the local PlayStore App,
but when I searched for it in Google Play, it showed up with the green
"installed" badge, and it proposed me to make an update... So did I.
Then, after this first manual update, the App is in v2, nice, and
better: it appears well listed in my PlayStore.
Optimistically, I uploaded a v3 of the App... just to see if my
PlayStore would automatically update my app (as is does for all the
other ones), but sadly no luck : even if my app is still listed in the
playstore, and proposing the "update" button... it never
updates by itself as it should ; I still need to click on it manually.
Isn't it a strange behavior ? If some have ideas about it, I would really need to be able to rely on the Play Store functionalities but so far no luck, and I cannot believe that Device-Owner app distribution is not compatible with PlayStore ?
Just in case, FYI here is the kind of provisioning code I'm using:
try {
Properties p = new Properties();
p.setProperty(
DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
"my.app.packagename");
p.setProperty(
DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
"http://www.example.com/myDeviceOwnerApp.apk");
p.setProperty(
DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
"U55o3fO0cXQtUoQCbQEO9c_gKrs");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStream out = new ObjectOutputStream(bos);
p.store(out, "");
final byte[] bytes = bos.toByteArray();
NdefMessage msg = new NdefMessage(NdefRecord.createMime(
DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, bytes));
return msg;
} catch (Exception e) {
throw new RuntimeException(e);
}
Write your package name as an AAR record in the tag.
To confirm that this functionality works, use this app to write the tag with.
You need to set Base64 encoded SHA1 or SHA256 (from M forward) of the apk in the
EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM
field when provisioning through NFC otherwise the provisioned device will not accept the URL for download.
Also see this answer for properly encoding the checksum.
Related
Implemented in-app update feature, using the following code snippet:
private void showInAppUpdateDialog(boolean isMandatoryUpdate) {
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
|| appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
int appUpdateType = isMandatoryUpdate ? IMMEDIATE : AppUpdateType.FLEXIBLE;
int requestCode = isMandatoryUpdate ? REQUEST_APP_UPDATE_IMMEDIATE : REQUEST_APP_UPDATE_FLEXIBLE;
if (appUpdateInfo.isUpdateTypeAllowed(appUpdateType)) {
// start the app update
try {
appUpdateManager.startUpdateFlowForResult(appUpdateInfo, appUpdateType, targetActivity, requestCode);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
}
}).addOnFailureListener(e -> {
e.printStackTrace();
});
}
I am testing in-app update on the device which has Android 9. Still, it is giving me following an error (ERROR_API_NOT_AVAILABLE):
com.google.android.play.core.install.InstallException: Install Error(-3): The API is not available on this device. (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_API_NOT_AVAILABLE)
at com.google.android.play.core.appupdate.i.a(Unknown Source:24)
at com.google.android.play.core.internal.o.a(Unknown Source:13)
at com.google.android.play.core.internal.j.onTransact(Unknown Source:22)
at android.os.Binder.execTransact(Binder.java:731)
It is saying that check the following link:
https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_API_NOT_AVAILABLE)
Using Play core library version: 1.6.5
Latest core library version:
implementation 'com.google.android.play:core:1.7.0'
However, I am not able to get why it is saying that ERROR_API_NOT_AVAILABLE. Any help would be appreciated!
Firstly, please check that you are using the latest version of the play library.
Secondly, understated fact: Please check the app you are testing has the same package name which is available on the play store.
Example:
You have an app on the play store with package name com.example.app but you are testing your app with package name com.example.app.debug. You will get this error: ERROR_API_NOT_AVAILABLE
Note: You need to have at least one version of your app on the play store when you are testing.
On top of what Vipal suggested, the issue may be due to a signature mismatch between the version you have installed on the device and the one that Play Store would deliver (this is a common issue if you try to test it with debug builds). See https://developer.android.com/guide/playcore/in-app-updates#troubleshoot
Recently the Play Core API started returning an API_NOT_AVAILABLE error if the app is not owned by the user or the signatures mismatch, while before it used to return a successful UPDATE_NOT_AVAILABLE Task.
The recommendation is:
if you use the Kotlin Extension, make sure that you are catching the exception thrown by requestAppUpdateInfo
if you use PlayCore Java, make sure you have an onFailureListener that handles failures from getAppUpdateInfo
in order to test a debug build, you can use Internal App Sharing, as explained here: https://developer.android.com/guide/playcore/in-app-updates#internal-app-sharing
Source: I work on the Play Core team
My app was working fine before today, but I started getting this error today. One temporary workaround is to clear your Google Play Store cache and storage and then try launching the app. For me, it works only the first time, but fails afterwards. Before launching the app again, I have to clear the cache and storage again. I think there is something wrong on Google Play Store side due to which this issue is happening because everything was fine for me before today.
Got the same error, tried all solutions described here, nothing works.
App installed from Play Store Internal Test track with Version Code 267, then submitted new update to the same track with Version Code 268.
Play Store shows available update but the application still says ERROR_API_NOT_AVAILABLE
Clear Play Store data and cache does not help.
Description of ERROR_API_NOT_AVAILABLE say that it means “API is not available on device”
After carefully reading again this page, I have noticed that “in-app updates support apps running on only Android mobile devices and tablets, and Chrome OS devices”
Android TV not mentioned, I think it’s the reason in my case.
Temporary workaround for the moment is to surround the OnCompleteListener with a :
try {...} catch(e: RuntimeExecutionException) {...}
Just to avoid having to clear the PlayStore cache everytime I relaunch the app
After long time of debugging. I found, this is because of we are testing the app directly in mobile. Even though we generate and use signed apk, this error will occur.
The only way to get rid of this error is, we need to download the app from google play.
We can use Internal app sharing to test or simply publish our app.
Well, in my case we've cleared Google Play app cache and we didn't launch the Google Play before our app. You have to do it to download fresh data from the store, which is necessary for the SDK.
I am trying to customize Android for a media device. We got the firmware from the manufacturer and it's based on Android 7 with minor modifications.
One of the things we would like to do is to restrict installation of apps on the device to certain apps only. We won't install any app store like Google Play on the device. We will build the firmware and install all apps onto the devices at our workshops and deliver to the customers. In future, we may want to install more apps on the devices via OTA or some mechanisms.
We would like to disallow customers sideloading other apps via USB port. We created a file (eg., whitelisted_apps.txt) that has the list of all approved app names, such as -
com.mycompany.android.app1
com.mycompany.android.app2
com.ourpartner.android.appx
We tweaked the PackageInstaller app of AOSP so that when a *.APK file is opened via the file browser and when the methods in PackageInstallerActivity.java are called, it will compare the name of the app to be installed against those in whitelisted_apps.txt and if the name is not found, disallow installation of the new app. It's working.
Now, we want to improve it further because whitelisted_apps.txt can be manipulated. Some people suggest using sqlite to keep the list. We are not sure if it will be the best solution.
Some suggest using certificates and signing and we think it's a better solution than others. We will sign all the apps we want to install on the device with our key. When a *.APK file is sideloaded, the PackageInstaller will get the signature of the APK and compare against ours. If they match, the app can be sideloaded.
We followed this excellent resource: SignatureCheck.java. It's working with the hardcoded APP_SIGNATURE. We have this currently:
public boolean validateAppSignature() throws NameNotFoundException {
boolean sigMatch = false;
String APP_SIGNATURE = "123456784658923C4192E61B16999";
PackageInfo packageInfo3 = mPm.getPackageInfo(
getPackageName(), PackageManager.GET_SIGNATURES);
try {
for (Signature signature : packageInfo3.signatures) {
// SHA1 the signature
String sha1 = getSHA1(signature.toByteArray());
Log.i(TAG, "got this SHA1 of the signature ... "+sha1);
// check if it matches hardcoded value
sigMatch = APP_SIGNATURE.equals(sha1);
if (sigMatch){
return sigMatch;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Just that we don't know how to do that in a real world situation. When we build, install apps or release with OTA, we sign those apps with our private key and then on the device (PackageInstaller app), we hardcode our certificate's SHA1 signature as APP_SIGNATURE so that we can compare? Any other ideas?
Thank you so much.
Seems like you're trying way too hard to partially shut down sideloading. Use a Device Owner to completely turn off installation, and just preload the apps you want. Or make it so the only app that can install is your own downloader that only looks at your server.
I'm developing a game for Android using the Google Play Services for creating a turnbased match.
At first everything was fine I load the turnbased matches for the signed in user using
Games.TurnBasedMultiplayer.loadMatchesByStatus(getApiClient(),
new int[]{TurnBasedMatch.MATCH_TURN_STATUS_MY_TURN,
TurnBasedMatch.MATCH_TURN_STATUS_THEIR_TURN,
TurnBasedMatch.MATCH_TURN_STATUS_INVITED,
TurnBasedMatch.MATCH_TURN_STATUS_COMPLETE})
.setResultCallback(this);
It always loaded all matches that are any of the given states. But since last weekend the callback is called but there are no matches, as I'm not participating in any match (Status response is OK). I deleted the cache of Google Play Services on my phone and rebooted the device. At that moment all the matches were shown again until the next time I opened the app. Again all matches were missing.
Once I start a new match the match keeps showing up with the above method (refreshing the list) until I close the app. At the next launch that match is also gone.
I have to say the game is not published yet but in a test phase on the Google Play Developer Console. I found the same issue on an emulator. It ran fine for days but suddenly got the same problem as my real device (with a later build so it is not that a single change of code causes this).
Did anyone else notice this behaviour or has an idea on how to resolve it?
Might it be related to having multiple apps connected to one game? I had two apps signed with debug certificates connected and this afternoon added one for a signed apk. When I used the signed apk it worked again until I deployed a new test app (debug signed). After switching back to the signed apk the bug is still around.
As nobody seems to know the answer let me rephrase the question. Should I cache TurnBasedMatches myself on the device? I just deleted the play services cache again and reopened my app. Result? A list of hundreds of games (since I have to start a new game every time while testing...)
My code to handle the loadMatchesResult
#Override
public void onResult(TurnBasedMultiplayer.LoadMatchesResult loadMatchesResult)
{
showToast("GotMatches status: " + loadMatchesResult.getStatus().getStatusCode());
//add matches to listview (only caching matchId, no references to turnbasedmatch)
loadMatchesResult.getMatches().getMyTurnMatches().close();
loadMatchesResult.getMatches().getInvitations().close();
loadMatchesResult.getMatches().getTheirTurnMatches().close();
loadMatchesResult.getMatches().getCompletedMatches().close();
loadMatchesResult.release();
}
Found another interest point.. it starts to look like the issue occurs when deploying a new apk to the device... Once I deploy a new apk (either by install alpha version from google play or directly debug version from Android Studio) the matches are gone. When I don't change the apk I can reboot my phone/close the app and it works fine...
Issue also occurs if I update the app through the play store... There should be more people having this problem!
Gotten from https://developer.android.com/reference/com/google/android/gms/common/api/PendingResult.html#setResultCallback(com.google.android.gms.common.api.ResultCallback)
After the result has been retrieved using await() or delivered to the result callback, it is an error to attempt to retrieve the result again. It is the responsibility of the caller or callback receiver to release any resources associated with the returned result. Some result types may implement Releasable, in which case release() should be used to free the associated resources.
After you retrieve the result, an error is given when you try to get the results again, until you free the resources associated with the returned result, which is why clearing the cache works to make them visible again. You need to either access the device's cache and display results from there as well, or clear the associated resources (within the program) whenever you want to access the results again.
I had the same problem until I found "Saved Games" in my Developer Console:
Go to Game Services -> Game Details -> Saved Games
Set the item to "On"
This should solve your issue.
I'm trying to find a solution to do a remote update of an APK to 80 tablets. This should preferably be as automated as possible and if this can happen completely in the background without any user input that would be great. Basically what the Playstore currently do which I unfortunately can't use.
Is something like this possible without rooting the device? Any suggestion on libraries/ services that does this?
I'm running Android 4.1.1 and they will all be connected to a Wi-Fi.
You can download the new APK file to SD card, then call this to install it:
Intent shareIntent = new Intent(Intent.ACTION_VIEW);
shareIntent.setDataAndType(Uri.fromFile(new File("path-to-APK-file")),
"application/vnd.android.package-archive");
try {
context.startActivity(shareIntent);
} catch (Throwable t) {
// handle the exception here
}
There is only one thing not automatic: the final step. The system will ask the user to confirm installation.
About the MIME type of APK files, here's the wiki page.
No, in the background isn't possible without rooting or having the device's signing key at least as a standard Android APK update. The only semi-reasonable way I can envision something similar to this working is for your app to always check for/download code to run which you load using a class loader. This would be a significant amount of work and not easy.
However, if you're willing to live with some user interaction, it really shouldn't be that hard (though it'll still take some building of infrastructure). Keep a web service that returns the latest version number, compare with the current version number and download the new APK as necessary. Installing an APK programmatically has been covered in many SO questions.
has anybody tried implementing amazon's in-app purchase? I'm having problem implementing the example on their site. below example, it's giving me INVALID_SKU response:
when purchase is initiated,
PurchasingManager.initiateItemDataRequest("DeveloperSKU-1234");
the response of above will be recieved by below code:
public void onPurchaseResponse(final PurchaseResponse purchaseResponse) {
Log.v(TAG, "onPurchaseResponse recieved");
Log.v(TAG, "PurchaseRequestStatus:" + purchaseResponse.getPurchaseRequestStatus());
}
unfortunately, i always got "PurchaseRequestStatus:INVALID_SKU".
any of you knows how to make this work or do you know a good site for the tutorial?
Also at their site, dialog box should show if I got an INVALID_SKU which I didnt get any.
Here's how I got the In App purchases to work on Kindle Fire (after several hrs of struggle...)
adb install AmazonSDKTester.apk (Install SDKTester on Kindle Fire)
Create a file amazon.sdktester.json in the SDCARD directory (The connected KF shows up as SDCARD in Finder on ur Mac)
Contents of amazon.sdktester.json - {
"com.yourcompany.yourpkgname.200_coins" : {
"itemType": "CONSUMABLE",
"price": 0.99,
"title": "200 COINS",
"description": "2 COINS",
"smallIconUrl": "http://www.yourcompany.com/icon.png"
}
}
Press the power button on KF & press "Disconnect" button - Now KF is no longer a mounted drive on ur Mac.
Run the AmazonSDKTester app on KF.
Run your app from Eclipse. Make sure the package name in the JSON matches the In App Item package name on Amazon's website & in the PurchasingManager.initiatePurchaseRequest("com.yourcompany.yourpkgname.200_coins");
Now you should see the In App interstitials showing up.
Still doesn't work - Force Close both ur app & AmazonSDKTester on KF; Hard Reset KF ; Restart Eclipse & Restart from Step 1
INVALID_SKU occurs when SKU is not matching or not entered in JSON file. If you are implementing your test app using SDK Tester, then please make sure that the SKU which you are using in your app must have an entry in JSON file too. JSON files resides under /mnt/sdcard/ with name amazon.sdktester.json. Request you to verify whether you have created JSON file with SKU details under this path in the device.
A good approach for managing SKUs would be:
Reference SKUs as strings in your app's strings.xml file -- the same way you would use any string constant.
You can test your app using SDK Tester and the amazon.sdktester.json file without doing anything on the Developer Portal.
When you're ready to submit your app (after testing with SDKTester), make sure you have also submitted your IAP items on the Developer portal -- watch for typos!
We recommend using . for a SKU format, just because it makes it obvious what it is and what app it belongs to. For example:
com.yoyodyne.beachblanketbingo.500_coins