Service Reference in Xamarin.Android issue - android

I'm trying to develop a Xamarin App (Xam 4.7.10.38 with Xam.And.SDK 8.0.2.1 using PCL approach) connected to an Dynamics 365 Finance And Operation Custom Web Service.
Just to sum it up, it's a WCF Soap service that uses Azure Active Directory Client Id + Client Secret to connect to the soap service.
I've posted the most important part of the code below for reference.
So as instructed, I used the Silverlight SDK to create the service reference proxy class and also added the necessary framework references.
Then I included this proxy class in my Android Project as wel as in a standard .Net Windows App, both using VS.Net 2017.
Everything is identical up until I call var resultAsync = service.BeginValidateUser(validateUserRequest, null, null);
In my Windows App, it returns
System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.
I do the WaitOne to conclude it and my EndValidateUser returns a correct result.
However in my Android App, it returns
System.Runtime.Remoting.Messaging.AsyncResult
which is a different object with different members/methods.
The Waitone still works, but when I call the EndValidateUser, I got an exception saying
"System.ArgumentOutOfRangeException: Specified argument was out of the
range of valid values.\nParameter name: value\n at (wrapper
managed-to-native)
System.Object:__icall_wrapper_mono_delegate_end_invoke
(object,intptr)\n at (wrapper delegate-end-invoke)
:end_invoke_object__this___object[]&_IAsyncResult
(object[]&,System.IAsyncResult)\n at
System.ServiceModel.MonoInternal.ClientRuntimeChannel.EndProcess
(System.Reflection.MethodBase method, System.String operationName,
System.Object[] parameters, System.IAsyncResult result) [0x0001f] in
<475dec2c1fe44b95bbfbd21b550b63f8>:0 \n at
System.ServiceModel.ClientBase1+ChannelBase1[TChannel,T].EndInvoke
(System.String methodName, System.Object[] args, System.IAsyncResult
result) [0x00045] in <475dec2c1fe44b95bbfbd21b550b63f8>:0 \n
Any idea's or approaches on how to solve this ?
Regards,
Sven Peeters
Code for Reference :
AuthenticationContext authenticationContext = new AuthenticationContext(Static_Functions.activeDirectoryTenant);
string aadClientAppSecret = Static_Functions.activeDirectoryClientAppSecret;
ClientCredential creadential = new ClientCredential(Static_Functions.activeDirectoryClientAppId, aadClientAppSecret);
AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync(Static_Functions.activeDirectoryResource, creadential).Result;
string oAuthHeader = authenticationResult.CreateAuthorizationHeader();
string serviceName = "PWBMobilityServiceGroup";
string soapServiceUriString = Static_Functions.GetSoapServiceUriString(serviceName, Static_Functions.aosUri);
EndpointAddress endpointAddress = new EndpointAddress(soapServiceUriString);
System.ServiceModel.Channels.Binding binding = Static_Functions.GetBinding();
SchindlerTechAssist.Droid.D365Service.MobilityServiceClient client = new SchindlerTechAssist.Droid.D365Service.MobilityServiceClient(binding, endpointAddress);
IClientChannel dimServiceChannel = client.InnerChannel;
using (OperationContextScope dimServiceOperContext = new OperationContextScope(dimServiceChannel))
{
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers[Static_Functions.OAuthHeader] = oAuthHeader;
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
CallContext callContext = new CallContext { Company = "DAT" };
string empId = "000509";
string pass = "test";
#region Validate User
SchindlerTechAssist.Droid.D365Service.PWBMobServiceValidateUserRequest validateUserRequest = new SchindlerTechAssist.Droid.D365Service.PWBMobServiceValidateUserRequest();
validateUserRequest.EmployeeId = empId;
validateUserRequest.Password = pass;
SchindlerTechAssist.Droid.D365Service.MobilityService service = (SchindlerTechAssist.Droid.D365Service.MobilityService)dimServiceChannel;
var resultAsync = service.BeginValidateUser(validateUserRequest, null, null);
resultAsync.AsyncWaitHandle.WaitOne();
var result = service.EndValidateUser(resultAsync);
#endregion
}

The fix for me was to enable TLS 1.2 in Android Properties. Dynamics Test/Production environments require TLS 1.2 from the client.

Related

Endpoints generated client library

My problem is that I migrated to Endpoints v2, and at some point down the line my GCM registration code stopped working.
Stopped working? More specifically, the generated client library is attempting to send a POST request in the form provided in the top line of this image:
The second line is what happens when I send the request myself manually with Postman (changing it so that it sends the data in the URL fragment instead of in the query string). This works, and is added to my database.
The registration is sent using the standard API builder:
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl("https://"+Constants.PROJECT_ID+".appspot.com/_ah/api/");
regService = builder.build();
regService.registerDevice(gcmRegistrationId).execute();
The endpoint itself looks like this:
#ApiMethod(name = "registerDevice", httpMethod = "post")
public void registerDevice(#Named("regId") String regId) {
if(findRecord(regId) != null) {
log.info("Device " + regId + " already registered, skipping register");
return;
}
RegistrationRecord record = new RegistrationRecord();
record.setRegId(regId);
ofy().save().entity(record).now();
}
How can this be resolved?
My code is being deployed and generated with the following commands:
gradlew endpointsOpenApiDocs
gcloud endpoints services deploy backend\build\endpointsOpenApiDocs\openapi.json
gradlew appengineDeploy
gradlew endpointsClientLibs
If you want the parameter to be a query string, it should be marked #Nullable as well. This will tell take the parameter out of the path. Looks like there is some mismatch between the configuration in the old and new frameworks, but it is more correct to use #Nullable for query parameters and omit it for path parameters.

Go-Ethereum: Android Smart Contract Interaction Issue

I am attempting to interact with a smart contract via mobile (android) using the go-ethereum library.
Android
final String address_string = "0x8607e627604495ae9812c22bb1c98bdcba581978";
String abi = "[{\"constant\":false,\"inputs\":[],\"name\":\"get_s\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"new_s\",\"type\":\"string\"}],\"name\":\"set_s\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"d_s\",\"type\":\"string\"}],\"payable\":false,\"type\":\"constructor\"}]";
Address address = Geth.newAddressFromHex(address_string);
BoundContract contract = Geth.bindContract(address, abi, ec);
CallOpts callOpts = Geth.newCallOpts();
callOpts.setContext(ctx);
callOpts.setGasLimit(31500);
System.out.println("OUTPUT: " + getString(contract, callOpts));
//Setter String to Test Contract
Interfaces params = Geth.newInterfaces(1);
Interface anInterface = Geth.newInterface();
anInterface.setString(teststring);
params.set(0,anInterface);
return contract.transact(opts, "set_s", params);
//Getter String from Test Contract
Interfaces args = Geth.newInterfaces(0);
Interfaces results = Geth.newInterfaces(1);
Interface result = Geth.newInterface();
result.setDefaultString();
results.set(0, result);
contract.call(opts, results, "get_s", args);
String string = results.get(0).getString();
return string;
Contract
pragma solidity ^0.4.9;
contract echo {
string s;
function echo(string d_s) {
s = d_s;
}
function set_s(string new_s) {
s = new_s;
}
function get_s() returns (string) {
return s;
}
}
Expected behaviour
Successful interaction with a deployed smart contract on the Rinkeby blockchain.
Actual behaviour
For setter (on contract):
'abi: cannot use slice as type string as argument'
For getter (on contract):
'abi: cannot unmarshal string in to []interface {}'
Steps to reproduce the behaviour
1.) Connect to Rinkeby Testnet via mobile
2.) Create an account via mobile
3.) Deploy a smart contract via desktop
4.) Try to interact w/ the smart contract via mobile
Bottom Line
If anyone has been able to interact with smart contracts through go-ethereum android,
I would appreciate some assistance.
fix for that issue.
https://github.com/ethereum/go-ethereum/pull/15402
I'm waiting for feedback.

How do I send a serialized object over network stream to a tcp listener in Xamarin?

Can someone show me a code example of how to send serialized objects back and forth from a Xamarin Android application using a BinaryFormatter instead of Json? It's going to be over WiFi inside the Server Farm.
I'm currently trying to port a simple administrative console application over to an Xamarin Android forms application. I don't understand PCL yet or it's lack of [serializable] attribute. I've heard from the guys at Xamarin that I should probably try Android specific Xamarin instead of forms. I'm new to this so I'm not sure. This will be connecting to a custom Windows Service using standard TCPListeners. Any help would be greatly appreciated. Thanks.
This is an example of the type of console code that I am trying to port over.
public static void HeartBeatPulseListener()
{
Int32 hbPort = 8002;
Console.WriteLine("\nStarting Heart Beat Listener on Port: {0}", hbPort.ToString());
TcpListener heartBeatListener = new TcpListener(IPAddress.Any, hbPort);
heartBeatListener.Start();
while (true)
{
using (TcpClient client = heartBeatListener.AcceptTcpClient())
{
Console.BackgroundColor = ConsoleColor.DarkRed;
Console.ForegroundColor = ConsoleColor.Yellow;
NetworkStream netStream = client.GetStream();
IFormatter formater = new BinaryFormatter();
HeartBeatPulse pulseMSG = (HeartBeatPulse)formater.Deserialize(netStream);
if (pulseMSG != null) Console.WriteLine("\nPulse:{0} \n tStamp:{1}\n FROM:{2}\n Instance:{3} \n Original Unique:{4} \n Type: {5}", pulseMSG.Id.ToString(), pulseMSG.TimeStamp.ToString(), pulseMSG.A.ToString(), pulseMSG.ServerCoreInstanceId, pulseMSG.OriginalUnique, pulseMSG.Type);
if (pulseMSG.Roles.Count() > 1)
{
Console.WriteLine("\nRoles:");
foreach (string role in pulseMSG.Roles)
{
Console.WriteLine("\n{0}", role);
}
}
else Console.WriteLine("\nSum Ting Wong");
Console.ResetColor();
}
}
}

get_current_user() returns None due to "Audience not allowed" warning in Cloud Endpoints

I am receiving an "Audience not allowed" warning in my google developers console logs when trying to make an authenticated request via Google Cloud Endpoints from an Android app.
Looking through the Endpoints source code, that corresponds to:
aud = parsed_token.get('aud')
cid = parsed_token.get('azp')
if aud != cid and aud not in audiences:
logging.warning('Audience not allowed: %s', aud)
My calling code in the android app:
public static final String WEB_CLIENT_ID = "web-client-id.apps.googleusercontent.com";
public static final String AUDIENCE = "server:client_id:" + WEB_CLIENT_ID;
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(
mContext,
AUDIENCE
);
Grapi.Builder builder = new Grapi.Builder(HTTP_TRANSPORT,
JSON_FACTORY, credential);
Grapi service = builder.build()
Where "web-client-id" is the alpha numeric client id generated in google developers console. This service is used to make authenticated calls.
This is also the same WEB_CLIENT_ID that is passed to the api decorator in my backend python code:
WEB_CLIENT_ID = 'web-client-id.apps.googleusercontent.com'
ANDROID_CLIENT_ID = 'android-client-id.apps.googleusercontent.com'
ANDROID_AUDIENCE = WEB_CLIENT_ID
grapi_client_ids = [ANDROID_CLIENT_ID,
WEB_CLIENT_ID,
endpoints.API_EXPLORER_CLIENT_ID]
grapi_audiences = [ANDROID_AUDIENCE]
#endpoints.api(name='grapi', version='v1',
allowed_client_ids=grapi_client_ids, audiences=grapi_audiences,
scopes=[endpoints.EMAIL_SCOPE])
It looks like all of this is causing endpoints.get_current_user() to return None, and my authenticated call to fail.
When I initialized my web client id and android client id variables in the python backend, I used backslashes for line continuation to conform with PEP8 (80 character line length) ie.
WEB_CLIENT_ID = 'web-client-id'\
'.apps.googleusercontent.com'
ANDROID_CLIENT_ID = 'android-client-id'\
'.apps.googleusercontent.com'
I am not sure why this was not read correctly, but when I used line continuation inside parenthesis it worked fine.
WEB_CLIENT_ID = ('web-client-id'
'.apps.googleusercontent.com')
ANDROID_CLIENT_ID = ('android-client-id'
'.apps.googleusercontent.com')

Google Play Android Developer API from C#/.NET service - (400) Bad Request

I'm trying to access a Purchase Status API from my ASP.NET web server using Google APIs .NET Client Library which is a recommended way for using Purchase API v1.1. However, the Authorization page of this API suggests direct web requests to Google's OAuth2 pages instead of using the corresponding client libraries.
OK, I tried both methods with all variations I could imagine and both of them lead to "The remote server returned an error: (400) Bad Request.".
Now what I've done to get to my point. First I've made all steps 1-8 under the Creating an APIs Console project of the Authorization page. Next I generated a refresh token as described there. During refresh token generation I chose the same Google account as I used to publish my Android application (which is in published beta state now).
Next I've created a console C# application for test purposes in Visual Studio (may be console app is the problem?)
and tried to call the Purchase API using this code (found in some Google API examples):
private static void Main(string[] args)
{
var provider =
new WebServerClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = "91....751.apps.googleusercontent.com",
ClientSecret = "wRT0Kf_b....ow"
};
var auth = new OAuth2Authenticator<WebServerClient>(
provider, GetAuthorization);
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = APP_NAME
});
var request = service.Inapppurchases.Get(
PACKAGE_NAME, PRODUCT_ID, PURCHASE_TOKEN);
var purchaseState = request.Execute();
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
}
private static IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state =
new AuthorizationState(
new[] {"https://www.googleapis.com/auth/androidpublisher"})
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI"
};
// below is my redirect URI which I used to get a refresh token
// I tried with and without this statement
state.Callback = new Uri("https://XXXXX.com/oauth2callback/");
client.RefreshToken(state); // <-- Here we have (400) Bad request
return state;
}
Then I tried this code to get the access token (I found it here: Google Calendar API - Bad Request (400) Trying To Swap Code For Access Token):
public static string GetAccessToken()
{
var request = WebRequest.Create(
"https://accounts.google.com/o/oauth2/token");
request.Method = "POST";
var postData =
string.Format(
#"code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",
// refresh token I got from browser
// also tried with Url encoded value
// 4%2FlWX1B3nU0_Yax....gAI
"4/lWX1B3nU0_Yax....gAI",
// ClientID from Google APIs Console
"919....1.apps.googleusercontent.com",
// Client secret from Google APIs Console
"wRT0Kf_bE....w",
// redirect URI from Google APIs Console
// also tried Url encoded value
// https%3A%2F%2FXXXXX.com%2Foauth2callback%2F
"https://XXXXX.com/oauth2callback/");
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
try
{
// request.GetResponse() --> (400) Bad request again!
using (var response = request.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (var reader = new StreamReader(dataStream))
{
var responseFromServer = reader.ReadToEnd();
var jsonResponse = JsonConvert.DeserializeObject<OAuth2Response>(responseFromServer);
return jsonResponse.access_token;
}
}
}
}
catch (Exception ex) { var x = ex; }
return null;
}
So, to sum up all my long story:
Is it possible at all to pass OAuth2 authorization using either of methods above from a C# Console Application (without user interaction)?
I've double checked the redirect URI (since I saw a lot of discussed troubles because of it here on stackoverflow) and other parameters like ClientID and ClientSecret. What else I could do wrong in the code above?
Do I need to URL encode a slash in the refresh token (I saw that the first method using client library does it)?
What is the recommended way of achieving my final goal (Purchase API access from ASP.NET web server)?
I'll try to answer your last question. If you access your own data account, you dont need to use client id in oAuth2. Let's use service account to access Google Play API.
Create a service account in Google Developer Console > Your project > APIs and auth > Credentials > Create a new key. You will download a p12 key.
Create a C# project. You can choose console application.
Install google play api library from Google.Apis.androidpublisher. Nuget. You can find other library for dotnet in Google APIs Client Library for .NET
Link google api project with your google play account in API access
Authenticate and try to query information. I'll try with listing all inapp item. You can just change to get purchase's status
String serviceAccountEmail = "your-mail-in-developer-console#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"physical-path-to-your-key\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/androidpublisher" }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GooglePlay API Sample",
});
// try catch this function because if you input wrong params ( wrong token) google will return error.
var request = service.Inappproducts.List("your-package-name");
var purchaseState = request.Execute();
// var request = service.Purchases.Products.Get(
//"your-package-name", "your-inapp-item-id", "purchase-token"); get purchase'status
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
You should do the following in your
private static IAuthorizationState GetAuthorization(WebServerClient client) method:
private IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state = AuthState;
if (state != null)
{
return state;
}
state = new AuthorizationState()
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI",
Callback = new Uri(#"https://XXXXX.com/oauth2callback/")
};
client.RefreshToken(state);
// Store and return the credentials.
HttpContext.Current.Session["AUTH_STATE"] = _state = state;
return state;
}
Let me know if it works for you.
Be aware that we know that the whole OAuth2 flow is awkward today, and we are working to improve it.

Categories

Resources