44import android .content .Context ;
55import android .content .Intent ;
66import android .content .ServiceConnection ;
7+ import android .os .Binder ;
78import android .os .IBinder ;
9+ import android .os .IInterface ;
10+ import android .os .Parcel ;
811import android .os .RemoteException ;
912
1013import com .adjust .sdk .ILogger ;
11- import com .android .vending .licensing .ILicensingService ;
1214
1315public 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}
0 commit comments