diff --git a/Adjust/build.gradle b/Adjust/build.gradle index 4ecb4962c..810887ec6 100644 --- a/Adjust/build.gradle +++ b/Adjust/build.gradle @@ -9,7 +9,7 @@ ext { coreMinSdkVersion = 21 coreCompileSdkVersion = 36 coreTargetSdkVersion = 36 - coreVersionName = '5.4.5' + coreVersionName = '5.4.6' defaultVersionCode = 1 webbridgeMinSdkVersion = 21 samsungReferrerMinSdkVersion = 21 diff --git a/Adjust/examples/example-app-kotlin/build.gradle b/Adjust/examples/example-app-kotlin/build.gradle index dc4296b8d..985d785bf 100644 --- a/Adjust/examples/example-app-kotlin/build.gradle +++ b/Adjust/examples/example-app-kotlin/build.gradle @@ -40,5 +40,6 @@ dependencies { implementation 'com.google.android.gms:play-services-appset:16.1.0' implementation project(':sdk-core') + implementation 'com.adjust.signature:adjust-android-signature:3.61.0' } diff --git a/Adjust/plugins/sdk-plugin-google-lvl/proguard-rules.pro b/Adjust/plugins/sdk-plugin-google-lvl/proguard-rules.pro index c3806c93a..4a9685c4a 100644 --- a/Adjust/plugins/sdk-plugin-google-lvl/proguard-rules.pro +++ b/Adjust/plugins/sdk-plugin-google-lvl/proguard-rules.pro @@ -23,11 +23,7 @@ # Keep all classes in the LVL licensing AIDL package -keep class com.android.vending.licensing.** { *; } -# Keep interface method signatures --keep interface com.android.vending.licensing.ILicensingService --keep interface com.android.vending.licensing.ILicenseResultListener - # Prevent obfuscation of the stub classes used for IPC -keepclassmembers class * implements android.os.IInterface { ; -} \ No newline at end of file +} diff --git a/Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicenseResultListener.aidl b/Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicenseResultListener.aidl deleted file mode 100644 index 869cb16f6..000000000 --- a/Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicenseResultListener.aidl +++ /dev/null @@ -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); -} diff --git a/Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicensingService.aidl b/Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicensingService.aidl deleted file mode 100644 index 9541a2090..000000000 --- a/Adjust/plugins/sdk-plugin-google-lvl/src/main/aidl/com/android/vending/licensing/ILicensingService.aidl +++ /dev/null @@ -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); -} diff --git a/Adjust/plugins/sdk-plugin-google-lvl/src/main/java/com/adjust/sdk/google/lvl/LicenseChecker.java b/Adjust/plugins/sdk-plugin-google-lvl/src/main/java/com/adjust/sdk/google/lvl/LicenseChecker.java index 72dc9aee6..ed645943a 100644 --- a/Adjust/plugins/sdk-plugin-google-lvl/src/main/java/com/adjust/sdk/google/lvl/LicenseChecker.java +++ b/Adjust/plugins/sdk-plugin-google-lvl/src/main/java/com/adjust/sdk/google/lvl/LicenseChecker.java @@ -4,21 +4,35 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; import android.os.RemoteException; import com.adjust.sdk.ILogger; -import com.android.vending.licensing.ILicensingService; public class LicenseChecker { private static final String GOOGLE_PLAY_PACKAGE = "com.android.vending"; + private static final String LICENSING_SERVICE_DESCRIPTOR = "com.android.vending.licensing.ILicensingService"; + private static final String RESULT_LISTENER_DESCRIPTOR = "com.android.vending.licensing.ILicenseResultListener"; + private static final int TRANSACTION_CHECK_LICENSE = IBinder.FIRST_CALL_TRANSACTION; + + // Error codes for better diagnostics + private static final int ERROR_GENERIC = -1; + private static final int ERROR_BIND_FAILED = -5; + private static final int ERROR_NO_BINDER = -3; + private static final int ERROR_TRANSACT_FAILED = -2; + private static final int ERROR_REMOTE_EXCEPTION = -4; + private static final int ERROR_BINDER_DIED = -6; private final Context mContext; private final LicenseRawCallback mCallback; private final ILogger logger; private final long timestamp; + private final ResultListenerBinder resultListenerBinder; - private ILicensingService mService; + private IBinder mServiceBinder; private boolean mBound; private int retryCount = 0; private static final int MAX_RETRIES = 3; @@ -28,6 +42,7 @@ public LicenseChecker(Context context, LicenseRawCallback callback, ILogger logg this.mCallback = callback; this.logger = logger; this.timestamp = timestamp; + this.resultListenerBinder = new ResultListenerBinder(); } public synchronized void checkAccess() { @@ -40,6 +55,11 @@ public synchronized void checkAccess() { serviceIntent.setPackage(GOOGLE_PLAY_PACKAGE); boolean isBind = mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); logger.debug("LVL bindService result: " + isBind); + + if (!isBind) { + logger.error("LVL failed to bind licensing service"); + mCallback.onError(ERROR_BIND_FAILED); + } } public void onDestroy() { @@ -48,53 +68,128 @@ public void onDestroy() { mContext.unbindService(mServiceConnection); mBound = false; } + // Ensure binder reference is cleared even if unbind fails + mServiceBinder = null; } private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { logger.debug("LVL service connected"); - mService = ILicensingService.Stub.asInterface(service); + mServiceBinder = service; mBound = true; retryCount = 0; + + // Detect binder death proactively + try { + mServiceBinder.linkToDeath(() -> { + logger.error("LVL binder died unexpectedly"); + mCallback.onError(ERROR_BINDER_DIED); + onDestroy(); + }, 0); + } catch (RemoteException e) { + logger.error("LVL failed to link binder death recipient", e); + } + executeLicenseCheck(); } @Override public void onServiceDisconnected(ComponentName name) { logger.debug("LVL service disconnected"); - mService = null; + mServiceBinder = null; mBound = false; } }; private void executeLicenseCheck() { try { + if (mServiceBinder == null) { + logger.error("LVL binder unavailable for license check"); + mCallback.onError(ERROR_NO_BINDER); + return; + } + String packageName = mContext.getPackageName(); long nonce = generateNonce(timestamp); logger.debug("LVL generated nonce: " + nonce); - mService.checkLicense(nonce, packageName, new ResultListener()); + Parcel data = Parcel.obtain(); + try { + data.writeInterfaceToken(LICENSING_SERVICE_DESCRIPTOR); + data.writeLong(nonce); + data.writeString(packageName); + data.writeStrongBinder(resultListenerBinder); + boolean transacted = mServiceBinder.transact( + TRANSACTION_CHECK_LICENSE, + data, + null, + IBinder.FLAG_ONEWAY + ); + logger.debug("LVL binder transact sent (code " + TRANSACTION_CHECK_LICENSE + "): " + transacted); + if (!transacted) { + logger.error("LVL binder transact failed to enqueue"); + mCallback.onError(ERROR_TRANSACT_FAILED); + } + } finally { + data.recycle(); + } + } catch (RemoteException e) { + logger.error("LVL remote exception during license check: ", e); + mCallback.onError(ERROR_REMOTE_EXCEPTION); } catch (Exception e) { logger.error("LVL license check failed: ", e); - mCallback.onError(-1); + mCallback.onError(ERROR_GENERIC); } } - private class ResultListener extends com.android.vending.licensing.ILicenseResultListener.Stub { + private class ResultListenerBinder extends Binder implements IInterface { + ResultListenerBinder() { + attachInterface(this, RESULT_LISTENER_DESCRIPTOR); + } + + @Override + public IBinder asBinder() { + return this; + } + @Override - public void verifyLicense(int responseCode, String signedData, String signature) throws RemoteException { - logger.debug("LVL Received license response: " + responseCode); - - LicenseResponseHandler handler = new LicenseResponseHandler(mCallback, logger, MAX_RETRIES); - boolean shouldRetry = handler.handleResponse(responseCode, signedData, signature, retryCount); - if (shouldRetry) { - retryCount++; - logger.debug("LVL retrying license check... Attempt: " + retryCount); - executeLicenseCheck(); - } else { - onDestroy(); + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + if (code == IBinder.INTERFACE_TRANSACTION) { + if (reply != null) { + reply.writeString(RESULT_LISTENER_DESCRIPTOR); + } + return true; } + + if (code == IBinder.FIRST_CALL_TRANSACTION) { + try { + data.enforceInterface(RESULT_LISTENER_DESCRIPTOR); + int responseCode = data.readInt(); + String signedData = data.readString(); + String signature = data.readString(); + + logger.debug("LVL received license response: " + responseCode); + + LicenseResponseHandler handler = new LicenseResponseHandler(mCallback, logger, MAX_RETRIES); + boolean shouldRetry = handler.handleResponse(responseCode, signedData, signature, retryCount); + if (shouldRetry) { + retryCount++; + logger.debug("LVL retrying license check... Attempt: " + retryCount); + executeLicenseCheck(); + } else { + onDestroy(); + } + return true; + } catch (Exception ex) { + logger.error("LVL failed to process license response: ", ex); + mCallback.onError(ERROR_GENERIC); + onDestroy(); + return true; + } + } + + return super.onTransact(code, data, reply, flags); } } @@ -111,5 +206,4 @@ public static long generateNonce(long timestamp) { // Shift timestamp to occupy bits 8–63, reserve LSB for flags/version return (reducedTimestamp << 8) | 0x01; } - } diff --git a/Adjust/plugins/sdk-plugin-google-lvl/src/test/java/com/adjust/sdk/google/lvl/LicenseCheckerIntegrationTest.java b/Adjust/plugins/sdk-plugin-google-lvl/src/test/java/com/adjust/sdk/google/lvl/LicenseCheckerIntegrationTest.java index 4e44e6fa3..41c1ad61b 100644 --- a/Adjust/plugins/sdk-plugin-google-lvl/src/test/java/com/adjust/sdk/google/lvl/LicenseCheckerIntegrationTest.java +++ b/Adjust/plugins/sdk-plugin-google-lvl/src/test/java/com/adjust/sdk/google/lvl/LicenseCheckerIntegrationTest.java @@ -3,15 +3,13 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Binder; import android.os.IBinder; import android.content.ServiceConnection; +import android.os.Parcel; import android.os.RemoteException; import com.adjust.sdk.ILogger; -import com.adjust.sdk.google.lvl.LicenseChecker; -import com.adjust.sdk.google.lvl.LicenseRawCallback; -import com.android.vending.licensing.ILicenseResultListener; -import com.android.vending.licensing.ILicensingService; import org.junit.Before; import org.junit.Test; @@ -43,10 +41,38 @@ public void setUp() { public void testSuccessfulLicenseCheck_shouldCallCallback() { doAnswer(invocation -> { ServiceConnection conn = invocation.getArgument(1); - IBinder binder = new ILicensingService.Stub() { + IBinder binder = new Binder() { + private static final String SERVICE_DESCRIPTOR = "com.android.vending.licensing.ILicensingService"; + private static final String LISTENER_DESCRIPTOR = "com.android.vending.licensing.ILicenseResultListener"; + @Override - public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { - listener.verifyLicense(0, "signedData", "signature"); + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + if (code == IBinder.INTERFACE_TRANSACTION) { + if (reply != null) { + reply.writeString(SERVICE_DESCRIPTOR); + } + return true; + } + + if (code == IBinder.FIRST_CALL_TRANSACTION) { + data.enforceInterface(SERVICE_DESCRIPTOR); + long nonce = data.readLong(); + String packageName = data.readString(); + IBinder listenerBinder = data.readStrongBinder(); + + Parcel response = Parcel.obtain(); + try { + response.writeInterfaceToken(LISTENER_DESCRIPTOR); + response.writeInt(0); + response.writeString("signedData"); + response.writeString("signature"); + listenerBinder.transact(IBinder.FIRST_CALL_TRANSACTION, response, null, IBinder.FLAG_ONEWAY); + } finally { + response.recycle(); + } + return true; + } + return super.onTransact(code, data, reply, flags); } }; conn.onServiceConnected(new ComponentName("com.android.vending", "LicensingService"), binder); diff --git a/Adjust/plugins/sdk-plugin-huawei-referrer/build.gradle b/Adjust/plugins/sdk-plugin-huawei-referrer/build.gradle index 85cd9fdaa..5a9787c15 100644 --- a/Adjust/plugins/sdk-plugin-huawei-referrer/build.gradle +++ b/Adjust/plugins/sdk-plugin-huawei-referrer/build.gradle @@ -27,7 +27,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' } // read local properties diff --git a/Adjust/plugins/sdk-plugin-imei/build.gradle b/Adjust/plugins/sdk-plugin-imei/build.gradle index af352b96a..c995d8ad0 100644 --- a/Adjust/plugins/sdk-plugin-imei/build.gradle +++ b/Adjust/plugins/sdk-plugin-imei/build.gradle @@ -30,7 +30,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' } // read local properties diff --git a/Adjust/plugins/sdk-plugin-meta-referrer/build.gradle b/Adjust/plugins/sdk-plugin-meta-referrer/build.gradle index 7ad438ec4..fa26fd195 100644 --- a/Adjust/plugins/sdk-plugin-meta-referrer/build.gradle +++ b/Adjust/plugins/sdk-plugin-meta-referrer/build.gradle @@ -27,7 +27,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' } // read local properties diff --git a/Adjust/plugins/sdk-plugin-oaid/build.gradle b/Adjust/plugins/sdk-plugin-oaid/build.gradle index f41b41071..3cebb2859 100644 --- a/Adjust/plugins/sdk-plugin-oaid/build.gradle +++ b/Adjust/plugins/sdk-plugin-oaid/build.gradle @@ -33,7 +33,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' implementation 'com.huawei.hms:ads-identifier:3.4.56.300' } diff --git a/Adjust/plugins/sdk-plugin-samsung-clouddev/build.gradle b/Adjust/plugins/sdk-plugin-samsung-clouddev/build.gradle index 684701bdf..dc31bc5da 100644 --- a/Adjust/plugins/sdk-plugin-samsung-clouddev/build.gradle +++ b/Adjust/plugins/sdk-plugin-samsung-clouddev/build.gradle @@ -27,7 +27,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' // Add Samsung clouddev lib. compileOnly fileTree(include: ['*.aar'], dir: 'libs') diff --git a/Adjust/plugins/sdk-plugin-samsung-referrer/build.gradle b/Adjust/plugins/sdk-plugin-samsung-referrer/build.gradle index 466f5f4f7..9bc5fff8a 100644 --- a/Adjust/plugins/sdk-plugin-samsung-referrer/build.gradle +++ b/Adjust/plugins/sdk-plugin-samsung-referrer/build.gradle @@ -27,7 +27,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' // Add Samsung referrer lib via Maven. implementation 'store.galaxy.samsung.installreferrer:samsung_galaxystore_install_referrer:3.0.1' diff --git a/Adjust/plugins/sdk-plugin-vivo-referrer/build.gradle b/Adjust/plugins/sdk-plugin-vivo-referrer/build.gradle index 702445ffc..d8b77c964 100644 --- a/Adjust/plugins/sdk-plugin-vivo-referrer/build.gradle +++ b/Adjust/plugins/sdk-plugin-vivo-referrer/build.gradle @@ -30,7 +30,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' } // read local properties diff --git a/Adjust/plugins/sdk-plugin-webbridge/build.gradle b/Adjust/plugins/sdk-plugin-webbridge/build.gradle index 560ad213d..aef9f8bb9 100644 --- a/Adjust/plugins/sdk-plugin-webbridge/build.gradle +++ b/Adjust/plugins/sdk-plugin-webbridge/build.gradle @@ -30,7 +30,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' } // read local properties diff --git a/Adjust/plugins/sdk-plugin-webbridge/src/main/assets/adjust.js b/Adjust/plugins/sdk-plugin-webbridge/src/main/assets/adjust.js index 3fc88263d..98e42989e 100644 --- a/Adjust/plugins/sdk-plugin-webbridge/src/main/assets/adjust.js +++ b/Adjust/plugins/sdk-plugin-webbridge/src/main/assets/adjust.js @@ -295,7 +295,7 @@ var Adjust = { if (this.adjustConfig) { return this.adjustConfig.getSdkPrefix(); } else { - return 'web-bridge5.4.5'; + return 'web-bridge5.4.6'; } }, diff --git a/Adjust/plugins/sdk-plugin-xiaomi-referrer/build.gradle b/Adjust/plugins/sdk-plugin-xiaomi-referrer/build.gradle index 556b6658e..0290bdc30 100644 --- a/Adjust/plugins/sdk-plugin-xiaomi-referrer/build.gradle +++ b/Adjust/plugins/sdk-plugin-xiaomi-referrer/build.gradle @@ -31,7 +31,7 @@ dependencies { // Add SDK via module. compileOnly project(':sdk-core') // Add SDK via Maven. - // implementation 'com.adjust.sdk:adjust-android:5.4.5' + // implementation 'com.adjust.sdk:adjust-android:5.4.6' // Add xiaomi referrer lib via Maven. implementation 'com.miui.referrer:homereferrer:1.0.0.6' diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java index 068504c53..4ad955b45 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java @@ -32,7 +32,7 @@ private Adjust() { */ public static synchronized AdjustInstance getDefaultInstance() { @SuppressWarnings("unused") - String VERSION = "!SDK-VERSION-STRING!:com.adjust.sdk:adjust-android:5.4.5"; + String VERSION = "!SDK-VERSION-STRING!:com.adjust.sdk:adjust-android:5.4.6"; if (defaultInstance == null) { defaultInstance = new AdjustInstance(); diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/Constants.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/Constants.java index f8428393b..52e1f5e66 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/Constants.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/Constants.java @@ -38,7 +38,7 @@ public interface Constants { String SCHEME = "https"; String AUTHORITY = "app.adjust.com"; - String CLIENT_SDK = "android5.4.5"; + String CLIENT_SDK = "android5.4.6"; String LOGTAG = "Adjust"; String REFTAG = "reftag"; String INSTALL_REFERRER = "install_referrer"; diff --git a/CHANGELOG.md b/CHANGELOG.md index d7c0fdf65..62b79e237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### Version 5.4.6 (7th November 2025) +#### Changed +- Refactored LVL plugin to use direct `Binder` interface instead of `AIDL`. + +--- + ### Version 5.4.5 (23rd October 2025) #### Changed - Updated the Adjust Signature library version to 3.61.0. diff --git a/VERSION b/VERSION index 8ce222e90..bb0915fd5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.4.5 +5.4.6