14

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.

CalumMcCall
  • 1,665
  • 4
  • 24
  • 46
  • "Do I still need to register the dongle?" -- not in terms of registering something with Google, as a Miracast dongle is not a Google Cast device (e.g., Chromecast). "Is there a simpler way of programmatically casting to a device via miracast?" -- programmatically *connecting* to a Miracast device is impossible AFAIK. The user has to do that. Given that you're already connected to it, you are already "casting" to it (mirrored display output by default, or use a `Presentation` to control the external display separately). – CommonsWare Nov 20 '15 at 14:25
  • I have managed to connect to the device via WiFi-direct successfully. Are you saying that there is no way for me to then do the 'Cast Device' step programmatically? – CalumMcCall Nov 20 '15 at 14:28
  • "I have managed to connect to the device via WiFi-direct successfully" -- I do not know what you mean by that. "Are you saying that there is no way for me to then do the 'Cast Device' step programmatically?" -- if you're asking "can I start the display mirroring programmatically", then AFAIK the answer is "no". However, I would describe that as "connecting". With Chromecast, there is a dedicated "cast" operation to say what media to play back. With external displays (HDMI, MHL, SlimPort, Miracast, etc.), if there's a connection, display mirroring is automatic. – CommonsWare Nov 20 '15 at 14:34
  • The dongle I'm using requires me to connect via WiFi-direct, and then after a connection is established, I then have to click the cast button and select the target device. Only then does casting begin. The first step I've managed to do, using WifiP2pManager, that's what I meant. It sounds like perhaps I'm using a protocol that isn't miracast, given your description of how that works, since you only list a single step. – CalumMcCall Nov 20 '15 at 14:38
  • 1
    Well, Miracast uses WiFi P2P IIRC. My guess is that we're just using different terminology. Regardless, I know of no way to start showing the device screen (or a `Presentation`) on an external display without user involvement. In fact, if it *is* possible to do that without user involvement, that's a bug that needs to be fixed. Apps should not be able to start projecting to random equipment (that they detect and connect to) without user consent, for privacy and security reasons. – CommonsWare Nov 20 '15 at 14:41
  • Yes agreed, it's definitely a security concern. In this case, the phone would only broadcast to devices the user has explicitly authorized. But the casting doesn't seem to have any 'memory'. What is the feasibility of doing miracast programmatically on a rooted phone? – CalumMcCall Nov 20 '15 at 15:02
  • @CommonsWare Also, can you please write an answer to this question saying this isn't possible and I'll accept it? – CalumMcCall Nov 20 '15 at 15:24
  • 1
    "What is the feasibility of doing miracast programmatically on a rooted phone?" -- no idea, sorry. "Also, can you please write an answer to this question saying this isn't possible and I'll accept it?" -- since I can't completely rule it out as being possible, I'll leave these as comments. – CommonsWare Nov 20 '15 at 15:28
  • Ok, thanks very much for all your help! It's much appreciated. – CalumMcCall Nov 20 '15 at 15:46
  • @CommonsWare: Is this possible now? (to connect to Miracast Device and Mirror through our Application). Can you please share some updated Docs related to this. Thanks! – Azhar Bandri Jul 18 '18 at 11:45
  • @AzharBandri: My understanding is that modern versions of Android do not support Miracast at all, at least from the end user's perspective. I am not aware of any public APIs that would support automatically establishing a Miracast connection, and I never tried the hidden stuff cited in the accepted answer. Since hidden APIs are being banned (slowly) on Android 9.0+, using those APIs would be risky. – CommonsWare Jul 18 '18 at 12:13
  • @CommonsWare: Thanks! Can we atleast programatically enable on default Cast Option in Phone's settings? – Azhar Bandri Jul 19 '18 at 13:53
  • @AzharBandri: Not that I am aware of, but I have not looked in this area recently. – CommonsWare Jul 19 '18 at 22:03
  • @CommonsWare: Okay, no worries. I found a method to Open the Cast Settings by starting intent for Settings.ACTION_CAST_SETTINGS. But again, may be here user needs to manually On the Setting. No control code found to On in Programatically. – Azhar Bandri Jul 20 '18 at 06:07
  • @CommonsWare: can I simply get Broadcast receiver of the Cast Connectivity. I dont want to cast from my application, but need to get the status of connection. I have created seperate question here, https://stackoverflow.com/questions/51683422/broadcast-receiver-for-cast-connectivity-in-android – Azhar Bandri Aug 04 '18 at 14:02

2 Answers2

12

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.

CalumMcCall
  • 1,665
  • 4
  • 24
  • 46
  • Does this `WifiDisplay` API provide the server implementation? I have an embedded device, which runs on Android N/O. It doesn't have Miracast feature by default. So i want to implement a server, which announces the Miracast Service Capability. Can i use this API to implement the same? My purpose is, when i goto my phone's wifi display feature, the embedded device should be listed in the devices list (Which currently doesn't show). And i should be able to cast my phone to this embedded device. – Vishnu Jan 10 '18 at 04:47
  • I believe that the WifiDisplay API is only the client, but I'm not an expert, and I haven't touched this stuff since I wrote this answer. I suspect as long as you implement the appropriate interface, using perhaps a 3rd party implementation of miracast, it should work. IIRC, I used a miracast daemon running on an embedded Linux device, so I wasn't using Android. – CalumMcCall Jan 11 '18 at 16:12
  • 1
    can you please share guide(https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/) link access to me, really appreciate your efforts – Giteeka Sawlani Dec 11 '19 at 10:34
  • were you able to add this functionality – Giteeka Sawlani Dec 11 '19 at 10:35
  • 1
    @GiteekaSawlani I've updated my answer with a mirror from archive.org, and thanks for letting me know about the broken link! – CalumMcCall Dec 11 '19 at 13:57
0
try {
         startActivity(new Intent("android.settings.CAST_SETTINGS"));
         return;
     } catch (Exception exception1) {
         Toast.makeText(getApplicationContext(), "Device not supported", Toast.LENGTH_LONG).show();
     }

hope this will help you, done screen mirroring with the cast settings, it uses your device's cast service. but you have to connect with same wifi both device and tv.

vishal
  • 308
  • 1
  • 19
Makvin
  • 3,475
  • 27
  • 26
  • This prompts the user to connect using the UI, but my question is about doing so programmatically, without any user interaction. So this answer doesn't work I'm afraid. – CalumMcCall Aug 04 '20 at 19:02