Android Permissions

Hi,
I’ve been trying to get some native code running in Android to return the device id
using the TelephonyManager.

FuseforeignCodeExample.unoproj:
{
  "RootNamespace":"",
  "Packages": [
    "Fuse",
    "FuseJS"
  ],
  "Includes": [
    "*.uno",
    "*.uxl",
    "*.ux"
  ]
}
MainView.ux:
<App>
	<DockPanel>
		<StatusBarBackground Dock="Top" />
		<BottomBarBackground Dock="Bottom" />

		<Device ux:Global="Device" />

		<JavaScript>
			var Observable = require("FuseJS/Observable");
			var device = require("Device");

			var mcDeviceUUID = Observable("DeviceID");

			function button_pressed() {
				mcDeviceUUID.value = "DeviceID:" + device.getDeviceUUID();
			}

			module.exports = {
				button_pressed: button_pressed,
				mcDeviceUUID: mcDeviceUUID
			};
		</JavaScript>

		<StackPanel>
			<Text FontSize="30" Alignment="HorizontalCenter" Value="{mcDeviceUUID}" Margin="0,0,0,10" TextColor="#000" />
			<Button Text="Press me!" Clicked="{button_pressed}" />
		</StackPanel>
	</DockPanel>
</App>
MainView.uxl:
<Package>
	<Extensions Backend="CPlusPlus" Condition="Android">
		<Require AndroidManifest.Permission="android.permission.READ_PHONE_STATE" />
	</Extensions>
</Package>
Device.uno:
using Uno;
using Uno.Collections;
using Uno.Compiler.ExportTargetInterop;

using Fuse;
using Fuse.Scripting;

[ForeignInclude(Language.Java, "android.app.Activity",
                               "android.provider.Settings",
                               "android.telephony.TelephonyManager",
                               "android.content.*",
                               "java.security.*",
                               "java.net.*",
                               "java.nio.*",
                               "java.io.*")]

public class Device : NativeModule
{
	public Device()
	{
		AddMember(new NativeFunction("getDeviceUUID", (NativeCallback)GetDeviceUUID));
	}

	object GetDeviceUUID(Context c, object[] args)
	{
		return GetDeviceUUID();
	}

	[Foreign(Language.Java)]
    [Require("AndroidManifest.RootElement", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>")]
	static extern(Android) string GetDeviceUUID()
	@{	
        final android.app.Activity loContext = com.fuse.Activity.getRootActivity();
        final TelephonyManager loTm = (TelephonyManager)loContext.getSystemService(Context.TELEPHONY_SERVICE);
        return "" + loTm.getDeviceId();
	@}

	static extern(!Android) string GetDeviceUUID()
	{
		debug_log("Not supported on this platform.");
		return "";
	}

	static extern(!(iOS||Android)) string GetDeviceUUID()
	{
		return "Default";
	}
}

It all compiles and runs ok on Android however when I press the button on the MainView.ux screen I get an
error:

Error: java.lang.SecurityException: getDeviceId: Neither user 10280 nor current process has android.permission.READ_PHONE_STATE.

As you see I’ve included the required permission in the .uxl file and in the .uno file.

Regards,
Matt

Hi!

On modern Android phones permissions are a runtime request. We have to specify them in the manifest but also check with the user at runtime to make sure they approve. Luckily we have a nice api for this:

First add this to the Packages list in your unoproj file:

"Uno.Permissions"

Then add this using to the uno file

using Uno.Permissions;

And finally something like this to your class:

    public void TakePicture(object a1, EventArgs a2)
    {
        var permissionPromise = Permissions.Request(Permissions.Android.READ_PHONE_STATE);
        permissionPromise.Then(OnPermitted, OnRejected);
    }

    void OnPermitted(PlatformPermission permission)
    {
        debug_log "Woo, we can read the state now";
    }

    void OnRejected(Exception e)
    {
        debug_log "Damn: " + e.Message;
    }

The nice thing is that not only does this let you query in a nice way. It also make sure the permission is in the AndroidManifest file so you don’t need the uxl for that.

I hope this helps

Thanks Chris.

I tried that and am unable to get it to work.

I’ve updated the .uno file:

using Uno;
using Uno.Collections;
using Uno.Compiler.ExportTargetInterop;
using Uno.Permissions;

using Fuse;
using Fuse.Scripting;

[ForeignInclude(Language.Java, "android.app.Activity",
                               "android.provider.Settings",
                               "android.telephony.TelephonyManager",
                               "android.content.*",
                               "java.security.*",
                               "java.net.*",
                               "java.nio.*",
                               "java.io.*")]

[ForeignInclude(Language.ObjC, "sys/types.h", "sys/sysctl.h")]

public class Device : NativeModule
{
	static string cachedUUID;

	public Device()
	{
		AddMember(new NativeFunction("getDeviceUUID", (NativeCallback)GetDeviceUUID));
	}

	object GetDeviceUUID(Context c, object[] args)
	{
		var permissionPromise = Permissions.Request(Permissions.Android.READ_PHONE_STATE);
        permissionPromise.Then(OnPermitted, OnRejected);

        return cachedUUID;
	}

	void OnPermitted(PlatformPermission permission)
   	{
        cachedUUID = GetDeviceUUID();
    	debug_log("Woo, we can read the state now");
    }

    void OnRejected(Exception e)
    {
    	cachedUUID = "0";
        debug_log("Damn: " + e.Message);
    }

	[Foreign(Language.Java)]
    [Require("AndroidManifest.RootElement", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>")]
	static extern(Android) string GetDeviceUUID()
	@{	
		final android.app.Activity loContext = com.fuse.Activity.getRootActivity();
        final TelephonyManager loTm = (TelephonyManager)loContext.getSystemService(Context.TELEPHONY_SERVICE);
        cachedUUID = "" + loTm.getDeviceId();
    	//cachedUUID = "1";
    	return cachedUUID;
	@}

	[Foreign(Language.ObjC)]
    private static extern(iOS) string GetDeviceUUID()
    @{
        NSUUID *oNSUUID = [[UIDevice currentDevice] identifierForVendor];
        return [oNSUUID UUIDString];
    @}

	static extern(!(iOS||Android)) string GetDeviceUUID()
	{
		return "Default";
	}
}

I know it’s something simple I’m doing wrong!

Thanks Chris.

I tried that and am unable to get it to work.

I’ve updated the .uno file:

using Uno;
using Uno.Collections;
using Uno.Compiler.ExportTargetInterop;
using Uno.Permissions;

using Fuse;
using Fuse.Scripting;

[ForeignInclude(Language.Java, "android.app.Activity",
                               "android.provider.Settings",
                               "android.telephony.TelephonyManager",
                               "android.content.*",
                               "java.security.*",
                               "java.net.*",
                               "java.nio.*",
                               "java.io.*")]

[ForeignInclude(Language.ObjC, "sys/types.h", "sys/sysctl.h")]

public class Device : NativeModule
{
	static string cachedUUID;

	public Device()
	{
		AddMember(new NativeFunction("getDeviceUUID", (NativeCallback)GetDeviceUUID));
	}

	object GetDeviceUUID(Context c, object[] args)
	{
		var permissionPromise = Permissions.Request(Permissions.Android.READ_PHONE_STATE);
        permissionPromise.Then(OnPermitted, OnRejected);

        return cachedUUID;
	}

	void OnPermitted(PlatformPermission permission)
   	{
        cachedUUID = GetDeviceUUID();
    	debug_log("Woo, we can read the state now");
    }

    void OnRejected(Exception e)
    {
    	cachedUUID = "0";
        debug_log("Damn: " + e.Message);
    }

	[Foreign(Language.Java)]
    [Require("AndroidManifest.RootElement", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>")]
	static extern(Android) string GetDeviceUUID()
	@{	
		final android.app.Activity loContext = com.fuse.Activity.getRootActivity();
        final TelephonyManager loTm = (TelephonyManager)loContext.getSystemService(Context.TELEPHONY_SERVICE);
        cachedUUID = "" + loTm.getDeviceId();
    	//cachedUUID = "1";
    	return cachedUUID;
	@}

	[Foreign(Language.ObjC)]
    private static extern(iOS) string GetDeviceUUID()
    @{
        NSUUID *oNSUUID = [[UIDevice currentDevice] identifierForVendor];
        return [oNSUUID UUIDString];
    @}

	static extern(!(iOS||Android)) string GetDeviceUUID()
	{
		return "Default";
	}
}

I know it’s something simple I’m doing wrong!

BTW - sorry for the duplicate post!

Sorry for the slow reply.

What isn’t working now? Did you remove the uxl file? Logs/descriptions of how the failure has changed would be really useful.

Thanks