mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-08 05:24:39 +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,24 +1106,27 @@ public class DBUtils {
|
|||
|
||||
public static byte[] loadGame( Context context, GameLock lock )
|
||||
{
|
||||
byte[] result = null;
|
||||
long rowid = lock.getRowid();
|
||||
Assert.assertTrue( ROWID_NOTFOUND != rowid );
|
||||
byte[] result = getCached( rowid );
|
||||
if ( null == result ) {
|
||||
String[] columns = { DBHelper.SNAPSHOT };
|
||||
String selection = String.format( ROW_ID_FMT, rowid );
|
||||
initDB( context );
|
||||
synchronized( s_dbHelper ) {
|
||||
Cursor cursor = query( TABLE_NAMES.SUM, columns, selection );
|
||||
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
|
||||
result = cursor.getBlob( cursor
|
||||
.getColumnIndex(DBHelper.SNAPSHOT));
|
||||
} else {
|
||||
Log.e( TAG, "loadGame: none for rowid=%d", rowid );
|
||||
if ( Quarantine.safeToOpen( rowid ) ) {
|
||||
result = getCached( rowid );
|
||||
if ( null == result ) {
|
||||
String[] columns = { DBHelper.SNAPSHOT };
|
||||
String selection = String.format( ROW_ID_FMT, rowid );
|
||||
initDB( context );
|
||||
synchronized( s_dbHelper ) {
|
||||
Cursor cursor = query( TABLE_NAMES.SUM, columns, selection );
|
||||
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
|
||||
result = cursor.getBlob( cursor
|
||||
.getColumnIndex(DBHelper.SNAPSHOT));
|
||||
} else {
|
||||
Log.e( TAG, "loadGame: none for rowid=%d", rowid );
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
cursor.close();
|
||||
setCached( rowid, result );
|
||||
}
|
||||
setCached( rowid, result );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ public class DlgDelegate {
|
|||
SEND_EMAIL,
|
||||
WRITE_LOG_DB,
|
||||
CLEAR_LOG_DB,
|
||||
CLEAR_QUARANTINE,
|
||||
|
||||
// BoardDelegate
|
||||
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
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -1237,13 +1256,7 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
// an empty room name.
|
||||
if ( clicked instanceof GameListItem ) {
|
||||
long rowid = ((GameListItem)clicked).getRowID();
|
||||
if ( ! m_launchedGames.contains( rowid ) ) {
|
||||
makeNotAgainBuilder( R.string.not_again_newselect,
|
||||
R.string.key_notagain_newselect,
|
||||
Action.OPEN_GAME )
|
||||
.setParams( rowid, summary )
|
||||
.show();
|
||||
}
|
||||
openWithChecks( rowid, summary );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1344,6 +1357,13 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
case OPEN_GAME:
|
||||
doOpenGame( params );
|
||||
break;
|
||||
case CLEAR_QUARANTINE:
|
||||
long rowid = (long)params[0];
|
||||
Quarantine.clear( rowid );
|
||||
GameSummary summary = (GameSummary)params[0];
|
||||
openWithChecks( rowid, summary );
|
||||
break;
|
||||
|
||||
case CLEAR_SELS:
|
||||
clearSelections();
|
||||
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.Log;
|
||||
import org.eehouse.android.xw4.NetLaunchInfo;
|
||||
import org.eehouse.android.xw4.Quarantine;
|
||||
import org.eehouse.android.xw4.Utils;
|
||||
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
|
||||
|
||||
|
@ -46,6 +47,7 @@ public class XwJNI {
|
|||
m_ptrGame = ptr;
|
||||
m_rowid = rowid;
|
||||
mStack = android.util.Log.getStackTraceString(new Exception());
|
||||
Quarantine.recordOpened( rowid );
|
||||
}
|
||||
|
||||
public synchronized long ptr()
|
||||
|
@ -73,6 +75,7 @@ public class XwJNI {
|
|||
// getClass().getName(), this, m_rowid, m_refCount );
|
||||
if ( 0 == m_refCount ) {
|
||||
if ( 0 != m_ptrGame ) {
|
||||
Quarantine.recordClosed( m_rowid );
|
||||
if ( haveEnv( getJNI().m_ptrGlobals ) ) {
|
||||
game_dispose( this ); // will crash if haveEnv fails
|
||||
} else {
|
||||
|
|
|
@ -2520,6 +2520,11 @@
|
|||
<string name="history_msg_fmt"> Message: %1$s.</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>
|
||||
<!-- Debug-build-only menu to turn on saving of logs -->
|
||||
<string name="gamel_menu_logstore_on">Enable log storage</string>
|
||||
|
|
Loading…
Reference in a new issue