views:

53

answers:

2

I'm trying to figure out the whole Android licensing thing, and getting frustrated. In the emulator, I run the app with no account, or one that isn't in the testing environment, and it seems to work correctly, returning the not licensed response and pops up the buy the app now message.

When I try to run it on an actual Android device, it returns licensed every time, even though the device account isn't one that is in the testing environment. Also, even though it returns licensed, the "checking license" box never goes away, unless you click cancel. Then it just lets you use the app as if it was licensed. It's mostly C&P from the example, with a few changes. I removed the check license button and the status text box.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mHandler = new Handler();

    // Try to use more data here. ANDROID_ID is a single point of attack.
    String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

    // Library calls this when it's done.
    mLicenseCheckerCallback = new MyLicenseCheckerCallback();
    // Construct the LicenseChecker with a policy.
    mChecker = new LicenseChecker(
        this, new ServerManagedPolicy(this,
            new AESObfuscator(SALT, getPackageName(), deviceId)),
        BASE64_PUBLIC_KEY);
    doCheck();

    ArrayAdapter<String> booksAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mBooks);

    this.setListAdapter(booksAdapter);
}

protected Dialog onCreateDialog(int id) {
    // We have only one dialog.
    return new AlertDialog.Builder(this)
        .setTitle(R.string.unlicensed_dialog_title)
        .setMessage(R.string.unlicensed_dialog_body)
        .setPositiveButton(R.string.buy_button, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                    "http://market.android.com/details?id=" + getPackageName()));
                startActivity(marketIntent);
                finish();
            }
        })
        .setNegativeButton(R.string.quit_button, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        })
        .create();
}

private void doCheck() {
    setProgressBarIndeterminateVisibility(true);
    alertbox("status", getString(R.string.checking_license));
    mChecker.checkAccess(mLicenseCheckerCallback);
}

protected void alertbox(String title, String mymessage)  
{  
    new AlertDialog.Builder(this)  
       .setMessage(mymessage)  
       .setTitle(title)  
       .setCancelable(true)  
       .setNeutralButton(android.R.string.cancel,  
          new DialogInterface.OnClickListener() {  
          public void onClick(DialogInterface dialog, int whichButton){}  
         })  
      .show();  
}

private void displayResult(final String result) {
    mHandler.post(new Runnable() {
        public void run() {
            alertbox("status", result);

            setProgressBarIndeterminateVisibility(false);
        }
    });
}

private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
    public void allow() {
        if (isFinishing()) {
            // Don't update UI if Activity is finishing.
            return;
        }
        // Should allow user access.

        //displayResult(getString(R.string.allow));
    }

    public void dontAllow() {
        if (isFinishing()) {
            // Don't update UI if Activity is finishing.
            return;
        }
        //displayResult(getString(R.string.dont_allow));

        // Should not allow access. In most cases, the app should assume
        // the user has access unless it encounters this. If it does,
        // the app should inform the user of their unlicensed ways
        // and then either shut down the app or limit the user to a
        // restricted set of features.
        // In this example, we show a dialog that takes the user to Market.
        showDialog(0);
    }

    public void applicationError(ApplicationErrorCode errorCode) {
        if (isFinishing()) {
            // Don't update UI if Activity is finishing.
            return;
        }
        // This is a polite way of saying the developer made a mistake
        // while setting up or calling the license checker library.
        // Please examine the error code and fix the error.
        String result = String.format(getString(R.string.application_error), errorCode);
        displayResult(result);
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mChecker.onDestroy();
}

I just don't know what I need to change to make it work... or if the license is somehow cached (even though this is the first time I've run it on this device) and if I can uncache it without wiping the device, as that'll be nice for when I do testing on other apps. Also, how do I remove the "checking license" message without having to click the cancel button... should I just make it so that it doesn't show up?

A: 

I'm just getting into licensing myself so don't take this as gospel but a few things stick out:

or if the license is somehow cached (even though this is the first time I've run it on this device) and if I can uncache it without wiping the device, as that'll be nice for when I do testing on other apps.

You are using the ServerManagedPolicy so the approval will be cached and obfuscated. This is the recommended way to do it. ( I assume to provide a better user experience and better response time ) In order to debug your approval you need to log into your market profile and change the "Test response" option. You need to use a device that has the same account as your publisher profile for the test response to work for a app that isn't released to the market yet.

You also have no code in your allow() method for your MyLicenseCheckerCallback class which should probably be where you clear the dialog (outside the isFinishing conditional).

if I can uncache it without wiping the device, as that'll be nice for when I do testing on other apps

Based on LicenseValidator.java It looks like the approval is stored in a prefs file at com.android.vending.licensing.ServerManagedPolicy in private mode. You can use the sharedpreferences editor to clear it from another place in the app.

Again I'm not a pro on this yet so I could be wrong but I think you might be able to troubleshoot your bug if you get it configured right.

T. Markle
AndyD273
A: 

I'm having a lot of trouble with this myself lately. The license server implementation of my app CueBrain is working correctly online and on my device, but I cannot get it working on the emulator. Is there some port I need to open in my router to get this to work?

It's frustrating because I'm trying to create a LaxPolicy for the newer version that, once it caches a license, holds onto it indefinitely until it gets a clear response from the server that the user is not licensed (that way users can use the app in low connectivity situations without getting license check failures). My new implementation works perfectly on my own device, but the license check fails on my tester's device. On the emulator it always fails (just like the old published version which I know works correctly).

Does upping it to "Compiler compliance level 1.6" mess anything up perhaps ?

swinefeaster
I don't know. Why don't you start a new question instead of trying to hijack my question? In your efforts, have you figured out how to clear an alert box? Be a good member and make yourself useful by helping answer a question, or start your own question that others can learn from.
AndyD273