blob: 773b8cdebaf2132168518c9ae9d9b667f1ed0df3 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.vending.licensing;
import java.security.SecureRandom;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.android.vending.licensing.LicenseCheckerCallback.ApplicationErrorCode;
import com.android.vending.licensing.Policy.LicenseResponse;
/**
* Client library for Android Market license verifications.
*
* The LicenseChecker is configured via a {@link Policy} which contains the
* logic to determine whether a user should have access to the application.
* For example, the Policy can define a threshold for allowable number of
* server or client failures before the library reports the user as not having
* access.
*
* This library is not thread-safe. Multiple, concurrent checks will result in
* an error.
*/
public class LicenseChecker implements ServiceConnection {
private static final String TAG = "LicenseChecker";
private static final SecureRandom RANDOM = new SecureRandom();
private ILicensingService mService;
/** Validator for the request in progress. */
private LicenseValidator mValidator;
private final Context mContext;
private final Policy mPolicy;
/** Listener for service (IPC) calls. */
private final ResultListener mListener;
private final String mPackageName;
private final String mVersionCode;
public LicenseChecker(Context context, Policy policy) {
mContext = context;
mPolicy = policy;
mListener = new ResultListener();
mPackageName = mContext.getPackageName();
mVersionCode = getVersionCode(context, mPackageName);
}
private boolean isInProgress() {
return mValidator != null;
}
/**
* Checks if the user should have access to the app.
*
* @param callback
*/
public synchronized void checkAccess(LicenseCheckerCallback callback) {
if (isInProgress()) {
callback.applicationError(ApplicationErrorCode.CHECK_IN_PROGRESS);
}
mValidator = new LicenseValidator(mPolicy, callback, generateNonce(), mPackageName,
mVersionCode);
Log.i(TAG, "Binding to licensing service.");
boolean bindResult = mContext.bindService(new Intent(ILicensingService.class.getName()),
this, // ServiceConnection.
Context.BIND_AUTO_CREATE);
if (!bindResult) {
Log.e(TAG, "Could not bind to service.");
callback.dontAllow();
// No need to unbind at this point.
return;
}
}
private class ResultListener extends ILicenseResultListener.Stub {
public void verifyLicense(int responseCode, String signedData, String signature) {
mValidator.verify(responseCode, signedData, signature);
cleanup();
}
}
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ILicensingService.Stub.asInterface(service);
try {
Log.i(TAG, "Calling checkLicense on service for " + mValidator.getPackageName());
mService.checkLicense(mValidator.getNonce(), mValidator.getPackageName(), mListener);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in checkLicense call.", e);
handleServiceConnectionError();
// cleanup unbinds service.
cleanup();
}
}
public void onServiceDisconnected(ComponentName name) {
// Called when the connection with the service has been
// unexpectedly disconnected. That is, Market crashed.
Log.w(TAG, "Service unexpectedly disconnected.");
handleServiceConnectionError();
// cleanup unbinds service.
cleanup();
}
private void handleServiceConnectionError() {
if (mPolicy.allowAccess(LicenseResponse.CLIENT_RETRY)) {
mValidator.getCallback().allow();
} else {
mValidator.getCallback().dontAllow();
}
}
/** Resets request state. */
private synchronized void cleanup() {
mContext.unbindService(this);
mValidator = null;
}
/** Generates a nonce (number used once). */
private int generateNonce() {
return RANDOM.nextInt();
}
/**
* Get version code for the application package name.
*
* @param context
* @param packageName application package name
* @return the version code or empty string if package not found
*/
private static String getVersionCode(Context context, String packageName) {
try {
return String.valueOf(context.getPackageManager().getPackageInfo(packageName, 0).
versionCode);
} catch (NameNotFoundException e) {
Log.e(TAG, "Package not found. could not get version code.");
return "";
}
}
}