2525import android .content .ServiceConnection ;
2626import android .os .IBinder ;
2727import android .os .Looper ;
28+ import android .os .NetworkOnMainThreadException ;
2829import android .os .ParcelFileDescriptor ;
2930import android .os .RemoteException ;
3031import android .util .Log ;
3435import com .nextcloud .android .sso .aidl .IThreadListener ;
3536import com .nextcloud .android .sso .aidl .NextcloudRequest ;
3637import com .nextcloud .android .sso .aidl .ParcelFileDescriptorUtil ;
38+ import com .nextcloud .android .sso .exceptions .NextcloudApiNotRespondingException ;
3739import com .nextcloud .android .sso .helper .ExponentialBackoff ;
3840import com .nextcloud .android .sso .model .SingleSignOnAccount ;
3941
4951import java .io .ObjectOutputStream ;
5052import java .io .Reader ;
5153import java .lang .reflect .Type ;
54+ import java .util .concurrent .atomic .AtomicBoolean ;
5255
5356import io .reactivex .Observable ;
5457import io .reactivex .annotations .NonNull ;
5760
5861public class NextcloudAPI {
5962
63+ private static final String TAG = NextcloudAPI .class .getCanonicalName ();
64+
65+ private Gson gson ;
66+ private IInputStreamService mService = null ;
67+ private final AtomicBoolean mBound = new AtomicBoolean (false ); // Flag indicating whether we have called bind on the service
68+ private boolean mDestroyed = false ; // Flag indicating if API is destroyed
69+ private SingleSignOnAccount mAccount ;
70+ private ApiConnectedListener mCallback ;
71+ private Context mContext ;
72+
73+
74+
6075 public interface ApiConnectedListener {
6176 void onConnected ();
62-
6377 void onError (Exception ex );
6478 }
6579
@@ -72,17 +86,6 @@ public NextcloudAPI(Context context, SingleSignOnAccount account, Gson gson, Api
7286 connectApiWithBackoff ();
7387 }
7488
75- private static final String TAG = NextcloudAPI .class .getCanonicalName ();
76-
77- private Gson gson ;
78- private IInputStreamService mService = null ;
79- private boolean mBound = false ; // Flag indicating whether we have called bind on the service
80- private boolean mDestroyed = false ; // Flag indicating if API is destroyed
81- private SingleSignOnAccount mAccount ;
82- private ApiConnectedListener mCallback ;
83- private Context mContext ;
84-
85-
8689 private String getAccountName () {
8790 return mAccount .name ;
8891 }
@@ -106,7 +109,7 @@ private void connect() {
106109 }
107110
108111 // Disconnect if connected
109- if (mBound ) {
112+ if (mBound . get () ) {
110113 stop ();
111114 }
112115
@@ -132,13 +135,13 @@ public void stop() {
132135 mCallback = null ;
133136
134137 // Unbind from the service
135- if (mBound ) {
138+ if (mBound . get () ) {
136139 if (mContext != null ) {
137140 mContext .unbindService (mConnection );
138141 } else {
139142 Log .e (TAG , "Context was null, cannot unbind nextcloud single sign-on service connection!" );
140143 }
141- mBound = false ;
144+ mBound . set ( false ) ;
142145 mContext = null ;
143146 }
144147 }
@@ -152,7 +155,10 @@ public void onServiceConnected(ComponentName className, IBinder service) {
152155 Log .i (TAG , "Nextcloud Single sign-on: onServiceConnected" );
153156
154157 mService = IInputStreamService .Stub .asInterface (service );
155- mBound = true ;
158+ mBound .set (true );
159+ synchronized (mBound ) {
160+ mBound .notifyAll ();
161+ }
156162 mCallback .onConnected ();
157163 }
158164
@@ -161,14 +167,32 @@ public void onServiceDisconnected(ComponentName className) {
161167 // This is called when the connection with the service has been
162168 // unexpectedly disconnected -- that is, its process crashed.
163169 mService = null ;
164- mBound = false ;
170+ mBound . set ( false ) ;
165171
166172 if (!mDestroyed ) {
167173 connectApiWithBackoff ();
168174 }
169175 }
170176 };
171177
178+ private void waitForApi () throws NextcloudApiNotRespondingException {
179+ synchronized (mBound ) {
180+ // If service is not bound yet.. wait
181+ if (!mBound .get ()) {
182+ Log .v (TAG , "[waitForApi] - api not ready yet.. waiting [" + Thread .currentThread ().getName () + "]" );
183+ try {
184+ mBound .wait (10000 ); // wait up to 10 seconds
185+
186+ // If api is still not bound after 10 seconds.. throw an exception
187+ if (!mBound .get ()) {
188+ throw new NextcloudApiNotRespondingException ();
189+ }
190+ } catch (InterruptedException ex ) {
191+ Log .e (TAG , "WaitForAPI failed" , ex );
192+ }
193+ }
194+ }
195+ }
172196
173197 public <T > Observable <T > performRequestObservable (final Type type , final NextcloudRequest request ) {
174198 return Observable .fromPublisher (new Publisher <T >() {
@@ -249,7 +273,16 @@ public InputStream performNetworkRequest(NextcloudRequest request) throws Except
249273 * @throws IOException
250274 */
251275 private ParcelFileDescriptor performAidlNetworkRequest (NextcloudRequest request )
252- throws IOException , RemoteException {
276+ throws IOException , RemoteException , NextcloudApiNotRespondingException {
277+
278+ // Check if we are on the main thread
279+ if (Looper .myLooper () == Looper .getMainLooper ()) {
280+ throw new NetworkOnMainThreadException ();
281+ }
282+
283+ // Wait for api to be initialized
284+ waitForApi ();
285+
253286 // Log.d(TAG, request.url);
254287 request .setAccountName (getAccountName ());
255288 request .setToken (getAccountToken ());
0 commit comments