mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-09 05:24:44 +01:00
Avoid opening games that have crashed.
On each open, increment a counter. And if we're able to close without a crash intervening, decrement. Once we're trying to open with a non-0 counter we have a bad game. Open only after warning the user.
This commit is contained in:
parent
c39bede5c1
commit
8c50bb1895
6 changed files with 196 additions and 21 deletions
|
@ -1106,9 +1106,11 @@ public class DBUtils {
|
||||||
|
|
||||||
public static byte[] loadGame( Context context, GameLock lock )
|
public static byte[] loadGame( Context context, GameLock lock )
|
||||||
{
|
{
|
||||||
|
byte[] result = null;
|
||||||
long rowid = lock.getRowid();
|
long rowid = lock.getRowid();
|
||||||
Assert.assertTrue( ROWID_NOTFOUND != rowid );
|
Assert.assertTrue( ROWID_NOTFOUND != rowid );
|
||||||
byte[] result = getCached( rowid );
|
if ( Quarantine.safeToOpen( rowid ) ) {
|
||||||
|
result = getCached( rowid );
|
||||||
if ( null == result ) {
|
if ( null == result ) {
|
||||||
String[] columns = { DBHelper.SNAPSHOT };
|
String[] columns = { DBHelper.SNAPSHOT };
|
||||||
String selection = String.format( ROW_ID_FMT, rowid );
|
String selection = String.format( ROW_ID_FMT, rowid );
|
||||||
|
@ -1125,6 +1127,7 @@ public class DBUtils {
|
||||||
}
|
}
|
||||||
setCached( rowid, result );
|
setCached( rowid, result );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class DlgDelegate {
|
||||||
SEND_EMAIL,
|
SEND_EMAIL,
|
||||||
WRITE_LOG_DB,
|
WRITE_LOG_DB,
|
||||||
CLEAR_LOG_DB,
|
CLEAR_LOG_DB,
|
||||||
|
CLEAR_QUARANTINE,
|
||||||
|
|
||||||
// BoardDelegate
|
// BoardDelegate
|
||||||
UNDO_LAST_ACTION,
|
UNDO_LAST_ACTION,
|
||||||
|
|
|
@ -1226,6 +1226,25 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openWithChecks( long rowid, GameSummary summary )
|
||||||
|
{
|
||||||
|
if ( ! m_launchedGames.contains( rowid ) ) {
|
||||||
|
if ( Quarantine.safeToOpen( rowid ) ) {
|
||||||
|
makeNotAgainBuilder( R.string.not_again_newselect,
|
||||||
|
R.string.key_notagain_newselect,
|
||||||
|
Action.OPEN_GAME )
|
||||||
|
.setParams( rowid, summary )
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
makeConfirmThenBuilder( R.string.unsafe_open_warning,
|
||||||
|
Action.CLEAR_QUARANTINE )
|
||||||
|
.setPosButton( R.string.unsafe_open_disregard )
|
||||||
|
.setParams( rowid, summary )
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// SelectableItem interface
|
// SelectableItem interface
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1237,13 +1256,7 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
// an empty room name.
|
// an empty room name.
|
||||||
if ( clicked instanceof GameListItem ) {
|
if ( clicked instanceof GameListItem ) {
|
||||||
long rowid = ((GameListItem)clicked).getRowID();
|
long rowid = ((GameListItem)clicked).getRowID();
|
||||||
if ( ! m_launchedGames.contains( rowid ) ) {
|
openWithChecks( rowid, summary );
|
||||||
makeNotAgainBuilder( R.string.not_again_newselect,
|
|
||||||
R.string.key_notagain_newselect,
|
|
||||||
Action.OPEN_GAME )
|
|
||||||
.setParams( rowid, summary )
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1344,6 +1357,13 @@ public class GamesListDelegate extends ListDelegateBase
|
||||||
case OPEN_GAME:
|
case OPEN_GAME:
|
||||||
doOpenGame( params );
|
doOpenGame( params );
|
||||||
break;
|
break;
|
||||||
|
case CLEAR_QUARANTINE:
|
||||||
|
long rowid = (long)params[0];
|
||||||
|
Quarantine.clear( rowid );
|
||||||
|
GameSummary summary = (GameSummary)params[0];
|
||||||
|
openWithChecks( rowid, summary );
|
||||||
|
break;
|
||||||
|
|
||||||
case CLEAR_SELS:
|
case CLEAR_SELS:
|
||||||
clearSelections();
|
clearSelections();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2009-2020 by Eric House (xwords@eehouse.org). All rights
|
||||||
|
* reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.eehouse.android.xw4;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Quarantine {
|
||||||
|
private static final String TAG = Quarantine.class.getSimpleName();
|
||||||
|
private static final String DATA_KEY = TAG + "/key";
|
||||||
|
|
||||||
|
public static boolean safeToOpen( long rowid )
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
synchronized ( sDataRef ) {
|
||||||
|
count = get().getFor( rowid );
|
||||||
|
}
|
||||||
|
boolean result = count == 0; // Not too strict?
|
||||||
|
Log.d( TAG, "safeToOpen(%d) => %b (count=%d)", rowid, result, count );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear( long rowid )
|
||||||
|
{
|
||||||
|
synchronized ( sDataRef ) {
|
||||||
|
get().clear( rowid );
|
||||||
|
store();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void recordOpened( long rowid )
|
||||||
|
{
|
||||||
|
synchronized ( sDataRef ) {
|
||||||
|
get().increment( rowid );
|
||||||
|
store();
|
||||||
|
Log.d( TAG, "recordOpened(%d): %s", rowid, sDataRef[0].toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void recordClosed( long rowid )
|
||||||
|
{
|
||||||
|
synchronized ( sDataRef ) {
|
||||||
|
get().decrement( rowid );
|
||||||
|
store();
|
||||||
|
Log.d( TAG, "recordClosed(%d): %s", rowid, sDataRef[0].toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Data implements Serializable {
|
||||||
|
private HashMap<Long, Integer> mCounts = new HashMap<>();
|
||||||
|
|
||||||
|
synchronized void increment( long rowid ) {
|
||||||
|
if ( ! mCounts.containsKey(rowid) ) {
|
||||||
|
mCounts.put(rowid, 0);
|
||||||
|
}
|
||||||
|
mCounts.put( rowid, mCounts.get(rowid) + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void decrement( long rowid )
|
||||||
|
{
|
||||||
|
Assert.assertTrue( mCounts.containsKey(rowid) );
|
||||||
|
mCounts.put( rowid, mCounts.get(rowid) - 1 );
|
||||||
|
Assert.assertTrueNR( mCounts.get(rowid) >= 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized int getFor( long rowid )
|
||||||
|
{
|
||||||
|
int result = mCounts.containsKey(rowid) ? mCounts.get( rowid ) : 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void clear( long rowid )
|
||||||
|
{
|
||||||
|
mCounts.put( rowid, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder().append("[");
|
||||||
|
synchronized ( mCounts ) {
|
||||||
|
for ( long rowid : mCounts.keySet() ) {
|
||||||
|
int count = mCounts.get(rowid);
|
||||||
|
sb.append( String.format("{%d: %d}", rowid, count ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.append("]").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Data[] sDataRef = {null};
|
||||||
|
|
||||||
|
private static void store()
|
||||||
|
{
|
||||||
|
synchronized( sDataRef ) {
|
||||||
|
DBUtils.setSerializableFor( getContext(), DATA_KEY, sDataRef[0] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Data get()
|
||||||
|
{
|
||||||
|
Data data;
|
||||||
|
synchronized ( sDataRef ) {
|
||||||
|
data = sDataRef[0];
|
||||||
|
if ( null == data ) {
|
||||||
|
data = (Data)DBUtils.getSerializableFor( getContext(), DATA_KEY );
|
||||||
|
if ( null == data ) {
|
||||||
|
data = new Data();
|
||||||
|
} else {
|
||||||
|
Log.d( TAG, "loading existing: %s", data );
|
||||||
|
}
|
||||||
|
sDataRef[0] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Context getContext()
|
||||||
|
{
|
||||||
|
return XWApp.getContext();
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import org.eehouse.android.xw4.Assert;
|
||||||
import org.eehouse.android.xw4.BuildConfig;
|
import org.eehouse.android.xw4.BuildConfig;
|
||||||
import org.eehouse.android.xw4.Log;
|
import org.eehouse.android.xw4.Log;
|
||||||
import org.eehouse.android.xw4.NetLaunchInfo;
|
import org.eehouse.android.xw4.NetLaunchInfo;
|
||||||
|
import org.eehouse.android.xw4.Quarantine;
|
||||||
import org.eehouse.android.xw4.Utils;
|
import org.eehouse.android.xw4.Utils;
|
||||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ public class XwJNI {
|
||||||
m_ptrGame = ptr;
|
m_ptrGame = ptr;
|
||||||
m_rowid = rowid;
|
m_rowid = rowid;
|
||||||
mStack = android.util.Log.getStackTraceString(new Exception());
|
mStack = android.util.Log.getStackTraceString(new Exception());
|
||||||
|
Quarantine.recordOpened( rowid );
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long ptr()
|
public synchronized long ptr()
|
||||||
|
@ -73,6 +75,7 @@ public class XwJNI {
|
||||||
// getClass().getName(), this, m_rowid, m_refCount );
|
// getClass().getName(), this, m_rowid, m_refCount );
|
||||||
if ( 0 == m_refCount ) {
|
if ( 0 == m_refCount ) {
|
||||||
if ( 0 != m_ptrGame ) {
|
if ( 0 != m_ptrGame ) {
|
||||||
|
Quarantine.recordClosed( m_rowid );
|
||||||
if ( haveEnv( getJNI().m_ptrGlobals ) ) {
|
if ( haveEnv( getJNI().m_ptrGlobals ) ) {
|
||||||
game_dispose( this ); // will crash if haveEnv fails
|
game_dispose( this ); // will crash if haveEnv fails
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2520,6 +2520,11 @@
|
||||||
<string name="history_msg_fmt"> Message: %1$s.</string>
|
<string name="history_msg_fmt"> Message: %1$s.</string>
|
||||||
<string name="history_autopause">Auto-paused.</string>
|
<string name="history_autopause">Auto-paused.</string>
|
||||||
|
|
||||||
|
<string name="unsafe_open_warning">This game has caused CrossWords
|
||||||
|
to crash recently and is likely corrupt. Do you want to open it
|
||||||
|
anyway?</string>
|
||||||
|
<string name="unsafe_open_disregard">Open anyway</string>
|
||||||
|
|
||||||
<string name="gamel_menu_logs">Debug logs</string>
|
<string name="gamel_menu_logs">Debug logs</string>
|
||||||
<!-- Debug-build-only menu to turn on saving of logs -->
|
<!-- Debug-build-only menu to turn on saving of logs -->
|
||||||
<string name="gamel_menu_logstore_on">Enable log storage</string>
|
<string name="gamel_menu_logstore_on">Enable log storage</string>
|
||||||
|
|
Loading…
Reference in a new issue