Android - UnsupportedOperationException: Cant convert to color: type=0x2

 You are most likely trying to reference a styleable attribute of reference type in a drawable resource and it bailed out with an UnsupportedOperationException.

   <shape android:shape="rectangle">
        <solid android:color="?attr/item_background" />
    </shape>

Sadly this is the case with pre-lollipop android versions. There isn't any way to reference attributes in a drawable resource. This works just fine in Lollipop though and is tracked here. The only other alternative is to have a hard coded color specific drawable resource for older platform versions.

/drawable/red_rectangle.xml

   <shape android:shape="rectangle">
        <solid android:color="@android:color/red" />
    </shape>

/drawable/orange_rectangle.xml

   <shape android:shape="rectangle">
        <solid android:color="@android:color/orange" />
   </shape>

Android - negative x and y in onTouchEvent

    Android's handling of touch event recommends to use getX() and getY() when trying to determine the coordinates with respect to the actual view that is being touched. This makes sense for most use cases and developers might expect positive values starting from 0,0 to width-1,height-1.

   However, there are cases when getX() and getY() would return negative values and values greater than the actual view dimensions. The reason is due to padding added to the view. Padding unlike margin is internal to the view. Clicking on the padded space before the start of the view dimensions would result in a negative values. The best solution is just to check for the touch coordinates to be within the view boundary and then handle appropriately.

Android - Chrome browser tabs as separate tasks in Recent Apps Overview

    Chrome browser for Lollipop and above introduced a nice feature to show tabs as separate tasks in Recent Apps Overview. Users can turn off this feature via settings. For some reason, this was only supported from Lollipop and not for older versions of Android. This is most likely due to SDK APIs introduced in API level 21, ActivityManager.AppTask. This is meant for applications wanting to manage tasks in recent apps overview. Applications can determine how and when activities appear in the overview screen. Chrome browser is most likely using these APIs restricting the feature to Lollipop and above.

   Besides, Chrome also has the ability to resume the last used task when users launch the browser via launcher's shortcut. In a typical android application, android's launcher shortcut would always fire a specific intent resolving to the main activity of the application. In case of Chrome, this action is mapped to launch the last used tab and not the first tab. How is this implemented? Are there any hooks that applications can register with the framework? This is even more challenging as android supports third party launchers. Turns out, this is most likely extra bookkeeping done by Chrome (thanks to a crash report).

android.app.IAppTask$Stub$Proxy.moveToFront(IAppTask.java:172)
android.app.ActivityManager$AppTask.moveToFront(ActivityManager.java:2742)
com.google.android.apps.chrome.document.ChromeLauncherActivity.relaunchTask(ChromeLauncherActivity.java:458)
com.google.android.apps.chrome.document.ChromeLauncherActivity.launchLastViewedActivity(ChromeLauncherActivity.java:254)
com.google.android.apps.chrome.document.ChromeLauncherActivity.handleIntent(ChromeLauncherActivity.java:180)
com.google.android.apps.chrome.document.ChromeLauncherActivity.onCreate(ChromeLauncherActivity.java:104)
android.app.Activity.performCreate(Activity.java:5977)
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2259)

    ChromeLauncherActivity seems to be the main activity launched via launcher shortcut. This in turn seems to be aware of the last used tab and the corresponding activity (DocumentActivity). From here on, it just uses the corresponding AppTask and requests for it to be moved to front. As a side note, the activity for each tab, DocumentActivity doesn't have a taskAffinity.

Android - Tinting RippleDrawables

     Android starting from Lollipop provided ability to tint UI elements for better user feedback. Pressing and holding a button starts a tint animation which is stopped as soon as the user lets go of the button. This is done by handling the touch event and using appropriate drawable resources to redraw the button. Besides, the tint animation is pivoted at the touch coordinates known as a hot spot. Lollipop by default supports tint animations for all applications and the SDK offers API to customize the color. This would be needed for applications which have a custom color for the UI elements and the platform's tint color doesn't necessarily work with the app custom color. Besides, new versions of support library even offers compat versions to apply tint colors.

   DrawableCompat offers a setTintList API to apply tint colors based on various states of the drawable resource. This is usually applicable for stateful drawables (Drawable.isStateful).

        DrawableCompat.setTintList(Drawable drawable, ColorStateList tint)

  Now, applying this to a button by extracting its background as a Drawable ( Button.getBackground()) should work as expected. However, the tint animation doesn't seem to pick the specified color. It turns out that setTintList() doesn't have any effect on RippleDrawables. RippleDrawable being a extension of LayerDrawable should support setTintList(). This is more so because setTintList is defined at Drawable and it would seem that all kind of Drawables would support the feature. So what is different with RippleDrawable? It turns out that RippleDrawable disables layer specific feature of LayerDrawable. It doesn't super any specific constructor of LayerDrawable causing the default one to be invoked.

    LayerDrawable() {
        this((LayerState) null, null);

    }

   setTintList on a LayerDrawable constructed via default constructor is basically a no-op as mLayerState.mNum is 0.

    @Override
    public void setTintList(ColorStateList tint) {
        final ChildDrawable[] array = mLayerState.mChildren;
        final int N = mLayerState.mNum;
        for (int i = 0; i < N; i++) {
            final Drawable dr = array[i].mDrawable;
            if (dr != null) {
                dr.setTintList(tint);
            }
        }
    }

  RippleDrawable doesn't help by not overriding setTintList for ripple specific behavior instead just uses the LayerDrawable's version. This is why setting a ColorStateList on a RippleDrawable via DrawableCompat.setTintList() doesn't have any effect.

   Fortunately, RippleDrawable has another API, setColor(ColorStateList) for the same purpose. This basically means, application developers have to check instance of the Drawable and invoke an appropriate API. This could be fixed in the support library version abstracting the internals.

    public static void setTintList(Drawable drawable, ColorStateList tint) {
        if (drawable instanceof DrawableWrapperLollipop) {
            // GradientDrawable on Lollipop does not support tinting, so we'll use our compatible
            // functionality instead
            DrawableCompatBase.setTintList(drawable, tint);
        } else if ( drawable instanceof RippleDrawable) {
            ((RippleDrawable) drawable).setColor(tint);
        } else
            // Else, we'll use the framework API
            drawable.setTintList(tint);
        }
    }

In any case, all extensions of Drawable including RippleDrawable should support the base contract and override setTintList() for custom logic.

Android - Could not read input channel file descriptors from parcel

     This is one crazy runtime failure that doesn't really give any straight forward hint to application developers. The stack trace is usually related to android's internal classes with the top of the frame failing in InputChannel.

java.lang.RuntimeException: Could not read input channel file descriptors from parcel.
       at android.view.InputChannel.nativeReadFromParcel(InputChannel.java)
       at android.view.InputChannel.readFromParcel(InputChannel.java:148)
       at android.view.InputChannel$1.createFromParcel(InputChannel.java:39)
       at android.view.InputChannel$1.createFromParcel(InputChannel.java:36)
       at com.android.internal.view.InputBindResult.<init>(InputBindResult.java:62)
       at com.android.internal.view.InputBindResult$1.createFromParcel(InputBindResult.java:102)
       at com.android.internal.view.InputBindResult$1.createFromParcel(InputBindResult.java:99)
       at com.android.internal.view.IInputMethodManager$Stub$Proxy.windowGainedFocus(IInputMethodManager.java:851)
       at android.view.inputmethod.InputMethodManager.startInputInner(InputMethodManager.java:1292)
       at android.view.inputmethod.InputMethodManager.onWindowFocus(InputMethodManager.java:1518)
       at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:3550)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:157)
       at android.app.ActivityThread.main(ActivityThread.java:5293)
       at java.lang.reflect.Method.invokeNative(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:515)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
       at dalvik.system.NativeStart.main(NativeStart.java)


 A look at the InputChannel's code reveals the scenario in which the exception is thrown.

            int dupFd = dup(rawFd);
            if (dupFd < 0) {
                ALOGE("Error %d dup channel fd %d.", errno, rawFd);
                jniThrowRuntimeException(env,
                        "Could not read input channel file descriptors from parcel.");
                return;
            }

    The dup() system call has failed and bailed out with a negative file descriptor. The error code is being logged as well. One of the cases where the dup() system call fails is when the number of open file descriptors exceeds a certain limit. The application process is probably leaking file descriptors including sockets and pipes due to some bug. In most applications, this kind of a leak adds on with time and it's too late when the maximum limit is reached. The exception by itself is not a problem instead is just an effect of an earlier cause.

    This can be demonstrated with Android SDK's public API to duplicate files. The brute force logic to duplicate file descriptor in a loop causes a similar failure. The initial loop ends up opening 970 file descriptors. 970 is just based on trial and error for the application running in Nexus 4 with Lollipop. At the end of the first loop, the process had around 1010 open file descriptors, 14 short of the maximum limit. The next loop tries to create dialogs. The dialog creation logic involves a bunch of file operations including the one in InputChannel and as expected the process crashes with a "Too many open files" error.

                mCount = 0;

                while( true )
                {
                    try {
                        Os.dup( FileDescriptor.out );
                        mCount++;
                        if( mCount >= 970 )
                        {
                            break;
                        }
                    } catch (ErrnoException e) {
                        throw new RuntimeException( "Exception [" + e.getMessage() + "]" );
                    }
                }

                mCount = 0;

                while( true )
                {

                    mainHander.post(new Runnable() {
                        @Override
                        public void run() {
                              AlertDialog.Builder builder = new AlertDialog.Builder( this );
                              builder.setMessage("Dialog, hoping to be created");
                              AlertDialog mDialog = builder.create();
                              mDialog.show();
                        }
                    });

                    try {
                        Thread.sleep( 500 );
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

Android - Marshmallow Now on Tap

   One of the fascinating features of Marshmallow was Now on Tap. It crawls through the screen content and displays relevant information.




    The trigger event is to long press the home button for few seconds. This kind of suggests that some kind of intent is being fired and is being handled by a background application launching the relevant UI. However, an intent design can be hijacked by third party applications and might be confusing for users expecting Now on Tap. In fact, firefox managed to trap the launch intent for Google Now cards in earlier versions of Android. Based on the logs, this seems to be some kind of platform level core changes and surprisingly is coupled with speech and audio inputs. It looks like the touch event is being routed as audio inputs. This feature was either designed with speech input as a trigger event or speech input is further down the line.

MicrophoneInputStream: mic_starting com.google.android.apps.gsa.speech.audio.y@4799571
MicrophoneInputStream: mic_started com.google.android.apps.gsa.speech.audio.y@4799571
MicrophoneInputStream: mic_close com.google.android.apps.gsa.speech.audio.y@4799571

Android - Marshmallow WifiInfo's getMacAddress()

     Android Marshmallow changed the behavior for WifiInfo's getMacAddress() due to privacy concerns. The API wasn't removed or deprecated instead was just updated to return a default address 02:00:00:00:00:00. The intention is to block access to device's hardware identifier using WiFi and Bluetooth APIs. In addition, any background wifi or bluetooth scan is shown as being generated by random mac address starting from Android 6.0. This doesn't let tracking softwares like the ones used in coffee shops, malls do what they used to do, study customer behavior without their knowledge, just because they had their wifi or bluetooth turned on by default. This is similar to what Apple did to iOS8. In Android 6.0, there are still few apps like Settings which continue to have access to the mac address. This is accessed via Settings > About Phone > Status or via Settings > WiFi > Advanced.



     Is there any other hidden API that might return the actual mac address? A quick look at Settings code in AOSP reveals the very same API, WifiInfo.getMacAddress(). So how does settings app continue to have exclusive access to the mac address? Turns out, this is controlled by a permission LOCAL_MAC_ADDRESS. Obviously, this permission is hidden from the SDK to deny access for third party applications.

    <!-- @SystemApi Allows applications to read the local WiFi and Bluetooth MAC address.
    <permission android:name="android.permission.LOCAL_MAC_ADDRESS"
                android:protectionLevel="signature|privileged" />


   Settings application doesn't actually request for this permission in the manifest and yet this works due to the permission's prerequisites. Any application signed with the same certificate as that of the system declaring the permission would get an implicit grant.