package fi.iki.murgo.irssinotifier;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.android.vending.licensing.ILicenseResultListener;
import com.android.vending.licensing.ILicensingService;
import java.io.IOException;
import java.util.HashMap;
public class LicenseCheckingTask extends BackgroundAsyncTask<Void, Void, LicenseCheckingTask.LicenseCheckingMessage> {
private static final String TAG = LicenseCheckingTask.class.getName();
private ILicensingService service;
private static final int LICENSED = 0x0;
private static final int NOT_LICENSED = 0x1;
private static final int LICENSED_OLD_KEY = 0x2;
private Server server;
public LicenseCheckingTask(Activity activity) {
super(activity);
}
public LicenseCheckingTask(Activity activity, String titleText, String text) {
super(activity, titleText, text);
}
public enum LicenseCheckingStatus {
Allow,
Disallow,
Error,
}
public static class LicenseCheckingMessage {
public LicenseCheckingStatus licenseCheckingStatus;
public String errorMessage;
public LicenseCheckingMessage(LicenseCheckingStatus status) {
licenseCheckingStatus = status;
}
public LicenseCheckingMessage(String error) {
licenseCheckingStatus = LicenseCheckingStatus.Error;
errorMessage = error;
}
}
@Override
protected LicenseCheckingMessage doInBackground(Void... params) {
server = new Server(activity);
boolean authenticated = false;
try {
authenticated = server.authenticate();
} catch (IOException e) {
e.printStackTrace();
}
if (!authenticated) {
Log.e(TAG, "Unable to authenticate to server");
return new LicenseCheckingMessage("Unable to authenticate to server");
}
ServerResponse response;
int nonce;
try {
response = server.get(new MessageToServer(), Server.ServerTarget.GetNonce);
nonce = Integer.parseInt(response.getResponseString());
} catch (IOException e) {
e.printStackTrace();
return new LicenseCheckingMessage("IOException while connecting to server");
} catch (NumberFormatException e) {
e.printStackTrace();
return new LicenseCheckingMessage("Cannot parse nonce");
}
return checkLicense(nonce);
}
private LicenseCheckingMessage checkLicense(int nonce) {
boolean bindResult = activity.bindService(new Intent("com.android.vending.licensing.ILicensingService"),
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder s) {
service = ILicensingService.Stub.asInterface(s);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w(TAG, "Service unexpectedly disconnected.");
service = null;
}
},
Context.BIND_AUTO_CREATE);
if (!bindResult) {
Log.e(TAG, "Could not bind to service.");
return new LicenseCheckingMessage("Unable to bind to licensing service");
}
long startTime = System.currentTimeMillis();
while (service == null && System.currentTimeMillis() - startTime < 10000) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
return new LicenseCheckingMessage("Interrupted while connecting to licensing service");
}
}
if (service == null) {
Log.e(TAG, "Could not connect to service in time");
return new LicenseCheckingMessage("Could not connect to service in time");
}
final Object[] licenseResponseData = new Object[3];
try {
Log.i(TAG, "Calling checkLicense");
service.checkLicense(nonce, LicenseHelper.PACKAGE_PLUS, new ILicenseResultListener.Stub() {
@Override
public void verifyLicense(int responseCode, String signedData, String signature) throws RemoteException {
licenseResponseData[0] = responseCode;
licenseResponseData[1] = signedData;
licenseResponseData[2] = signature;
}
});
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in checkLicense call.", e);
return new LicenseCheckingMessage("RemoteException in checking license call.");
}
startTime = System.currentTimeMillis();
Object signedDataObject = null;
while (signedDataObject == null && System.currentTimeMillis() - startTime < 10000) {
signedDataObject = licenseResponseData[1];
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
return new LicenseCheckingMessage("Interrupted while calling licensing service");
}
}
if (signedDataObject == null) {
Log.e(TAG, "Could not check license from licensing service in time");
return new LicenseCheckingMessage("Could not check license from licensing service in time");
}
/* Response codes:
private static final int LICENSED = 0x0;
private static final int NOT_LICENSED = 0x1;
private static final int LICENSED_OLD_KEY = 0x2;
private static final int ERROR_NOT_MARKET_MANAGED = 0x3;
private static final int ERROR_SERVER_FAILURE = 0x4;
private static final int ERROR_OVER_QUOTA = 0x5;
private static final int ERROR_CONTACTING_SERVER = 0x101;
private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
private static final int ERROR_NON_MATCHING_UID = 0x103;
*/
int responseCode = (Integer) licenseResponseData[0];
String signedData = (String) licenseResponseData[1];
String signature = (String) licenseResponseData[2];
switch (responseCode) {
case LICENSED:
case LICENSED_OLD_KEY:
return verifyResponseData(signedData, signature);
case NOT_LICENSED:
return new LicenseCheckingMessage(LicenseCheckingStatus.Disallow);
default:
Log.e(TAG, "Some error: " + responseCode);
return new LicenseCheckingMessage("Invalid response code: " + responseCode);
}
}
private LicenseCheckingMessage verifyResponseData(String signedData, String signature) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("SignedData", makeBase64UrlSafe(signedData));
map.put("Signature", makeBase64UrlSafe(signature));
ServerResponse response;
try {
response = server.post(new MessageToServer(map), Server.ServerTarget.License);
} catch (IOException e) {
e.printStackTrace();
return new LicenseCheckingMessage("IOException while posting to server");
}
if (response == null) {
Log.w(TAG, "Licensing: null response");
return new LicenseCheckingMessage("Invalid response from server: null");
}
if (response.getException() != null) {
Log.w(TAG, "Licensing: Exception: " + response.getException());
return new LicenseCheckingMessage("Exception while posting to server: " + response.getException().getMessage());
}
if (response.getStatusCode() != 200) {
Log.w(TAG, "Licensing: Invalid response status code: " + response.getStatusCode());
return new LicenseCheckingMessage("Invalid response status code: " + response.getStatusCode());
}
if (response.getResponseString() == null) {
Log.w(TAG, "Licensing: null response string");
return new LicenseCheckingMessage("Null response body from server");
}
if (response.getResponseString().equals("OK")) {
Log.i(TAG, "IrssiNotifier+ licensed succesfully!");
Preferences prefs = new Preferences(activity);
prefs.setLastLicenseTime(System.currentTimeMillis());
prefs.setLicenseCount(prefs.getLicenseCount() + 1);
return new LicenseCheckingMessage(LicenseCheckingStatus.Allow);
}
Log.w(TAG, "Licensing: Disallowing, server said: " + response.getResponseString());
return new LicenseCheckingMessage(LicenseCheckingStatus.Disallow);
}
private String makeBase64UrlSafe(String data) {
return data.replace("=", "%3D").replace("&", "%26").replace("/", "%2F").replace("+", "%2B");
}
}