Enable scoped storage enforcement on Android 11+.

This has a number of UX issues and bugs we need to work through, but at least
games are playable, things mostly work. Upgrades are handled smoothly by
keeping existing storage access until you uninstall. After a reinstall, you'll
need to re-select your old PSP directory manually in settings :(
This commit is contained in:
Henrik Rydgård 2021-07-17 19:50:36 +02:00
parent 02449326cd
commit 793e79945f
6 changed files with 42 additions and 16 deletions

View file

@ -23,6 +23,7 @@
#include "Common/System/NativeApp.h"
#include "Common/System/System.h"
#include "Common/GPU/OpenGL/GLFeatures.h"
#include "Common/File/AndroidStorage.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Net/HTTPClient.h"
#include "Common/UI/Context.h"
@ -59,12 +60,8 @@
int GetD3DCompilerVersion();
#endif
#if PPSSPP_PLATFORM(ANDROID)
#include "android/jni/app-android.h"
#endif
static const char *logLevelList[] = {
"Notice",
"Error",
@ -595,6 +592,7 @@ void SystemInfoScreen::CreateViews() {
if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) {
storage->Add(new InfoItem("Scoped Storage", di->T("Yes")));
}
storage->Add(new InfoItem("IsStoragePreservedLegacy", Android_IsExternalStoragePreservedLegacy() ? di->T("Yes") : di->T("No")));
#endif
ViewGroup *buildConfigScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));

View file

@ -978,7 +978,8 @@ void MainScreen::CreateViews() {
tabHolder_->SetClip(true);
bool showRecent = g_Config.iMaxRecent > 0;
bool hasStorageAccess = System_GetPermissionStatus(SYSTEM_PERMISSION_STORAGE) == PERMISSION_STATUS_GRANTED;
bool hasStorageAccess = !System_GetPropertyBool(SYSPROP_SUPPORTS_PERMISSIONS) ||
System_GetPermissionStatus(SYSTEM_PERMISSION_STORAGE) == PERMISSION_STATUS_GRANTED;
bool storageIsTemporary = IsTempPath(GetSysDirectory(DIRECTORY_SAVEDATA)) && !confirmedTemporary_;
if (showRecent && !hasStorageAccess) {
showRecent = !g_Config.recentIsos.empty();

View file

@ -19,8 +19,8 @@
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="archos.permission.FULLSCREEN.FULL" />
@ -45,7 +45,9 @@
android:logo="@drawable/ic_banner"
android:isGame="true"
android:banner="@drawable/tv_banner"
android:requestLegacyExternalStorage="true">
android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true"
>
<meta-data android:name="android.max_aspect" android:value="2.4" />
<activity
android:name=".PpssppActivity"

View file

@ -44,8 +44,8 @@ android {
}
}
}
compileSdkVersion 29
buildToolsVersion '29.0.3'
compileSdkVersion 30
buildToolsVersion '30.0.0'
defaultConfig {
applicationId 'org.ppsspp.ppsspp'
if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) {
@ -61,7 +61,7 @@ android {
new File("versioncode.txt").write(androidGitVersion.code().toString())
minSdkVersion 9
targetSdkVersion 29
targetSdkVersion 30
if (project.hasProperty("ANDROID_VERSION_CODE") && project.hasProperty("ANDROID_VERSION_NAME")) {
versionCode ANDROID_VERSION_CODE
versionName ANDROID_VERSION_NAME

View file

@ -97,8 +97,18 @@ struct JNIEnv {};
bool useCPUThread = true;
// We'll turn this on when we target Android 12.
bool useScopedStorageIfRequired = false;
// We turn this on now that when we target Android 11+.
// Along with adding:
// android:preserveLegacyExternalStorage="true"
// To the already requested:
// android:requestLegacyExternalStorage="true"
//
// This will cause Android 11+ to still behave like Android 10 until the app
// is manually uninstalled. We can detect this state with
// Android_IsExternalStoragePreservedLegacy(), but most of the app will just see
// that scoped storage enforcement is disabled in this case.
static const bool useScopedStorageIfRequired = true;
enum class EmuThreadState {
DISABLED,
@ -433,7 +443,15 @@ float System_GetPropertyFloat(SystemProperty prop) {
bool System_GetPropertyBool(SystemProperty prop) {
switch (prop) {
case SYSPROP_SUPPORTS_PERMISSIONS:
return androidVersion >= 23; // 6.0 Marshmallow introduced run time permissions.
if (androidVersion < 23) {
// 6.0 Marshmallow introduced run time permissions.
return false;
} else {
// It gets a bit complicated here. If scoped storage enforcement is on,
// we also don't need to request permissions. We'll have the access we request
// on a per-folder basis.
return !System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE);
}
case SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE:
return sustainedPerfSupported; // 7.0 introduced sustained performance mode as an optional feature.
case SYSPROP_HAS_ADDITIONAL_STORAGE:
@ -458,8 +476,14 @@ bool System_GetPropertyBool(SystemProperty prop) {
case SYSPROP_CAN_JIT:
return true;
case SYSPROP_ANDROID_SCOPED_STORAGE:
if (useScopedStorageIfRequired && androidVersion >= 28)
return true;
if (useScopedStorageIfRequired && androidVersion >= 28) {
// Here we do a check to see if we ended up in the preserveLegacyExternalStorage path.
// That won't last if the user uninstalls/reinstalls though, but would preserve the user
// experience for simple upgrades so maybe let's support it.
return !Android_IsExternalStoragePreservedLegacy();
} else {
return false;
}
default:
return false;
}

View file

@ -5,6 +5,7 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.util.Log;