Reflection issue

NFOBoy

Active Member
Licensed User
Longtime User
Ok, trying to set a System Setting as discussed with Erel, as talked about here: http://www.b4x.com/forum/basic4android-updates-questions/23706-enforcing-checking-time-limit-demo-program.html#post137435

My limited understanding and use of the Reflection library so far, is to assign to the Reflector.Target some object, and then use Methods on them to get whatever functionality I'm looking to have happen.

I think I understand that if I want to do something to an object that doesn't exist within B4A (not a View, etc.) that I need to use Reflector.CreateObject or CreateObject2.

I Want to create an "android.provider.Settings.System" object and then run
two methods on it:

static boolean : putLong(ContentResolver cr, String name, long value)
Convenience function for updating a single settings value as a long integer.

and

static long : getLong(ContentResolver cr, String name)
Convenience function for retrieving a single system settings value as a long.


This also means I need to create a ContentResolver, to pass as part of the args().


Soooo,

I look at the public Constructor for ContentResolver, and see that it needs a Context... so I want to do the following first:


B4X:
   Dim oReflector As Reflector
   Dim oContext As Object
   
   oContext = oReflector.GetContext 'should now have an android.content.Context in it)
   
   args(0) = oContext
   types(0) = "android.content.Context"
   oReflector.Target=oReflector.CreateObject2("android.content.ContentResolver", args, types)

but of course, android.content.Context isn't supported by the Reflection library... so How would I pass the android.content.Context? (which then I think I would know how to then pass other types for use in arguments, that aren't of primitive types)

But wait... I put that on the backburner.. .and try to at least set the Target of a Reflector to my "android.provider.Settings.System"

B4X:
   Dim oReflector As Reflector
   
   oReflector.Target = oReflector.CreateObject("android.provider.Settings.System")

and I get: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassNotFoundException: android.provider.Settings.System

Which I most definitely see here: Settings.System | Android Developers

:BangHead:

So, over to someone with a little more experience... as what I can find for samples, is not leading me down the right path.



Ross
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Try this:
B4X:
Dim r As Reflector
Dim cr As Object
r.Target = r.GetContext
cr = r.RunMethod("getContentResolver")
r.RunStaticMethod("android.provider.Settings$System", "putLong", _
   Array As Object(cr, "some key", SomeLongValue), _
   Array As String("android.content.ContentResolver", "java.lang.String", "java.lang.long"))
 
Upvote 0

NFOBoy

Active Member
Licensed User
Longtime User
Erel,

First off, thanks so much for the very, very quick response. I am currently studying your code, to see if I can follow it well enough to continue use in future situations... so forgive me if my following code is a little verbose, but I am sure there are others that need to watch the transformations take place!


I added this to manifest editor:

<uses-permission android:name = "android.permission.WRITE_SETTINGS"/>),

to allow Reflection to run these methods.

Then I did the following test code:

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)

Dim strAppSetting As String
strAppSetting = "MyAppsDateData"


'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'use this line to set the Stored Setting Value to some new Value for testing purposes
'SystemSettings.SetSystemSettingLong(strAppSetting, DateTime.Now - (1000*60*60*72) )
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

If SystemSettings.SystemSettingExists(strAppSetting) Then
   'put code here to determine letting the Activity continue using the GetSystemSettingLong function
   If DateTime.Now > SystemSettings.GetSystemSettingLong(strAppSetting) + (1000*60*60*48) Then '2 days time of use
      Msgbox("Please consider purchasing the Pro Version", "Support your developer")
      Activity.Finish
   End If
Else
   Dim TodaysDate As Long 
   TodaysDate = DateTime.Now
   SystemSettings.SetSystemSettingLong(strAppSetting, TodaysDate)
End If




'Msgbox(SetSystemSettingLong( "MyAppsStoredDate", TodaysDate), "Result")

Dim InstallDate As Long
InstallDate = SystemSettings.GetSystemSettingLong(strAppSetting)
Msgbox(DateTime.Date(InstallDate), "Start Date")

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

and here is the Code Module SystemSettings:

B4X:
'Code module
'Subs in this code module will be accessible from all modules.
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub


Sub SystemSettingExists(SettingName As String) As Boolean
   Dim r As Reflector
   Dim cr As Object 'for storing the ContentResolver needed by android.provider.Settings.System")
   
   r.Target = r.GetContext
   cr = r.RunMethod("getContentResolver")
   
   Dim args(2) As Object
   args(0) = cr
   args(1) = SettingName
   
   Dim Types(2) As String
   Types(0) = "android.content.ContentResolver"
   Types(1) = "java.lang.String"

   Try
       r.RunStaticMethod("android.provider.Settings$System", "getLong", args, Types)
   Catch
      Return False
   End Try

   Return True
End Sub

Sub SetSystemSettingLong(SettingName As  String, SettingValue As Long) As Boolean

   Dim r As Reflector
   Dim cr As Object 'for storing the ContentResolver needed by android.provider.Settings.System")
   
   r.Target = r.GetContext
   cr = r.RunMethod("getContentResolver")
   
   Dim args(3) As Object
   args(0) = cr
   args(1) = SettingName
   args(2) = SettingValue
   
   Dim Types(3) As String
   Types(0) = "android.content.ContentResolver"
   Types(1) = "java.lang.String"
   Types(2) =  "java.lang.long"
   
   Return r.RunStaticMethod("android.provider.Settings$System", "putLong", args, Types)


End Sub

Sub GetSystemSettingLong(SettingName As String) As Long
   Dim r As Reflector
   Dim cr As Object 'for storing the ContentResolver needed by android.provider.Settings.System")
   
   r.Target = r.GetContext
   cr = r.RunMethod("getContentResolver")
   
   Dim args(2) As Object
   args(0) = cr
   args(1) = SettingName
   'args(2) = SettingValue
   
   Dim Types(2) As String
   Types(0) = "android.content.ContentResolver"
   Types(1) = "java.lang.String"
   'Types(2) =  "java.lang.long"
   
   Return r.RunStaticMethod("android.provider.Settings$System", "getLong", args, Types)


End Sub

This works great for me!

Of note, for anyone else that wants to use this... I don't see a way to Delete the Stored Setting off the top of my head from searching the Android Developer Website.

It does Not go away after un-installing. So if you want to update your program, and give users another chance to try it out, will need to determine some method to determine if it is re-install... or just use a different Setting Name (which would get sloppy I think)

Ross

(Now I understand the Reflection statement:

"Note that interfaces declared as internal to a class will need a "$" instead of a "." as their
final separator and all interfaces need to be fully qualified.
e.g android.view.View$OnTouchListener"

but still don't understand why when I ran my original code, I didn't get a Class Doesn't exist error, instead of Can't instantiate error....
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
but still don't understand why when I ran my original code, I didn't get a Class Doesn't exist error, instead of Can't instantiate error....
If it was for android.content.ContentResolver then it was because the class does exist but cannot be instantiated as it is an abstract class.
 
Upvote 0

NFOBoy

Active Member
Licensed User
Longtime User
Ok, totally missed that it was an abstract class, and understand why an abstract class exists.... but Context is also an abstract class, and in the code as shown by Erel, a Context is returned for the reflector to act upon to return a ContentResolver, and then that ContentResolver is passed to the static methods used in this code!

I realize that the Context that is returned is probably a ContextWrapper (or a MockContext) but I don't think that the ContentResolver is a MockContentResolver(??). Is there a way to peek into the target deep enough to know which one? (So that I can follow the flow using B4A... no desire right now to get into Java that deep... just want to understand what is happening behind the curtain at times, so I can ask the wizard to pull the right handle)

Ross
 
Upvote 0

NFOBoy

Active Member
Licensed User
Longtime User
So this program https://play.google.com/store/apps/details?id=de.SmartDyne.Free.DisplaySystemSettings allows anyone to quickly remove System Settings from their system.

However, it cannot remove Settings.Secure

I've been trying to rewrite the code such that I use Settings$Secure instead of Settings$System for the putLong and getLong, but I am getting the error that that pid = <somenumber> uid <somenumber> requires android.permission.WRITE_SETTINGS, or grantUriPermission().

So, I have added the following to the manifest editor

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="4" />
<supports-screens android:largeScreens="true" 
    android:normalScreens="true" 
    android:smallScreens="true" 
    android:anyDensity="true"/>
<uses-permission android:name = "android.permission.WRITE_SETTINGS"/>
<uses-permission android:name = "android.permission.WRITE_SECURE_SETTINGS"/>
)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.
AddApplicationText
(
<uses-permission android:grantUriPermissions = "true"/>
<grant-uri-permission android:pathPattern="\\*"/>
)

But I still get:
** Activity (main) Create, isFirst = true **


systemsettings_setsystemsettinglong (java line: 123)


java.lang.SecurityException: Permission denial: writing to secure settings requires android.permission.WRITE_SECURE_SETTINGS
at android.os.Parcel.readException(Parcel.java:1425)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:188)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:420)
at android.content.ContentResolver.insert(ContentResolver.java:866)
at android.provider.Settings$NameValueTable.putString(Settings.java:681)
at android.provider.Settings$Secure.putString(Settings.java:3663)
at android.provider.Settings$Secure.putLong(Settings.java:3810)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.agraham.reflection.Reflection.RunStaticMethod(Reflection.java:902)
at b4a.pasagosoft.setsytemsetting.systemsettings._setsystemsettinglong(systemsettings.java:123)
at b4a.pasagosoft.setsytemsetting.main._activity_create(main.java:249)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:165)
at b4a.pasagosoft.setsytemsetting.main.afterFirstLayout(main.java:84)
at b4a.pasagosoft.setsytemsetting.main.access$100(main.java:16)
at b4a.pasagosoft.setsytemsetting.main$WaitForLayout.run(main.java:72)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
at dalvik.system.NativeStart.main(Native Method)
java.lang.SecurityException: Permission denial: writing to secure settings requires android.permission.WRITE_SECURE_SETTINGS

I'm not seeing what I'm missing?

Ross
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
So this program https://play.google.com/store/apps/details?id=de.SmartDyne.Free.DisplaySystemSettings allows anyone to quickly remove System Settings from their system.

However, it cannot remove Settings.Secure

I've been trying to rewrite the code such that I use Settings$Secure instead of Settings$System for the putLong and getLong, but I am getting the error that that pid = <somenumber> uid <somenumber> requires android.permission.WRITE_SETTINGS, or grantUriPermission().

So, I have added the following to the manifest editor

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="4" />
<supports-screens android:largeScreens="true" 
    android:normalScreens="true" 
    android:smallScreens="true" 
    android:anyDensity="true"/>
<uses-permission android:name = "android.permission.WRITE_SETTINGS"/>
<uses-permission android:name = "android.permission.WRITE_SECURE_SETTINGS"/>
)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.
AddApplicationText
(
<uses-permission android:grantUriPermissions = "true"/>
<grant-uri-permission android:pathPattern="\\*"/>
)

But I still get:


I'm not seeing what I'm missing?

Ross

Starting from 4.2, Android does not allow to change any of the secure settings (e.g. airplane mode can't be toggled) if you're not a system app (and the user applications can't be system apps).
 
Upvote 0

NFOBoy

Active Member
Licensed User
Longtime User
ahhh, so no way to do this then... So, another app is allowed to delete the system setting, as long as it is not a Secure Setting? This seems a little out of line with trying to keep things "sandboxed"
 
Upvote 0
Top