Finishing an app for network capability for the LibreMesh OS

Hi! I’m Tomás on my last post about the LibreMesh Application (now just LimeApp). It was really fun to work with Altermundi on this project and I like the results that we achieve. I hope that everyone enjoys this post just like I enjoyed working on the application! So, let’s get started.

Finished the first part of the GSoC we started to build the next version of the app (1.0).

Resume

As a resume, the objectives for this part are:

  • Do a correct use of threads trying to avoid interruptions to the user.
  • To implement a new Graphical User Interface with the capability of returning messages to the user about the situation of the network (for example, could the app connect to thisnode.info? No? Why?).
  • Add the app to the LibreMesh Operating System.

Fixing the HTTPGet

First things first the previous app had a bug discovered at the first meeting of the second part. If someone has a server on the address thisnode.info the connection could be reached and, in this case, the app shows that the connection to LibreMesh was correct in all the possibles cases.

So, the solution is to do an HTTPGet to thisnode.info. There’s an interesting answer in StackOverflow about this topic:
https://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests

So, we will only focus on getting an HTTPGet, so with the tools that we got from the article, I built this code:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

try {
    connection.getInputStream();
    return true;
} catch (IOException e) {
    e.printStackTrace();
    return false;
}

Using threads

This code looked fine but there’s one thing that wasn’t correct according to the Android specifications. There’s a permitAll in the policy. This means that connections can happen in the principal thread of the application. To publish in the play store, we needed to change this and create a thread to run the HTTPGet, so the code now looks like this:

boolean[] success = {false};

Thread connectionThread = new Thread(new Runnable() {
    public void run() {
        try {
            connection.getInputStream();
            success[0] = true;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }
});

    connectionThread.start();
    connectionThread.join();

    return success[0];

This isn’t the best example of a Thread cause doesn’t work concurrently, but it helps the app to respect the Android specification.

Thanks to our testers, we discovered that there was a problem with the latest Androids version that required too many permits to access the SSID or even the ID of the WiFi, so we decided to disable the verification that used these attributes in the latest version of Android

public static boolean verifyWifiConnection(WifiManager wm) {
    if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.P)
        return wm.isWifiEnabled() && wm.getConnectionInfo().getNetworkId() != -1;
    return wm.isWifiEnabled();
}

Also, we needed to add the attribute android:usesCleartextTraffic=”true” in the Android Manifest because the latest Android version doesn’t allow HTTP sites on WebView.

Finally, we added an error screen and changed the use of the app from three buttons to an “automatically display” of the Lime-App, or an error message.

Graphical interface

We wanted to maintain the app light, so the graphical interface needed to be only two screens:

  • One with the WebView that access to the LibreMesh configuration.
  • Other one with an error and some tips to fix it.

So, with these use cases, we created an error screens that looks like this:

Error screen of the LimeApp

And here’s a video of how’s the app working:

Unit testing

With all the problems solved and the testers using the prerelease version, we added some unit testing using Mockito. This is an example of a Unit Test of a correct connection to the app:

@Test
public void correctConnection() {
    when(wm.isWifiEnabled()).thenReturn(Boolean.TRUE);
    when(wm.getConnectionInfo()).thenReturn(wi);
    when(wm.getConnectionInfo().getNetworkId()).thenReturn(1);

    try {
        when(urlc.getInputStream()).thenReturn(null);
    } catch (IOException e) {
        fail();
    }

    MainActivity ma = new MainActivity(wm, urlc);

    assertEquals(true, ma.accessToLibreMesh());

}

This test uses mocked objects to simulate a response and then it executes the function. We plan to add in the close future Integration tests.

Reducing the app size

One of the objectives that we set for the second part of the GSoC was to reduce the APK size to add the app to the LibreMesh OS. The original size was 2.91 MB and knowing that a LibreRouter has 8 MB of total space, uploading the app means to use 36% of the node space.

The first attempt to reduce the APK size was to start using the “Generate APK” function of Android Studio instead of using the debug APK. This is also needed to sign the app to publish in the Play Store. This reduced the size from 2.91 MB to 2.42 MB.

This reduction was great but isn’t enough, so we started using the tips of the ApkGolf project (https://github.com/fractalwrench/ApkGolf/b) to reduce the space. In an ideal case, we could use all of them, but we wanted to maintain things like a Constricted Layout for the Error message.

We started adding some lines to the build.gradle that add scripts that help to reduce the code and resources size.

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}  

(The proguardFiles were already in the code, but also helps in the size reduction).

Another interesting setting is the resConfig. The library imports usually bring support for multiple languages. We’re currently using Spanish, so setting this to only one language reduce the size a little

resConfigs "es"

Another thing that reduces space is to convert the images to WebP (and accept a good percentage of reduction of quality). We also deleted some files and hard-coded things like the colors (to remove de .xml).

The file that occupies the most space is the one that’s related to classes (ours and imports), so the best way to reduce space use was to remove them. In the build.gradle there were a lot of imports that we added in some moment, but we removed the use of them, so remove also from the build.gradle was a good idea.

They were particularly two interesting dependencies:

implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'

The first one was added by Android Studio and we didn’t use it, but the second one was an addition to the design of the application. We removed the calls to the import and the dependency. That reduced approximately 400 KB of space.

At the end of the process, the APK size is 730,337 KB, this means a reduction of approximately 85% of space use.

Finally, we decided to publish a close version to the Play Store because all the main objectives were completed. Currently, we’re waiting for Google to finish the revision on the App.

Jekyll page

We’ve also added a Jekyll page for the project (available on Github) that shows some information about the application and the problem that solves.

Github page of the project

Link to the Github project

I’m really happy with the work that we did during this GSoC and I’m glad that this application is going to help Community Networks around the world. Thanks to all for letting me be part of this great community and especially to Nicolas Pace and Germán Ferrero for all the support that they gave me during this two months. Greetings and I hope that I can help with other open-source projects in the future!

Building an app for network capability

This image has an empty alt attribute; its file name is AlterMundiInicio.png

Building an app for network capability

Hi! I’m Tomás. This post is a brief of the work that we did in the last few weeks. The prototype of a network capability app was achieved, and we’re starting to test it on communities. The app is still a prototype: it has only three functions (connect to a webpage using the WiFi, check if you’re in a LibreMesh network, and check the private IP of the device) and the front-end consists of only these three buttons, but it has now all the logic that was needed to start working on the rest of the app.

Basic functions

The first approach was to check if the user was able to connect to the LibreMesh local address by checking it with a ping, and then we decided to move forward to an HTTP GET instead. With this idea in mind, we prepared a new version of the application that sends a command to the device (a curl command) instead of a Java method with a previously developed android interface (for the ping version).

public boolean httpGetToLibreMesh() throws InterruptedException, IOException {
    //FIXME: modificar google por la IP de LibreMesh
    String[] cmdLine = {"sh", "-c", "curl --head --silent --fail google.com"};
    Process p1 = java.lang.Runtime.getRuntime().exec(cmdLine);
    int returnVal = p1.waitFor();
    return returnVal == 0;
}

This simple code solves the problem. It returns true if the HTTP GET to google.com worked, and false if it didn’t. It can be easily modified with the LibreMesh IP Address.

The next objective was to inform the user if the device wasn’t connected to the WiFi. In order to do so, we have to get the WifiManager from the ApplicationContext, and then check if the wifi is working.

public boolean verifyLibreMeshConnection() {
    WifiManager wm = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
    if(wm.isWifiEnabled()) {
        return (wm.getConnectionInfo().getNetworkId() == -1) ? false : true;
    }
    return false;
}

Then, we needed a web navigator (WebView) inside the app with the capability to run the LibreMesh router website (On the first approach, to a google.com website).

Using a WebView object with the shouldOverrideUrlLoading overridden we can show a webpage in the app without the requirement of showing an external navigator (Android provides the Android WebView App that does this inside the LibreMesh app).

So with this simple code, we can configure the WebView to enter to a site inside the app.

WebView navegador;
navegador = (WebView) findViewById(R.id.navegadorLibreMesh);
navegador.setWebViewClient(new WebViewClient() {

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    view.loadUrl(url);
    return true;
}
});
navegador.loadUrl("http://www.google.com");

Choosing through which network interface to send data to

Once having the WebView, the next step was to control through which network interface the application sends the network requests. In order to do that we have to access the ConnectivityManager. It was created as a class variable and defined on the function “onCreate” of the activity that holds the WebView. The connectivityMaganer isn’t a new instance but a reference to the object that controls the connections in the context of the App.

connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);

Then we needed a function that can request to use the WiFi. The idea is to make a NetworkRequest and send it to the connectivityManager, but it also needed a NetworkCallback to specify what to do when the Network was available to accomplish the request. So as a second parameter of the request there’s an anonymous class that overrides the methods needed.

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void requestWifi() {
    final NetworkRequest networkRequest = new NetworkRequest.Builder()
           .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
           .build();

    connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                connectivityManager.bindProcessToNetwork(network);
            else
                ConnectivityManager.setProcessDefaultNetwork(network);
        }

        @Override
        public void onLost(Network network) {
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
                connectivityManager.bindProcessToNetwork(null);
            else
                ConnectivityManager.setProcessDefaultNetwork(null);
        }

        @Override
        public void onUnavailable() {
            super.onUnavailable();
        }
    });
}

The last thing that I needed to do was a function that runs the WebView.

private void iniciarNavegador() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) requestWifi();

    WebView navegador;
    navegador = (WebView) findViewById(R.id.navegadorLibreMesh);
    navegador.setWebViewClient(new WebViewClient() {

       @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    });
    navegador.loadUrl("192.168.0.2");
}

Getting the LibreMesh address

The next step was to move forward with getting the LibreMesh IP address. On the other hand, that’s not more than just an algorithm or a gateway, either way, does the same results. This can give us an alternative way to see if the user is connected or not to the LibreMesh server (we get the IP through the algorithm and compare the gateway version).

The idea was pretty simple and only required a int to ip auxiliar function. So we decided to collect all the methods that returned wifi information and send them to a new WifiInformationManager class. So, this class sends all the information that we need from the WiFi:

public class WifiInformationManager extends AppCompatActivity {
    private static String intToIp(int addr) {
        return  ((addr & 0xFF) + "." +
                ((addr >>>= 8) & 0xFF) + "." +
                ((addr >>>= 8) & 0xFF) + "." +
                ((addr >>>= 8) & 0xFF));
    }

    public static String getPrivateIp(WifiManager wm) {
        int ip = wm.getConnectionInfo().getIpAddress();
        return intToIp(ip);
    }

    public static boolean verifyWifiConnection(WifiManager wm) {
        if (wm.isWifiEnabled()) {
            return wm.getConnectionInfo().getNetworkId() != -1;
        }
        return false;
    }

    public static String getGateway(WifiManager wm) {
        return intToIp(wm.getDhcpInfo().gateway);
    }

}

The function getGateway solves in an elegant way the problem of the LibreMesh Local-Address. The rest of the job was simply to change the address of the WebView to this one.

Using logcat to find bugs

The logic step then was to try the application and test if it worked okay, but when we did that the WebView that shows the Lime-App showed a white screen instead. Using the logcat inside the Android Studio we were able to easily find the error, showing the importance of using this type of debugging tools.

Using the logs it’s easy to see that there’s a TypeError when trying to get the property ‘getVoices’. The problem comes with the plugin ‘window.speechSynthesis’ that isn’t available for some browsers.

The Lime-App is the graphical interface that LibreMesh uses for the configuration of community networks. We found the .js that was calling the function:

let synth = window.speechSynthesis;
let voices = synth.getVoices();

export const speech = (text, lang) => {
let utterThis = new SpeechSynthesisUtterance(text);
utterThis.pitch = 0.9;
utterThis.rate = 1.2;
utterThis.voice = voices.filter(x => x.lang === lang)[0];
synth.cancel();
synth.speak(utterThis);
};

It can be seen that in the line 2 the variable voices is set to a synth.getVoices, but if synth is undefined, then that line will not succeed.
The solution was pretty simple, with a control structure we check if the speechSynthesis was available or not. So the fixed code is:

let synth = window.speechSynthesis;

export const speech = (text, lang) => {
if(synth != "undefined") {
let voices = synth.getVoices();
let utterThis = new SpeechSynthesisUtterance(text);
utterThis.pitch = 0.9;
utterThis.rate = 1.2;
utterThis.voice = voices.filter(x => x.lang === lang)[0];
synth.cancel();
synth.speak(utterThis);
}
};

I sent a pull request to the Lime-App repository fixing this problem and it’s currently waiting to be merged.

Next steps

With the functions of detecting and configuring a libremesh network, we plan to add some features to the app the next weeks:

  • A better graphical interface with the integrations of all the planned functions of the app.
  • Support other services in addition to Lime-App.
  • Add the app to the LibreMesh operating system, giving the posibility to the user to obtain the app directly from the router.

Video

Github project

Free Community Networks in Catalonia with Guifi.net

Ramon Roca from Guifi.net talking to ARTE-TV.

Thanks to Elektra for the translation.

Gurb in Spain
1002 km to Brussels

Ramon: The NSA scandal almost makes us laugh I have founded Guifi.net so we can build broadband internet access ourselves.

Voiceover: Here in the middle of the catalan nowhere a digital revolution started 10 years ago.

Ramon and his neighbors have build their own broadband internet because the big commercial providers had no interest in providing access to the village and its vicinity. Today, Guifi.net is one of the greatest altenative networks in the world.

Ramon: I want to show you where the cables merge together. Right here. This rack is the equivalent of a telephone switchboard in a village. It is a bit dirty because this shag actually belongs to my garden. What it can do is impressing. I can’t say it precisely, but these are the routers of 5000 households.

Overdub: In a Freifunk-Network the participants are networking among each other and not via Internet providers. They can directly share data with each other, chat, write e-mails. A decentralized network that the users control themselves.
Thereby their data is secured from access by third parties. Meanwhile, the Internet-Guerilla are able to roll out their own fiberline network. Today, Ramons colleagues are working since 3 AM. We all know that the government operates slowly.
We can’t wait until laws are solving all our problems.

In our case this also comfortable for us: With regards to communication, we can do or not do whatever we like.

Freifunk Google Summer of Code 2014

We are excited that Freifunk has been accepted as a Google Summer of Code Mentor organization again in 2014.

We are now looking for interesting student projects. Participating community networks and projects have already put up a list of ideas on the wiki.

Students can apply for example for projects developing packages for OpenWrt (the basis of the freifunk firmware) extending its functionality or other useful network tools such as management server tools, graphic interfaces for existing tools, map extensions, p2p tools that take routing into consideration, implementing wlan sleeping modes, routing protocols and more.

The deadline for student applications is 21 March: 19:00 UTC. We recommend to start your application now. You also need to add an enrolement form into the system.

 

Freifunk Google Summer of Code Timeline

21 March:

19:00 UTC

Student application deadline.

Interim Period:

Mentoring organizations review and rank student proposals; where necessary, mentoring organizations may request further proposal detail from the student applicant.

7 April:

Mentoring organizations should have requested slots via their profile in Melange by this point.

9 April:

Slot allocations published to mentoring organizations

Interim Period:

Slot allocation trades happen amongst organizations. Mentoring organizations review and rank student proposals; where necessary, mentoring organizations may request further proposal detail from the student applicant.

15 April:

First round of de-duplication checks happens; organizations work together to try to resolve as many duplicates as possible.

18 April:

  1. All mentors must be signed up and all student proposals matched with a mentor – 07:00 UTC

  2. Student acceptance choice deadline.

  3. IRC meeting to resolve any outstanding duplicate accepted students – 19:00 UTC #gsoc (organizations must send a delegate to represent them in this meeting regardless of if they are in a duplicate situation before the meeting.)

21 April:

19:00 UTC

Accepted student proposals announced on the Google Summer of Code 2014 site.

Community Bonding Period:

Students get to know mentors, read documentation, get up to speed to begin working on their projects.

19 May:

  1. Students begin coding for their Google Summer of Code projects;

  2. Google begins issuing initial student payments provided tax forms are on file and students are in good standing with their communities.

Work Period:

Mentors give students a helping hand and guidance on their projects.

23 June:

19:00 UTC

Mentors and students can begin submitting mid-term evaluations.

27 June:

19:00 UTC

  1. Mid-term evaluations deadline;

  2. Google begins issuing mid-term student payments provided passing student survey is on file.

Work Period:

Mentors give students a helping hand and guidance on their projects.

11 August:

Suggested ‘pencils down’ date. Take a week to scrub code, write tests, improve documentation, etc.

18 August:

19:00 UTC

Firm ‘pencils down’ date. Mentors, students and organization administrators can begin submitting final evaluations to Google.

22 August:

19:00 UTC

  1. Final evaluation deadline

  2. Google begins issuing student and mentoring organization payments provided forms and evaluations are on file.

22 August: 20:00 UTC

Students can begin submitting required code samples to Google

25 August:

Final results of Google Summer of Code 2014 announced

Links

* GSoC Page: https://www.google-melange.com/gsoc/org2/google/gsoc2014/freifunk

* Idea Page: http://wiki.freifunk.net/Ideas

Free Networks Italy – Ninux Day 2013 Call For Papers

The Ninux Day (the only day that lasts a weekend), organized by the wireless community network ninux.org, will be held on November 1,2,3, 2013 at Fusolab 2.0, in Rome.

The aim is to gather under the same roof members of Italian and European wireless community networks and all who are interested in:
 * the Net as a common
 * spontaneous networking, bottom-up
 * city networks
 * wireless mesh networks
 * routing protocols
 * networking-oriented operating systems
 * distributed management and monitoring
 * distributed and decentralized services
 * resilient networks
 * community automanagement
 * legal aspects of community networks and wireless networks
 * artistic expressions linked to community networks or wireless networks

If you want to propose a talk or a practical workshop on these topics, of the maximum length of 45 minutes, send an e-mail to
ninux-day-it@ninux.org with a 50-100 words abstract and a short bio before October 2, 2013.

When: November 1,2,3 2013
Where: Fusolab 2.0, Via della bella villa 94, Roma, Italy
Wiki: http://wiki.ninux.org/NinuxDay2013
Mailing list: http://ml.ninux.org/mailman/listinfo/ninux-day (en)
Blog: http://blog.ninux.org
Node Map: http://map.ninux.org
Ninux Day 2009: http://blog.ninux.org/tag/ninux-day/
Fee: free entrance, donations are welcome

[via Clauz]

Bristol Wireless Video

Bristol Wireless is a community project where people formed a cooperative to work together. They provide services all over Bristol, UK. I found a video that was produced already in 2005 now. Enjoy!

Links:

* http://www.bristolwireless.net
* YouTube http://www.youtube.com/watch?v=ZlB92EJgwIY
* Download: http://www.bristolwireless.net/video/bw_90_sec_challenge.mp4

Wireless Community Networks List on Wikipedia

Thee free wireless community is growing and for some time I tried to keep a list of communities on the Global Newswire Site at http://global.freifunk.net/free_global_wireless_community. The bigger the community gets the more difficult it becomes. Instead of maintaining a seperate list of freifunk communities I will join the maintainers of a list of communities in the English Wikipedia, who have created a wiki page here: List of wireless community networks by region.