Friday, April 1, 2016

Space review for Snappy mobile browser

April 1-st. Google Play published apps reviews from space. Here is what they say about Snappy - an extendable browser for Android:

Tuesday, February 16, 2016

Snappy: mobile browser with Chrome-like extensions

I'm happy to announce a brand new product for Android - Snappy. This is a mobile browser which supports Chrome-like extensions.

The extendable browsers are standard de-facto on desktops. Yet mobile browsers provide very limited facilities for extension. Some of them declare a support of so called "plug-ins" or "add-ons", while the others withdraw such possibility completely due to supposedly large resource consumption that "plug-ins" would impose. Anyway, the range of features supported by extensions for desktop browsers is far wider than that's available for mobile browsers. This is why I decided to port a considerable part of Chrome extensions API to Android, using the mobile WebView based on the same Chromium project.

Today you may try it yourself. The browser is available in Google Play (Android 5+, WebView 48+ is highly recommended). Please visit the home page for further details. As the very first release, it may have some issues, but I hope that the whole idea is so powerful and universal to produce positive impression nevertheless.

Tuesday, January 12, 2016

Google thinks my tablet is a car

As an Android developer I often analyze system logs from my tablet. Starting from some recent updates (presumably, since Android 6.0) I see peculiar records there from time to time.

I/ActivityManager(591): Start proc 7994:com.google.android.gms:car/u0a21 for service com.google.android.gms/.car.CarService ... D/ChimeraCfgMgr(7994): Loading module com.google.android.gms.car from APK com.google.android.gms ... D/CAR.SERVICE(7994): Connecting to CarCallService... D/CAR.SERVICE(7994): com.google.android.projection.gearhead isn't installed. D/CAR.TEL.Service(7994): Creating a new CarCallService. W/ActivityManager(591): Unable to start service Intent { act=local_bind cmp=com.google.android.gms/.car.InCallServiceImpl } U=0: not found D/CAR.TEL.PhoneAdapter(7994): setListener: com.google.android.gms.car.dn@43460a D/NativeLibraryUtils(7994): Install completed successfully. count=14 extracted=0 D/CAR.TEL.Service(7994): Starting CarCallService with initial phone null ... D/CAR.SERVICE(7994): mConnectedToCar = false, abort E/ActivityThread(7994): Service com.google.android.gms.car.CarService has leaked ServiceConnection com.google.android.gms.car.hr@b3012b2 that was originally bound here E/ActivityThread(7994): android.app.ServiceConnectionLeaked: Service com.google.android.gms.car.CarService has leaked ServiceConnection com.google.android.gms.car.hr@b3012b2 that was originally bound here E/ActivityThread(7994): at android.app.LoadedApk$ServiceDispatcher.(LoadedApk.java:1092) E/ActivityThread(7994): at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:986) E/ActivityThread(7994): at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1303) E/ActivityThread(7994): at android.app.ContextImpl.bindService(ContextImpl.java:1286) E/ActivityThread(7994): at android.content.ContextWrapper.bindService(ContextWrapper.java:604) E/ActivityThread(7994): at android.content.ContextWrapper.bindService(ContextWrapper.java:604) E/ActivityThread(7994): at android.content.ContextWrapper.bindService(ContextWrapper.java:604) E/ActivityThread(7994): at com.google.android.gms.common.stats.g.a(SourceFile:128) E/ActivityThread(7994): at com.google.android.gms.common.stats.g.a(SourceFile:145) E/ActivityThread(7994): at com.google.android.gms.car.hc.(SourceFile:319) E/ActivityThread(7994): at com.google.android.gms.car.CarChimeraService.onCreate(SourceFile:74) E/ActivityThread(7994): at com.google.android.chimera.ServiceProxy.setImpl(SourceFile:115) E/ActivityThread(7994): at com.google.android.chimera.ServiceProxy.onCreate(SourceFile:105) E/ActivityThread(7994): at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877) E/ActivityThread(7994): at android.app.ActivityThread.-wrap4(ActivityThread.java) E/ActivityThread(7994): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427) E/ActivityThread(7994): at android.os.Handler.dispatchMessage(Handler.java:102) E/ActivityThread(7994): at android.os.Looper.loop(Looper.java:148) E/ActivityThread(7994): at android.app.ActivityThread.main(ActivityThread.java:5417) E/ActivityThread(7994): at java.lang.reflect.Method.invoke(Native Method) E/ActivityThread(7994): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) E/ActivityThread(7994): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

What this means is that Android lives its own life, very far from reality, and tries to run tons of useless crap behind the scene, without user consent.

I don't have a car and my tablet has not ever been connected to one. Yet Google thinks it's acceptable to waste device's resources unconditionally and embed such ridiculous services into the system core. For example, the car service lives inside com.google.android.gms, which is a part of indispensable Google Play.

Monday, January 4, 2016

Chrome extension for secure, private, and faster Internet browsing

If you're reading this, you're definitely browsing Internet at the moment. Perhaps you spend a lot of time in the net. So am I. This is why it's important to know that we can surf the web securely, privately and fast. For this purpose I've made a browser extension which was originally developed for FireFox back in 2008, but then ported to Chrome. I've been using it for all these years and continue improving.

Not long ago the source code and downloadable crx-file have been moved from discontinued code.google.com to Bitbucket, where the latest version of Domain Cage is available.

You may say that there are a lot of analogous extensions and you'll be partially right. Yet Domain Cage has an optimal set of features: it's small, universal, standalone, customizable for your needs, and most importantly very restrictive by default - this helps you to keep privacy and security on unknown sites (what is very common situation in Internet). You may find further details in the introduction and the user guide.

Monday, December 14, 2015

Field research of Bluetooth Low Energy use for indoor positioning on Android

I've started investigations and practical tests of the modern Bluetooth Low Energy (BLE) technology applicability for indoor positioning tasks. This technology has been built with many useful applications in mind from very beginning, including IPS of course. The main question was if the technology is mature enough and widely adopted to provide real value.

You may perform a quick search on Internet and find numerous papers with academic research on the subject. I did it as well, and most interesting and thorough article was "An Analysis of the Accuracy of Bluetooth Low Energy for Indoor Positioning Applications" by R. Faragher and R. Harle. It mentions almost all Pros and Cons of BLE, and provides practical results of a comparison between WiFi- and BLE-based positioning approaches. Most important conclusions that I made for myself after the reading are:

  • a special non-connectable BLE devices are preferred for building IPS infrastructures because of their continuous discovery (connectable devices are reported only once per each scan request); as far as specific building environment may not comply with this requirement, a mobile application should explicitly repeat BLE scans in a loop, which may lead to additional resource consumption and occasional hangs (find more details below);
  • BLE signal range is shorter than WiFi's one, and RSSI level of the same beacon may change significantly as a result of ad hoc switching between 3 frequency bands used by BLE internally (it can be a problem to sort out measurements by BLE channel using provided mobile APIs); thus, a special fingerprinting algorithm should be used with an emphasis on specific beacon existence instead of its RSSI, or deep smoothing should be applied, making processed RSSI values more inertial;
  • a single BLE scan should be performed at least once at a second in order to provide 1-2 meter positioning accuracy; unfortunately this parameter can be hardly manageable and limited by hardware implementation specific to every manufacturer;
  • according to the article, BLE scanning is negatively affected by enabled WiFi receiver, so for better results BLE should be used while WiFi is disabled, which may not be feasible for all use cases;
  • for best result at least 5-6 beacons should be detectable in every spatial point; combining the fact with the fast decay of BLE signals one can deduce that a high density of beacons is required for good positioning performance;

Just as illustration of the issues mentioned in the 1-st and 3-rd points, here is a related discussion on the StackOverflow. To recap: BLE implementations may vary significantly between Android versions. This makes it difficult to build an application with consistent BLE behaviour on all variety of Android market, supporting BLE at the moment (70% of Android devices).

I added BLE scanning and fingerprinting in a test version of WiFiLine. One additional observation is that BLE is not yet widely used in local stores here in Moscow. Actually the number of BLE devices is 10-100 times less than WiFi hotspots. My thought is that BLE is not yet ready enough for efficient public use.

If you think you know a place with good BLE coverage and you're ready to play with the experimental version of WiFiLine, please, let me know and I'll send it to you.

Sunday, April 26, 2015

OFF: how Google makes Android worse

Some days ago I got OTA update with Android 5.1. This version does probably improve something, but it apparently impairs other things as well. And because it's related to the system carousel - the list of running applications (on the so called "recents" screen) - which has bad design from the very beginning of Android 5, I couldn't help but writing about this miserable part of UI.

First, I must say that the carousel is constantly changing from one Android version to another between horizontal and vertical layout, as though Google's developers are uncertain themselves which variant is appropriate. The carousel is used intensively, so even a little flaw in its design brings big problems. I'm currently speaking about standard Android in general, and standard Android 5 in particular. Other customized firmware may be free of the issues I'm going to describe.

In Android 5, the carousel is always vertical on my pad. Well, it could be appropriate for portrait screen orientation, but the carousel remains vertical even in landscape mode. When you try to flip through the apps from bottom to top, you will constantly activate the central button of the navigation bar, floating in foreground (over the carousel!), especially because the button has an enlarged sweetspot, because it's Google button (it invokes "Google App" including quicksearchbox and other stuff). It took me a long time to get accustomed to the new - not so convenient - practice: in order to swipe apps from bottom to top, one should swipe from center! Otherwise you'll get into the Google app.

That's how it was since Android 5.0. In Android 5.1 they introduce new strange bug in the carousel. It does not work when you tap an app in it first time. After the first tap the carousel is closed and the launcher is shown. Only if you open the carousel second time, it works as expected.

I know, this is far from the main subject of the blog, but at last it's all about Android. And I'd prefer that Android would not prevent me from working with my applications.

Monday, March 16, 2015

WiFiLine's synthetic probability algorithm revealed

As I promised, I'm posting a short explanation of the built-in "synthetic probability" location algorithm. To start with it let us consider how WiFi data looks like. When you scan a building you get a list of geographical points with WiFi measurements, each of them contains signals from several hotspots, and every hotspot signal is normally detected many times demonstrating varying levels - so called RSSI (Received Signal Strength Indication). All this data is stored in the map, and can be viewed - just switch to the "Scan and Edit" mode, open the list of points and expand any of them - you'll see the names of hotspots detected in this point. Tap a hotspot to open a dialog presenting this spot's RSSI distribution.

Usually it's bumpy, but the more scans you perform in a point the more smooth distribution you get. According to the Central Limit Theorem the distribution tends to be Normal (Gaussian), so one can estimate its mean and standard deviation. This is where the word "synthetic" in the algorithm name comes from: it implies that we can build a synthetic (near to real, yet artificial) probability distribution (mass) function from acquired WiFi data.

Of course, this is just a probability of getting specific signal level of a hotspot S in a point X. We need to combine all such probabilities somehow in order to calculate current user location. And here is the Bayes theorem comes in handy.

P(A | B) * P(B) = P(B | A) * P(A), where

  • P(A) and P(B) are the probabilities (priors) of A and B independent of each other;
  • P(A | B) is the conditional probability of A given that B is true;
  • P(B | A) is the conditional probability of B given that A is true;
Now let us add some suffixes to bring it into our context:

P(Asi | Bxj) * P(Bxj) = P(Bxj | Asi) * P(Asi), where

suffixes 'si' and 'xj' stand for i-th hotspot and j-th point respectively, so:

  • P(Asi) is the prior probability of i-th hotspot;
  • P(Asi | Bxj) is the conditional probability of observing i-th hotspot in j-th point;
  • P(Bxj) is the prior probability of j-th point;
  • P(Bxj | Asi) is the conditional probability of being in j-th point provided that i-th hotspot signal is detected;
Apparently, it's the last probability which we are after:

P(Bxj | Asi) = P(Asi | Bxj) * P(Bxj) / P(Asi)

In the right side of the equation, P(Asi) can be estimated as a percentage of points where the hotspot Si was detected. P(Bxj) is initially 1/N for every point, where N is the total number of points, but later it can be refined by applying our knowledge about current user location and reducing N to a reasonable neighbourhood. At last P(Asi | Bxj) is the probability obtained from the synthesized probability distribution of signals described above.

Having P(Bxj | Asi) for different i-s in every point j, we should combine them by hotspots (by i) in order to get the total probability of point Xj. According to the formula of mutually non-exclusive events:

P(Xj) = P(Σi(Bxj | Asi)) = 1 - Πi(1 - P(Bxj | Asi))

The point with largest value (or an average of K points with largerst values) is most likely the current location of the user.

This is a simplified description of the "synthetic probability" algorithm. It does not cover many nuances such as signal level calibration and in-motion scanning but gives you an idea of how WiFiLine works behind the scenes.