Skip to content

Commit 3182199

Browse files
author
lucky
committed
direct boot aware
1 parent 4812468 commit 3182199

File tree

13 files changed

+77
-63
lines changed

13 files changed

+77
-63
lines changed

app/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId "me.lucky.sentry"
1111
minSdk 23
1212
targetSdk 32
13-
versionCode 3
14-
versionName "1.0.2"
13+
versionCode 4
14+
versionName "1.0.3"
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}
@@ -48,4 +48,5 @@ dependencies {
4848
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
4949

5050
implementation 'androidx.security:security-crypto:1.0.0'
51+
implementation 'androidx.preference:preference-ktx:1.2.0'
5152
}

app/src/main/AndroidManifest.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828

2929
<receiver
3030
android:name=".DeviceAdminReceiver"
31-
android:description="@string/device_admin_description"
3231
android:permission="android.permission.BIND_DEVICE_ADMIN"
32+
android:directBootAware="true"
3333
android:exported="true">
3434
<meta-data android:name="android.app.device_admin"
3535
android:resource="@xml/device_admin" />

app/src/main/java/me/lucky/sentry/DeviceAdminManager.kt

-4
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,4 @@ class DeviceAdminManager(private val ctx: Context) {
3939
fun makeRequestIntent() =
4040
Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
4141
.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceAdmin)
42-
.putExtra(
43-
DevicePolicyManager.EXTRA_ADD_EXPLANATION,
44-
ctx.getString(R.string.device_admin_description),
45-
)
4642
}

app/src/main/java/me/lucky/sentry/DeviceAdminReceiver.kt

+7-16
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,21 @@ package me.lucky.sentry
33
import android.app.admin.DeviceAdminReceiver
44
import android.content.Context
55
import android.content.Intent
6+
import android.os.Build
67
import android.os.UserHandle
7-
import android.widget.Toast
8+
import android.os.UserManager
89

910
class DeviceAdminReceiver : DeviceAdminReceiver() {
1011
override fun onPasswordFailed(context: Context, intent: Intent, user: UserHandle) {
1112
super.onPasswordFailed(context, intent, user)
12-
val prefs = Preferences(context)
13+
val prefs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
14+
context.getSystemService(UserManager::class.java)?.isUserUnlocked != true)
15+
16+
PreferencesDirectBoot(context) else Preferences(context)
1317
val maxFailedPasswordAttempts = prefs.maxFailedPasswordAttempts
1418
if (!prefs.isEnabled || maxFailedPasswordAttempts <= 0) return
1519
val admin = DeviceAdminManager(context)
1620
if (admin.getCurrentFailedPasswordAttempts() >= maxFailedPasswordAttempts)
17-
admin.wipeData()
18-
}
19-
20-
override fun onDisabled(context: Context, intent: Intent) {
21-
super.onDisabled(context, intent)
22-
if (Preferences(context).isEnabled)
23-
Toast.makeText(context, R.string.service_unavailable_popup, Toast.LENGTH_SHORT).show()
24-
}
25-
26-
override fun onEnabled(context: Context, intent: Intent) {
27-
super.onEnabled(context, intent)
28-
DeviceAdminManager(context)
29-
.setMaximumFailedPasswordsForWipe(Preferences(context)
30-
.maxFailedPasswordAttempts.shl(1))
21+
try { admin.wipeData() } catch (exc: SecurityException) {}
3122
}
3223
}

app/src/main/java/me/lucky/sentry/MainActivity.kt

+8-11
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import me.lucky.sentry.databinding.ActivityMainBinding
1212

1313
class MainActivity : AppCompatActivity() {
1414
private lateinit var binding: ActivityMainBinding
15-
private lateinit var prefs: Preferences
15+
private lateinit var prefs: PreferencesProxy
1616
private lateinit var admin: DeviceAdminManager
1717

1818
private val registerForDeviceAdmin =
@@ -34,8 +34,11 @@ class MainActivity : AppCompatActivity() {
3434
}
3535

3636
private fun init() {
37-
prefs = Preferences(this)
37+
prefs = PreferencesProxy(this)
38+
prefs.clone()
3839
admin = DeviceAdminManager(this)
40+
if (prefs.isEnabled && prefs.maxFailedPasswordAttempts > 0)
41+
try { admin.setMaximumFailedPasswordsForWipe(0) } catch (exc: SecurityException) {}
3942
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
4043
!packageManager.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN))
4144
hideSecureLockScreenRequired()
@@ -51,11 +54,7 @@ class MainActivity : AppCompatActivity() {
5154
private fun setup() {
5255
binding.apply {
5356
maxFailedPasswordAttempts.addOnChangeListener { _, value, _ ->
54-
val num = value.toInt()
55-
prefs.maxFailedPasswordAttempts = num
56-
try {
57-
admin.setMaximumFailedPasswordsForWipe(num.shl(1))
58-
} catch (exc: SecurityException) {}
57+
prefs.maxFailedPasswordAttempts = value.toInt()
5958
}
6059
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
6160
usbDataSignaling.setOnCheckedChangeListener { _, isChecked ->
@@ -97,13 +96,11 @@ class MainActivity : AppCompatActivity() {
9796

9897
private fun setOff() {
9998
prefs.isEnabled = false
100-
admin.remove()
99+
try { admin.remove() } catch (exc: SecurityException) {}
101100
}
102101

103102
private fun update() {
104-
binding.apply {
105-
usbDataSignaling.isChecked = isUsbDataSignalingEnabled()
106-
}
103+
binding.usbDataSignaling.isChecked = isUsbDataSignalingEnabled()
107104
if (prefs.isEnabled && !admin.isActive())
108105
Snackbar.make(
109106
binding.toggle,

app/src/main/java/me/lucky/sentry/NotificationListenerService.kt

+3-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class NotificationListenerService : NotificationListenerService() {
3434
}
3535

3636
private fun deinit() {
37-
unregisterReceiver(lockReceiver)
37+
try { unregisterReceiver(lockReceiver) } catch (exc: IllegalArgumentException) {}
3838
}
3939

4040
override fun onListenerConnected() {
@@ -59,9 +59,8 @@ class NotificationListenerService : NotificationListenerService() {
5959

6060
@RequiresApi(Build.VERSION_CODES.S)
6161
private fun setUsbDataSignalingEnabled(ctx: Context, enabled: Boolean) {
62-
try {
63-
DeviceAdminManager(ctx).setUsbDataSignalingEnabled(enabled)
64-
} catch (exc: Exception) {}
62+
try { DeviceAdminManager(ctx).setUsbDataSignalingEnabled(enabled) }
63+
catch (exc: Exception) {}
6564
}
6665
}
6766
}

app/src/main/java/me/lucky/sentry/Preferences.kt

+50-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package me.lucky.sentry
22

33
import android.content.Context
4+
import android.os.Build
45
import androidx.core.content.edit
6+
import androidx.preference.PreferenceManager
57
import androidx.security.crypto.EncryptedSharedPreferences
68
import androidx.security.crypto.MasterKeys
79

8-
class Preferences(ctx: Context) {
10+
class Preferences(ctx: Context) : Prefs {
911
companion object {
10-
private const val ENABLED = "enabled"
11-
private const val MAX_FAILED_PASSWORD_ATTEMPTS = "max_failed_password_attempts"
12+
const val ENABLED = "enabled"
13+
const val MAX_FAILED_PASSWORD_ATTEMPTS = "max_failed_password_attempts"
1214

1315
private const val FILE_NAME = "sec_shared_prefs"
1416
// migration
@@ -24,11 +26,54 @@ class Preferences(ctx: Context) {
2426
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
2527
)
2628

27-
var isEnabled: Boolean
29+
override var isEnabled: Boolean
2830
get() = prefs.getBoolean(ENABLED, prefs.getBoolean(SERVICE_ENABLED, false))
2931
set(value) = prefs.edit { putBoolean(ENABLED, value) }
3032

31-
var maxFailedPasswordAttempts: Int
33+
override var maxFailedPasswordAttempts: Int
3234
get() = prefs.getInt(MAX_FAILED_PASSWORD_ATTEMPTS, 0)
3335
set(value) = prefs.edit { putInt(MAX_FAILED_PASSWORD_ATTEMPTS, value) }
3436
}
37+
38+
class PreferencesProxy(ctx: Context) {
39+
private val prefs = Preferences(ctx)
40+
private val prefsdb = PreferencesDirectBoot(ctx)
41+
42+
fun clone() {
43+
prefsdb.isEnabled = prefs.isEnabled
44+
prefsdb.maxFailedPasswordAttempts = prefsdb.maxFailedPasswordAttempts
45+
}
46+
47+
var isEnabled: Boolean
48+
get() = prefs.isEnabled
49+
set(value) {
50+
prefs.isEnabled = value
51+
prefsdb.isEnabled = value
52+
}
53+
54+
var maxFailedPasswordAttempts: Int
55+
get() = prefs.maxFailedPasswordAttempts
56+
set(value) {
57+
prefs.maxFailedPasswordAttempts = value
58+
prefsdb.maxFailedPasswordAttempts = value
59+
}
60+
}
61+
62+
interface Prefs {
63+
var isEnabled: Boolean
64+
var maxFailedPasswordAttempts: Int
65+
}
66+
67+
class PreferencesDirectBoot(ctx: Context) : Prefs {
68+
private val context = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
69+
ctx.createDeviceProtectedStorageContext() else ctx
70+
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
71+
72+
override var isEnabled: Boolean
73+
get() = prefs.getBoolean(Preferences.ENABLED, false)
74+
set(value) = prefs.edit { putBoolean(Preferences.ENABLED, value) }
75+
76+
override var maxFailedPasswordAttempts: Int
77+
get() = prefs.getInt(Preferences.MAX_FAILED_PASSWORD_ATTEMPTS, 0)
78+
set(value) = prefs.edit { putInt(Preferences.MAX_FAILED_PASSWORD_ATTEMPTS, value) }
79+
}

app/src/main/res/layout/activity_main.xml

+3-15
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,13 @@
77
android:padding="32dp"
88
tools:context=".MainActivity">
99

10-
<TextView
11-
android:id="@+id/description"
12-
android:layout_width="0dp"
13-
android:layout_height="wrap_content"
14-
android:text="@string/description"
15-
app:layout_constraintEnd_toEndOf="parent"
16-
app:layout_constraintStart_toStartOf="parent"
17-
app:layout_constraintTop_toTopOf="parent" />
18-
1910
<ScrollView
2011
android:id="@+id/scrollView"
21-
android:layout_width="0dp"
22-
android:layout_height="0dp"
23-
android:layout_marginVertical="16dp"
24-
android:padding="16dp"
25-
app:layout_constraintBottom_toTopOf="@+id/toggle"
12+
android:layout_width="match_parent"
13+
android:layout_height="wrap_content"
2614
app:layout_constraintEnd_toEndOf="parent"
2715
app:layout_constraintStart_toStartOf="parent"
28-
app:layout_constraintTop_toBottomOf="@+id/description">
16+
app:layout_constraintTop_toTopOf="parent">
2917

3018
<LinearLayout
3119
android:layout_width="match_parent"

app/src/main/res/values-ru/strings.xml

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<string name="app_name">Sentry</string>
4-
<string name="description">Включите приложение, чтобы применить дополнительные политики безопасности. Вы должны выдать разрешение на администрирование устройства.</string>
5-
<string name="device_admin_description">Разрешите приложению стирать данные устройства и отключать передачу данных по USB.</string>
64
<string name="service_unavailable_popup">Администратор устройства недоступен</string>
75
<string name="usb_data_signaling">Передача данных по USB</string>
86
<string name="usb_data_signaling_description">Когда отключено, соединения по USB (кроме зарядки) запрещены.</string>

app/src/main/res/values/strings.xml

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<string name="app_name">Sentry</string>
4-
<string name="description">Turn on Sentry to enforce security policies of your device. You have to grant device administration permission.</string>
5-
<string name="device_admin_description">Allow Sentry to wipe a device and disable USB data connections.</string>
64
<string name="service_unavailable_popup">Device admin unavailable</string>
75
<string name="usb_data_signaling">USB data signaling</string>
86
<string name="usb_data_signaling_description">When disabled, USB data connections (except from charging functions) are prohibited.</string>

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
plugins {
33
id 'com.android.application' version '7.2.1' apply false
44
id 'com.android.library' version '7.2.1' apply false
5-
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
5+
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
66
}
77

88
task clean(type: Delete) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
direct boot aware
Loading

0 commit comments

Comments
 (0)