Fix very low call volume on OnePlus 3

I recently faced this issue after a recent update on my OnePlus 3. The guide at – http://innov8tiv.com/fix-low-call-volume-oneplus-3t-calls/ is pretty good. However, the “Safe Mode” test failed for me, then next steps was clean with toothbrush.

I was very much convinced that my ear piece was pretty clean and anyway I wiped it with cloth many times and there was no visible dirt. However, when I took closeup picture of the earpiece I noticed that the grill mesh was very very small. And it seemed like maybe those tiny holes in the grill have been plugged by microscopic dirt.

Checkout the grill closeup below (this is after cleaning):-

I didn’t have a toothbrush with fine enough bristles. So instead I used a cotton swab, dipped in nail polish remover. I rubbed with that couple of times and presto, the low volume issue is fixed!

TIP: You need to tilt the handset sideways against the sunlight (as shown in the pic above) to be able to see the grill holes. If they look dark then the hole is open, else maybe they are plugged.

Tracing $scope events in AngularJS

Scope events in AngularJs is very powerful and useful in connecting disparate modules and directives. However, in a complex application things could become complicated and it would become hard to trace which event is triggered when and who act on those events.

I wrote a simple event tracer which displays a floating div with logs of all the events. The log is color coded, based on if the event was emitted, broadcasted or received.

The log looks like this:-

Event Trace

Event Trace

You can turn on or off the event data using the dataf link. The stackf toggles if the call stack or function code too will be shown which has emitted or consumed this event.

The code:-

var app = angular.module('tracer', []).run(Injector);

function Injector($rootScope) {
    'use strict';

    function log(msg, color) {
        var el = document.getElementById('tracerConsoleUl');
        el.innerHTML += itemHtml.format(color, msg)
    }

    function stringify(o) {
        try {
            return JSON.stringify(o, null, 3);
        } catch (err) {
            console.error('Scope Hack:', err, ' Actual object:', o);
            return '<i>Err: See console</i>';
        }
    }

    function getStackTrace() {
        var r;
        if (Error.captureStackTrace) {
            var obj = {};
            Error.captureStackTrace(obj, getStackTrace);
            r = obj.stack;
        } else if (Error.stack) {
            r = Error().stack;
        } else {
            r = '';
        }

        r = r.replace(/</gm, '&lt;').replace(/>/gm, '&gt;');
        r = r.replace(/^[^\s]+.+$/gm, ''); // removing first line
        r = r.replace(/^\s+at (.+) \([^(]+\)+$/gm, '$1');
        r = r.replace(/^\s+at [^()]+$/gm, ''); // removing rows with only text of format at ... and nothing in braces on right.
        r = r.replace(/\n\n/gm, '\n');
        r = r.replace(/\n/gm, ' &lt; ');

        return r;

    }

    function filter(e, clazz) {
        e.stopPropagation();
        e.preventDefault();

        var flag = angular.element('#tracerConsoleUl').hasClass(clazz);
        if (flag)
            angular.element('#tracerConsoleUl').removeClass(clazz);
        else
            angular.element('#tracerConsoleUl').addClass(clazz);
    }

    function fclear(e) {
        e.stopPropagation();
        e.preventDefault();

        angular.element('#tracerConsoleUl').empty();
    }

    function fmoveStart(e) {
        e.stopPropagation();
        e.preventDefault();

        angular.element(document).on('mousemove.scopehack', fmove).on('mouseup.scopehack', fmoveEnd);
        var el = angular.element('#tracerConsole');
        var left = e.pageX - parseInt(el.css('left'));
        var top = e.pageY - parseInt(el.css('top'));
        el.data('left', left);
        el.data('top', top);
    }

    function fmoveEnd(e) {
        e.stopPropagation();
        e.preventDefault();

        angular.element(document).off('.scopehack');
    }

    function fmove(e) {
        e.stopPropagation();
        e.preventDefault();

        var el = angular.element('#tracerConsole');

        var p = {
            left: (e.pageX - el.data('left')) + 'px',
            top: (e.pageY - el.data('top')) + 'px'
        };

        angular.element('#tracerConsole').css(p);

    }

    try {
        if (!String.prototype.format) {
            String.prototype.format = function() {

                var args = arguments;

                return this.replace(/{(\d+)}/g, function(match, number) {
                    return typeof args[number] != 'undefined' ? args[number] : '<i>NA</i>';
                });
            };

        }


        var html = "<div id='tracerConsole' style='position:absolute;top:50px;left:50px;background-color:white;border:2px solid black;z-index:1000;'>" +
            "<div><span class='ffilter' style='cursor:pointer;'>stackf</span> | <span class='dfilter' style='cursor:pointer;'>dataf</span> | <span class='fclear' style='cursor:pointer;'>clear</span> | <span class='fmove' style='cursor:move;'>move</span>" +
            "</div><ul id='tracerConsoleUl' style='display:block;list-style-type:none;max-height:500px;width:300px;overflow:scroll;'></ul></div>";



        var itemHtml = "<li style='border-bottom:1px solid gray;white-space:pre-wrap;background-color:{0};'>{1}</li>";

        var proto = Object.getPrototypeOf($rootScope);
        var oldOn = proto.$on;
        proto.$on = function mangaledOn(e, f) {
            var fWrapper = function fWrapper(e, d) {
                log('EVENT RECEIVED: {0} <span class="d">\nWITH DATA: {1}</span> <span class="f">\nBY f: {2}</span>'.format(e.name, stringify(d), f.toString()), '#7FD7B6');
                f.call(this, e, d);
            };

            oldOn.call(this, e, fWrapper);
        };

        var oldBroadcast = proto.$broadcast;
        proto.$broadcast = function mangaledBroadcast(e, d) {
            log('EVENT BROADCASTED: {0} <span class="d">\nWITH DATA: {1}</span> <span class="f">\nBY s: {2}</span>'.format(e, stringify(d), getStackTrace()), '#FF9C01');

            oldBroadcast.call(this, e, d);

        };

        var oldEmit = proto.$emit;
        proto.$emit = function mangaledEmit(e, d) {
            log('EVENT EMITTED: {0} <span class="d">\nWITH DATA: {1}</span> <span class="f">\nBY s: {2}</span>'.format(e, stringify(d), getStackTrace()), '#FFE78C');

            oldEmit.call(this, e, d);
        };

        console.log('Scope Hack Injected');
        angular.element('body')[0].innerHTML += html;
        angular.element('#tracerConsole .dfilter').on('click', function dfilter(e) {
            filter(e, 'dhide');
        });

        angular.element('#tracerConsole .ffilter').on('click', function ffilter(e) {
            filter(e, 'fhide');
        });
        angular.element('#tracerConsole .fclear').on('click', fclear);
        angular.element('#tracerConsole .fmove').on('mousedown', fmoveStart);
        angular.element(document).find('head').prepend('<style type="text/css">#tracerConsoleUl.dhide .d {display:none;}#tracerConsoleUl.fhide .f {display:none;} #tracerConsoleUl li{-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;}</style>');

    } catch (err) {
        console.error('Scope Hack:', err);
    }
}

To use this, paste this into some script block or some js file and make sure your app’s module depends on this module – tracer.

Using pinned self-signed SSL certificate with Android Volley

Recently for one of my Android apps I wanted to use a self-signed certificate on the server-side. However, if you try to connect to such a server with default settings, the connection is going to be refused. This is because it has been signed by you (your server domain), and ‘you’ is not trusted by any system.

Read the excellent article – Android security – Implementation of Self-signed SSL certificate for your App. This describes all the concepts and the pros and cons of pinning. However, it does not describe how to do this in Volley. The internet is littered with many articles on this, and most of them are outdated and can’t be used now.

So, here is an updated guide. I have tested this on my app which uses Android API 22 and Volley code downloaded on Jan 2015.

First use the official guide to create a singleton class to get the request queue. Now below is the modified code which takes care of the SSL pinning.

public class MySingleton {
    private static char[] KEYSTORE_PASSWORD = "YourKeyStorePass".toCharArray();

    ...

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, newSslSocketFactory()));
        }
        return mRequestQueue;
    }

    ...

    private SSLSocketFactory newSslSocketFactory() {
        try {
            // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");
            // Get the raw resource, which contains the keystore with
            // your trusted certificates (root and any intermediate certs)
            InputStream in = mCtx.getApplicationContext().getResources().openRawResource(R.raw.codeprojectssl);
            try {
                // Initialize the keystore with the provided trusted certificates
                // Provide the password of the keystore
                trusted.load(in, KEYSTORE_PASSWORD);
            } finally {
                in.close();
            }

            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(trusted);

            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);

            SSLSocketFactory sf = context.getSocketFactory();
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

Please note, that the above solution will work for Android API 9 and above. To support the below versions you need to pass an instance of HttpClientStack instead of HurlStack. So, replacing the line

mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HurlStack(null, newSslSocketFactory()));

with

mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(), new HttpClientStack(new MyHttpClient()));

might work. Where MyHttpClient is the one defined in the CodeProject article. I haven’t tested this part though.

Securing the WordPress Login page

Of late I have seen quite a lot of brute force attempts to login into the admin account of this blog. The source IPs are wide and varied, ranging from Istanbul, Germany, Greece, US and more. In fact according to ArsTechnica article this has been happening for sometime now on a huge scale. I see a POST request on my site every few hours. Recently I submitted a list of such IPs to Deutsche Telekom Abuse Team for the offending IPs from their network.

Protection from the attack ArsTechnica suggested using

Limit Login and WP Better Security plugins. Limit Login is a simple and must have plugin. It blocks an IP for some hours our repeated failure attempts. Of course for this to be useful you need to have a strong password like mine, which is ….. . However, WP Better Security is the best but makes some drastic changes to your site. I don’t like to make so many changes, and would prefer that WordPress came bundled with those features. One of those features is modifying the login url. By default it is like http://blog.host/wp-login.php. This makes it an easy target. One simple way to fix this is change it.

Changing your login url

I do not want to physically rename wp-login.php, since that would mean after a WordPress upgrade the change would be gone. The other way is to rename it in your web-server configuration. Below is my relevant Nginx configuration. (If you are still using Apache then you may want to switch to Nginx.)

#!cink
server {
    server_name blog.host;
    # Other configs like root etc...
    location ^~ /wp-login.php {
        include phpparams.conf;
        if ($request_method = POST) { return 444; }
    }
    location ^~ /your-secret-login-page-name.php {
        rewrite ^ /wp-login.php break;
        include phpparams.conf;
    }
    # ...
}

The above config blocks all POST request to wp-login.php but allows GET requests. So, wp-login.php would show up but if someone tries to submit on that page then the server will close the connection (status 444 is a special code which instructs Nginx to close the remote connection). Since we are using ^~ to prevent Nginx from matching to any more regex locations so, if you have any location directive to match .php won’t be used. So, effectively your wp-login.php file source would be sent to the user instead of executing them. That is why I have included phpparams.conf. See the Nginx migration guide for the contents of phpparams.conf.

One Caveat

Now even if you open your-secret-login-page-name.php, the form will still send POST requests to wp-login.php, because after all that is the page which is being served. So, either you need to use web developer tools like Inspector to modify the web code or better write a GreaseMonkey or TamperMonkey script to do that for you.

Process to get new LPG gas connection

There are no silver bullets but it is better to know the process to avoid harassment. I recently got Indane LPG gas after making rounds of their office for about a month. Most of these was due to the lack of info. SO, here you go.

First step is you need to get the KYC (Know Your Customer) form from the dealer. There is nothing special about it so, even if someone happens to have a soft copy of it then you can get it printed. I had to wait for 2 weeks to get this. One format of KYC document is available on Indane site (here), but the one I got from the dealer was a little different. They will also give you an affidavit form, which you need to get printed on a Rs 20 Stamp Paper and notarized by a lawyer. You can get that form from here.

Tip: If you live anywhere in Hitec City, Hyderabad then there is a small shop available in Madhapur to Stamp Papers and notarize documents. – http://maap.in/w

Fill the KYC form and affix a colored photograph. Along with the Stamp Paper and KYC you need to attach your address proof and photo Id proof. Also you should provide the Aadhaar number on the KYC form, though its photocopy need to be attached if you are providing another photo Id proof.

Some of the accepted address proofs are:-

  • Rental agreement
  • Credit / Bank statement
  • Electricity bill
  • Self-declaration attested by Gazetted officer
  • Flat allotment/possession letter

Some of the accepted photo Id proofs are:-

  • Passport
  • Aadhaar
  • Pan Card
  • Driving License

Once you submit all the above, they say, they will make a verification call in a week. After that you may go to your dealer and deposit a sum of Rs. 5000 to get the new connection.

However, in my case they did not call me. When I anyway went there after a week I got the connection. I had to dig through the applications dump to find mine. Rs. 5000 deposit includes price of gasket, regulator, one cylinder, a stove and other misc charges. Buying stove from them is purely optional, but the agency might try to force you to buy that.

Along with the above stuffs you get a connection certificate sort of paper and the gas booklet. According to the agency you can only apply for single cylinder when you are getting a new connection. You can get the second one after three months.

Hope the above info helps. May god help you with your Gas hunting. 😛

More info:-

Configure your Belkin N150 router for BSNL broadband

  1. Login to your router dashboard. It should usually be at http://192.168.2.1.
  2. Now under “Inertnet WAN” click on “Connection Type”.
  3. Here select “PPPoE” and click on Next.
  4. Provide your BSNL username and password. Leave the “Service Name” field blank.
  5. For “VPI/VCI” field provide 0 and 35 as values.
  6. For “Encapsulation” field choose “LLC”.
  7. Leave all other fileds to their default values.
  8. Now Apply Changes. It will take around 30 seconds for the router to restart.
  9. Notice the top-right corner of the page and check the status. If it says “Connected”, then you are good to go.
  10. Else click on the “Home” link near that. On the home page click on Connect button.

Now you should be able to use this Wifi router with your BSNL broadband. Happy surfing. 🙂

PS: Now you no longer need to use your computer to dial the BSNL connection and login. Your router has your BSNL username and password, it will do the dialing and logging in.