TL;DR: I am looking for a way to find and extract a native shared library (.so) file from an Android device that is running a vendor-specific custom Android build. I need this file as the missing piece in a Xamarin.Android Java bindings library.
I am attempting to create a Xamarin.Android bindings library for a Java SDK that exposes laser barcode scanning APIs on Famoco's Android devices. These devices run a vendor-specific Android build, which supports some special features, such as centralized device management.
I followed the usual procedures to create the bindings library, without using any custom transforms or additions, and there were no compiler errors.
Here is the factory method, generated within the bindings library, that attempts to create a new BarCodeReader instance:
[Register ("open", "(ILandroid/content/Context;)Lcom/zebra/adc/decoder/BarCodeReader;", "")]
public static unsafe global::Com.Zebra.Adc.Decoder.BarCodeReader Open (int readerId, global::Android.Content.Context context)
{
const string __id = "open.(ILandroid/content/Context;)Lcom/zebra/adc/decoder/BarCodeReader;";
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [2];
__args [0] = new JniArgumentValue (readerId);
__args [1] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle);
var __rm = _members.StaticMethods.InvokeObjectMethod (__id, __args);
return global::Java.Lang.Object.GetObject<global::Com.Zebra.Adc.Decoder.BarCodeReader> (__rm.Handle, JniHandleOwnership.TransferLocalRef);
} finally {
}
}
The above code fails while executing the following line:
var __rm = _members.StaticMethods.InvokeObjectMethod (__id, __args);
An exception is thrown: No implementation found for void com.zebra.adc.decoder.BarCodeReader.native_setup(java.lang.Object, int, java.lang.Object).
I learned from the troubleshooting guidelines that this type of failure is typically caused by the inability to resolve a required native library.
In an attempt to confirm this as the cause, I used JD-GUI to decompile the Famoco JAR, from which I extracted the following snippets of the implementation code:
// This is the underlying Java implementation of the above bindings library factory method
public static BarCodeReader open(int readerId, Context context)
{
return new BarCodeReader(readerId, context);
}
// This is the BarCodeReader constructor that is called by the factory method
BarCodeReader(int readerId, Context context)
{
this.mEventHandler = null;
this.mAutoFocusCallback = null;
this.mDecodeCallback = null;
this.mErrorCallback = null;
this.mPreviewCallback = null;
this.mSnapshotCallback = null;
this.mVideoCallback = null;
this.mZoomListener = null;
Looper aLooper = Looper.myLooper();
if (null == aLooper) {
aLooper = Looper.getMainLooper();
}
if (aLooper != null) {
this.mEventHandler = new EventHandler(this, aLooper);
}
native_setup(new WeakReference(this), readerId, context);
}
// This method is called by the above constructor, but fails because no implementation exists
private final native void native_setup(Object paramObject, int paramInt);
It seems to me that the above runtime error is occurring because the private method native_setup is not implemented within the Java code, but separately within a native library that is not referenced anywhere within my bindings library project. Does this seem like a reasonable diagnosis?
Unfortunately, I didn't find any .so (native library) files within the SDK kit supplied by Famoco. I have contacted their support team, who stated that it is not necessary to link an .so file when consuming their SDK JAR. Famoco are not keen to support cross-platform apps on their devices, but they did confirm that they have other customers using Xamarin, who appear to have solved this problem. Unfortunately, Famoco support don't seem able to tell me how to achieve this.
Could it be that the required native library already exists on the device (deployed as part of the custom Android build)? To verify this hypothesis, I installed the Famoco sample laser scanning app, which runs correctly, even though there is no sign of a .so file within its project source kit.
If so, is it feasible to find and extract the .so file from the Android environment and how should I do this?
Yes, the libbarcodereader44.so file should be preinstalled on the custom device. It may be either in the /system/lib or in the /vendor/lib directory. You must load this library from your code before calling the open () method. Here you can find more details about the Famoco SDK.
Related
I'm developing an app using Xamarin's HCE feature.
The project structure is as follows.
hceSample
hceSample.Android
hceSample.iOS
I am implementing hce simulation code called hceService in hceSample, not hceSample.Android.
A function called Enable_Card exists in the hce service, and you want to use the NfcFCardEmulation.EnableService function in that function.
Activity and ComponentName are requested as parameters of the function.
The ComponentName area was handled easily, but I don't know how to get the Activity. Please advise.
This is the contents of enable_Card function of hceService.
private Activity activity = null;
private bool enable_Card(cardModel card)
{
try
{
sid = card.cardSN;
tag = "Felica";
emulation.EnableService(, componentName); //<- How to get Activity??
emulation.SetNfcid2ForService(componentName, sid);
return true;
}
catch
{
return false;
}
}
This is my first time asking a question on Stackoverflow.
I would appreciate it if you could point out any missing or incorrect parts.
I trying this
activity = Xamarin.Essentials.Platform.CurrentActivity; //<- this function is not found!
Added missing information!
The namespace of the Enable_Card function is located in hceSample.Service.
Are you using the NfcFCardEmulation.EnableService(Activity, ComponentName) Method, right?
The method is an android api from android sdk,you cannot use it directly in xamarin.form(yours is hceSample) project.
If you want to call the function in xamarin form project(hceSample) from native platform(hceSample.Android, or hceSample.iOS),you can use Xamarin.Forms DependencyService to achieve this.
The DependencyService class is a service locator that enables Xamarin.Forms applications to invoke native platform functionality from shared code.
For more information about DependencyService, you can check document Xamarin.Forms DependencyService. And there is a sample included in above document,which is helpful for you to understand DependencyService.
Note:
We recognize that hardware service is the right and ideal way to
implement in each OS project. However, I'm curious if there is a way
to code Android and iOS at the same time
Since the api you used is from android sdk, you can call it in native android or use DependencyService to call it on xamarin.form(yours is hceSample) project.
If you call it on xamarin.form(yours is hceSample) project, you also need to find the the corresponding function or interface in iOS.
I have a folder called Documentation inside the shared project, named App2 in this case. How do I access the files stored inside the Documentation folder? Attached image below shows the project structure.
Visual Studio Solution Page
I have tried out following commands but they aren't working :
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
AppDomain.CurrentDomain.BaseDirectory
System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
If it's troublesome to access the file in that folder, I'm open to hearing other alternatives.
This is how I have done it for JSON files in my shared project (using PCL). As Jason pointed out in the comments, if you are using .NET Standard, you can simply define the GetSharedFile method in your shared project instead of creating platform specific references.
Add the file to the shared project and set it as Embedded Resource
Create an IFileHelper interface in your shared project
public interface IFileHelper {
Stream GetSharedFile(string fileResourceName);
}
Create a new FileHelper class in each project (Android and iOS) with the following
public class FileHelper : IFileHelper {
public Stream GetSharedFile(string fileResourceName) {
Type type = typeof(IFileHelper); // We could use any type here, just needs to be something in your shared project so we get the correct Assembly below
return type.Assembly.GetManifestResourceStream(fileResourceName);
}
}
Add a documentation handler class in your shared project. Something like below (make sure to change the App namespace to match yours):
public class Documentation {
private const string ResourcePath = "App.Documentation.index.html"; // App would be your application's namespace, you may need to play with the Documentation path part to get it working
public string GetDocs() {
IFileHelper helper = DependencyService.Get<IFileHelper>(); // Your platform specific helper will be filled in here
Stream stream = helper.GetSharedFile(ResourcePath);
using (stream)
using (StreamReader reader = new StreamReader(stream)) {
return reader.ReadToEnd(); // This should be the file contents, you could serialize/process it further
}
}
}
I wrote this mostly by hand so let me know if something is not working. If you cannot load your file, I suggest trying to put it into the root of your shared project and then changing ResourcePath in the code above to the following (again using your app's namespace instead of App):
private const string ResourcePath = "App.index.html";
I just want to log to console and to a log file, using a standard TraceSource, in my Xamarin app that will run on UWP, Mac OS X, iOS and Android. I'm developing/debugging on UWP.
TraceSource, TraceListener, and TextWriterTraceListener are indeed all available in .Net Standard library, so perhaps I'm setting it up incorrectly? Most places on the Internet insist on setting up trace listeners in an app.config file, but this is not applicable nor possible for Xamarin apps. So here is my logging initialization code, mostly based on an example in Microsoft docs:
private void SetupLogging()
{
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out, "consoleTraceListener"));
string logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Application.log");
if (!File.Exists(logFilePath)) File.Create(logFilePath);
var logFileTraceListener = new TextWriterTraceListener(logFilePath, "logFileTraceListener");
Trace.Listeners.Add(logFileTraceListener);
Trace.Write("Test");
Trace.TraceInformation("Logging Initialized. Log file location: " + logFilePath);
Trace.Flush();
}
When I run this in a Xamarin UWP app, a file is created but nothing is written to it, nor can I find anything in the Output of the program (there is no ConsoleTraceListener so I'm trying to write a TextWriterTraceListener to Console.Out). Can someone provide a working example for Xamarin? (I haven't tried the Android or iOS apps yet; want to get UWP on the local machine working first.)
The problem is that you passed wrong string parameter to TextWriterTraceListener method. Please try to pass Stream parameter. You could use following code directly. by the way, you'd better use LocalApplicationData SpecialFolder that could be accessed successfully within uwp.
private void SetupLogging()
{
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out, "consoleTraceListener"));
string logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Application.log");
if (!File.Exists(logFilePath))
{
File.Create(logFilePath);
}
var logFileTraceListener = new TextWriterTraceListener(File.Open(logFilePath,FileMode.Open), "logFileTraceListener");
Trace.Listeners.Add(logFileTraceListener);
Trace.Write("Test");
Trace.TraceInformation("Logging Initialized. Log file location: " + logFilePath);
Trace.Flush();
}
I created a new Xamarin.Forms app to test basic Bluetooth functionality. I downloaded this plugin into both the Android project and the shared project:
https://github.com/aritchie/bluetoothle
I wrote this function in the shared project and am calling it from the OnCreate of my launch activity in my Android project:
public static async Task BroadcastBluetooth()
{
// (I do not await this function when I call it)
try
{
await Task.Delay(5000); // just to make sure we give enough time for all initialization to complete
_server = CrossBleAdapter.Current.CreateGattServer();
// exception thrown on this line
await _server.Start(new AdvertisementData
{
LocalName = "TestServer",
ServiceUuids = new List<Guid>()
});
}
catch (Exception e)
{
}
}
It throws this exception:
{System.NullReferenceException: Object reference not set to an
instance of an object. at
Plugin.BluetoothLE.Server.GattServer.StartAdvertising
(Plugin.BluetoothLE.Server.AdvertisementData adData) [0x00095] in
C:\dev\acr\bluetoothle\Plugin.BluetoothLE.Android\Server\GattServer.cs:135
at Plugin.BluetoothLE.Server.GattServer.Start
(Plugin.BluetoothLE.Server.AdvertisementData adData) [0x00011] in
C:\dev\acr\bluetoothle\Plugin.BluetoothLE.Android\Server\GattServer.cs:70
at App2.App+d__7.MoveNext () [0x00097] in
C:\Projects\app2\App2\App2\App.xaml.cs:49 }
I'm only doing something really basic so I must be missing something? The exception is referencing a directory path of the plugin's developer's machine (C:\dev\acr...) so either this plugin is broken or I'm doing something really wrong?
I am using the same plugin in a current project of mine and it is working fine. So I doubt the issue is within the plugin.
Some thoughts about what could cause the issue:
Are you testing this code on a real device that is capable of performing BLE Advertisement?
Do you have set the permissions accordingly in your android project?
Does BLE Advertising work when you use the native android apis?
It also would be helpful, if you could attach a repository with which I can reproduce the issue.
I'm using RGiesecke DLLExport library to produce a C# DLL that can be dynamically loaded from legacy application built on VC6. It exported methods and they were called from VC6 code. No problems. However, as long as I tried to declare a variable as of any one of my .net classes, it crashed.
//I tried CallingConvention = CallingConvention.StdCall too
[DllExport(CallingConvention = CallingConvention.Winapi)]
static void GetDwgReferences(string fileName)
{
//OK: inialize System classes of .net
DateTime dateTime = DateTime.Now;
//crashing here: declare a variable of my static class (.net assemebly)
//SafeString safeString;
//crashing here: declare a variable of my class (.net assemebly)
//Email email;
//crashing here: initialize an object of my class (.net assemebly)
//DwgXrefs dwgXrefs = new DwgXrefs();
//crashing here by declcare a variable of third-party library (.net assemebly)
//ExSystemServices _serv;
}
What's wrong? Please help.
I had a similar problem here trying to use unmanaged exports with Metatrader to load associated managed dlls.
After some digging I think I have found the problem. The app domain is probably not where you would expect it to be, the CLR is trying to resolve your assembly but failing with a nondescript error. In my case the app domain was actually executing in the directory of the host application, so I assume this is always the case.
What I would suggest you do is build a bare dll with no dependencies, and place in something such as the following:
static void Initialize()
{
SimpleLog.WriteLog("App -" + AppDomain.CurrentDomain.BaseDirectory);
}
[DllExport("Test", CallingConvention = CallingConvention.StdCall)]
public static void Test()
{
Initialize();
}
I am not sure but I think you possibly cannot use a static constructor here?
In the log you should see the executing directory for that domain. If you put your assemblies here it (hopefully) should work. It has fixed the problem for me here. I guess the next question is can we change the domain at runtime as I might not want to put these assemblies here.
Have a google if you need the source code for a simple logger - obviously do not use a third party logging framework with dll dependencies!
I think mine is an adaptation of this one:
http://www.codeproject.com/Articles/80175/Really-Simple-Log-Writer
As the other answer stated, it is difficult to know what error C# is throwing without explicitly catching the error in a try / catch block within each method of a C# dll.
You likely need to export as CallingConvention.StdCall and additionally marshal the incoming string as an unmanaged LPWStr type:
[DllExport(CallingConvention = CallingConvention.StdCall)]
static void GetDwgReferences([MarshalAs(UnmanagedType.LPWStr)] string fileName)
{
}
Please see Code to Export C# DLL to Metatrader Build 600+ for a working example using Robert Giesecke's C# Project Template for Unmanaged Exports to export a C# dll to a legacy application (MT4).
Additionally, you might find Native and .NET Interopability interesting though it is mostly geared toward accessing native code from within .NET.