Merge commit '40e665a015c67a8dd21838894a1634a3e101b760' into eclair-plus-aosp

* commit '40e665a015c67a8dd21838894a1634a3e101b760':
  cleanup
This commit is contained in:
James Yum
2010-02-10 13:01:55 -08:00
committed by Android Git Automerger
8 changed files with 0 additions and 598 deletions

View File

@@ -1,21 +0,0 @@
/*
* 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;
oneway interface ILicenseResultListener {
void verifyLicense(int responseCode, String signedData, String signature);
}

View File

@@ -1,23 +0,0 @@
/*
* 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 com.android.vending.licensing.ILicenseResultListener;
oneway interface ILicensingService {
void checkLicense(long nonce, String packageName, in ILicenseResultListener listener);
}

View File

@@ -1,165 +0,0 @@
/*
* 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 "";
}
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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;
/**
* Callback for the license checker library.
*
* Upon checking with the Market server and conferring with the policy, the
* library calls a appropriate callback method to communicate the result.
*/
public interface LicenseCheckerCallback {
/**
* Allow use. App should proceed as normal.
*/
public void allow();
/**
* Don't allow use. App should inform user and take appropriate action.
*/
public void dontAllow();
/** Application error codes. */
public enum ApplicationErrorCode {
/** Package is not installed. */
INVALID_PACKAGE_NAME,
/** Requested for a package that is not the current app. */
NON_MATCHING_UID,
/** Market does not know about the package. */
NOT_MARKET_MANAGED,
/** A previous check request is already in progress.
* Only one check is allowed at a time. */
CHECK_IN_PROGRESS
}
/**
* Error in application code. Caller did not call or set up license
* checker correctly. Should be considered fatal.
*/
public void applicationError(ApplicationErrorCode errorCode);
}

View File

@@ -1,165 +0,0 @@
/*
* 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 android.util.Log;
import com.android.vending.licensing.LicenseCheckerCallback.ApplicationErrorCode;
import com.android.vending.licensing.Policy.LicenseResponse;
/**
* Contains data related to a licensing request and methods to verify
* and process the response.
*/
class LicenseValidator {
private static final String TAG = "LicenseValidator";
// Server 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_INVALID_KEYS = 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;
private final Policy mPolicy;
private final LicenseCheckerCallback mCallback;
private final int mNonce;
private final String mPackageName;
private final String mVersionCode;
LicenseValidator(Policy policy, LicenseCheckerCallback callback, int nonce, String packageName,
String versionCode) {
mPolicy = policy;
mCallback = callback;
mNonce = nonce;
mPackageName = packageName;
mVersionCode = versionCode;
}
public LicenseCheckerCallback getCallback() {
return mCallback;
}
public int getNonce() {
return mNonce;
}
public String getPackageName() {
return mPackageName;
}
/**
* Verifies the response from server and calls appropriate callback method.
*
* @param responseCode server response code
* @param signedData signed data from server
* @param signature server signature
*/
public void verify(int responseCode, String signedData, String signature) {
// Parse and validate response.
// TODO(jyum): decode data with signature.
// TODO(jyum): verify timestamp is within reason. However, relying
// on device clock may lead to problems?
ResponseData data;
try {
data = ResponseData.parse(signedData);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Could not parse response.");
handleInvalidResponse();
return;
}
if (data.responseCode != responseCode) {
Log.e(TAG, "Response codes don't match.");
handleInvalidResponse();
return;
}
if (data.nonce != mNonce) {
Log.e(TAG, "Nonce doesn't match.");
handleInvalidResponse();
return;
}
if (!data.packageName.equals(mPackageName)) {
Log.e(TAG, "Package name doesn't match.");
handleInvalidResponse();
return;
}
if (!data.versionCode.equals(mVersionCode)) {
Log.e(TAG, "Version codes don't match.");
handleInvalidResponse();
return;
}
switch (responseCode) {
case LICENSED:
case LICENSED_OLD_KEY:
handleResponse(LicenseResponse.LICENSED);
break;
case NOT_LICENSED:
handleResponse(LicenseResponse.NOT_LICENSED);
break;
case ERROR_CONTACTING_SERVER:
handleResponse(LicenseResponse.CLIENT_RETRY);
break;
case ERROR_INVALID_KEYS:
case ERROR_OVER_QUOTA:
handleResponse(LicenseResponse.SERVER_RETRY);
break;
case ERROR_INVALID_PACKAGE_NAME:
handleApplicationError(ApplicationErrorCode.INVALID_PACKAGE_NAME);
break;
case ERROR_NON_MATCHING_UID:
handleApplicationError(ApplicationErrorCode.NON_MATCHING_UID);
break;
case ERROR_NOT_MARKET_MANAGED:
handleApplicationError(ApplicationErrorCode.NOT_MARKET_MANAGED);
break;
default:
Log.e(TAG, "Unknown response code for license check.");
handleInvalidResponse();
}
}
/**
* Confers with policy and calls appropriate callback method.
*
* @param response
*/
private void handleResponse(LicenseResponse response) {
if (mPolicy.allowAccess(response)) {
mCallback.allow();
} else {
mCallback.dontAllow();
}
}
private void handleApplicationError(ApplicationErrorCode code) {
mCallback.applicationError(code);
}
private void handleInvalidResponse() {
mCallback.dontAllow();
}
}

View File

@@ -1,55 +0,0 @@
/*
* 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;
/**
* Policy used by {@link LicenseChecker} to determine whether a user should
* have access to the application.
*/
public interface Policy {
/**
* Result of a license check.
*/
public enum LicenseResponse {
/**
* User is licensed to use the app.
*/
LICENSED,
/**
* User is not licensed to use the app.
*/
NOT_LICENSED,
/**
* Retryable error on the client side e.g. no network.
*/
CLIENT_RETRY,
/**
* Retryable error on the server side e.g. application is over request
* quota.
*/
SERVER_RETRY,
}
/**
* Determines whether the user should be allowed access.
*
* @param response result of the license check request
* @return true iff access should be allowed
*/
boolean allowAccess(LicenseResponse response);
}

View File

@@ -1,84 +0,0 @@
/*
* 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.util.Iterator;
import java.util.regex.Pattern;
import android.text.TextUtils;
/**
* ResponseData from licensing server.
*/
class ResponseData {
public int responseCode;
public int nonce;
public String packageName;
public String versionCode;
public String userId;
public long timestamp;
/** Response-specific data. */
public String extra;
/**
* Parses response string into ResponseData.
*
* @param responseData response data string
* @throws IllegalArgumentException upon parsing error
* @return ResponseData object
*/
public static ResponseData parse(String responseData) {
// Must parse out main response data and response-specific data.
TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(':');
splitter.setString(responseData);
Iterator<String> it = splitter.iterator();
if (!it.hasNext()) {
throw new IllegalArgumentException("Blank response.");
}
final String mainData = it.next();
// Response-specific (extra) data is optional.
String extraData = "";
if (it.hasNext()) {
extraData = it.next();
}
String [] fields = TextUtils.split(mainData, Pattern.quote("|"));
if (fields.length < 5) {
throw new IllegalArgumentException("Wrong number of fields.");
}
ResponseData data = new ResponseData();
data.extra = extraData;
data.responseCode = Integer.parseInt(fields[0]);
data.nonce = Integer.parseInt(fields[1]);
data.packageName = fields[2];
data.versionCode = fields[3];
// TODO(jyum): userId is not there yet.
// data.userId = fields[4];
data.timestamp = Long.parseLong(fields[4]);
return data;
}
@Override
public String toString() {
return TextUtils.join("|", new Object [] { responseCode, nonce, packageName, versionCode,
userId, timestamp });
}
}

View File

@@ -1,30 +0,0 @@
/*
* 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;
/**
* Strict policy.
*
* Should never be used in a real application as it strictly disallows access
* upon retryable errors such as no connection present.
*/
public class StrictPolicy implements Policy {
public boolean allowAccess(LicenseResponse response) {
return LicenseResponse.LICENSED == response;
}
}