Skip to content

Commit 3c1abb5

Browse files
muhomorrthestinger
authored andcommitted
add per-app Play Integrity API settings
1 parent 32ff1c8 commit 3c1abb5

File tree

7 files changed

+169
-0
lines changed

7 files changed

+169
-0
lines changed

AndroidManifest.xml

+16
Original file line numberDiff line numberDiff line change
@@ -5524,5 +5524,21 @@
55245524
android:value="@string/menu_key_apps"/>
55255525
</activity>
55265526

5527+
<activity
5528+
android:name=".Settings$AppManagePlayIntegrityApiActivity"
5529+
android:exported="true">
5530+
5531+
<intent-filter>
5532+
<action android:name="android.settings.OPEN_APP_MANAGE_PLAY_INTEGRITY_API_SETTINGS" />
5533+
<category android:name="android.intent.category.DEFAULT" />
5534+
<data android:scheme="package" />
5535+
</intent-filter>
5536+
5537+
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
5538+
android:value="com.android.settings.applications.AppManagePlayIntegrityApiFragment"/>
5539+
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
5540+
android:value="@string/menu_key_apps"/>
5541+
</activity>
5542+
55275543
</application>
55285544
</manifest>

res/values/strings_ext.xml

+17
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,21 @@ Duress PIN works the same way duress password does."</string>
208208
<string name="alt_touchscreen_mode_confirm_message">Confirm that the touchscreen is working. Without confirmation, alternative touchscreen mode will be disabled after several seconds.</string>
209209
<string name="alt_touchscreen_mode_confirm_button">Confirm</string>
210210

211+
<string name="app_play_integrity_api">Play Integrity API</string>
212+
<string name="app_play_integrity_top_intro">Play Integrity API provides a way to check that a
213+
device meets certain standards, mainly that it runs the stock OS on a device licensing
214+
Google Mobile Services which means the device is considered certified by Google. Some apps
215+
use it without enforcing certification but the other functionality it provides doesn’t work
216+
well without checking for it since it provides no other way to verify the device and OS.
217+
</string>
218+
<string name="app_play_integrity_contact_app_developer">Contact app developer</string>
219+
<string name="app_play_integrity_show_usage_notifications">Show usage notifications</string>
220+
<string name="app_play_integrity_block_usage_attempts">Block usage attempts</string>
221+
<string name="app_play_integrity_block_usage_attempts_summary_on">
222+
Play Integrity API usage attempts will fail with the “API is not available” error code
223+
</string>
224+
<string name="app_play_integrity_api_blocked">Blocked</string>
225+
<string name="app_play_integrity_api_not_blocked">Not blocked</string>
226+
<string name="app_play_integrity_see_all_apps">Show apps that used the Play Integrity API</string>
227+
211228
</resources>

src/com/android/settings/Settings.java

+2
Original file line numberDiff line numberDiff line change
@@ -526,5 +526,7 @@ public static class AppNativeDebuggingActivity extends SettingsActivity {}
526526

527527
public static class AppStorageDynCodeLoadingActivity extends SettingsActivity {}
528528

529+
public static class AppManagePlayIntegrityApiActivity extends SettingsActivity {}
530+
529531
public static class ExploitProtectionActivity extends SettingsActivity {}
530532
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.android.settings.applications
2+
3+
import android.app.compat.gms.GmsUtils
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.content.pm.ApplicationInfo
7+
import android.content.pm.GosPackageState
8+
import android.content.pm.GosPackageStateFlag
9+
import android.content.pm.PackageManager.NameNotFoundException
10+
import android.ext.PackageId
11+
import android.ext.settings.app.AswBlockPlayIntegrityApi
12+
import android.net.Uri
13+
import android.os.Bundle
14+
import androidx.appcompat.app.AlertDialog
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.platform.LocalContext
17+
import androidx.preference.Preference
18+
import androidx.preference.PreferenceScreen
19+
import androidx.preference.SwitchPreferenceCompat
20+
import com.android.settings.R
21+
import com.android.settings.spa.app.appinfo.AswPreference
22+
import com.android.settingslib.spaprivileged.model.app.userId
23+
import com.android.settingslib.widget.TopIntroPreference
24+
25+
object AswAdapterManagePlayIntegrityApi : AswAdapter<AswBlockPlayIntegrityApi>() {
26+
override fun getAppSwitch() = AswBlockPlayIntegrityApi.I
27+
28+
override fun getAswTitle(ctx: Context) = ctx.getText(R.string.app_play_integrity_api)
29+
override fun getOnTitle(ctx: Context) = ctx.getText(R.string.app_play_integrity_api_blocked)
30+
override fun getOffTitle(ctx: Context) = ctx.getText(R.string.app_play_integrity_api_not_blocked)
31+
32+
override fun getDetailFragmentClass() = AppManagePlayIntegrityApiFragment::class
33+
34+
override fun shouldIncludeInAppListPage(app: ApplicationInfo, gosPs: GosPackageState): Boolean {
35+
return gosPs.hasFlag(GosPackageStateFlag.PLAY_INTEGRITY_API_USED_AT_LEAST_ONCE)
36+
}
37+
}
38+
39+
@Composable
40+
fun AppManagePlayIntegrityApiPreference(app: ApplicationInfo) {
41+
if (GosPackageState.get(app.packageName, app.userId)
42+
.hasFlag(GosPackageStateFlag.PLAY_INTEGRITY_API_USED_AT_LEAST_ONCE)) {
43+
AswPreference(LocalContext.current, app, AswAdapterManagePlayIntegrityApi)
44+
}
45+
}
46+
47+
class AppManagePlayIntegrityApiFragment : AppInfoWithHeader() {
48+
private lateinit var showUsageNotifsSwitch: SwitchPreferenceCompat
49+
private lateinit var blockUsageAttemptsSwitch: SwitchPreferenceCompat
50+
51+
override fun onCreate(savedInstanceState: Bundle?) {
52+
super.onCreate(savedInstanceState)
53+
54+
requireActivity().setTitle(R.string.app_play_integrity_api)
55+
56+
val prefCtx = prefContext
57+
val screen: PreferenceScreen = preferenceManager.createPreferenceScreen(prefCtx)
58+
59+
TopIntroPreference(prefCtx).apply {
60+
setTitle(R.string.app_play_integrity_top_intro)
61+
screen.addPreference(this)
62+
}
63+
Preference(prefCtx).apply {
64+
setTitle(R.string.app_play_integrity_contact_app_developer)
65+
setOnPreferenceClickListener {
66+
val pkgName = mPackageName
67+
val intent: Intent
68+
if (isPlayStoreAvailable()) {
69+
intent = GmsUtils.createAppPlayStoreIntent(pkgName)
70+
} else {
71+
val uri = Uri.parse("https://play.google.com/store/apps/details?id=$pkgName")
72+
intent = Intent.createChooser(Intent(Intent.ACTION_VIEW, uri), null)
73+
}
74+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
75+
startActivity(intent)
76+
true
77+
}
78+
screen.addPreference(this)
79+
}
80+
showUsageNotifsSwitch = SwitchPreferenceCompat(prefCtx).apply {
81+
setTitle(R.string.app_play_integrity_show_usage_notifications)
82+
setOnPreferenceChangeListener { _, value ->
83+
val ed = GosPackageState.edit(mPackageName, mUserId)
84+
AswBlockPlayIntegrityApi.I.setNotificationEnabled(ed, value as Boolean)
85+
ed.apply()
86+
}
87+
screen.addPreference(this)
88+
}
89+
blockUsageAttemptsSwitch = SwitchPreferenceCompat(prefCtx).apply {
90+
setTitle(R.string.app_play_integrity_block_usage_attempts)
91+
setSummaryOn(R.string.app_play_integrity_block_usage_attempts_summary_on)
92+
setOnPreferenceChangeListener { _, value ->
93+
val ed = GosPackageState.edit(mPackageName, mUserId)
94+
AswBlockPlayIntegrityApi.I.set(ed, value as Boolean)
95+
ed.apply()
96+
}
97+
screen.addPreference(this)
98+
}
99+
AswAdapterManagePlayIntegrityApi.addAppListPageLink(
100+
screen, getText(R.string.app_play_integrity_see_all_apps))
101+
102+
setPreferenceScreen(screen)
103+
}
104+
105+
override fun refreshUi(): Boolean {
106+
val gosPs = GosPackageState.get(mPackageName, mUserId)
107+
108+
showUsageNotifsSwitch.isChecked = AswBlockPlayIntegrityApi.I.isNotificationEnabled(gosPs)
109+
110+
blockUsageAttemptsSwitch.isChecked = AswBlockPlayIntegrityApi.I
111+
.get(requireContext(), mUserId, mPackageInfo.applicationInfo!!, gosPs)
112+
113+
return true
114+
}
115+
116+
private fun isPlayStoreAvailable(): Boolean {
117+
val pkgManager = requireContext().packageManager
118+
val appInfo = try {
119+
pkgManager.getApplicationInfoAsUser(PackageId.PLAY_STORE_NAME, 0, mUserId)
120+
} catch (e: NameNotFoundException) { return false }
121+
122+
if (!appInfo.enabled) {
123+
return false
124+
}
125+
return appInfo.ext().packageId == PackageId.PLAY_STORE
126+
}
127+
128+
override fun createDialog(id: Int, errorCode: Int): AlertDialog? = null
129+
130+
override fun getMetricsCategory(): Int = METRICS_CATEGORY_UNKNOWN
131+
}

src/com/android/settings/core/gateway/SettingsGateway.java

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ public class SettingsGateway {
219219
com.android.settings.applications.AppHardenedMallocFragment.class.getName(),
220220
com.android.settings.applications.AppMemoryDynCodeLoadingFragment.class.getName(),
221221
com.android.settings.applications.AppStorageDynCodeLoadingFragment.class.getName(),
222+
com.android.settings.applications.AppManagePlayIntegrityApiFragment.class.getName(),
222223
com.android.settings.safetycenter.ExploitProtectionFragment.class.getName(),
223224
AdvancedConnectedDeviceDashboardFragment.class.getName(),
224225
CreateShortcut.class.getName(),

src/com/android/settings/spa/SettingsSpaEnvironment.kt

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
107107
com.android.settings.applications.AswAdapterWebViewDynCodeLoading.makeAppListPageProvider(),
108108
com.android.settings.applications.AswAdapterMemoryDynCodeLoading.makeAppListPageProvider(),
109109
com.android.settings.applications.AswAdapterStorageDynCodeLoading.makeAppListPageProvider(),
110+
com.android.settings.applications.AswAdapterManagePlayIntegrityApi.makeAppListPageProvider(),
110111
AllAppListPageProvider,
111112
AppInfoSettingsProvider,
112113
SpecialAppAccessPageProvider,

src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt

+1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
164164
}
165165

166166
Category(title = stringResource(R.string.advanced_apps)) {
167+
com.android.settings.applications.AppManagePlayIntegrityApiPreference(app)
167168
UserAspectRatioAppPreference(app)
168169
DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app)
169170
ModifySystemSettingsAppListProvider.InfoPageEntryItem(app)

0 commit comments

Comments
 (0)