androidcastinggoogle-castwifi-directmiracast

Steps to programmatically cast from android to miracast receiver


I'm trying to write an app that will start casting the screen from an Android phone to a TV via miracast. I'm using an HDMI dongle since the TV in question doesn't natively support miracast. I have been trying the code here, but it needs an Application ID which I have got following these steps. My question is, the instructions seem to indicate that I need to register the miracast dongle so it will talk to an unpublished 'debug' app. However, only Google Cast devices are mentioned and that isn't the same protocol as miracast. Do I still need to register the dongle?

Is there a simpler way of programmatically casting to a device via miracast? A requirement is no user interaction, so I can't just display a cast button.

I'm using Android 5.1 if that's relevant.

EDIT: After further research, I realized that Google Cast uses a completely different protocol from Miracast, and thus all the talk of registering the dongle is irrelevant. No registration is required at all to do Miracast in Android. The issue is the API is hidden, see my answer below for details.


Solution

  • So this is possible, but only on custom versions of Android due to permission problems.

    What you need to use

    The hidden part of the WifiDisplay API makes it all possible. This file contains examples of how to use the API to cast the display. It appears that Google will release it publicly at some point, although it's still hidden in the latest master of API 23 as far as I can see.

    How to access the hidden API

    To use hidden APIs, this guide(mirror here) provides a good introduction. If you're using API 22+ however, then that guide won't work as the format of android.jar has changed and classes.dex has been split across multiple files. So this advice is more accurate in that case. Note that the postscript about framework-classes2.dex must also be done; it isn't optional.

    The latest version of the dex2jar tool fails to turn the .dex file from API 22 into a jar. The solution is mentioned by the author here. I opted to patch the tool instead of changing the dex, as that didn't work for me. Simply change the line the author mentions from throwing a RuntimeException to:

    return TypeClass.INT;
    

    How to get permission to use the hidden API

    Once that is all done, the next step is giving your app the CONFIGURE_WIFI_DISPLAY permission. Unfortunately, as you can see here, it has system-level protection. This means that your app must be signed by the same key as the system to use this permission. So unless you have Google's private key, you can't have your app run on normal Android phones. My solution was to build a custom version of CyanogenMod(using this guide), with the permission changed from 'system' to 'normal'. This eliminates the need to bother with signing anything. I also did the same for the CONTROL_WIFI_DISPLAY permission. Whilst I'm not entirely sure this is necessary, it doesn't hurt. Both these permissions are located in frameworks/base/core/res/AndroidManifest.xml. Change the lines 2161-2169 from:

    <permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
        android:protectionLevel="signature" /> 
    <permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
        android:protectionLevel="signature" />
    

    To:

    <permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
        android:protectionLevel="normal" /> 
    <permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
        android:protectionLevel="normal" />
    

    Then build CyanogenMod as normal. I can confirm this does work, but this limits your app to running on devices which have this custom version of CyanogenMod installed. Furthermore, installing CyanogenMod on an Android phone will generally invalidate the warranty.