当我们的应用上架google应用市场时,可能希望应用在被授权后才能使用。因此Google play为我们提供了许可服务,借助该服务,应用可以在运行时查询google play服务器,获取当前用户的许可状态,APP可以根据具体的情况禁止或允许用户进一步使用。以下是官方文档的链接:应用许可。
本博客的重点在于如何集成官方提供的License Verification Library,来实现Google play的权限验证。
1. 设置发布账号,并生成应用的许可公钥;
2. 下载LVL;
git clone https://github.com/google/play-licensing
这是google的一个开源库,其中包括LVL库的源代码以及示例代码(比较奇怪的是这个库官方并没有放到sdk manager中,而且项目代码使用的sdk版本也非常的旧)。
LVL下载成功后,需要关注的是lvl_library中的内容,为了能够兼容集成应用的sdk版本,先要修改如下位置(app/build.gradle):
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 23
targetSdkVersion 29
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
将编译的sdk版本和目标版本改为29,同时最小支持sdk版本改为23。
3. 集成LVL
将lvl_library文件夹拷贝到应用的app目录下,同时在app/build.gradle中加上:
implementation project(':lvl_library')
在settings.gradle中添加:
include ':app', ':lvl_library'
sync project即可。
4. 编写代码进行权限验证
权限验证的代码可以放到任意的Activity中,但是为了在某些情况下禁止未授权用户使用应用,往往是在作为应用入口的Activity中,比如MainActivity。
具体步骤如下:
4.1 声明常量保存公钥和盐:
private static final String BASE64_PUBLIC_KEY = "YOUR_PUBLIC_KEY";
private static final byte[] SALT = new byte[] {
-46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64, 89
};
这里使用的盐限定使用长度为20的字节数组。
4.2 通过公钥、盐和设备id实例化LicenseChecker:
private LicenseChecker mLicenseChecker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
String deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
mLicenseChecker = new LicenseChecker(
this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),
BASE64_PUBLIC_KEY);
checkLicense();
}
4.3 检查权限checkLicense方法
private void checkLicense() {
if (!isAllowToUse()) {
mLicenseChecker.checkAccess(new LicenseCheckerCallback() {
@Override
public void allow(int reason) {
Log.d(TAG, "allow to use with reason " + reason);
saveMsgToPrefs("isAllowToUse", "isAllowToUse", String.valueOf(true));
}
@Override
public void dontAllow(int reason) {
Log.d(TAG, "don't allow to use with reason " + reason);
showNotAllowedDialog(reason == Policy.RETRY);
}
@Override
public void applicationError(int errorCode) {
Log.d(TAG, "application error with errorCode " + errorCode);
}
});
}
}
在检查权限前,为了避免每次应用启动时重复验证,可以将验证的结果保存下来。isAllowToUse方法就是从本地获取之前的验证状态。
检查用户权限的核心方法是mLicenseChecker.checkAccess(),这个方法接收一个实现LicenseCheckedCallback接口的实例对象作为参数,上面实现的接口方法中:
(1)allow:说明当前用户是通过google应用市场下载的已授权的应用;
(2)dontAllow:大致可以分为两种情况,一种是用户网络问题导致无法验证权限;另一种则是用户未获得该应用的使用权限;
上面两个方法中都返回reason参数,reason是一个定义在Policy接口中的常量,有如下取值:
public interface Policy {
......
public static final int LICENSED = 0x0100; // 已授权
public static final int NOT_LICENSED = 0x0231; // 未授权
public static final int RETRY = 0x0123; // 无法获取授权信息,极有可能是网络不通导致无法验证授权信息
......
}
在dontAllow中,我们可以弹出一个对话框提示用户未获得使用权限,强制用户退出应用。或者提供一个按钮让用户重试(针对RETRY的情况)。例如:showNotAllowedDialog方法
private AlertDialog.Builder mNotAllowedDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
mNotAllowedDialog = new AlertDialog.Builder(this);
mNotAllowedDialog
.setTitle(getResources().getString(R.string.unlicensed_dialog_title));
mNotAllowedDialog.setCancelable(false);
......
}
private void showNotAllowedDialog(boolean isRetry) {
if (isRetry) {
mNotAllowedDialog.setMessage(getResources().getString(R.string.unlicensed_dialog_retry_body));
mNotAllowedDialog.setPositiveButton(getResources().getString(R.string.retry), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
checkLicense();
}
});
mNotAllowedDialog.setNegativeButton(getResources().getString(R.string.exit), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
} else {
mNotAllowedDialog.setMessage(getResources().getString(R.string.unlicensed_dialog_body));
mNotAllowedDialog.setPositiveButton(getResources().getString(R.string.exit), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
}
mNotAllowedDialog.show();
}
(3)applicationError:这里包含了额外的异常情况,通过提供的errorCode参数来标识,errorCode定义在接口LicenseCheckerCallback中,有如下取值:
public interface LicenseCheckerCallback {
......
public static final int ERROR_INVALID_PACKAGE_NAME = 1; // 无效的包名
public static final int ERROR_NON_MATCHING_UID = 2; // 没有匹配的UID
public static final int ERROR_NOT_MARKET_MANAGED = 3; // 未上架google应用市场
public static final int ERROR_CHECK_IN_PROGRESS = 4;
public static final int ERROR_INVALID_PUBLIC_KEY = 5; // 无效的公钥
public static final int ERROR_MISSING_PERMISSION = 6; // 缺少权限
......
}
4.4 在主应用的AndroidManifest.xml中添加权限:
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
以上基本上就是完整的集成LVL的流程了。