Is there a standard way to limit the number of installations allowed by an .apk
If I understand correctly Timestamp limits the time an .apk is valid for installation, is there something similar to this that limits the installation count.
I'm planning on using DeviceID and UserID to populate a DB that checks logs for used User ID / Device ID combination to allow or not allow access, once the app is installed.
Thing is I'm trying to prevent the app from even being installed if its over x number of installations.
I was thinking about something like:
package com.tchip.kw;
public class mysoft {
static {
try{
System.loadLibrary("tchipmysoft");
}
catch(Exception e)
{}
}
public native int getmachinestatus1();
public native int getmachinestatus2();
}
///this will read/call 2 solutions from mysoft when the end users try to run app. If the return value is 0, then it will say’ Quit’. For example:
mysoft kw = new mysoft();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//this.findViewById(id)
if (kw.getmachinestatus1() && kw.getmachinestatus2() )
setContentView(R.layout.main);
/////Star splash activity
else
{
You do not have proper license to use this software and it will quit
}
}
I've thought about writing an installation .APK for the for the app.apk that checks for a set of Key's held remotely and then grants access.
If there is a standardized method or am I missing something really simple, I'm all ears.
Related
I’ve seen apps where users can create a guest account (anonymous) without password, and when they delete the game and reinstall it, they’re still logged in to their guest account.
I’ve also seen some apps where this situation would create a new guest account every time. I’d like to avoid this in my app but I don’t know how to.
Anyone knows how this is done?
Thanks,
Tom
So you want to generate userId and persist it through app reinstalls. In android I heard that there is no problem to get unique identifier. In iOS you can use framework like JNKeychain. Then you generate unique value. In obj-c you may wish to use such code.
NSString* deviceId = [JNKeychain loadValueForKey: #"deviceId"];
if (!deviceId)
{
if (![JNKeychain saveValue: [[NSUUID UUID] UUIDString] forKey: #"deviceId"])
{
deviceId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
else
{
deviceId = [JNKeychain loadValueForKey: #"deviceId"];
}
}
I've been looking for a solution to this problem for a while (days, not minutes), but it eludes me quite effectively.
Please note that this is NOT a question about starting up the registration procedure. This must happen automatically without any user interaction.
I would like to add a Google account to my custom device (1000's of them). The account will mostly be used to activate Google Play store on the device so that the app can update when newer versions are available.
My existing code (the shortest snippet of those I tried):
AccountManager mgr = AccountManager.get(this);
Account acc = new Account("email#gmail.com", "com.google");
mgr.addAccountExplicitly(acc, "password", new Bundle()));
naturally yields a
java.lang.SecurityException: caller uid 10047 is different than the authenticator's uid
So how would I go about actually achieving this? My device is rooted so that's not an obstacle if it's the only way.
It is not possible to add/create a Google account using addAccountExplicitly(). You can only add accounts for your own services. even your device is rooted because it will rejected by Google web server. For more detail check this link
Warning: this solution doesn't work well. See comments for explanation.
Well, as it turns out, this is not something easily solved. I ended up registering one device, then pulled the users file from it. Location of users file : /data/system/users/0/accounts.db (if there are multiple user profiles on the device, the last directory may differ according to profile in question).
I stored this file into my app's assets (gzipped, make sure the extension is not something.gz because that gets lost during packaging - didn't bother checking out why).
First I check if my user already exists:
AccountManager mgr = AccountManager.get(this);
for (Account acc: mgr.getAccountsByType("com.google")) {
if (acc.name.equalsIgnoreCase("email#gmail.com"))
return;
}
If it does, I just skip the step. Otherwise I unpack the users file and overwrite existing one (using su). I then also do a reboot to make sure changes are registered.
I'm new to Android developement (I know very basic stuffs), and there is a chance that soon I'll be tasked with porting a WP7 app to Android (fortunately, I can use MonoDroid...).
Now that app has a trial functionality (see here), which for WP7 means that I can check whether the user bought it (so I can enable additional features inside the app) or downloaded the free edition. I do not want the trial to expire, I want a "free edition" of my app to be limited to certain features.
Is there anything similiar for Android? (And can it be done on MonoDroid?)
I've looked at Google Licensing Service, but I don't see how that helps me.
I would go for two apps solution. One "real" application, which contains all the functionality. Second "key" application which only check licensing.
First application will check if the key application is installed. If the check is positive then display full content, enable all features. If the key application is missing the application behaves like free version.
It is also very important to check if the private key that signed both applications is the same. Without this check someone might create their own key application and unlock your functionality. To do so consider this snippet, which I took from this blog: http://www.yoki.org/2010/07/31/creating-a-freepaid-app-pair-for-the-android-market/
protected boolean isProInstalled(Context context) {
// the packagename of the 'key' app
String proPackage = "org.yoki.android.pkgname";
// get the package manager
final PackageManager pm = context.getPackageManager();
// get a list of installed packages
List<PackageInfo> list =
pm.getInstalledPackages(PackageManager.GET_DISABLED_COMPONENTS);
// let's iterate through the list
Iterator<PackageInfo> i = list.iterator();
while(i.hasNext()) {
PackageInfo p = i.next();
// check if proPackage is in the list AND whether that package is signed
// with the same signature as THIS package
if((p.packageName.equals(proPackage)) &&
(pm.checkSignatures(context.getPackageName(), p.packageName) == PackageManager.SIGNATURE_MATCH))
return true;
}
return false;
}
This approach gives you few advantages in flexibility:
separate paid areas. You can assign sets of features to different key applications. eg. app key1 unlocks additional game levels a1,a2,a3 and app key2 unlocks levels b1,b2
time licensing - instead of only checking the existence of key application. You can query it to check if the licence is still valid. That way you can achieve time licences.
Probably the best way for you would be to use in-app purchases
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Supporting Amazon and Android market links inside application
I was wondering if and how you could differentiate between an Amazon App Store installed app and one installed from the Market.
For example, say I have my app called "Example App", and I want to develop for Amazon and the Market. In the app I have links to rate Example App. I also have a link to buy Example App Pro. This poses a problem because Amazon will not release my app if it links to a different App store.
This requires me to make 2 APK files, which is a pain. It only takes about 30 seconds extra to export both, but it creates extra clutter and testing time.
So has anyone found a way to make a single APK that can be uploaded to both Amazon and Android Market without making any changes between the two so that at run time I can check whether it's the Amazon or the Market that installed it and change the links accordingly?
Edit: At the time of this post, I wasn't aware of it, but there does exist getInstallerPackageName() but I'm not sure how reliable that is. I'm also not sure what it returns for Amazon / Market, etc. It might be worth looking at, but if it doesn't work, then the below method works for Google vs Amazon.
You will have to sign the application as normal, run on your test device, get the value of sig.hashCode() from your logs, then replace -1545485543 with whatever value you got for sig.hashCode() then export and sign again (WITH THE SAME KEY AS BEFORE) and then upload to Amazon and Market both - from the same APK.
Do it:
public static boolean isMarket(Context context){
boolean isMarketSig = false;
int currentSig = 1; // I just set this to 1 to avoid any exceptions later on.
try {
Signature[] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : sigs)
{
currentSig = sig.hashCode();
Log.i("MyApp", "Signature hashcode : " + sig.hashCode());
// This Log is for first time testing so you can find out what the int value of your signature is.
}
} catch (Exception e){
e.printStackTrace();
}
//-1545485543 was the int I got from the log line above, so I compare the current signature hashCode value with that value to determine if it's market or not.
if (currentSig==-1545485543){
isMarketSig = true;
} else {
isMarketSig = false;
}
return isMarketSig;
}
public static void openStore(Context context){
if (isMarket(context)){
Intent goToMarket = new Intent(Intent.ACTION_VIEW,Uri.parse("market://d" +
"etails?id=com.jakar.myapp"));
goToMarket.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goToMarket);
} else {
Intent goToAppstore = new Intent(Intent.ACTION_VIEW,Uri.parse("http://www.amazon.com/gp/mas/dl/andro" +
"id?p=com.jakar.myapp"));
goToAppstore.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goToAppstore);
}
}
Basically, the hashCode() that you get from the app installed on your testing device will be the same one from the market. The hash code from the app store will be different because according to https://developer.amazon.com/help/faq.html, the app store signs the application with a signature specific to your developer account, so that will return a different value that what you actually signed it with.
And I put the isMarket and openStore methods in a different class called OtherClass so that I only have to code it once. Then from any activity where I need to open the proper link, I just call OtherClass.openStore(context);
Note: It works to open the market successfully, but I haven't yet deployed this method on the App Store, so I haven't completely tested it. I am confident it will work, but can make no guarantees, so if you use what I've suggested and it fails, please don't hold me accountable.
This was a big help in coming up with an answer so I could test which signature was being used.
I'm developing a non-public Android app, i.e. the app won't be available in the global Android Market. The app will be installed on a limited number of clients, e.g. by using an apk file.
How can I enable an auto-update functionality in this app?
I see different potential options (I do not know if those are technically hard or even impossible to implement or if there are any existing functionalities that can be reused):
On each launch the app tests if a new version exists (by requesting a server), if so downloads the new apk and replaces itself with the new version.
Use (or develop?) a separated app or service that undertakes the update-check and replacement-process.
Use (or develop?) a private market app which has an auto-update option. This option is similar to the second one, but more generic: The market app would be connected to a repository, i.e. it would handle an arbitrary number of (private) apps.
I would prefer option one since the auto-update functionality is included in the app which needs less development efforts.
janjonas, in the company I work we had a similar problem with Windows Mobile 6.x, and we use pretty much the same solution pointed by EboMike:
The main app check if it's updated, against a WebService. It receives the current version & the URL from where download the new version, if necessary. The main app then start the Updater app, passing the URL, and quit.
The Updater do the download of the new program, via HTTP, showing to the user the % downloaded. The user can cancel the download anytime, in a controlled way, and the Updater can registry this cancellation.
Since the new app is downloaded, the Updater run the new app, and quit.
I think option one is the least amount of work for you, and actually the cleanest one too since it will go through the proper channel of using Android's built-in package installer which includes user notification and the option for the user to abort the installation if desired.
You already have it all outlined - check for a new version on a server (would be nice to give the user the option to turn that off), and if there is a new version, you could either just link to the URL with the APK (which will, IIRC, use the browser's download manager to download it), or you could download it with your app and then point the intent to your local file. Using the HTTP link is technically less work and cleaner - the more you let the operating system do, the better - unless there's a reason not to.
Enabling "Install non-market app" is still needed for any application outside the Google Play. If it not enabled, the installation process is going to ask for it and redirect the user to the Application Settings, and after that, the user can install the app.
Depending on your needs, you can delegate to a third part lib.
Some of the permissions we'll use to get this done are the following:
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Let me explain a bit... The last, WRITE_EXTERNAL_STORAGE, is self-explanatory. With ACCESS_SUPERUSER we'll tell the system that we intend to use root privileges. READ_EXTERNAL_STORAGE will be needed in the future in order for your app to read files on SD card.
Assuming that you have downloaded the file and that all those devices can be rooted (limited number of clients, not on Play, etc.), you could do this:
String filePath = Environment.getExternalStorageDirectory().toString() + "/your_app_directory/your_app_filename.apk";
Process installProcess = null;
int installResult = -1337;
try {
installProcess = Runtime.getRuntime().exec("su -c pm install -r " + filePath);
} catch (IOException e) {
// Handle IOException the way you like.
}
if (installProcess != null) {
try {
installResult = installProcess.waitFor();
} catch(InterruptedException e) {
// Handle InterruptedException the way you like.
}
if (installResult == 0) {
// Success!
} else {
// Failure. :-/
}
} else {
// Failure 2. :-(
}
Here might be a very lame method but for some companies, if you believe its applicable, this might be very easy to implement.
Create an password screen (passwordActivity) that asks a password to access the application.
Once the password is entered, raise a flag (set a boolean value from false to true using sharedpreferences)
Place the .apk file on Google Store.
Change the password once everyone installs the app, and release a new update on Google Play Store.
Since the software is going to cache the flag value, the password screen won`t show up even the password is change. It will only show up for new installations so might need to repeat the process.
Note: This method might better fit if there is not hundreds of users using the application. And don`t forget this method is also not secure. To sum up, if you are looking a way to keep the application private and have no security concerns, this is what I recommend.
Update app
Make sure that you already have your new apk download on location
void installNewVersion(String location) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(location + "app-debug.apk")),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}