diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d2c904..ce12451 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: jobs: build: - name: Build CI + name: Build runs-on: ubuntu-latest steps: - name: Check out repository @@ -63,7 +63,6 @@ jobs: name: Upload Builds runs-on: ubuntu-latest needs: build - if: github.ref == 'refs/heads/dev' steps: - name: Download Artifacts uses: actions/download-artifact@v4 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fbd3c4c..fe21232 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -23,7 +23,8 @@ android { defaultConfig { applicationId = "com.bintianqi.owndroid" minSdk = 21 - targetSdk = 34 + targetSdk = 35 + compileSdk = 35 versionCode = 35 versionName = "6.3" multiDexEnabled = false diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index daf3620..7212e08 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ android:enableOnBackInvokedCallback="true" android:testOnly="false" android:manageSpaceActivity=".ManageSpaceActivity" - tools:targetApi="34"> + tools:targetApi="35"> = 24 && (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) || - (profileOwner && dpm.isManagedProfile(receiver))) + (VERSION.SDK_INT < 24 && !deviceOwner) || (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) || + (profileOwner && dpm.isManagedProfile(receiver)) ) ) { HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile", navCtrl) diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt index 58be73a..39e0c40 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -136,7 +136,7 @@ fun ApplicationManage(navCtrl:NavHostController, vm: MyViewModel) { ) } ) { paddingValues-> - NavHost( + @Suppress("NewApi") NavHost( modifier = Modifier.padding(top = paddingValues.calculateTopPadding()), navController = localNavCtrl, startDestination = "Home", enterTransition = Animations.navHostEnterTransition, @@ -225,7 +225,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String) { onCheckedChange = { appControlAction = 3; appControl(it) }, onClickBlank = { appControlAction = 3; dialogStatus = 4 } ) - if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) { + if(VERSION.SDK_INT >= 30 && (deviceOwner || (VERSION.SDK_INT >= 33 && profileOwner))) { FunctionItem(title = R.string.ucd, icon = R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") } } if(VERSION.SDK_INT>=23) { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt index 89d5032..87934a9 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt @@ -375,7 +375,7 @@ fun processSecurityLogs(securityEvents: List, outputS @RequiresApi(24) fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? { - return when(event.tag) { //TODO: backup service tag (API35) + return when(event.tag) { SecurityLog.TAG_ADB_SHELL_CMD -> JsonPrimitive(event.data as String) SecurityLog.TAG_ADB_SHELL_INTERACTIVE -> null SecurityLog.TAG_APP_PROCESS_START -> { @@ -389,6 +389,14 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? { put("apk_hash", payload[5] as String) } } + SecurityLog.TAG_BACKUP_SERVICE_TOGGLED -> { + val payload = event.data as Array<*> + buildJsonObject { + put("admin", payload[0] as String) + put("admin_user_id", payload[1] as Int) + put("state", payload[2] as Int) + } + } SecurityLog.TAG_BLUETOOTH_CONNECTION -> { val payload = event.data as Array<*> buildJsonObject { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt index 03c06b8..0d45c2b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt @@ -75,16 +75,16 @@ fun WorkProfile(navCtrl: NavHostController) { if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) { FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") } } - if(VERSION.SDK_INT<24 || (VERSION.SDK_INT>=24 && dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))) { + if(VERSION.SDK_INT < 24 || dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)) { FunctionItem(R.string.create_work_profile, icon = R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") } } if(dpm.isOrgProfile(receiver)) { FunctionItem(R.string.suspend_personal_app, icon = R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") } } - if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { + if(profileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))) { FunctionItem(R.string.intent_filter, icon = R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") } } - if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { + if(profileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))) { FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") } } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index d9ac1f9..c539d0c 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -1081,7 +1081,7 @@ fun NetworkStats(navCtrl: NavHostController, vm: MyViewModel) { } } if(VERSION.SDK_INT >= 24 && (target == NetworkStatsTarget.UidTag || target == NetworkStatsTarget.UidTagState)) - ExposedDropdownMenuBox( + ExposedDropdownMenuBox( activeTextField == NetworkStatsActiveTextField.Tag, { activeTextField == if(it) NetworkStatsActiveTextField.Tag else NetworkStatsActiveTextField.None } ) { @@ -1155,7 +1155,7 @@ fun NetworkStats(navCtrl: NavHostController, vm: MyViewModel) { querying = true coroutine.launch { val buckets = try { - if(queryType == 1) { + @Suppress("NewApi") if(queryType == 1) { if(target == NetworkStatsTarget.Device) listOf(nsm.querySummaryForDevice(networkType.type, subscriberId, startTime, endTime)) else listOf(nsm.querySummaryForUser(networkType.type, subscriberId, startTime, endTime)) diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index ed41cd2..8a20be4 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -131,7 +131,7 @@ fun Permissions(navCtrl: NavHostController) { if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) FunctionItem(R.string.delegated_admins) { navCtrl.navigate("DelegatedAdmins") } FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { navCtrl.navigate("DeviceInfo") } - if((VERSION.SDK_INT >= 26 && deviceOwner) || (VERSION.SDK_INT >= 24 && profileOwner)) { + if(VERSION.SDK_INT >= 24 && (profileOwner || (VERSION.SDK_INT >= 26 && deviceOwner))) { FunctionItem(R.string.org_name, icon = R.drawable.corporate_fare_fill0) { dialog = 2 } } if(VERSION.SDK_INT >= 31 && (profileOwner || deviceOwner)) { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt index d42225d..dfaa974 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -3,6 +3,7 @@ package com.bintianqi.owndroid.dpm import android.annotation.SuppressLint import android.app.ActivityOptions import android.app.AlertDialog +import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK @@ -166,7 +167,7 @@ fun SystemManage(navCtrl: NavHostController) { if(VERSION.SDK_INT >= 24 && deviceOwner) { FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 } } - if(deviceOwner && ((VERSION.SDK_INT >= 28 && dpm.isAffiliatedUser) || VERSION.SDK_INT >= 24)) { + if(deviceOwner && VERSION.SDK_INT >= 24 && (VERSION.SDK_INT < 28 || dpm.isAffiliatedUser)) { FunctionItem(R.string.bug_report, icon = R.drawable.bug_report_fill0) { dialog = 2 } } if(VERSION.SDK_INT >= 28 && (deviceOwner || dpm.isOrgProfile(receiver))) { @@ -175,6 +176,8 @@ fun SystemManage(navCtrl: NavHostController) { } /*if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner)) FunctionItem(R.string.key_pairs, icon = R.drawable.key_vertical_fill0) { navCtrl.navigate("KeyPairs") }*/ + if(VERSION.SDK_INT >= 35 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) + FunctionItem(R.string.content_protection_policy, icon = R.drawable.search_fill0) { navCtrl.navigate("ContentProtectionPolicy") } if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) { FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") } } @@ -274,7 +277,7 @@ fun SystemOptions(navCtrl: NavHostController) { getState = { dpm.autoTimeRequired }, onCheckedChange = { dpm.setAutoTimeRequired(receiver,it) }, padding = false) } } - if(deviceOwner || (profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && !dpm.isManagedProfile(receiver))))) { + if(deviceOwner || profileOwner) { SwitchItem(R.string.master_mute, icon = R.drawable.volume_up_fill0, getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) } ) @@ -812,6 +815,37 @@ fun KeyPairs(navCtrl: NavHostController) { } }*/ +@RequiresApi(35) +@Composable +fun ContentProtectionPolicy(navCtrl: NavHostController) { + val context = LocalContext.current + val dpm = context.getDPM() + val receiver = context.getReceiver() + var policy by remember { mutableIntStateOf(DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY) } + fun refresh() { policy = dpm.getContentProtectionPolicy(receiver) } + LaunchedEffect(Unit) { refresh() } + MyScaffold(R.string.content_protection_policy, 8.dp, navCtrl) { + mapOf( + DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY to R.string.not_controlled_by_policy, + DevicePolicyManager.CONTENT_PROTECTION_ENABLED to R.string.enabled, + DevicePolicyManager.CONTENT_PROTECTION_DISABLED to R.string.disabled + ).forEach { (policyId, string) -> + RadioButtonItem(string, policy == policyId) { policy = policyId } + } + Button( + onClick = { + dpm.setContentProtectionPolicy(receiver, policy) + refresh() + context.showOperationResultToast(true) + }, + modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp) + ) { + Text(stringResource(R.string.apply)) + } + InfoCard(R.string.info_content_protection_policy) + } +} + @RequiresApi(23) @Composable fun PermissionPolicy(navCtrl: NavHostController) { @@ -1515,7 +1549,7 @@ fun WipeData(navCtrl: NavHostController) { ) } Spacer(Modifier.padding(vertical = 5.dp)) - if(VERSION.SDK_INT < 34 || (VERSION.SDK_INT >= 34 && !userManager.isSystemUser)) { + if(VERSION.SDK_INT < 34 || !userManager.isSystemUser) { Button( onClick = { focusMgr.clearFocus() diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt index a693c78..1bcfddf 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt @@ -35,7 +35,7 @@ fun UserRestriction(navCtrl:NavHostController) { MyScaffold(R.string.user_restriction, 0.dp, navCtrl) { Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp)) if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) } - if(context.isProfileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { + if(context.isProfileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))) { Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp)) } Spacer(Modifier.padding(vertical = 2.dp)) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 276afc9..6a041fb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -176,6 +176,8 @@ Change timezone Идентификатор часового пояса Автоматический часовой пояс должен быть отключен перед установкой пользовательского часового пояса. + Content protection policy + Not controlled by policy Политика разрешений Автоматически разрешать Автоматически запрещать diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 26cfd79..d04910a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -178,6 +178,8 @@ Change timezone Saat dilimi kimliği Özel bir saat dilimi ayarlamadan önce otomatik saat dilimi devre dışı bırakılmalıdır + Content protection policy + Not controlled by policy İzin politikası Otomatik ver Otomatik reddet diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5a6eb40..077bb61 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -169,6 +169,8 @@ 更改时区 时区ID 在设置时区前需要关闭自动时区 + 内容保护策略 + 不受策略控制 权限策略 自动允许 自动拒绝 @@ -664,6 +666,7 @@ 从密钥环中移除用户的凭证加密密钥。用户需要再次输入凭证才能将密钥存储回密钥环中。为了保护用户数据,用户将重新启动 打电话时不能使用此功能 输入以毫秒为单位的UNIX时间 + 内容保护策略控制对欺骗性应用程序的扫描。 设置应用申请运行时权限时的默认选择 设置内存标记扩展(Memory Tagging Extension)策略。重启设备以应用更改。 应用流式传输:当app在虚拟显示器上启动,传输这个app的视频流到附近的设备。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a65f4cc..e3e1fd3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -90,7 +90,6 @@ Add delegated admin Dhizuku will be deactivated Reset device policy - Activate Device admin dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver dpm set-profile-owner --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver @@ -139,7 +138,6 @@ dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver Activate Device owner Activate organization-owned work profile - Shizuku service disconnected Accounts @@ -199,6 +197,8 @@ Base info Individual certificate Generate--> + Content protection policy + Not controlled by policy Permission policy Auto grant Auto deny @@ -702,6 +702,7 @@ Evict the user\'s credential encryption key from the keyring. The user\'s credential will need to be entered again in order to derive the credential encryption key that will be stored back in the keyring for future use. In order to secure user data, the user will be stopped and restarted. You can\'t use this function if there is an ongoing call on the device. Input UNIX time in milliseconds + Content protection policy controls scanning for deceptive apps. Set the default response for future runtime permission requests by applications. Set the Memory Tagging Extension policy. Reboot the device to apply changes. App streaming is when the device starts an app on a virtual display and sends a video stream of the app to nearby devices. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b76cb63..13626cb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ agp = "8.8.0" kotlin = "2.0.21" navigation-compose = "2.8.5" -composeBom = "2024.12.01" +composeBom = "2025.01.00" accompanist-drawablepainter = "0.35.0-alpha" accompanist-permissions = "0.37.0" shizuku = "13.1.5"