Xamarin and Java Processes (root)

|

I have seen this question pop up from time to time, where someone asks how to run a native process on Android. This is usually because they want to run something with root permissions, which could allow you to do various things, such as reading files you are usually not allowed. This could be reading VPN state from /data/misc/vpn/state. Blocking Advertisements by adding domains to /etc/hosts and much more.

This blog post will show you how to start a Java process and read the exit code, and the output from the process.

Note: If you want to try this yourself, you will need to have a rooted device. I like to run LineageOS on my devices. They support a large amount of popular devices. On LineageOS you can install SU by grabbing it from the Extras web page and flashing using your recovery. This is up to the reader to do.

Linux Processes

Linux is the operating system Android is based on. A standard process on Linux will use

  • stdin to read input from the console (i.e. keyboard)
  • stdout to write to the console
  • stderr to write errors to the console

These are in fact file handles to /proc/<pid>/fd/{0,1,2}, where <pid> is your process id and 0, 1, 2 are stdin, stdout, stderr respectively. You should not worry about this as this is usually handled by the OS.

A process has an exit code. 0 means everything was OK, anything else than 0 means it probably failed.

Starting a Process On Android

On Android you can start a native Process using with Java.Lang.Runtime.GetRuntime().Exec(<command>), which will return a Java.Lang.Process. Alternatively (also preferred way) if you need more control you can use Java.Lang.ProcessBuilder, which allows you to redirect stdin, stdout, stderr. Its Start() method, similarly to Exec() gives you a Java.Lang.Process. This object reflects the Linux process and has streams for stdin, stdout and stderr. These are our way into reading what the process writes out.

Xamarin has some nice helpers for us in the Java.Lang.Process they expose. Instead of blocking when we want to wait for the process to finish, they have added WaitForAsync(), which is very nice. Also the streams for stdin, stdout and stderr are nice C# Streams that you can read and write to very easily. Namely InputStream, OutputStream and ErrorStream.

var builder = new ProcessBuilder("echo", "hello");
var process = builder.Start();
var exitCode = await process.WaitForAsync();

The example above runs the echo command, which just echoes what follows after. In this case “hello”. The WaitForAsync() method returns us a Task<int> with the exit code of the process.

Keep in mind that WaitForAsync() also can throw exceptions such as IOException and SecurityException depending on what you are doing.

Reading From The Process

When we start a Process, it should be considered that we start a process and execute some commands that redirect into this Process. Meaning most of the time, when we want to read the result from the commands we run, we will actually need to read from InputStream, rather than OutputStream which would be the usual stdout. However, since our Process is the one starting other Processes, then their output stream gets redirected into our input stream.

Anyways, reading the stream is super easy.

using (var outputStreamReader = new StreamReader(process.InputStream))
{
    var output = await outputStreamReader.ReadToEndAsync();
}

Bingo! If we ran this on the echo hello process we created further up, output would be hello in this case. Neat!

Running a Process as Root

Running a process as root is super easy. Just add su and -c to the commands you execute as follows.

var builder = new ProcessBuilder("su", "-c", "echo hello");

-c means that the root process su (super user aka. root) should run a specific command. Notice that echo and hello became one argument. This is because we are redirecting into su’s stdin. Apart from that everything else stays the same.

Most Android OS’s with root, will show a dialog when root is requested.

Root dialog

Reading Large Files

Reading large files by using cat to our Process stdin might be inefficient. You could consider using the ProcessBuilder to redirect the stdin directly to a file at a destination you have better access to.

var myFile = new File("/some/path");
builder.RedirectInput(myFile);

Alternatively you could do the same with the InputStream and read it to a file.

using (var inputStream = process.InputStream)
using (var fileStream = File.Create("/some/path"))
{
    await inputStream.CopyToAsync(fileStream);
}

Convenience Helper Method

Here is a little helper method for running simple commands and getting their result.

async Task<(int exitCode, string result)> RunCommand(params string[] command)
{
    string result = null;
    var exitCode = -1;
    try
    {
        var builder = new ProcessBuilder(command);
        var process = builder.Start();
        exitCode = await process.WaitForAsync();

        if (exitCode == 0)
        {
            using (var inputStreamReader = new StreamReader(process.InputStream))
            {
                result = await inputStreamReader.ReadToEndAsync();
            }
        }
        else if (process.ErrorStream != null)
        {
            using (var errorStreamReader = new StreamReader(process.ErrorStream))
            {
                var error = await errorStreamReader.ReadToEndAsync();
                result = $"Error {error}";
            }
        }
    }
    catch (IOException ex)
    {
        result = $"Exception {ex.Message}";
    }

    return (exitCode, result);
}

Usage would be something like.

var (exitCode, result) = await RunCommand("su", "-c", "cat /data/misc/vpn/state");

This would return with the exitCode 0 if everything went well, and the string result would contain something like.

ppp0
10.0.0.42/32
0.0.0.0/0
109.226.17.2 144.117.5.51

Nice! Now you should be an expert in native processes and how to launch them from your Android App. It is up to you the reader as an exercise to figure out how OutputStream works. Enjoy!

MvvmCross Binding Target

|

I had some colleagues, who where a bit confused about what target is when creating bindings. Lets first establish what Source and Target means when talking about a binding:

  • Target, the Property on your View you are binding
  • Source, the Property in your ViewModel your View binds to

So Target will be any public property on your View, that you want to bind. Examples of these are Text, ItemsSource, SelectedItem and many more. Keep in mind that any public property when using MvvmCross on a View can be bound in OneWay mode.

To confuse things a bit, MvvmCross uses something we call TargetBinding, to create a definition of how a View can bind, when wanting modes other than OneWay. This means, if you want to bind something in TwoWay mode, there needs to exist a TargetBinding which describes how to achieve this. Internally in a TargetBinding, what usually happens is that it simply subscribes EventHandlers to relevant events, in order to notify when something has changed on the View in order to feed them back to the ViewModel. You can read more about how to create your own TargetBinding in the MvvmCross documentation about Custom Data Binding.

The Source in your ViewModel, will also need to be a public property. Similarly to every other MVVM framework, if you implement INotifyPropertyChanged on your ViewModel and remember to fire the NotifyPropertyChanged event when updating your properties, the MvvmCross binding engine will figure out how to feed the value to the Target.

Binding Expressions

OK! Now, we have established Target, Source and TargetBinding. Now, let us look a bit into MvvmCross binding expressions.

Swiss/Tibet (Android AXML, iOS string bindings etc.)

Android bindings and some string binding descriptions you can find for MvxTableViewCell and the likes use Swiss/Tibet binding expressions, which is a description language specific to MvvmCross.

<SomeView
  local:MvxBind="ViewProperty ViewModelProperty" />

SomeView in this case is the bindable object, the View. ViewProperty is the Target, ViewModelProperty is the Source.

When binding MvxTableViewCell, the bindable object, will be the cell itself. Hence, the public properties you bind to need to be on the cell.

XAML

In XAML binding expressions as per usual XAML bindings will look something as follows.

<SomeView ViewProperty="{Binding ViewModelProperty}" />

SomeView in this case is the bindable object, the View. ViewProperty is the Target, ViewModelProperty is the Source.

When using MvvmCross XAML BindingEx extensions, which uses Swiss/Tibet, where you can alternatively bind like so.

<SomeView mvx:Bi.nd="ViewProperty ViewModelProperty" />

Fluent Bindings

MvvmCross also provides something called Fluent Bindings to create type strong binding expressions in code behind. For these to work, the place you create the fluent binding, needs to be a derivative of IMvxBindingContextOwner, which means the type will have a BindingContext property, which the Fluent Binding will use to find the Source.

These Fluent Bindings usually look something as follows.

var set = this.CreateBindingSet<ContextOwnerType, ViewModelType>();
set.Bind(someView)
  .For(view => view.ViewProperty)
  .To(viewModel => viewModel.ViewModelProperty);

The ContextOwnerType is usually a MvxViewController, MvxTableViewCell or MvxActivity. This is typically the type containing the views you want to bind to.

When using the BindingSet.Bind() method, it expects the Target as argument, typically the actual View you want to bind. I have seen a couple of cases where someone tries to use the ContextOwnerType instead like so.

public class MyTableViewCell : MvxTableViewCell
{
    ...
    private UILabel _someView;

    private void CreateBindings()
    {
        this.DelayBind(() => 
        {
            var set = this.CreateBindingSet<MyTableViewCell, SomeViewModel>();
            set.Bind(this)
                .To(cell => cell._someView.Text)
                .To(viewModel => viewModel.SomeProperty);
            set.Apply();
        });
    }
}

Now, you may know that in order for MvvmCross to be able to bind something it needs to be a public property. So the above binding expression will not work, since _someView is not a public property. Another problem with that binding description is that MvxPropertyInfoTargetBindingFactory will attempt to create the binding using by trying to find the ViewProperty on the Target. This means if you use set.Bind(ViewContainer).For(vc => vc.SomeView.Prop) it will attempt to find Prop on ViewContainer and not SomeView, which ViewContainer contains.

So the conclusion is, when you use Fluent Bindings, what you use as Target needs to be the actual View you want to bind to or you will need to expose what you want to bind as a public property. So to fix the binding above you can do it by either:

  1. Exposing the Text property on _someView as a public property and bind to that
  2. Change the binding from using this to _someView

So for the first suggestion it will look like:

public class MyTableViewCell : MvxTableViewCell
{
    ...
    private UILabel _someView;

    public string Text 
    { 
        get => _someView.Text;
        set => _someView.Text = value;
    }

    private void CreateBindings()
    {
        this.DelayBind(() => 
        {
            var set = this.CreateBindingSet<MyTableViewCell, SomeViewModel>();
            set.Bind(this)
                .To(cell => cell.Text)
                .To(viewModel => viewModel.SomeProperty);
            set.Apply();
        });
    }
}

For the second suggestion it will look like:

public class MyTableViewCell : MvxTableViewCell
{
    ...
    private UILabel _someView;

    private void CreateBindings()
    {
        this.DelayBind(() => 
        {
            var set = this.CreateBindingSet<MyTableViewCell, SomeViewModel>();
            set.Bind(_someView)
                .To(view => view.Text)
                .To(viewModel => viewModel.SomeProperty);
            set.Apply();
        });
    }
}

One caveat with the first solution is. Since, the target is MvxTableViewCell we are using the default public property One-Way bindings. This means if UILabel was UITextField instead and we wanted to get changes when someone edits the UITextField in our ViewModel, this would not work, since there does not exist any TargetBinding classes, which describe this behavior for MvxTableViewCell. Hence, you will need to add your own for MvxTableViewCell or you will need to use the actual UITextField as Target.

In short terms, Target in a binding matters a lot!

Thank you for reading, if you want to read a bit more about bindings, value converters and value combiners in MvvmCross. Take a look at the official documentation about this topic.

Constraint Layout and Proguard

|

I have made an App which uses RelativeLayout and LinearLayout a lot. I wanted to convert some of these layouts to use ConstraintLayout, which in many cases simplifies a layout. Also when using LinearLayout with weights it can improve performance as well.

My App also uses Google Play Services and other libraries which increase the DEX count by a lot. Hence, I need to have multi-dex and Proguard enabled, which can complicate things a bit.

As a reader you may know, Xamarin does not provide Proguard rules which are merged with the default rules, similar to what a native Android dev might be used to from Android Studio, gradle and the ecosystem around that. So you have to add rules yourself to prevent Proguard from removing code that you are actually using.

In the case of ConstraintLayout I added the following rules in my proguard.cfg file in the app project, to have it survive Proguard’s stripping process.

# support constraint
-dontwarn android.support.constraint.**
-keep class android.support.constraint.** { *; }
-keep interface android.support.constraint.** { *; }
-keep public class android.support.constraint.R$* { *; }

These rules can be applied to other namespaces that you might find that Proguard is stripping out. Usually this will be reflected in your App, through an exception thrown at runtime telling you some class cannot be found in the Dex List.

You can read more about Proguard in the Xamarin Android documentation and how to configure it for your App.

Finding My Mac Mini On The Network

|

Often when I reboot my Mac Mini or it has been off for a long while, I find myself not being able to figure out which IP it has, when it is up and running again. I need this IP to connect to it through VNC from my Windows PC when I want to do stuff on it.

Now the Xamarin integration into Visual Studio (VS) can see the Mac. However, I really don’t want to spin up VS just to figure out the IP of my Mac. So question is… How does VS find my Mac? Easy! Using something called Bonjour. A lot of modern devices and Mac’s use Bonjour to advertise themselves on the network, along with the services they provide. In the Mac case this is often used by iTunes, AirPlay and other services to find devices to play to and from.

So by utilizing Bonjour I can find my Mac easily. For this I’ve created a little LinqPad script to help me.

I am using the Zeroconf NuGet by Oren Novotny to get a nice and easy to use client for discovering devices.

So first thing I do is to enumerate all the devices and their services on the network, which is done simply with.

private async Task<IReadOnlyList<IZeroconfHost>> EnumerateAllServicesFromAllHosts()
{
    // get all domains
    var domains = await ZeroconfResolver.BrowseDomainsAsync();

    // get all hosts
    return await ZeroconfResolver.ResolveAsync(domains.Select(g => g.Key));
}

Going through these I can simply look for a Host with the DisplayName of my Mac. Mine is called macmini-tomasz.

var services = await EnumerateAllServicesFromAllHosts();
var host = services.FirstOrDefault(s => s.DisplayName == "macmini-tomasz");

Now that I have the actual host it has a property IPAddress which I can use for my VNC client!

Voila! With these simple steps you can find your device on the network, which broadcasts using Bonjour.

Restore NuGets From Private Feed With Cake

|

I have just been wrestling with Cake, NuGet and the private feed I have for some of the stuff part of a build I am working on. When restoring stuff from private feeds with NuGet there are a couple of things you need to know about and put into consideration.

So there are basically two places where NuGet is used in Cake.

  1. When running the build script it restores NuGet packages for Tools and AddIns
  2. When you restore NuGet packages for a Solution or Project

For NuGet.exe to figure out how to contact a private feed you need to provide a configuration. It would usually look something in the lines of:

<configuration>
    <packageSources>
        <add key="PrivateRepo" value="https://some.url" />
    </packageSources>
</configuration>

For the private package sources you would additionally have a section with credentials or API keys like:

<packageSourceCredentials>
    <PrivateRepo>
        <add key="Username" value="cheesebaron" />
        <add key="ClearTextPassword" value="super_secret_passw0rd" />
    </PrivateRepo>
</packageSourceCredentials>

You can read more about the schema and each section in the official NuGet documentation for the NuGet.config.

1. Restoring NuGet packages for Tools and AddIns in Cake

So a couple of things to think about when restoring NuGets for tools and AddIns in Cake, is how you specify them.

Usually you simply add something like the following in the top of your cake script:

#tool nuget:?package=MyPackage

This will tell NuGet to look in all configured sources for MyPackage latest stable version.

You can control which source to look explicitly in by specifying the source before the ? like so:

#tool nuget:https://some.url?package=MyPackage

When you explicitly set the source, NuGet.exe will only look for packages in this source. Meaning, if your package has other dependencies, lets say it depends on System.IO.Compression from NuGet and your private repository does not contain this package or does not proxy it. NuGet will not be able to restore this dependency and fail!

As for credentials and sources specified in the nuget.config, Cake will automatically pick this up, if the nuget.config is next to your bootstrap build.ps1 file. Previously I had mine in .nuget/nuget.config and nothing seemed to work, moving it helped.

2. Restoring NuGet packages for solutions and projects

After moving the nuget.config to where the build script executes from, it seems like this is applied too for restoring packages when using the NuGetRestore tool Cake provides out of the box.

This can always be overridden by another configuration if desired, by providing NuGetRestoreSettings:

var nugetRestoreSettings = new NuGetRestoreSettings {
    ConfigFile = new FilePath("/path/to/nuget.config")
};

NuGetRestore(sln, nugetRestoreSettings);

Cake In Process NuGet Client

Now as of Cake 0.22.0 there is something called In Process NuGet Client, which you can enable by adding:

$ENV:CAKE_NUGET_USEINPROCESSCLIENT='true'

To your build.ps1 or by using the --nuget_useinprocessclient=true argument. Read more about configuring it in the Cake docs for Default Configuration Values.

All the above seems to also apply to the In Process Client.