불법복제방지 - 라이선스 적용(Google Play Application License Verification )
LVL을 테스트하기 위해서는 어플리케이션이 안드로이드 마켓에 '유료' 로 업로드가되어야하고
내부 앱 공유로는 유료앱은 노출이 되지 않는다.
공개 또는 비공개 테스트 트랙에 게시해야 확인이 된다.
Android LVL : [ 링크 ]
UnityTechonologies에서 제공하는 LVL를 확인해봤는데
여러 삽질을 했었다.
Unity-Technologies - GooglePlayLicenseVerification
결론은..
RSA를 사용하는 과정에서 Momo로 사용해야 사용이 가능하다. (레거시 버전인듯하다.)
(Mono.Security.ASN1 : http://docs.go-mono.com/index.aspx?link=T%3aMono.Security.ASN1)
지금 본인이 필요한 환경은 IL2CPP의 Script Backend 상황이라서 사용하지 못했다.
Android Google Play Licensing Library를 Native Code로 처리하기(.aar)로 결정했다.
Android Studio 에서 Android SDK ->Google Play Licensing Library를 설치한다.
C:\Users\seo\AppData\Local\Android\Sdk\extras\google\market_licensing\library\AndroidManifest.xml
신규 프로젝트에서 Import Module로 해당 경로를 임포트한다.
Google Licensing Validator 참조 : [링크]
<응답 코드>
응답 코드 및 설명은 다음과 같다.
LICENSED = Hex: 0x0100, Decimal: 256
NOT_LICENSED = Hex: 0x0231, Decimal: 561
RETRY = Hex: 0x0123, Decimal: 291
LICENSED_OLD_KEY = Hex: 0x2, Decimal: 2
ERROR_NOT_MARKET_MANAGED = Hex: 0x3, Decimal: 3
ERROR_SERVER_FAILURE = Hex: 0x4, Decimal: 4
ERROR_OVER_QUOTA = Hex: 0x5, Decimal: 5
ERROR_CONTACTING_SERVER = Hex: 0x101, Decimal: 257
ERROR_INVALID_PACKAGE_NAME = Hex: 0x102, Decimal: 258
ERROR_NON_MATCHING_UID = Hex: 0x103, Decimal: 259
응답코드 | 설명 |
LICENSED | 사용자에게 애플리케이션의 라이선스가 부여되었습니다. 사용자가 애플리케이션을 구매했거나 사용자에게 애플리케이션의 알파 또는 베타 버전을 다운로드하고 설치할 권한이 부여되었습니다. |
LICENSED_OLD_KEY | 사용자에게 애플리케이션의 라이선스가 부여되었지만 다른 키로 서명되어 업데이트된 사용 가능한 애플리케이션 버전이 있습니다. (설치된 애플리케이션 버전에서 사용하는 키 쌍이 무효하거나 손상되었다고 표시할 수 있습니다. 애플리케이션은 필요한 경우 액세스를 허용하거나 사용자에게 업그레이드를 할 수 있다고 알리고 업그레이드할 때까지 추가 사용을 제한할 수 있습니다.) |
NOT_LICENSED | 사용자에게 애플리케이션의 라이선스가 부여되지 않았습니다. |
ERROR_CONTACTING_SERVER | 로컬 오류 - Google Play 애플리케이션이 라이선스 서버에 도달할 수 없었으며 네트워크 가용성 문제가 원인일 수 있습니다. |
ERROR_SERVER_FAILURE | 서버 오류 - 서버가 라이선스를 위한 애플리케이션의 키 쌍을 로드할 수 없었습니다. |
ERROR_INVALID_PACKAGE_NAME | 로컬 오류 - 애플리케이션이 기기에 설치되지 않은 패키지의 라이선스 확인을 요청했습니다. (일반적으로 개발 오류로 인해 발생합니다.) |
ERROR_NON_MATCHING_UID | 로컬 오류 - UID(패키지, 사용자 ID 쌍)가 요청하는 애플리케이션의 UID와 일치하지 않는 패키지의 라이선스 확인을 애플리케이션이 요청했습니다. (일반적으로 개발 오류로 인해 발생합니다.) |
ERROR_NOT_MARKET_MANAGED | 서버 오류 - 애플리케이션(패키지 이름)을 Google Play에서 인식하지 못했습니다. (애플리케이션이 Google Play를 통해 게시되지 않았거나 라이선스 구현에 개발 오류가 있다고 표시할 수 있습니다.) |
ERROR_OVER_QUOTA |
라이선스 서버가 할당량을 초과하여 이 장치와 통신하는 것을 거부합니다. |
Java 코드를 작성한다.
package com.shc.unity.plugin;
import android.app.Activity;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import com.unity3d.player.UnityPlayer;
import com.google.android.vending.licensing.AESObfuscator;
import com.google.android.vending.licensing.LicenseChecker;
import com.google.android.vending.licensing.LicenseCheckerCallback;
import com.google.android.vending.licensing.ServerManagedPolicy;
public class UnityPlugin
{
private static UnityPlugin _instance;
private static Activity _context;
private static final String BASE64_PUBLIC_KEY = ""; // TODO replace with your own key
private LicenseCheckerCallback mLicenseCheckerCallback;
private LicenseChecker mChecker;
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
};
private Handler mHandler;
private boolean bInitalize =false;
private PluginCallback callback = null;
public static UnityPlugin instance()
{
if(_instance == null)
{
_instance = new UnityPlugin();
_context = UnityPlayer.currentActivity;
}
Log.i("[LVL]", "UnityPlugin instance");
return _instance;
}
public void showToast(String _messag)
{
_context.runOnUiThread(new Runnable(){
@Override
public void run(){
Toast.makeText(_context, _messag, Toast.LENGTH_SHORT).show();
}
});
}
private void Initalize(String _packageName, PluginCallback _callback)
{
Log.i("[LVL]", "Initalize Start");
String deviceId = Settings.Secure.getString(_context.getContentResolver(), Settings.Secure.ANDROID_ID);
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
// Construct the LicenseChecker with a policy.
mChecker = new LicenseChecker(
_context, new ServerManagedPolicy(_context,
//new AESObfuscator(SALT, _context.getPackageName(), deviceId)),
new AESObfuscator(SALT, _packageName, deviceId)),
BASE64_PUBLIC_KEY);
bInitalize = true;
callback =_callback;
Log.i("[LVL]", "Initalize End");
}
private void doCheck() {
mChecker.checkAccess(mLicenseCheckerCallback);
}
public void CheckLicense(String _pacakageName, PluginCallback callback)
{
Log.i("[LVL]", "CheckLicense :: "+ _pacakageName);
if(false == bInitalize)
Initalize(_pacakageName, callback);
doCheck();
}
public void unitySendMessage(String _objectName, String _methodName, String _param)
{
UnityPlayer.UnitySendMessage(_objectName, _methodName, _param);
}
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow(int policyReason) {
Log.i("[LVL]", "allow :: "+policyReason);
showToast("allow :: "+policyReason);
callback.Allow(policyReason);
if (_context.isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
}
public void dontAllow(int policyReason) {
Log.i("[LVL]", "dontAllow :: "+ policyReason);
showToast("dontAllow :: "+ policyReason);
callback.DontAllow(policyReason);
if (_context.isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// 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 a deep
// link returned by the license checker.
// If the reason for the lack of license is that the service is
// unavailable or there is another problem, we display a
// retry button on the dialog and a different message.
//_context.displayDialog(policyReason == Policy.RETRY);
}
public void applicationError(int errorCode) {
Log.i("[LVL]", "applicationError :: "+ errorCode);
showToast("applicationError :: "+ errorCode);
callback.Error(errorCode);
if (_context.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.
}
}
}
실제 라이선스에 대한 테스트를 해보면 다음과 같다.
<로그로 확인해본 결과>
정상적으로 응답코드가 오는것을 확인했다.
2022-04-27 23:44:48.266 24447-24712/? I/Unity: [AndroidPluginCallback] DontAllow : 561
AndroidPluginCallback:DontAllow(Int32)
System.Reflection.MonoMethod:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
UnityEngine.AndroidJavaProxy:Invoke(String, Object[])
UnityEngine._AndroidJNIHelper:InvokeJavaProxyMethod(AndroidJavaProxy, IntPtr, IntPtr)
★정상적인 계정 로그인할경우(라이선스 테스트에 등록된 계정)
2022-04-27 23:46:25.697 26870-27266/? I/Unity: [AndroidPluginCallback] Allow : 256
AndroidPluginCallback:Allow(Int32)
System.Reflection.MonoMethod:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
UnityEngine.AndroidJavaProxy:Invoke(String, Object[])
UnityEngine._AndroidJNIHelper:InvokeJavaProxyMethod(AndroidJavaProxy, IntPtr, IntPtr)
★다른 계정(아내님)
2022-04-27 23:48:59.064 30025-30497/? I/Unity: [AndroidPluginCallback] Error : 3
AndroidPluginCallback:Error(Int32)
System.Reflection.MonoMethod:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
UnityEngine.AndroidJavaProxy:Invoke(String, Object[])
UnityEngine._AndroidJNIHelper:InvokeJavaProxyMethod(AndroidJavaProxy, IntPtr, IntPtr)
Android 라이선스 개요(LVL) : [링크]
★ Android Google Licencing 적용 방법 :
★ Lorg/apache/http/client/utils/URLEncodedUtils; 이슈 해결방법
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/client/utils/URLEncodedUtils;
at com.google.android.vending.licensing.ServerManagedPolicy.decodeExtras(ServerManagedPolicy.java:266)
at com.google.android.vending.licensing.ServerManagedPolicy.processServerResponse(ServerManagedPolicy.java:113)
★ 구글 계정 없을때 LVL에서 에러터지는것 해결방법
java.lang.NullPointerException: Attempt to invoke virtual method 'byte[] java.lang.String.getBytes()' on a null object reference
at com.google.android.vending.licensing.LicenseValidator.verify(LicenseValidator.java:99)
at com.google.android.vending.licensing.LicenseChecker$ResultListener$2.run(LicenseChecker.java:236
★★★☆☆
'개발 > Unity' 카테고리의 다른 글
Unity)애니메이션 리깅(Animation Rigging) (0) | 2022.04.24 |
---|---|
Unity) 그림자(Shadow),외곽선(OutLine) 설정 (0) | 2022.04.21 |
Unity)Plugin)Rider Flow (0) | 2022.04.07 |
문제해결)Unity)There are no audio listeners in the scene. Please ensure there is always one audio listener in the scene (0) | 2022.04.06 |
문제해결)Unity)Unity Project Open Erorr (AssetImportWorker0.log 에러) (0) | 2022.04.05 |
댓글