Why do I get " I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4 " message? - android

I am writing an android application using Kotlin ,Retrofit ,OKHttp and Rxjava when make a call to my API in order to add a new user to database i get the following message in logcat:
I/system_server: oneway function results will be dropped but finished with status OK and parcel size 4
The problem is that nothing happens no user is added in fact the API does not receive any requests from my app even thought i have the appropriate permissions and a android:usesCleartextTraffic="true" annotation in my AndroidManifest.xml.
By putting println() in various places in the code I was able to confirm that the function responsible for making the call is in fact executed but code responsible for handling response is never called and no error is reported.
Here is the code of above mentioned function:
private fun addUser(user: UserDataObject) {
val client = OkHttpClient.Builder()
.addInterceptor(BasicAuthInterceptor("admin", "admin")) // temporarily hardcoded
.build()
val requestInterface = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.client(client)
.build().create(GetData::class.java)
myCompositeDisposable?.add(
requestInterface.addUser(user)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::onError)
)
}
and the code of the interface:
interface GetData {
#POST("/api/some/API/URL")
fun addUser(#Body user: UserDataClass) : Completable}
also ( just in case ) the code of interceptor:
class BasicAuthInterceptor(username: String, password: String): Interceptor {
private var credentials: String = Credentials.basic(username, password)
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", credentials).build()
return chain.proceed(request)
}
}
Can you please tell me why is my app not able to communicate with API ?
While searching for answer on the internet i found this question so I gather that it has something to do with selinux permissions but this doesn't help me at all since I'm just a beginner.

Related

Kotlin Android oauth2 token request only returning errors

I am working on an user app for a local charitable organization, and need to access their API. The API is from wild apricot, and this is the documentation for making a token request:
Authentication tokens are obtained from Wild Apricot's authentication service, which is located at https://oauth.wildapricot.org. This service adheres to oAuth 2.0.
This is the access option I need to implement:
-----------------In order to obtain access token with API key, you have to make the following request:
POST /auth/token HTTP/1.1
Host: oauth.wildapricot.org
Authorization: Basic BASE64_ENCODED("APIKEY:YOUR_API_KEY")
Content-type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=auto
-------------------------------So. finally your request will look like:
POST /auth/token HTTP/1.1
Host: oauth.wildapricot.org
Authorization: Basic QVBJS0VZOm85c2U4N3Jnb2l5c29lcjk4MDcwOS0=
Content-type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=auto
I am attempting to make this call with retrofit2, and an okhttp3 interceptor, and getting a bad request response (I am very much new and learning, and have not been able to get anything other response than a 400 bad request (when I use "/auth/token" as the endpoint), or a 404 not found (when I use "/auth/token HTTP/1.1" as the endpoint). If someone could tell me where exactly I am messing this up It would be greatly appreciated, the code I have tried is below.
Interface:
interface WAApiCall {
#POST("auth/token")
fun callPost(#Body body:String ): Call<AuthToken>
}
Call Service:
object WAApiCallService {
private const val API_KEY = "xxxxxxxxIxHavexAxValidxKeyxxxx"
private const val BASE_URL = "https://oauth.wildapricot.org/"
private val AUTH = "Basic" + Base64.encodeToString(API_KEY.toByteArray(), Base64.NO_WRAP)
private const val CONTENT_TYPE = "application/x-www-form-urlencoded"
private var api:WAApiCall? = null
private fun getWAApi(context: Context) : WAApiCall {
if(api==null){
val OkHttpClient = OkHttpClient.Builder()
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BASIC
OkHttpClient.addInterceptor{chain ->
val request = chain.request()
Log.d("CALL", request.body.toString())
val newRequest = request.newBuilder()
.addHeader("Host", "oauth.wildapricot.org")
.addHeader("Authorization", AUTH )
.addHeader("Content-type", CONTENT_TYPE)
.method(request.method, request.body)
.build()
chain.proceed(newRequest)
}
api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient.build())
.build()
.create(WAApiCall::class.java)
}
return api!!
}
fun call(context: Context) =
getWAApi(context)
}
Function in Main Activity to make the call:
fun testRequest(){
val call = WAApiCallService.call(this)
call.callPost("grant_type=client_credentials&scope=auto")
.enqueue(object: Callback<AuthToken>{
override fun onFailure(call: Call<AuthToken>, t: Throwable) {
Log.i("FAILURE", t.localizedMessage)
}
override fun onResponse(call: Call<AuthToken>, response: Response<AuthToken>) {
Log.i("SUCCESS", "TOKEN = ${response.body().toString()}")
Log.i("SUCCESS", "${response}")
val token = response.body()?.accessToken
Log.i("SUCCESS", "TOKEN = $token")
}
})
}
Error message:
I/SUCCESS: TOKEN = null
I/SUCCESS: Response{protocol=http/1.1, code=400, message=Bad Request, url=https://oauth.wildapricot.org/auth/token}
I think that I am just not understanding how to implement this type of request in some basic way, I could not get it to work in Postman either. I understand that I need to send the credentials to the authentication server, and receive an access token, that will expire and need to be refreshed, and that It will be included in each actual API endpoint call, I guess I'm just missing something crucial in the most important step of that process (getting the actual token, I am imagining it is a simple, forehead slapping kind of misunderstanding on my part?). The wild apricot API is on swagger hub, and I am able to gain access through that UI, with my API key, and see the responses, so I know that it is valid.
Your client credentials request looks mostly all good. The only thing I can see that looks wrong is no space character in the AUTH header between 'Basic' and the encoded credential.
If that doesn't work, could you trace the HTTP request and verify that you are sending the message you think you are.
Thank you for that observation, it led me to figuring out what ultimately was wrong in my initial attempt. After adding that space, I traced the request and found that It was actually sending two headers for content type.
The fix for that was to set the header in the retrofit call from the interface:
interface WAApiCall {
#POST("auth/token")
fun callPost(#Body Body: okhttp3.RequestBody, #Header("Content-type") type: String): Call<AuthToken>
}
As you can see the body is also slightly different, the call was getting through but was returning:
"unsupported_grant_type".
I was passing a raw string as the body parameter, which was including the quotation marks in the request. The solution there was to pass the okhttp3.Request body type rather than a raw string, in the function that makes the actual call it looks like this:
val body: "grant_type=client_credentials&scope=auto&obtain_refresh_token=true"
val requestBody = RequestBody.create("text/plain".toMediaTypeOrNull(),body)
val call = WAApiCallService.call(this)
call.callPost(requestBody,"application/x-www-form-urlencoded")
.enqueue(object: Callback<AuthToken>{
With those changes the call succeeds and my long running headache is over.

Unable to successfully send a POST request using Retrofit 2.6.1 - Problems with JSON coverter

I am using the new Retrofit2 with suspending coroutines, and with GET requests everything works fine.
But I now have to implement a POST request, and just can't get it to work
I have a CURL example that looks like this:
curl -X POST -H "Content-Type: application/json;charsets: utf-8" -d '{"tx_guapptokenlist_tokenitem":{"tokenchar":"my-token-string","platform":"android"}}' https://www.example-url.com/tokens?type=56427890283537921
This works fine, and returns this response: {"errors":false,"success":true}%
So here's what my request looks like in my Api class right now:
#Headers( "Content-Type: application/json" )
#POST("/tokens?type=56427890283537921")
suspend fun sendFirebaseToken(#Body tokenRequest: RequestBody) : Call<TokenResponse>
This is my TokenResponse class:
#JsonClass(generateAdapter = true)
data class TokenResponse(
#Json(name="errors")
val errors: Boolean,
#Json(name="success")
val success: Boolean)
and the ApiClient class I'm using:
object ApiClient {
private const val BASE_URL = "https://myExampleUrl.com"
private var retrofit: Retrofit? = null
var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val client: Retrofit?
get() {
if (retrofit == null) {
retrofit = Retrofit.Builder().baseUrl(
BASE_URL
).client(getOkHttpClient())
.addConverterFactory(MoshiConverterFactory.create())
.build()
}
return retrofit
}
fun getOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(getLoggingInterceptor())
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS).writeTimeout(90, TimeUnit.SECONDS).build()
}
private fun getLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().setLevel(
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.HEADERS
else HttpLoggingInterceptor.Level.NONE
)
}
}
The first odd thing I noticed: Even with the #POST annotation, if my suspend fun has no return type, I get no error, but okhttp will always send a GET request (at least the endpoint always receives a GET). Not sure if that is supposed to be like that?
Anyway: I need the return values, so I'm returning Call<TokenResponse>.
This leads me to my main problem, that I can't solve: If now I execute my code, it crashes with this log:
java.lang.IllegalArgumentException: Unable to create converter for retrofit2.Call<myapp.communication.TokenResponse>
for method TokenApi.sendToken
at retrofit2.Utils.methodError(Utils.java:52)
To try and deal with this I have used moshi-kotlin-codegen to generate the proper adapter (hence the annotations in the data class), but to no avail. The class is generated, but not used. I have tried to pass a Moshi with JsonAdapterFactory like this var moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()to my ConverterFactory but that doesn't work either.
Tried to add the generated adapter maually to moshi but that also did not work.
I've also tried returning different types in my request. The Retrofit docs state that without a converter one could only return a ResponseBody, but same result: Retrofit complains it has no converter. The same for returning Call<Void>
I feel like I'm missing something here? Who can help? Happy to provide more details, please request what's needed.
Your request function should look like this.
#Headers( "Content-Type: application/json" )
#POST("/tokens?type=56427890283537921")
suspend fun sendFirebaseToken(#Body tokenRequest: RequestBody): TokenResponse
You don't use Call<...> since you have marked it as suspend.
Think the annotation should be:
#JsonClass(generateAdapter = true)
data class TokenResponse(
#field:Json(name = "errors") val errors: Integer,
#field:Json(name = "success") val success: Boolean
)
And try to remove the suspend keyword once, which might clash with generateAdapter = true.
I've got it working now, this is what I learned:
First of all: #Dominic Fischer here is right, Call is wrong, and with everything set up correctly, there is no need to wrap the result object at all (I noticed by the way the #Headers annotation looks to be not necessary, Retrofit seems to just take care of it).
The second and biggest problem is that the client object in my ApiClient class was not used correctly. See the new version:
fun getRetrofitService(): ApiService {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(getOkHttpClient())
.addConverterFactory(MoshiConverterFactory.create())
.build().create(ApiService::class.java)
}
See that now the 'create()' step is added, which before I handled outside of this class. There I used my Retrofit object to create the service just like here, but I accidentally passed ApiClient::class.java. Interestingly that compiles and runs just fine, but of course this must mess up somewhere - it's unable to properly build the JSON adapters.
As a result I pulled this step into my ApiClientin order to prevent such accidents in the future.
If anybody has suggestions as to meking this question + answer more useful for future readers, please let me know!

How to stop Okhttp Authenticator from being called multiple times?

I have an Authenticator like this
#Singleton
class TokenAutheticator #Inject constructor(private val tokenHolder: Lazy<TokenHolder>,private val tokenInterceptor: TokenInterceptor):Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
val resp = tokenHolder.get().tokenService.relogin(tokenInterceptor.token).execute()
println("### res "+resp.code())
if (resp.code()==200) {
val body = tokenHolder.get().tokenService.relogin(tokenInterceptor.token).execute()?.body()
val newToken = body?.token
println("########## authenticator ########## ")
val url = route?.address()?.url()?.newBuilder()?.addQueryParameter("token", newToken)?.build()
return response.request().newBuilder().url(url).build()
}else{
return null
}
}
}
When the resp.code != 200 the Authenticator is called multiple times.
I am plugging it in Okhttp like this
#Provides
#Singleton
fun provideOkhttp(tokenInterceptor: TokenInterceptor, tokenAutheticator: TokenAutheticator): OkHttpClient {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
.authenticator(tokenAutheticator)
.addInterceptor(logging)
.addInterceptor(tokenInterceptor)
.build()
return client
}
So what I want to do is have the Authenticator try it only once and if it is able to get a new token then use the new token from now on and if it can't get a new token then exit. But the Authenticator is called multiple times and the API responds with Too many attempts. Any help is appreciated. Thanks
Only when you return null, Authenticator will stop getting called (stop retrying authentication).
Since you always return something, when response code is 200, it will get called a few times. Luckily, as far I understand, Authenticator detects your endless loop, and breaks out of it eventually.
Basically, you should use Authenticator to react to 401 (and similar) response code and only add authorization header in that case.

Error in posting data to Rest API server with auth Token in android

I am trying to post data to REST API server with retrofit + RxJava . When I am trying to send data to server , it said " HTTP 500 Internal Server Error Occurred". But when the data is send with POSTMAN, it succeeded.
This is the function for sending data in Model.
// Encountering with 500 server error
fun postSchedule(data : ScheduleResponse , errorLD: MutableLiveData<String>){
Log.d("POST DATA", "${data.title} ${data.remindMeAt}" )
userClient.postScheduleItem(data)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(object : io.reactivex.Observer<ServerResponse>{
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: ServerResponse) {
errorLD.value = t.status
}
override fun onError(e: Throwable) {
errorLD.value = e.message
}
})
}
This is my API interface
#Headers("Accept: application/json")
#POST("schedules")
fun postScheduleItem(#Body data: ScheduleResponse): Observable<ServerResponse>
This is the retrofit client.
val httpLoggingInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
val httpClient = OkHttpClient.Builder()
var dbInstance: TodoDB = TodoDB.getInstance(context)
var rxJavaAdapter = RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())
val retrofitBuilder =
Retrofit.Builder()
.baseUrl(AppConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(rxJavaAdapter)
fun <T> createService(serviceClass: Class<T>, authToken: String?): T {
if (!TextUtils.isEmpty(authToken)) {
val interceptor = AuthenticationInterceptor(authToken!!)
if (!httpClient.interceptors().contains(interceptor)) {
httpClient.addInterceptor(interceptor)
retrofitBuilder.client(httpClient.build())
}
}
return retrofitBuilder.build().create(serviceClass)
}
Please help me with this.Thank you.
Client side code is not enough to determine what causes the server to respond with 500. The best you can do is start debugging the issue.
There are several directions you can go:
If you have access to the server or know someone who does, you could debug the server and determine what causes the Internal server error. Maybe the server logs can help as well and you don't have to actually step through the server code.
If you don't have access to the server, you could look at the body of the server response. Maybe there's a detailed error description there in html, json or some other format that will help you find out the root cause.
If the above steps don't help then it's very useful that you know the request works with POSTMAN. You can compare the exact POSTMAN request with the exact Retrofit request, header by header,
line by line. To do that, you should first add your httpLoggingInterceptor to your okhttp client builder with
val httpClient = OkHttpClient.Builder().addNetworkInterceptor(httpLoggingInterceptor)
and look for the request log in logcat.
If you spot the differences between the working and the not working requests, then you should work your way through all the differences, and adjust the retrofit request by adding or modifying headers using okhttp interceptors so that, at the end, the retrofit request looks exactly the same as the POSTMAN request. I suggest you remove the AuthenticationInterceptor at first and simulate it "manually" with a custom interceptor and a hard coded auth token.
Retry the request every time you eliminate a difference to isolate the cause of the internal server error.
Hope this helps!

Retry using Retrofit2 + RxJava + Jackson for 202 Status Code

I have an API which returns 200, 202, 4xx based on different scenarios. When I get a 202, I am supposed to make the same API until I get a 200 or 4xx. I tried using doOnErrorNext, onError, onNext. I was not able to crack the problem
Observable<MyPOJO> makeAPI();
Observable<MyPOJO> makeAPIImpl(){
makeAPI().doOnErrorNext(/*how to I access the error code here*/);
makeAPI().doOnNext(/*given that 202 is a success code, I expected it to come here, but it goes to Error because of JSON Mapping*/);
}
doOnErrorNext -> I was able to make the API call again but then it would happen for all the error scenarios which I dont want
I have checked multiple answers regarding this, but nobody has solved this combination specifically and am unable to incorporate other answers to my use case.
I would suggest you use OkHttp and use an interceptor to retry your request, something along these lines (this is from one of my apps in Kotlin, but it should give you the idea):
inner class ExpiredSessionInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
if (response.code() == 202) {
val newRequest = request.newBuilder().build()
return chain.proceed(newRequest)
} else {
return response;
}
}
}
then
val httpClientBuilder = OkHttpClient.Builder()
httpClientBuilder.addInterceptor(ExpiredSessionInterceptor())
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(SERVER_ENDPOINT_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(httpClientBuilder.build())
.build()

Categories

Resources