Android - Orange WiFi Icon in Quick Settings

     Android's Quick Settings occasionally shows an orange WiFi icon of varying strengths and this kind of indicates connectivity issues even though the device is connected to a wireless network. So who is responsible for updating this and what is the trigger entry point?

   The actual drawable resources used is specific to the SystemUI application provided as,

              R.drawable.ic_qs_wifi_0,
              R.drawable.ic_qs_wifi_1,
              R.drawable.ic_qs_wifi_2,
              R.drawable.ic_qs_wifi_3,
              R.drawable.ic_qs_wifi_4,
              R.drawable.ic_qs_wifi_0,
              R.drawable.ic_qs_wifi_full_1,
              R.drawable.ic_qs_wifi_full_2,
              R.drawable.ic_qs_wifi_full_3,
              R.drawable.ic_qs_wifi_full_4

   An appropriate drawable resource is picked up based on the reported connectivity status via broadcast intent ConnectivityManager.INET_CONDITION_ACTION. This intent is supposed to be fired with an integer extra (ConnectivityManager.EXTRA_INET_CONDITION) indicating the status. Connectivity is considered to be good if the reported status is more than 50, otherwise the WiFi icon turns orange and a corresponding drawable resource based on the signal strength is picked up.

    So who fires this Intent and does it need any permission? The intent action INET_CONDITION_ACTION is declared as a protected broadcast and is fired by the framework's ConnectivityService when the status is reported.

<protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />

  This status change is reported by a ConnectivityManager API, reportInetCondition which is hidden from the SDK and any update is actually validated by the framework by enforcing a permission check. The client for this status update should have android.Manifest.permission.STATUS_BAR permission in its manifest.

   But whats the point of having a hidden API and how can it possibly be executed? Thanks to a Null Pointer Exception, it turns out that few google applications actually invoke this API via reflection,

E/GTalkService( 1291): calling reportInetCondition failed
E/GTalkService( 1291): java.lang.reflect.InvocationTargetException
E/GTalkService( 1291):  at java.lang.reflect.Method.invokeNative(Native Method)
E/GTalkService( 1291):  at java.lang.reflect.Method.invoke(Method.java:507)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.service.GTalkService.reportInetCondition(GTalkService.java:329)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.GTalkConnection.doConnect(GTalkConnection.java:2004)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.GTalkConnection.access$300(GTalkConnection.java:110)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.GTalkConnection$WorkerHandler.handleMessage(GTalkConnection.java:278)
E/GTalkService( 1291):  at android.os.Handler.dispatchMessage(Handler.java:99)
E/GTalkService( 1291):  at android.os.Looper.loop(Looper.java:134)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.service.GTalkService$WorkerThread.run(GTalkService.java:244)

  So how about a test application using the same reflection approach?

    private void reportINetCondition() {
        ConnectivityManager manager = (ConnectivityManager) this.getSystemService(CONNECTIVITY_SERVICE);

        Class<?> c = manager.getClass();
        Method[] allMethods = c.getDeclaredMethods();

        try {

            for (Method method : allMethods) {
                if (method.getName().startsWith("reportInetCondition")) {
                    method.invoke(manager, ConnectivityManager.TYPE_WIFI, 25);
                    break;
                }
            }

        } catch (Exception e ) {
        }
    }

    Unfortunately, this fails with a Security Exception,

Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10086 nor current process has android.permission.STATUS_BAR.

   The permission STATUS_BAR is meant only for system applications and indeed quite a few google applications are installed as system applications and they get to update the connectivity status.

updated-package name="com.google.android.googlequicksearchbox" codePath="/system/priv-app/googlequicksearchbox.apk"
updated-package name="com.android.vending" codePath="/system/priv-app/Phonesky.apk"

 Lets try pushing our application to the system partition on a rooted device.

adb root
adb shell mount -o rw, remount /system
adb push test.apk /system/app

  Unfortunately, this too doesn't work as the permission STATUS_BAR is granted only to those system applications which are signed by the platform signature. But yes, once the test application is signed with an appropriate signature, we would be able to update the connectivity status with ease.

No comments: