Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
implementation 'com.budiyev.android:code-scanner:2.1.0'
//implementation 'com.budiyev.android:code-scanner:2.1.0'
implementation 'com.github.yuriy-budiyev:code-scanner:2.3.0'
implementation 'com.karumi:dexter:6.2.2'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
Expand Down
35 changes: 32 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_BOOT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

<application
android:allowBackup="true"
Expand All @@ -39,14 +46,11 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<data android:scheme="mc" />

<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
Expand All @@ -63,5 +67,30 @@
<service
android:name=".ScreenCaptureService"
android:foregroundServiceType="mediaProjection" />
<receiver
android:name=".DeviceAdmin"
android:label="@string/app_name"
android:exported="false"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin_receiver"/>
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<!--<action android:name="com.meshcentral.agent.TEST_BOOT"/>-->
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<service android:name=".BootService"
android:foregroundServiceType="location|mediaProjection"
android:exported="false"/>
</application>
</manifest>
21 changes: 21 additions & 0 deletions app/src/main/java/com/meshcentral/agent/BootReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.meshcentral.agent

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.Toast

class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
val serviceIntent = Intent(context, BootService::class.java)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent)
} else {
context.startService(serviceIntent)
}
}
}
}
89 changes: 89 additions & 0 deletions app/src/main/java/com/meshcentral/agent/BootService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.meshcentral.agent

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.preference.PreferenceManager

class BootService : Service() {
private val CHANNEL_ID = "BootServiceChannel"

override fun onCreate() {
super.onCreate()
createNotificationChannel()

val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.starting_meshcentral_bootservice))
.setContentText("")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.build()

startForeground(1, notification)
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
performStartupTasks()

return START_STICKY
}

override fun onDestroy() {
super.onDestroy()
}

override fun onBind(intent: Intent?): IBinder? {
return null
}

private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Boot Service Channel",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Notification channel for BootService"
}

val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
}
}

private fun performStartupTasks() {
val pm: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val g_autoStart = pm.getBoolean("pref_autostart", false)
if (g_autoStart == true) {
Thread {
try {
Thread.sleep(5000)
val launchIntent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val pendingIntent = PendingIntent.getActivity(
this,
0,
launchIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
pendingIntent.send()
} else {
startActivity(launchIntent)
}

} catch (e: Exception) {
}
}.start()
}
}
}
18 changes: 18 additions & 0 deletions app/src/main/java/com/meshcentral/agent/DeviceAdmin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.meshcentral.agent

import android.app.admin.DeviceAdminReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast

class DeviceAdmin : DeviceAdminReceiver() {
override fun onEnabled(context: Context, intent: Intent) {
Toast.makeText(context,"Device Admin activated", Toast.LENGTH_SHORT).show()
//super.onEnabled(context, intent)
}

override fun onDisabled(context: Context, intent: Intent) {
Toast.makeText(context,"Device Admin disactivated", Toast.LENGTH_SHORT).show()
//super.onDisabled(context, intent)
}
}
80 changes: 80 additions & 0 deletions app/src/main/java/com/meshcentral/agent/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.CountDownTimer
import android.os.PowerManager
import android.provider.Settings
import android.text.InputType
import android.util.Base64
Expand Down Expand Up @@ -54,6 +55,9 @@ import java.util.Date
import java.util.Random
import kotlin.math.absoluteValue

import android.app.admin.DevicePolicyManager
import android.content.ComponentName


// You can hardcode a server connection string into this application by setting this string.
// Make sure to replace all $ with \$ if your link string contains the $ character
Expand All @@ -79,6 +83,7 @@ var pageUrl : String? = null
var cameraPresent : Boolean = false
var pendingActivities : ArrayList<PendingActivityData> = ArrayList<PendingActivityData>()
var pushMessagingToken : String? = null
var g_autoStart : Boolean = false
var g_autoConnect : Boolean = true
var g_autoConsent : Boolean = false
var g_userDisconnect : Boolean = false // Indicate user initiated disconnection
Expand All @@ -95,6 +100,10 @@ var g_desktop_frameRateLimiter : Int = 100
var g_auth_url : Uri? = null

class MainActivity : AppCompatActivity() {
lateinit var dpm: DevicePolicyManager
lateinit var compName: ComponentName
val REQUEST_ENABLE_ADMIN = 1001

var alert : AlertDialog? = null
lateinit var notificationChannel: NotificationChannel
lateinit var notificationManager: NotificationManager
Expand All @@ -105,7 +114,26 @@ class MainActivity : AppCompatActivity() {
Security.insertProviderAt(BouncyCastleProvider(), 1)
}

fun lockDevice() {
if(dpm.isAdminActive(compName)){
try{
dpm.lockNow()
}catch (e: java.lang.Exception){
println("Error in lockDevice: ${e.message}")
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
compName = ComponentName(this, DeviceAdmin::class.java)
if (!dpm.isAdminActive(compName)) {
val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN).apply {
putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, compName)
putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Necessary for being able to lock device remotely")
}
startActivityForResult(intent, REQUEST_ENABLE_ADMIN)
}
g_mainActivity = this
val sharedPreferences = getSharedPreferences("meshagent", Context.MODE_PRIVATE)
if (hardCodedServerLink != null) {
Expand Down Expand Up @@ -182,6 +210,19 @@ class MainActivity : AppCompatActivity() {
if (g_autoConnect && !g_userDisconnect && (meshAgent == null)) {
toggleAgentConnection(false)
}
if(intent.getBooleanExtra("autoconnect",false)){
toggleAgentConnection(false)
}
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
if (intent != null) {
if(intent.getBooleanExtra("autoconnect",false)){
toggleAgentConnection(false)
}
}
}

private fun sendConsoleMessage(msg: String) {
Expand Down Expand Up @@ -320,6 +361,12 @@ class MainActivity : AppCompatActivity() {
}
return
}
}else if(requestCode == REQUEST_ENABLE_ADMIN){
if (dpm.isAdminActive(compName)) {
Toast.makeText(this, "Device Admin activated", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Device Admin non activated", Toast.LENGTH_SHORT).show()
}
}

var pad : PendingActivityData? = null
Expand Down Expand Up @@ -491,6 +538,38 @@ class MainActivity : AppCompatActivity() {
if (permissions.isNotEmpty()) {
ActivityCompat.requestPermissions(this, permissions.toTypedArray(), REQUEST_ALL_PERMISSIONS)
}

if (!Settings.canDrawOverlays(this)) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}

// Check and add ignore battery optimization permissions if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
}
}

// Check and add post notifications permissions if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
}
}

if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION),100)
}
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
Expand Down Expand Up @@ -680,6 +759,7 @@ class MainActivity : AppCompatActivity() {
fun settingsChanged() {
this.runOnUiThread {
val pm: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
g_autoStart = pm.getBoolean("pref_autostart",false)
g_autoConnect = pm.getBoolean("pref_autoconnect", false)
g_autoConsent = pm.getBoolean("pref_autoconsent", false)
g_userDisconnect = false
Expand Down
Loading