Skip to content

Commit b0c927b

Browse files
authored
Merge pull request #652 from adjust/v546
Version 5.4.6
2 parents f94bb09 + 045cb0e commit b0c927b

File tree

21 files changed

+168
-89
lines changed

21 files changed

+168
-89
lines changed

Adjust/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ext {
99
coreMinSdkVersion = 21
1010
coreCompileSdkVersion = 36
1111
coreTargetSdkVersion = 36
12-
coreVersionName = '5.4.5'
12+
coreVersionName = '5.4.6'
1313
defaultVersionCode = 1
1414
webbridgeMinSdkVersion = 21
1515
samsungReferrerMinSdkVersion = 21

Adjust/examples/example-app-kotlin/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ dependencies {
4040
implementation 'com.google.android.gms:play-services-appset:16.1.0'
4141

4242
implementation project(':sdk-core')
43+
4344
implementation 'com.adjust.signature:adjust-android-signature:3.61.0'
4445
}

Adjust/plugins/sdk-plugin-google-lvl/proguard-rules.pro

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@
2323
# Keep all classes in the LVL licensing AIDL package
2424
-keep class com.android.vending.licensing.** { *; }
2525

26-
# Keep interface method signatures
27-
-keep interface com.android.vending.licensing.ILicensingService
28-
-keep interface com.android.vending.licensing.ILicenseResultListener
29-
3026
# Prevent obfuscation of the stub classes used for IPC
3127
-keepclassmembers class * implements android.os.IInterface {
3228
<methods>;
33-
}
29+
}

Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicenseResultListener.aidl

Lines changed: 0 additions & 21 deletions
This file was deleted.

Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicensingService.aidl

Lines changed: 0 additions & 23 deletions
This file was deleted.

Adjust/plugins/sdk-plugin-google-lvl/src/main/java/com/adjust/sdk/google/lvl/LicenseChecker.java

Lines changed: 113 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,35 @@
44
import android.content.Context;
55
import android.content.Intent;
66
import android.content.ServiceConnection;
7+
import android.os.Binder;
78
import android.os.IBinder;
9+
import android.os.IInterface;
10+
import android.os.Parcel;
811
import android.os.RemoteException;
912

1013
import com.adjust.sdk.ILogger;
11-
import com.android.vending.licensing.ILicensingService;
1214

1315
public class LicenseChecker {
1416
private static final String GOOGLE_PLAY_PACKAGE = "com.android.vending";
17+
private static final String LICENSING_SERVICE_DESCRIPTOR = "com.android.vending.licensing.ILicensingService";
18+
private static final String RESULT_LISTENER_DESCRIPTOR = "com.android.vending.licensing.ILicenseResultListener";
19+
private static final int TRANSACTION_CHECK_LICENSE = IBinder.FIRST_CALL_TRANSACTION;
20+
21+
// Error codes for better diagnostics
22+
private static final int ERROR_GENERIC = -1;
23+
private static final int ERROR_BIND_FAILED = -5;
24+
private static final int ERROR_NO_BINDER = -3;
25+
private static final int ERROR_TRANSACT_FAILED = -2;
26+
private static final int ERROR_REMOTE_EXCEPTION = -4;
27+
private static final int ERROR_BINDER_DIED = -6;
1528

1629
private final Context mContext;
1730
private final LicenseRawCallback mCallback;
1831
private final ILogger logger;
1932
private final long timestamp;
33+
private final ResultListenerBinder resultListenerBinder;
2034

21-
private ILicensingService mService;
35+
private IBinder mServiceBinder;
2236
private boolean mBound;
2337
private int retryCount = 0;
2438
private static final int MAX_RETRIES = 3;
@@ -28,6 +42,7 @@ public LicenseChecker(Context context, LicenseRawCallback callback, ILogger logg
2842
this.mCallback = callback;
2943
this.logger = logger;
3044
this.timestamp = timestamp;
45+
this.resultListenerBinder = new ResultListenerBinder();
3146
}
3247

3348
public synchronized void checkAccess() {
@@ -40,6 +55,11 @@ public synchronized void checkAccess() {
4055
serviceIntent.setPackage(GOOGLE_PLAY_PACKAGE);
4156
boolean isBind = mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
4257
logger.debug("LVL bindService result: " + isBind);
58+
59+
if (!isBind) {
60+
logger.error("LVL failed to bind licensing service");
61+
mCallback.onError(ERROR_BIND_FAILED);
62+
}
4363
}
4464

4565
public void onDestroy() {
@@ -48,53 +68,128 @@ public void onDestroy() {
4868
mContext.unbindService(mServiceConnection);
4969
mBound = false;
5070
}
71+
// Ensure binder reference is cleared even if unbind fails
72+
mServiceBinder = null;
5173
}
5274

5375
private final ServiceConnection mServiceConnection = new ServiceConnection() {
5476
@Override
5577
public void onServiceConnected(ComponentName name, IBinder service) {
5678
logger.debug("LVL service connected");
57-
mService = ILicensingService.Stub.asInterface(service);
79+
mServiceBinder = service;
5880
mBound = true;
5981
retryCount = 0;
82+
83+
// Detect binder death proactively
84+
try {
85+
mServiceBinder.linkToDeath(() -> {
86+
logger.error("LVL binder died unexpectedly");
87+
mCallback.onError(ERROR_BINDER_DIED);
88+
onDestroy();
89+
}, 0);
90+
} catch (RemoteException e) {
91+
logger.error("LVL failed to link binder death recipient", e);
92+
}
93+
6094
executeLicenseCheck();
6195
}
6296

6397
@Override
6498
public void onServiceDisconnected(ComponentName name) {
6599
logger.debug("LVL service disconnected");
66-
mService = null;
100+
mServiceBinder = null;
67101
mBound = false;
68102
}
69103
};
70104

71105
private void executeLicenseCheck() {
72106
try {
107+
if (mServiceBinder == null) {
108+
logger.error("LVL binder unavailable for license check");
109+
mCallback.onError(ERROR_NO_BINDER);
110+
return;
111+
}
112+
73113
String packageName = mContext.getPackageName();
74114
long nonce = generateNonce(timestamp);
75115
logger.debug("LVL generated nonce: " + nonce);
76116

77-
mService.checkLicense(nonce, packageName, new ResultListener());
117+
Parcel data = Parcel.obtain();
118+
try {
119+
data.writeInterfaceToken(LICENSING_SERVICE_DESCRIPTOR);
120+
data.writeLong(nonce);
121+
data.writeString(packageName);
122+
data.writeStrongBinder(resultListenerBinder);
123+
boolean transacted = mServiceBinder.transact(
124+
TRANSACTION_CHECK_LICENSE,
125+
data,
126+
null,
127+
IBinder.FLAG_ONEWAY
128+
);
129+
logger.debug("LVL binder transact sent (code " + TRANSACTION_CHECK_LICENSE + "): " + transacted);
130+
if (!transacted) {
131+
logger.error("LVL binder transact failed to enqueue");
132+
mCallback.onError(ERROR_TRANSACT_FAILED);
133+
}
134+
} finally {
135+
data.recycle();
136+
}
137+
} catch (RemoteException e) {
138+
logger.error("LVL remote exception during license check: ", e);
139+
mCallback.onError(ERROR_REMOTE_EXCEPTION);
78140
} catch (Exception e) {
79141
logger.error("LVL license check failed: ", e);
80-
mCallback.onError(-1);
142+
mCallback.onError(ERROR_GENERIC);
81143
}
82144
}
83145

84-
private class ResultListener extends com.android.vending.licensing.ILicenseResultListener.Stub {
146+
private class ResultListenerBinder extends Binder implements IInterface {
147+
ResultListenerBinder() {
148+
attachInterface(this, RESULT_LISTENER_DESCRIPTOR);
149+
}
150+
151+
@Override
152+
public IBinder asBinder() {
153+
return this;
154+
}
155+
85156
@Override
86-
public void verifyLicense(int responseCode, String signedData, String signature) throws RemoteException {
87-
logger.debug("LVL Received license response: " + responseCode);
88-
89-
LicenseResponseHandler handler = new LicenseResponseHandler(mCallback, logger, MAX_RETRIES);
90-
boolean shouldRetry = handler.handleResponse(responseCode, signedData, signature, retryCount);
91-
if (shouldRetry) {
92-
retryCount++;
93-
logger.debug("LVL retrying license check... Attempt: " + retryCount);
94-
executeLicenseCheck();
95-
} else {
96-
onDestroy();
157+
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
158+
if (code == IBinder.INTERFACE_TRANSACTION) {
159+
if (reply != null) {
160+
reply.writeString(RESULT_LISTENER_DESCRIPTOR);
161+
}
162+
return true;
97163
}
164+
165+
if (code == IBinder.FIRST_CALL_TRANSACTION) {
166+
try {
167+
data.enforceInterface(RESULT_LISTENER_DESCRIPTOR);
168+
int responseCode = data.readInt();
169+
String signedData = data.readString();
170+
String signature = data.readString();
171+
172+
logger.debug("LVL received license response: " + responseCode);
173+
174+
LicenseResponseHandler handler = new LicenseResponseHandler(mCallback, logger, MAX_RETRIES);
175+
boolean shouldRetry = handler.handleResponse(responseCode, signedData, signature, retryCount);
176+
if (shouldRetry) {
177+
retryCount++;
178+
logger.debug("LVL retrying license check... Attempt: " + retryCount);
179+
executeLicenseCheck();
180+
} else {
181+
onDestroy();
182+
}
183+
return true;
184+
} catch (Exception ex) {
185+
logger.error("LVL failed to process license response: ", ex);
186+
mCallback.onError(ERROR_GENERIC);
187+
onDestroy();
188+
return true;
189+
}
190+
}
191+
192+
return super.onTransact(code, data, reply, flags);
98193
}
99194
}
100195

@@ -111,5 +206,4 @@ public static long generateNonce(long timestamp) {
111206
// Shift timestamp to occupy bits 8–63, reserve LSB for flags/version
112207
return (reducedTimestamp << 8) | 0x01;
113208
}
114-
115209
}

Adjust/plugins/sdk-plugin-google-lvl/src/test/java/com/adjust/sdk/google/lvl/LicenseCheckerIntegrationTest.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
import android.content.ComponentName;
44
import android.content.Context;
55
import android.content.Intent;
6+
import android.os.Binder;
67
import android.os.IBinder;
78
import android.content.ServiceConnection;
9+
import android.os.Parcel;
810
import android.os.RemoteException;
911

1012
import com.adjust.sdk.ILogger;
11-
import com.adjust.sdk.google.lvl.LicenseChecker;
12-
import com.adjust.sdk.google.lvl.LicenseRawCallback;
13-
import com.android.vending.licensing.ILicenseResultListener;
14-
import com.android.vending.licensing.ILicensingService;
1513

1614
import org.junit.Before;
1715
import org.junit.Test;
@@ -43,10 +41,38 @@ public void setUp() {
4341
public void testSuccessfulLicenseCheck_shouldCallCallback() {
4442
doAnswer(invocation -> {
4543
ServiceConnection conn = invocation.getArgument(1);
46-
IBinder binder = new ILicensingService.Stub() {
44+
IBinder binder = new Binder() {
45+
private static final String SERVICE_DESCRIPTOR = "com.android.vending.licensing.ILicensingService";
46+
private static final String LISTENER_DESCRIPTOR = "com.android.vending.licensing.ILicenseResultListener";
47+
4748
@Override
48-
public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException {
49-
listener.verifyLicense(0, "signedData", "signature");
49+
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
50+
if (code == IBinder.INTERFACE_TRANSACTION) {
51+
if (reply != null) {
52+
reply.writeString(SERVICE_DESCRIPTOR);
53+
}
54+
return true;
55+
}
56+
57+
if (code == IBinder.FIRST_CALL_TRANSACTION) {
58+
data.enforceInterface(SERVICE_DESCRIPTOR);
59+
long nonce = data.readLong();
60+
String packageName = data.readString();
61+
IBinder listenerBinder = data.readStrongBinder();
62+
63+
Parcel response = Parcel.obtain();
64+
try {
65+
response.writeInterfaceToken(LISTENER_DESCRIPTOR);
66+
response.writeInt(0);
67+
response.writeString("signedData");
68+
response.writeString("signature");
69+
listenerBinder.transact(IBinder.FIRST_CALL_TRANSACTION, response, null, IBinder.FLAG_ONEWAY);
70+
} finally {
71+
response.recycle();
72+
}
73+
return true;
74+
}
75+
return super.onTransact(code, data, reply, flags);
5076
}
5177
};
5278
conn.onServiceConnected(new ComponentName("com.android.vending", "LicensingService"), binder);

Adjust/plugins/sdk-plugin-huawei-referrer/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies {
2727
// Add SDK via module.
2828
compileOnly project(':sdk-core')
2929
// Add SDK via Maven.
30-
// implementation 'com.adjust.sdk:adjust-android:5.4.5'
30+
// implementation 'com.adjust.sdk:adjust-android:5.4.6'
3131
}
3232

3333
// read local properties

Adjust/plugins/sdk-plugin-imei/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencies {
3030
// Add SDK via module.
3131
compileOnly project(':sdk-core')
3232
// Add SDK via Maven.
33-
// implementation 'com.adjust.sdk:adjust-android:5.4.5'
33+
// implementation 'com.adjust.sdk:adjust-android:5.4.6'
3434
}
3535

3636
// read local properties

Adjust/plugins/sdk-plugin-meta-referrer/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies {
2727
// Add SDK via module.
2828
compileOnly project(':sdk-core')
2929
// Add SDK via Maven.
30-
// implementation 'com.adjust.sdk:adjust-android:5.4.5'
30+
// implementation 'com.adjust.sdk:adjust-android:5.4.6'
3131
}
3232

3333
// read local properties

0 commit comments

Comments
 (0)