Loading eprivacymoduledemo/build.gradle +2 −2 Original line number Diff line number Diff line Loading @@ -4,12 +4,12 @@ plugins { } android { compileSdkVersion 30 compileSdkVersion 29 defaultConfig { applicationId "foundation.e.eprivacymoduledemo" minSdkVersion 24 targetSdkVersion 30 targetSdkVersion 29 versionCode 1 versionName "1.0" Loading eprivacymoduledemo/privapp-permissions-foundation.e.eprivacymoduledemo.xml +1 −0 Original line number Diff line number Diff line Loading @@ -5,5 +5,6 @@ <permission name="android.permission.WATCH_APPOPS" /> <permission name="android.permission.GET_APP_OPS_STATS" /> <permission name="android.permission.WRITE_SECURE_SETTINGS" /> <permission name="android.permission.CONTROL_VPN" /> </privapp-permissions> </permissions> No newline at end of file eprivacymoduledemo/src/main/AndroidManifest.xml +4 −3 Original line number Diff line number Diff line Loading @@ -5,9 +5,8 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" tools:ignore="ProtectedPermissions" /> <application android:allowBackup="true" Loading @@ -25,6 +24,8 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".FakeLocationActivity"/> </application> </manifest> No newline at end of file eprivacymoduledemo/src/main/java/foundation/e/eprivacymoduledemo/FakeLocationActivity.kt 0 → 100644 +167 −0 Original line number Diff line number Diff line package foundation.e.eprivacymoduledemo import android.app.AppOpsManager import android.content.Context import android.location.Location import android.location.LocationListener import android.location.LocationManager import android.os.Bundle import android.os.Process.myUid import android.util.Log import android.view.View import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import foundation.e.eprivacymoduledemo.databinding.FakeLocationActivityBinding import foundation.e.privacymodules.location.FakeLocation import foundation.e.privacymodules.location.IFakeLocation import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription class FakeLocationActivity : AppCompatActivity() { private val fakeLocationModule: IFakeLocation by lazy { FakeLocation(this) } private val permissionsModule by lazy { PermissionsPrivacyModule(this) } private lateinit var binding: FakeLocationActivityBinding private val appDesc by lazy { ApplicationDescription( packageName = packageName, uid = myUid(), label = getString(R.string.app_name), icon = null ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.fake_location_activity) getActionBar()?.setDisplayHomeAsUpEnabled(true); binding.view = this updateData("") } override fun onResume() { super.onResume() updateData("") } fun updateData(mockedLocation: String) { binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED binding.mockedLocation = mockedLocation } private val listener = object: LocationListener { override fun onLocationChanged(location: Location?) { binding.currentLocation = if (location == null) "null" else "lat: ${location.latitude} - lon: ${location.longitude}" } override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { TODO("Not yet implemented") } override fun onProviderEnabled(provider: String?) { binding.providerInfo = "onProdivderEnabled: $provider" } override fun onProviderDisabled(provider: String?) { binding.providerInfo = "onProdivderDisabled: $provider" } } fun onClickToggleListenLocation(view: View?) { //permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_COARSE_LOCATION, // AppOpModes.ALLOWED // ) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_FINE_LOCATION, // AppOpModes.ALLOWED // ) val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager if (binding.toggleListenLocation.isChecked) { try { Log.e("DebugLoc", "requestLocationUpdates") locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, // TODO: tight this with fakelocation module. 1000L, 1f, listener ) binding.currentLocation = "listening started" } catch (se: SecurityException) { Log.e("DebugLoc", "Missing permission", se) } } else { locationManager.removeUpdates(listener) binding.currentLocation = "no listening" } } fun onClickPermission(view: View?) { val manualAction = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) if (manualAction == null) { updateData("") } else { //dev mode disabled val alertDialog = AlertDialog.Builder(this) alertDialog .setTitle(manualAction.title) .setMessage(manualAction.instructions) .setPositiveButton("Ok") { dialog, which -> startActivityForResult(manualAction.intent, 0) } .setNegativeButton("Cancel") { dialog, which -> //user cancel } alertDialog.create().show() } } fun onClickReset(view: View?) { try { fakeLocationModule.stopFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't stop FakeLocation", e) } } private fun setFakeLocation(latitude: Double, longitude: Double) { try { fakeLocationModule.startFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't startFakeLocation", e) } fakeLocationModule.setFakeLocation(latitude, longitude) updateData("lat: ${latitude} - lon: ${longitude}") } fun onClickParis(view: View?) { setFakeLocation(48.8502282, 2.3542286) } fun onClickLondon(view: View?) { setFakeLocation(51.5287718, -0.2416803) } fun onClickAmsterdam(view: View?) { setFakeLocation(52.3547498, 4.8339211) } } No newline at end of file eprivacymoduledemo/src/main/java/foundation/e/eprivacymoduledemo/MainActivity.kt +35 −142 Original line number Diff line number Diff line package foundation.e.eprivacymoduledemo import android.app.AppOpsManager import android.app.AppOpsManager.* import android.content.Context import android.location.Location import android.location.LocationListener import android.location.LocationManager import androidx.appcompat.app.AppCompatActivity import android.content.Intent import android.net.IConnectivityManager import android.os.Bundle import android.os.Process.myUid import android.util.Log import android.os.ServiceManager import android.view.View import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import foundation.e.eprivacymoduledemo.databinding.ActivityMainBinding import foundation.e.privacymodules.location.FakeLocation import foundation.e.privacymodules.location.IFakeLocation import foundation.e.eprivacymoduledemo.databinding.MainActivityBinding import android.os.UserHandle import android.util.Log import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription class MainActivity : AppCompatActivity() { import java.lang.Exception import java.security.PermissionCollection private val fakeLocationModule: IFakeLocation by lazy { FakeLocation(this) } private val permissionsModule by lazy { PermissionsPrivacyModule(this) } private lateinit var binding: ActivityMainBinding private val appDesc by lazy { ApplicationDescription( packageName = packageName, uid = myUid(), label = getString(R.string.app_name), icon = null ) } class MainActivity : AppCompatActivity() { private lateinit var binding: MainActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) getActionBar()?.setDisplayHomeAsUpEnabled(true); binding = DataBindingUtil.setContentView(this, R.layout.main_activity) binding.view = this updateData("") } override fun onResume() { super.onResume() updateData("") } fun updateData(mockedLocation: String) { binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED binding.mockedLocation = mockedLocation } private val listener = object: LocationListener { override fun onLocationChanged(location: Location?) { binding.currentLocation = if (location == null) "null" else "lat: ${location.latitude} - lon: ${location.longitude}" fun onClickFakeLocation(view: View) { startActivity(Intent(this, FakeLocationActivity::class.java)) } override fun onProviderEnabled(provider: String?) { binding.providerInfo = "onProdivderEnabled: $provider" fun onClickPermissions(view: View) { //startActivity(Intent(this, PermissionsListActivity::class.java)) } override fun onProviderDisabled(provider: String?) { binding.providerInfo = "onProdivderDisabled: $provider" } } fun onClickToggleListenLocation(view: View?) { //permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_COARSE_LOCATION, // AppOpModes.ALLOWED // ) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_FINE_LOCATION, // AppOpModes.ALLOWED // ) val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager if (binding.toggleListenLocation.isChecked) { try { Log.e("DebugLoc", "requestLocationUpdates") locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, // TODO: tight this with fakelocation module. 1000L, 1f, listener ) binding.currentLocation = "listening started" } catch (se: SecurityException) { Log.e("DebugLoc", "Missing permission", se) } } else { locationManager.removeUpdates(listener) binding.currentLocation = "no listening" } } fun onClickPermission(view: View?) { val manualAction = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) if (manualAction == null) { updateData("") } else { //dev mode disabled val alertDialog = AlertDialog.Builder(this) alertDialog .setTitle(manualAction.title) .setMessage(manualAction.instructions) .setPositiveButton("Ok") { dialog, which -> startActivityForResult(manualAction.intent, 0) } .setNegativeButton("Cancel") { dialog, which -> //user cancel } alertDialog.create().show() } } fun onClickReset(view: View?) { try { fakeLocationModule.stopFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't stop FakeLocation", e) } } private fun setFakeLocation(latitude: Double, longitude: Double) { try { fakeLocationModule.startFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't startFakeLocation", e) } fakeLocationModule.setFakeLocation(latitude, longitude) updateData("lat: ${latitude} - lon: ${longitude}") } fun onClickParis(view: View?) { setFakeLocation(48.8502282, 2.3542286) } fun onClickLondon(view: View?) { setFakeLocation(51.5287718, -0.2416803) } fun onClickQuickTest(view: View) { val permissionsPrivacyModule = PermissionsPrivacyModule(this) permissionsPrivacyModule.setVpnPackageAuthorization("foundation.e.privacycentralapp.e") // val mService: IConnectivityManager = IConnectivityManager.Stub.asInterface( // ServiceManager.getService(Context.CONNECTIVITY_SERVICE)) // // val mPackage = "foundation.e.privacycentralapp.e" // try { // if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) { // // Authorize this app to initiate VPN connections in the future without user // // intervention. // mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true) // binding.quickTestLog = "Done!" // } // } catch (e: Exception) { // Log.e("QuickTest", "onClick", e) // binding.quickTestLog = e.message // } fun onClickAmsterdam(view: View?) { setFakeLocation(52.3547498, 4.8339211) } } Loading
eprivacymoduledemo/build.gradle +2 −2 Original line number Diff line number Diff line Loading @@ -4,12 +4,12 @@ plugins { } android { compileSdkVersion 30 compileSdkVersion 29 defaultConfig { applicationId "foundation.e.eprivacymoduledemo" minSdkVersion 24 targetSdkVersion 30 targetSdkVersion 29 versionCode 1 versionName "1.0" Loading
eprivacymoduledemo/privapp-permissions-foundation.e.eprivacymoduledemo.xml +1 −0 Original line number Diff line number Diff line Loading @@ -5,5 +5,6 @@ <permission name="android.permission.WATCH_APPOPS" /> <permission name="android.permission.GET_APP_OPS_STATS" /> <permission name="android.permission.WRITE_SECURE_SETTINGS" /> <permission name="android.permission.CONTROL_VPN" /> </privapp-permissions> </permissions> No newline at end of file
eprivacymoduledemo/src/main/AndroidManifest.xml +4 −3 Original line number Diff line number Diff line Loading @@ -5,9 +5,8 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" tools:ignore="ProtectedPermissions" /> <application android:allowBackup="true" Loading @@ -25,6 +24,8 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".FakeLocationActivity"/> </application> </manifest> No newline at end of file
eprivacymoduledemo/src/main/java/foundation/e/eprivacymoduledemo/FakeLocationActivity.kt 0 → 100644 +167 −0 Original line number Diff line number Diff line package foundation.e.eprivacymoduledemo import android.app.AppOpsManager import android.content.Context import android.location.Location import android.location.LocationListener import android.location.LocationManager import android.os.Bundle import android.os.Process.myUid import android.util.Log import android.view.View import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import foundation.e.eprivacymoduledemo.databinding.FakeLocationActivityBinding import foundation.e.privacymodules.location.FakeLocation import foundation.e.privacymodules.location.IFakeLocation import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription class FakeLocationActivity : AppCompatActivity() { private val fakeLocationModule: IFakeLocation by lazy { FakeLocation(this) } private val permissionsModule by lazy { PermissionsPrivacyModule(this) } private lateinit var binding: FakeLocationActivityBinding private val appDesc by lazy { ApplicationDescription( packageName = packageName, uid = myUid(), label = getString(R.string.app_name), icon = null ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.fake_location_activity) getActionBar()?.setDisplayHomeAsUpEnabled(true); binding.view = this updateData("") } override fun onResume() { super.onResume() updateData("") } fun updateData(mockedLocation: String) { binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED binding.mockedLocation = mockedLocation } private val listener = object: LocationListener { override fun onLocationChanged(location: Location?) { binding.currentLocation = if (location == null) "null" else "lat: ${location.latitude} - lon: ${location.longitude}" } override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { TODO("Not yet implemented") } override fun onProviderEnabled(provider: String?) { binding.providerInfo = "onProdivderEnabled: $provider" } override fun onProviderDisabled(provider: String?) { binding.providerInfo = "onProdivderDisabled: $provider" } } fun onClickToggleListenLocation(view: View?) { //permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_COARSE_LOCATION, // AppOpModes.ALLOWED // ) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_FINE_LOCATION, // AppOpModes.ALLOWED // ) val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager if (binding.toggleListenLocation.isChecked) { try { Log.e("DebugLoc", "requestLocationUpdates") locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, // TODO: tight this with fakelocation module. 1000L, 1f, listener ) binding.currentLocation = "listening started" } catch (se: SecurityException) { Log.e("DebugLoc", "Missing permission", se) } } else { locationManager.removeUpdates(listener) binding.currentLocation = "no listening" } } fun onClickPermission(view: View?) { val manualAction = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) if (manualAction == null) { updateData("") } else { //dev mode disabled val alertDialog = AlertDialog.Builder(this) alertDialog .setTitle(manualAction.title) .setMessage(manualAction.instructions) .setPositiveButton("Ok") { dialog, which -> startActivityForResult(manualAction.intent, 0) } .setNegativeButton("Cancel") { dialog, which -> //user cancel } alertDialog.create().show() } } fun onClickReset(view: View?) { try { fakeLocationModule.stopFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't stop FakeLocation", e) } } private fun setFakeLocation(latitude: Double, longitude: Double) { try { fakeLocationModule.startFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't startFakeLocation", e) } fakeLocationModule.setFakeLocation(latitude, longitude) updateData("lat: ${latitude} - lon: ${longitude}") } fun onClickParis(view: View?) { setFakeLocation(48.8502282, 2.3542286) } fun onClickLondon(view: View?) { setFakeLocation(51.5287718, -0.2416803) } fun onClickAmsterdam(view: View?) { setFakeLocation(52.3547498, 4.8339211) } } No newline at end of file
eprivacymoduledemo/src/main/java/foundation/e/eprivacymoduledemo/MainActivity.kt +35 −142 Original line number Diff line number Diff line package foundation.e.eprivacymoduledemo import android.app.AppOpsManager import android.app.AppOpsManager.* import android.content.Context import android.location.Location import android.location.LocationListener import android.location.LocationManager import androidx.appcompat.app.AppCompatActivity import android.content.Intent import android.net.IConnectivityManager import android.os.Bundle import android.os.Process.myUid import android.util.Log import android.os.ServiceManager import android.view.View import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import foundation.e.eprivacymoduledemo.databinding.ActivityMainBinding import foundation.e.privacymodules.location.FakeLocation import foundation.e.privacymodules.location.IFakeLocation import foundation.e.eprivacymoduledemo.databinding.MainActivityBinding import android.os.UserHandle import android.util.Log import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription class MainActivity : AppCompatActivity() { import java.lang.Exception import java.security.PermissionCollection private val fakeLocationModule: IFakeLocation by lazy { FakeLocation(this) } private val permissionsModule by lazy { PermissionsPrivacyModule(this) } private lateinit var binding: ActivityMainBinding private val appDesc by lazy { ApplicationDescription( packageName = packageName, uid = myUid(), label = getString(R.string.app_name), icon = null ) } class MainActivity : AppCompatActivity() { private lateinit var binding: MainActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) getActionBar()?.setDisplayHomeAsUpEnabled(true); binding = DataBindingUtil.setContentView(this, R.layout.main_activity) binding.view = this updateData("") } override fun onResume() { super.onResume() updateData("") } fun updateData(mockedLocation: String) { binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED binding.mockedLocation = mockedLocation } private val listener = object: LocationListener { override fun onLocationChanged(location: Location?) { binding.currentLocation = if (location == null) "null" else "lat: ${location.latitude} - lon: ${location.longitude}" fun onClickFakeLocation(view: View) { startActivity(Intent(this, FakeLocationActivity::class.java)) } override fun onProviderEnabled(provider: String?) { binding.providerInfo = "onProdivderEnabled: $provider" fun onClickPermissions(view: View) { //startActivity(Intent(this, PermissionsListActivity::class.java)) } override fun onProviderDisabled(provider: String?) { binding.providerInfo = "onProdivderDisabled: $provider" } } fun onClickToggleListenLocation(view: View?) { //permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_COARSE_LOCATION, // AppOpModes.ALLOWED // ) // permissionsModule.setAppOpMode( // appDesc, AppOpsManager.OPSTR_FINE_LOCATION, // AppOpModes.ALLOWED // ) val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager if (binding.toggleListenLocation.isChecked) { try { Log.e("DebugLoc", "requestLocationUpdates") locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, // TODO: tight this with fakelocation module. 1000L, 1f, listener ) binding.currentLocation = "listening started" } catch (se: SecurityException) { Log.e("DebugLoc", "Missing permission", se) } } else { locationManager.removeUpdates(listener) binding.currentLocation = "no listening" } } fun onClickPermission(view: View?) { val manualAction = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) if (manualAction == null) { updateData("") } else { //dev mode disabled val alertDialog = AlertDialog.Builder(this) alertDialog .setTitle(manualAction.title) .setMessage(manualAction.instructions) .setPositiveButton("Ok") { dialog, which -> startActivityForResult(manualAction.intent, 0) } .setNegativeButton("Cancel") { dialog, which -> //user cancel } alertDialog.create().show() } } fun onClickReset(view: View?) { try { fakeLocationModule.stopFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't stop FakeLocation", e) } } private fun setFakeLocation(latitude: Double, longitude: Double) { try { fakeLocationModule.startFakeLocation() } catch(e: Exception) { Log.e("FakeLoc", "Can't startFakeLocation", e) } fakeLocationModule.setFakeLocation(latitude, longitude) updateData("lat: ${latitude} - lon: ${longitude}") } fun onClickParis(view: View?) { setFakeLocation(48.8502282, 2.3542286) } fun onClickLondon(view: View?) { setFakeLocation(51.5287718, -0.2416803) } fun onClickQuickTest(view: View) { val permissionsPrivacyModule = PermissionsPrivacyModule(this) permissionsPrivacyModule.setVpnPackageAuthorization("foundation.e.privacycentralapp.e") // val mService: IConnectivityManager = IConnectivityManager.Stub.asInterface( // ServiceManager.getService(Context.CONNECTIVITY_SERVICE)) // // val mPackage = "foundation.e.privacycentralapp.e" // try { // if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) { // // Authorize this app to initiate VPN connections in the future without user // // intervention. // mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true) // binding.quickTestLog = "Done!" // } // } catch (e: Exception) { // Log.e("QuickTest", "onClick", e) // binding.quickTestLog = e.message // } fun onClickAmsterdam(view: View?) { setFakeLocation(52.3547498, 4.8339211) } }