Try to implement write permission checking and requesting permissions on newer Android devices

This commit is contained in:
Alibek Omarov (a1batross) 2017-03-07 00:00:16 +03:00
parent 139972e5c8
commit ea60581142
4 changed files with 251 additions and 163 deletions

View file

@ -61,12 +61,12 @@
<string name="write_failed">Write test has failed</string>
<string name="ask_about_new_basedir_msg">Move your game files somewhere else, for example Android/data/%s or internal memory. At next run I will ask you about folder again.</string>
<string name="ask_about_new_basedir_msg">Move your game files somewhere else, for example Android/data/in.celest.xash3d.hl or internal memory. At next run I will ask you about folder again.</string>
<string name="lollipop_request_permission_msg">Due to writing politics of newer Android versions, you need to select a root folder of storage where game data is located.</string>
<string name="lollipop_select_folder_btn">Select folder</string>
<!-- These strings must be concat with a "ask_about_new_basedir" string -->
<string name="lollipop_request_permission_fail_msg">Write test has been failed twice. </string>
<string name="kitkat_write_fail_msg">Due to writing politics of Android 4.4, you can't use this storage. </string>
<string name="kitkat_write_fail_msg">Due to writing politics of Android 4.4, you can\'t use this storage. </string>
<string name="readonly_fs_fail_msg">Seems you have read-only filesystem. </string>
</resources>

View file

@ -0,0 +1,72 @@
package in.celest.xash3d;
import android.content.*;
import android.view.*;
import android.os.*;
import android.util.*;
import android.graphics.*;
import android.text.method.*;
import android.text.*;
import android.media.*;
import android.hardware.*;
import android.content.*;
import android.widget.*;
import android.content.pm.*;
import java.lang.*;
import java.util.List;
import java.security.MessageDigest;
import in.celest.xash3d.hl.BuildConfig;
import in.celest.xash3d.XashConfig;
public class CertCheck
{
// Certificate checking
private static String SIG = "DMsE8f5hlR7211D8uehbFpbA0n8=";
private static String SIG_TEST = ""; // a1ba: mittorn, add your signature later
private static String TAG = "XASH3D:CertCheck";
public static boolean dumbAntiPDALifeCheck( Context context )
{
if( BuildConfig.DEBUG ||
!XashConfig.CHECK_SIGNATURES )
return false; // disable checking for debug builds
try
{
PackageInfo info = context.getPackageManager()
.getPackageInfo( context.getPackageName(), PackageManager.GET_SIGNATURES );
for( Signature signature: info.signatures )
{
MessageDigest md = MessageDigest.getInstance( "SHA" );
final byte[] signatureBytes = signature.toByteArray();
md.update( signatureBytes );
final String curSIG = Base64.encodeToString( md.digest(), Base64.NO_WRAP );
if( XashConfig.PKG_TEST )
{
if( SIG_TEST.equals(curSIG) )
return false;
}
else
{
if( SIG.equals(curSIG) )
return false;
}
}
}
catch( Exception e )
{
e.printStackTrace();
}
Log.e(TAG, "Please, don't resign our public release builds!");
Log.e(TAG, "If you want to insert some features, rebuild package with ANOTHER package name from git repository.");
return true;
}
}

View file

@ -55,6 +55,7 @@ import org.json.*;
import in.celest.xash3d.hl.R;
import in.celest.xash3d.XashActivity;
import in.celest.xash3d.CertCheck;
public class LauncherActivity extends Activity {
// public final static String ARGV = "in.celest.xash3d.MESSAGE";
@ -89,7 +90,7 @@ public class LauncherActivity extends Activity {
super.setTheme( 0x01030224 );
else super.setTheme( 0x01030005 );
if( XashActivity.dumbAntiPDALifeCheck( this ) )
if( CertCheck.dumbAntiPDALifeCheck( this ) )
{
finish();
return;

View file

@ -19,6 +19,9 @@ import android.hardware.*;
import android.content.*;
import android.widget.*;
import android.content.pm.*;
import android.net.Uri;
import android.provider.*;
import android.database.*;
import android.view.inputmethod.*;
@ -26,9 +29,11 @@ import java.lang.*;
import java.util.List;
import java.security.MessageDigest;
import in.celest.xash3d.hl.R;
import in.celest.xash3d.hl.BuildConfig;
import in.celest.xash3d.XashConfig;
import in.celest.xash3d.JoystickHandler;
import in.celest.xash3d.CertCheck;
/**
Xash Activity
@ -47,13 +52,16 @@ public class XashActivity extends Activity {
public static JoystickHandler handler;
public static ImmersiveMode mImmersiveMode;
public static boolean keyboardVisible = false;
public static boolean mEngineReady = false;
private static Vibrator mVibrator;
private static boolean mHasVibrator;
private int mReturingWithResultCode = 0;
private static int OPEN_DOCUMENT_TREE_RESULT = 1;
private static int FPICKER_RESULT = 2;
// Joystick constants
public final static byte JOY_HAT_CENTERED = 0; // bitmasks for hat current status
public final static byte JOY_HAT_UP = 1;
@ -73,9 +81,6 @@ public class XashActivity extends Activity {
private static boolean mUseVolume;
public static View mDecorView;
// Certificate checking
private static String SIG = "DMsE8f5hlR7211D8uehbFpbA0n8=";
private static String SIG_TEST = ""; // a1ba: mittorn, add your signature later
// Load the .so
static {
@ -83,56 +88,15 @@ public class XashActivity extends Activity {
System.loadLibrary("xash");
}
// Shared between this activity and LauncherActivity
public static boolean dumbAntiPDALifeCheck( Context context )
{
if( BuildConfig.DEBUG ||
!XashConfig.CHECK_SIGNATURES )
return false; // disable checking for debug builds
try
{
PackageInfo info = context.getPackageManager()
.getPackageInfo( context.getPackageName(), PackageManager.GET_SIGNATURES );
for( Signature signature: info.signatures )
{
MessageDigest md = MessageDigest.getInstance( "SHA" );
final byte[] signatureBytes = signature.toByteArray();
md.update( signatureBytes );
final String curSIG = Base64.encodeToString( md.digest(), Base64.NO_WRAP );
if( XashConfig.PKG_TEST )
{
if( SIG_TEST.equals(curSIG) )
return false;
}
else
{
if( SIG.equals(curSIG) )
return false;
}
}
}
catch( Exception e )
{
e.printStackTrace();
}
Log.e(TAG, "Please, don't resign our public release builds!");
Log.e(TAG, "If you want to insert some features, rebuild package with ANOTHER package name from git repository.");
return true;
}
// Setup
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "onCreate()");
super.onCreate(savedInstanceState);
mEngineReady = false;
if( dumbAntiPDALifeCheck(this) )
if( CertCheck.dumbAntiPDALifeCheck(this) )
{
finish();
return;
@ -170,27 +134,6 @@ public class XashActivity extends Activity {
checkWritePermission( basedir );
}
if( sdk < 12 )
handler = new JoystickHandler();
else
handler = new JoystickHandler_v12();
handler.init();
mPixelFormat = mPref.getInt("pixelformat", 0);
mUseVolume = mPref.getBoolean("usevolume", false);
if( mPref.getBoolean("enableResizeWorkaround", true) )
AndroidBug5497Workaround.assistActivity(this);
// Immersive Mode is available only at >KitKat
Boolean enableImmersive = (sdk >= 19) && (mPref.getBoolean("immersive_mode", true));
if( enableImmersive )
mImmersiveMode = new ImmersiveMode_v19();
mDecorView = getWindow().getDecorView();
mVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = ( mVibrator != null ) && ( mVibrator.hasVibrator() );
}
@Override
@ -198,43 +141,145 @@ public class XashActivity extends Activity {
{
if( resultCode != RESULT_OK )
{
Log.v(TAG, "onActivityResult: result is not OK. Code: " + requestCode + ". Will exit now");
finish();
Log.v(TAG, "onActivityResult: result is not OK. ReqCode: " + requestCode + ". ResCode: " + resultCode);
}
if( requestCode == FPICKER_RESULT )
else
{
String newBaseDir = resultData.getStringExtra("GetPath");
setNewBasedir( newBaseDir );
setFolderAsk( false ); // don't ask on next run
checkWritePermission( newBaseDir );
}
else if( requestCode == OPEN_DOCUMENT_TREE_RESULT )
{
String basedir = getStringExtraFromIntent( getIntent(), "basedir", mPref.getString("basedir","/sdcard/xash/"));
if( !nativeTestWritePermission( basedir ) )
// it's not possible to create dialogs here
// so most work will be done after Activity resuming, in onPostResume()
mReturingWithResultCode = requestCode;
if( requestCode == FPICKER_RESULT )
{
String msg = getString(R.string.lollipop_request_permission_fail_msg) + getString(R.string.ask_about_new_basedir_msg);
new AlertDialog.Builder(this)
.setTitle( R.string.write_failed )
.setMessage( msg )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = (XashActivity)getActivity();
act.setFolderAsk( true );
act.finish();
}
})
.setCancelable(false)
.show();
String newBaseDir = resultData.getStringExtra("GetPath");
setNewBasedir( newBaseDir );
setFolderAsk( false ); // don't ask on next run
Log.v(TAG, "Got new basedir from FPicker: " + newBaseDir );
}
else if( requestCode == OPEN_DOCUMENT_TREE_RESULT )
{
Uri uri = resultData.getData();
if( uri != null )
{
getContentResolver().takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
}
}
}
}
@Override
public void onPostResume()
{
super.onPostResume();
if( mReturingWithResultCode != 0 )
{
if( mReturingWithResultCode == FPICKER_RESULT )
{
String basedir = mPref.getString( "basedir", "/sdcard/xash/" );
checkWritePermission( basedir );
}
else if( mReturingWithResultCode == OPEN_DOCUMENT_TREE_RESULT )
{
String basedir = getStringExtraFromIntent( getIntent(), "basedir", mPref.getString("basedir","/sdcard/xash/"));
Log.v(TAG, "Got permissions. Checking writing again...");
if( nativeTestWritePermission( basedir ) == 0 )
{
Log.v(TAG, "Write test has failed twice!");
String msg = getString(R.string.lollipop_request_permission_fail_msg) + getString(R.string.ask_about_new_basedir_msg);
new AlertDialog.Builder(this)
.setTitle( R.string.write_failed )
.setMessage( msg )
.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
})
.setCancelable(false)
.show();
}
else
{
launchSurfaceAndEngine();
}
}
mReturingWithResultCode = 0;
}
}
// Events
@Override
protected void onPause() {
Log.v(TAG, "onPause()");
if( mEngineReady )
{
// let engine save all configs before exiting.
nativeOnPause();
// wait until Xash will save all configs
mSurface.engineThreadWait();
}
super.onPause();
}
@Override
protected void onResume() {
Log.v(TAG, "onResume()");
super.onResume();
}
@Override
protected void onStop() {
Log.v(TAG, "onStop()");
if( mEngineReady )
{
// let engine properly exit, instead of killing it's thread
nativeOnStop();
// wait for engine
mSurface.engineThreadWait();
}
super.onStop();
}
@Override
protected void onDestroy() {
Log.v(TAG, "onStop()");
if( mEngineReady )
{
// let engine a chance to properly exit
nativeOnDestroy();
// wait until Xash will exit
mSurface.engineThreadJoin();
}
super.onDestroy();
}
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
if( mImmersiveMode != null )
mImmersiveMode.apply();
}
public void setFolderAsk( Boolean b )
{
SharedPreferences.Editor editor = mPref.edit();
@ -247,7 +292,7 @@ public class XashActivity extends Activity {
{
SharedPreferences.Editor editor = mPref.edit();
editor.putBoolean( "basedir", baseDir );
editor.putString( "basedir", baseDir );
editor.commit();
}
@ -261,9 +306,11 @@ public class XashActivity extends Activity {
private void checkWritePermission( String basedir )
{
if( !nativeTestWritePermission( basedir ) )
Log.v(TAG, "Checking write permissions...");
if( nativeTestWritePermission( basedir ) == 0 )
{
Object lock = new Object;
Log.v(TAG, "First check has failed!");
if( sdk > 20 )
{
@ -278,8 +325,7 @@ public class XashActivity extends Activity {
public void onClick(DialogInterface dialog, int whichButton)
{
Intent intent = new Intent("android.intent.action.OPEN_DOCUMENT_TREE");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getActivity().startActivityForResult(intent, OPEN_DOCUMENT_TREE_RESULT);
XashActivity.this.startActivityForResult(intent, OPEN_DOCUMENT_TREE_RESULT);
}
})
.setCancelable(false)
@ -297,7 +343,7 @@ public class XashActivity extends Activity {
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = (XashActivity)getActivity();
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
@ -318,7 +364,7 @@ public class XashActivity extends Activity {
{
public void onClick(DialogInterface dialog, int whichButton)
{
XashActivity act = (XashActivity)getActivity();
XashActivity act = XashActivity.this;
act.setFolderAsk( true );
act.finish();
}
@ -336,10 +382,11 @@ public class XashActivity extends Activity {
private void launchSurfaceAndEngine()
{
Log.v(TAG, "Everything is OK. Launching engine...");
setupEnvironment();
InstallReceiver.extractPAK(this, false);
// Set up the surface
mSurface = new EngineSurface(getApplication());
@ -350,6 +397,28 @@ public class XashActivity extends Activity {
SurfaceHolder holder = mSurface.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
if( sdk < 12 )
handler = new JoystickHandler();
else
handler = new JoystickHandler_v12();
handler.init();
mPixelFormat = mPref.getInt("pixelformat", 0);
mUseVolume = mPref.getBoolean("usevolume", false);
if( mPref.getBoolean("enableResizeWorkaround", true) )
AndroidBug5497Workaround.assistActivity(this);
// Immersive Mode is available only at >KitKat
Boolean enableImmersive = (sdk >= 19) && (mPref.getBoolean("immersive_mode", true));
if( enableImmersive )
mImmersiveMode = new ImmersiveMode_v19();
mDecorView = getWindow().getDecorView();
mVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = ( mVibrator != null ) && ( mVibrator.hasVibrator() );
mEngineReady = true;
}
private void setupEnvironment()
@ -391,60 +460,6 @@ public class XashActivity extends Activity {
}
}
// Events
@Override
protected void onPause() {
Log.v(TAG, "onPause()");
// let engine save all configs before exiting.
nativeOnPause();
// wait until Xash will save all configs
mSurface.engineThreadWait();
super.onPause();
}
@Override
protected void onResume() {
Log.v(TAG, "onResume()");
super.onResume();
}
@Override
protected void onStop() {
Log.v(TAG, "onStop()");
// let engine properly exit, instead of killing it's thread
nativeOnStop();
// wait for engine
mSurface.engineThreadWait();
super.onStop();
}
@Override
protected void onDestroy() {
Log.v(TAG, "onStop()");
// let engine a chance to properly exit
nativeOnDestroy();
// wait until Xash will exit
mSurface.engineThreadJoin();
super.onDestroy();
}
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
if( mImmersiveMode != null )
mImmersiveMode.apply();
}
public static native int nativeInit(Object arguments);
public static native void nativeQuit();