From b1a4b1a030ef8fc772014dd6d2c5a35b7bb5aa1d Mon Sep 17 00:00:00 2001 From: Eric House Date: Mon, 11 Feb 2019 20:39:17 -0800 Subject: [PATCH] Make JNIThread autoclosable and use everywhere JNIThread is somehow sticking around sometimes and holding the lock for a game so that that game can never be opened again. On the theory that there's some place retain() was called but not release(), use the thing in try-with-resources wherever possible. Which is pretty much everywhere. Also added age to the lock-holder report being uploaded. --- .../eehouse/android/xw4/BoardDelegate.java | 63 ++++++------- .../org/eehouse/android/xw4/ChatDelegate.java | 31 +++---- .../org/eehouse/android/xw4/DbgUtils.java | 2 +- .../android/xw4/GameConfigDelegate.java | 51 ++++++---- .../org/eehouse/android/xw4/GameLock.java | 25 +++-- .../org/eehouse/android/xw4/GameUtils.java | 92 +++++++++---------- .../eehouse/android/xw4/XWServiceHelper.java | 36 ++++---- .../eehouse/android/xw4/jni/JNIThread.java | 28 +++++- .../org/eehouse/android/xw4/jni/XwJNI.java | 1 - .../org/eehouse/android/xw4/CrashTrack.java | 2 +- .../org/eehouse/android/xw4/CrashTrack.java | 5 +- 11 files changed, 184 insertions(+), 152 deletions(-) diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java index d54cbaac4..fca8ad21e 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardDelegate.java @@ -89,7 +89,6 @@ public class BoardDelegate extends DelegateBase private Activity m_activity; private BoardView m_view; private GamePtr m_jniGamePtr; - private GameLock m_gameLock; private CurGameInfo m_gi; private GameSummary m_summary; private boolean m_relayMissing; @@ -539,16 +538,16 @@ public class BoardDelegate extends DelegateBase m_haveInvited = args.getBoolean( GameUtils.INVITED, false ); m_overNotShown = true; - GameLock.callWithLock( m_rowid, 100L, new Handler(), - new GameLock.LockProc() { + GameLock.getLockThen( m_rowid, 100L, new Handler(), + new GameLock.GotLockProc() { @Override public void gotLock( GameLock lock ) { if ( null == lock ) { finish(); if ( BuildConfig.REPORT_LOCKS && ++s_noLockCount == 3 ) { String msg = "BoardDelegate unable to get lock; holder stack: " - + GameLock.getHolderStack( m_rowid ); - CrashTrack.logAndSend( msg ); + + GameLock.getHolderDump( m_rowid ); + CrashTrack.logAndSend( TAG, msg ); } } else { s_noLockCount = 0; @@ -1501,7 +1500,7 @@ public class BoardDelegate extends DelegateBase private void deleteAndClose() { - GameUtils.deleteGame( m_activity, m_gameLock, false ); + GameUtils.deleteGame( m_activity, m_jniThread.getLock(), false ); waitCloseGame( false ); finish(); } @@ -2158,7 +2157,6 @@ public class BoardDelegate extends DelegateBase m_jniThread = m_jniThreadRef.retain(); m_gi = m_jniThread.getGI(); m_summary = m_jniThread.getSummary(); - m_gameLock = m_jniThread.getLock(); m_view.startHandling( m_activity, m_jniThread, m_connTypes ); @@ -2427,8 +2425,6 @@ public class BoardDelegate extends DelegateBase m_jniThread = null; m_view.stopHandling(); - - m_gameLock = null; } } @@ -2440,7 +2436,6 @@ public class BoardDelegate extends DelegateBase // m_jniGamePtr = null; // m_gameLock.unlock(); // likely the problem - m_gameLock = null; } } @@ -2793,34 +2788,32 @@ public class BoardDelegate extends DelegateBase GamePtr gamePtr = null; GameSummary summary = null; CurGameInfo gi = null; - JNIThread thread = JNIThread.getRetained( rowID ); - if ( null != thread ) { - gamePtr = thread.getGamePtr().retain(); - summary = thread.getSummary(); - gi = thread.getGI(); - } else { - try ( GameLock lock = GameLock.tryLockRO( rowID ) ) { - if ( null != lock ) { - summary = DBUtils.getSummary( activity, lock ); - gi = new CurGameInfo( activity ); - gamePtr = GameUtils.loadMakeGame( activity, gi, lock ); - } else { - DbgUtils.toastNoLock( TAG, activity, rowID, - "setupRematchFor(%d)", rowID ); + + try ( JNIThread thread = JNIThread.getRetained( rowID ) ) { + if ( null != thread ) { + gamePtr = thread.getGamePtr().retain(); + summary = thread.getSummary(); + gi = thread.getGI(); + } else { + try ( GameLock lock = GameLock.tryLockRO( rowID ) ) { + if ( null != lock ) { + summary = DBUtils.getSummary( activity, lock ); + gi = new CurGameInfo( activity ); + gamePtr = GameUtils.loadMakeGame( activity, gi, lock ); + } else { + DbgUtils.toastNoLock( TAG, activity, rowID, + "setupRematchFor(%d)", rowID ); + } } } - } - if ( null != gamePtr ) { - doRematchIf( activity, null, rowID, DBUtils.GROUPID_UNSPEC, - summary, gi, gamePtr ); - gamePtr.release(); - } else { - Log.w( TAG, "setupRematchFor(): unable to lock game" ); - } - - if ( null != thread ) { - thread.release(); + if ( null != gamePtr ) { + doRematchIf( activity, null, rowID, DBUtils.GROUPID_UNSPEC, + summary, gi, gamePtr ); + gamePtr.release(); + } else { + Log.w( TAG, "setupRematchFor(): unable to lock game" ); + } } } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ChatDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ChatDelegate.java index f3239e590..76e80f714 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ChatDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/ChatDelegate.java @@ -59,7 +59,6 @@ public class ChatDelegate extends DelegateBase { private EditText m_edit; private TableLayout m_layout; private ScrollView m_scroll; - private JNIThread m_jniThreadRef; public ChatDelegate( Delegator delegator, Bundle savedInstanceState ) { @@ -130,28 +129,20 @@ public class ChatDelegate extends DelegateBase { protected void onResume() { super.onResume(); - m_jniThreadRef = JNIThread.getRetained( m_rowid ); - if ( null == m_jniThreadRef ) { - Log.w( TAG, "onResume(): m_jniThreadRef null; exiting" ); - finish(); - } else { - s_visibleThis = this; - int[] startAndEnd = new int[2]; - String curMsg = DBUtils.getCurChat( m_activity, m_rowid, - m_curPlayer, startAndEnd ); - if ( null != curMsg && 0 < curMsg.length() ) { - m_edit.setText( curMsg ); - m_edit.setSelection( startAndEnd[0], startAndEnd[1] ); - } + + s_visibleThis = this; + int[] startAndEnd = new int[2]; + String curMsg = DBUtils.getCurChat( m_activity, m_rowid, + m_curPlayer, startAndEnd ); + if ( null != curMsg && 0 < curMsg.length() ) { + m_edit.setText( curMsg ); + m_edit.setSelection( startAndEnd[0], startAndEnd[1] ); } } @Override protected void onPause() { - if ( null != m_jniThreadRef ) { - m_jniThreadRef.release(); - } s_visibleThis = null; String curText = m_edit.getText().toString(); @@ -205,7 +196,11 @@ public class ChatDelegate extends DelegateBase { addRow( text, m_curPlayer, (int)ts ); m_edit.setText( null ); - m_jniThreadRef.sendChat( text ); + try ( JNIThread thread = JNIThread.getRetained( m_rowid ) ) { + if ( null != thread ) { + thread.sendChat( text ); + } + } } @Override diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java index c2266e947..35557e46b 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java @@ -66,7 +66,7 @@ public class DbgUtils { } Log.w( tag, format, args ); Log.w( tag, "stack for lock owner for %d", rowid ); - Log.w( tag, GameLock.getHolderStack( rowid ) ); + Log.w( tag, GameLock.getHolderDump( rowid ) ); } public static void assertOnUIThread() diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java index 6a5174e68..a349a7ce5 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java @@ -536,17 +536,16 @@ public class GameConfigDelegate extends DelegateBase if ( null == m_giOrig ) { m_giOrig = new CurGameInfo( m_activity ); - GameLock gameLock = null; XwJNI.GamePtr gamePtr = null; - if ( null == m_jniThread ) { - gameLock = GameLock.tryLockRO( m_rowid ); - if ( null != gameLock ) { - gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, - gameLock ); - } + if ( null != m_jniThread ) { + gamePtr = m_jniThread.getGamePtr().retain(); } else { - gameLock = m_jniThread.getLock(); - gamePtr = m_jniThread.getGamePtr(); + try ( GameLock lock = GameLock.tryLockRO( m_rowid ) ) { + if ( null != lock ) { + gamePtr = GameUtils.loadMakeGame( m_activity, m_giOrig, + lock ); + } + } } if ( null == gamePtr ) { @@ -588,10 +587,7 @@ public class GameConfigDelegate extends DelegateBase buildDisabledsMap( gamePtr ); setDisableds(); - if ( null == m_jniThread ) { - gamePtr.release(); - gameLock.unlock(); - } + gamePtr.release(); m_car = new CommsAddrRec( m_carOrig ); @@ -1230,17 +1226,32 @@ public class GameConfigDelegate extends DelegateBase m_car.conTypes = m_conTypes; } // saveChanges + private void applyChanges( GameLock lock, boolean forceNew ) + { + GameUtils.applyChanges( m_activity, m_gi, m_car, m_disabMap, + lock, forceNew ); + DBUtils.saveThumbnail( m_activity, lock, null ); // clear it + } + private void applyChanges( boolean forceNew ) { if ( !isFinishing() ) { - GameLock gameLock = m_jniThread == null - ? GameLock.tryLock( m_rowid ) : m_jniThread.getLock(); - GameUtils.applyChanges( m_activity, m_gi, m_car, m_disabMap, - gameLock, forceNew ); - DBUtils.saveThumbnail( m_activity, gameLock, null ); // clear it - if ( null == m_jniThread ) { - gameLock.unlock(); + if ( null != m_jniThread ) { + applyChanges( m_jniThread.getLock(), forceNew ); + } else { + try ( GameLock lock = GameLock.tryLock( m_rowid ) ) { + applyChanges( lock, forceNew ); + } } + // } + // GameLock gameLock = m_jniThread == null + // ? GameLock.tryLock( m_rowid ) : m_jniThread.getLock(); + // GameUtils.applyChanges( m_activity, m_gi, m_car, m_disabMap, + // gameLock, forceNew ); + // DBUtils.saveThumbnail( m_activity, gameLock, null ); // clear it + // if ( null == m_jniThread ) { + // gameLock.unlock(); + // } } } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameLock.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameLock.java index 0e0620a5c..464bcf92f 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameLock.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameLock.java @@ -60,18 +60,24 @@ public class GameLock implements AutoCloseable, Serializable { private static class Owner { Thread mThread; String mTrace; + long mStamp; Owner() { mThread = Thread.currentThread(); mTrace = android.util.Log.getStackTraceString(new Exception()); + setStamp(); } @Override public String toString() { - return String.format( "Owner: {%s/%s}", mThread, mTrace ); + long ageMS = System.currentTimeMillis() - mStamp; + return String.format( "Owner: {age: %dms; thread: {%s}; stack: {%s}}", + ageMS, mThread, mTrace ); } + + void setStamp() { mStamp = System.currentTimeMillis(); } } private static class GameLockState { @@ -233,6 +239,7 @@ public class GameLock implements AutoCloseable, Serializable { { synchronized ( mOwners ) { mOwners.pop(); + owner.setStamp(); mOwners.push( owner ); } } @@ -296,7 +303,7 @@ public class GameLock implements AutoCloseable, Serializable { return getFor( rowid ).lock( maxMillis ); } - public static GameLock lockRO( long rowid, long maxMillis ) + public static GameLock lockRO( long rowid, long maxMillis ) throws GameLockedException { return getFor( rowid ).lockRO( maxMillis ); } @@ -317,17 +324,17 @@ public class GameLock implements AutoCloseable, Serializable { return m_rowid; } - public interface LockProc { + public interface GotLockProc { public void gotLock( GameLock lock ); } // Meant to be called from UI thread, returning immediately, but when it // gets the lock, or time runs out, calls the callback (using the Handler // passed in) with the lock or null. - public static void callWithLock( final long rowid, - final long maxMillis, - final Handler handler, - final LockProc proc ) + public static void getLockThen( final long rowid, + final long maxMillis, + final Handler handler, + final GotLockProc proc ) { // capture caller thread and stack final Owner owner = new Owner(); @@ -360,11 +367,11 @@ public class GameLock implements AutoCloseable, Serializable { } ).start(); } - public static String getHolderStack( long rowid ) + public static String getHolderDump( long rowid ) { GameLockState state = getFor( rowid ); Owner owner = state.mOwners.peek(); - return owner.mTrace; + return owner.toString(); } // used only for asserts diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java index eb813b5e8..e5eab57b1 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java @@ -225,24 +225,16 @@ public class GameUtils { long maxMillis ) { GameSummary result = null; - JNIThread thread = JNIThread.getRetained( rowid ); - GameLock lock = null; - if ( null != thread ) { - lock = thread.getLock(); - } else { - try { - lock = GameLock.lockRO( rowid, maxMillis ); - } catch ( GameLock.GameLockedException gle ) { - Log.ex( TAG, gle ); - } - } - - if ( null != lock ) { - result = DBUtils.getSummary( context, lock ); - if ( null == thread ) { - lock.unlock(); + try ( JNIThread thread = JNIThread.getRetained( rowid ) ) { + if ( null != thread ) { + result = DBUtils.getSummary( context, thread.getLock() ); } else { - thread.release(); + try ( GameLock lock = GameLock.lockRO( rowid, maxMillis ) ) { + if ( null != lock ) { + result = DBUtils.getSummary( context, lock ); + } + } catch ( GameLock.GameLockedException gle ) { + } } } return result; @@ -260,35 +252,41 @@ public class GameUtils { public static long dupeGame( Context context, long rowidIn, long groupID ) { - long rowid = DBUtils.ROWID_NOTFOUND; - GameLock lockSrc = null; + long result = DBUtils.ROWID_NOTFOUND; - JNIThread thread = JNIThread.getRetained( rowidIn ); - if ( null != thread ) { - lockSrc = thread.getLock(); - } else { - lockSrc = GameLock.lockRO( rowidIn, 300 ); + try ( JNIThread thread = JNIThread.getRetained( rowidIn ) ) { + if ( null != thread ) { + result = dupeGame( context, thread.getLock(), groupID ); + } else { + try ( GameLock lockSrc = GameLock.lockRO( rowidIn, 300 ) ) { + if ( null != lockSrc ) { + result = dupeGame( context, lockSrc, groupID ); + } + } catch ( GameLock.GameLockedException gle ) { + } + } } - if ( null != lockSrc ) { - boolean juggle = CommonPrefs.getAutoJuggle( context ); - GameLock lockDest = resetGame( context, lockSrc, null, groupID, - juggle ); - rowid = lockDest.getRowid(); - lockDest.unlock(); - - if ( null != thread ) { - thread.release(); - } else { - lockSrc.unlock(); - } - } else { + if ( DBUtils.ROWID_NOTFOUND == result ) { Log.d( TAG, "dupeGame: unable to open rowid %d", rowidIn ); } - return rowid; + return result; } - public static void deleteGame( Context context, GameLock lock, boolean informNow ) + private static long dupeGame( Context context, GameLock lock, long groupID ) + { + long result; + boolean juggle = CommonPrefs.getAutoJuggle( context ); + try ( GameLock lockDest = resetGame( context, lock, + null, groupID, + juggle ) ) { + result = lockDest.getRowid(); + } + return result; + } + + public static void deleteGame( Context context, GameLock lock, + boolean informNow ) { if ( null != lock ) { tellDied( context, lock, informNow ); @@ -1290,14 +1288,14 @@ public class GameUtils { + " failed for rowid %d", rowid ); } } else { - JNIThread jniThread = JNIThread.getRetained( rowid ); - if ( null != jniThread ) { - jniThread.handle( JNIThread.JNICmd.CMD_RESEND, false, - false, false ); - jniThread.release(); - } else { - Log.w( TAG, "Resender.doInBackground: unable to unlock %d", - rowid ); + try ( JNIThread thread = JNIThread.getRetained( rowid ) ) { + if ( null != thread ) { + thread.handle( JNIThread.JNICmd.CMD_RESEND, false, + false, false ); + } else { + Log.w( TAG, "Resender.doInBackground: unable to unlock %d", + rowid ); + } } } } diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java index a29e76904..f6aaaec09 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWServiceHelper.java @@ -76,21 +76,23 @@ abstract class XWServiceHelper { { boolean allConsumed = true; boolean[] isLocalP = new boolean[1]; - JNIThread jniThread = JNIThread.getRetained( rowid ); boolean consumed = false; - if ( null != jniThread ) { - consumed = true; - jniThread.receive( msg, addr ).release(); - } else { - GameUtils.BackMoveResult bmr = new GameUtils.BackMoveResult(); - if ( null == sink ) { - sink = getSink( rowid ); - } - if ( GameUtils.feedMessage( context, rowid, msg, addr, - sink, bmr, isLocalP ) ) { + + try ( JNIThread jniThread = JNIThread.getRetained( rowid ) ) { + if ( null != jniThread ) { + jniThread.receive( msg, addr ); consumed = true; - GameUtils.postMoveNotification( context, rowid, bmr, - isLocalP[0] ); + } else { + GameUtils.BackMoveResult bmr = new GameUtils.BackMoveResult(); + if ( null == sink ) { + sink = getSink( rowid ); + } + if ( GameUtils.feedMessage( context, rowid, msg, addr, + sink, bmr, isLocalP ) ) { + GameUtils.postMoveNotification( context, rowid, bmr, + isLocalP[0] ); + consumed = true; + } } } if ( allConsumed && !consumed ) { @@ -149,10 +151,10 @@ abstract class XWServiceHelper { if ( null == gi ) { // locked. Maybe it's open? - JNIThread thread = JNIThread.getRetained( rowid ); - if ( null != thread ) { - gi = thread.getGI(); - thread.release( false ); + try ( JNIThread thrd = JNIThread.getRetained( rowid ) ) { + if ( null != thrd ) { + gi = thrd.getGI(); + } } } success = null != gi && gi.forceChannel != nli.forceChannel; diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java index 66ce7e955..c0ca56de5 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java @@ -49,7 +49,7 @@ import java.util.Iterator; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; -public class JNIThread extends Thread { +public class JNIThread extends Thread implements AutoCloseable { private static final String TAG = JNIThread.class.getSimpleName(); public enum JNICmd { CMD_NONE, @@ -265,7 +265,16 @@ public class JNIThread extends Thread { } catch ( java.lang.InterruptedException ie ) { Log.ex( TAG, ie ); } - m_lock.unlock(); + + unlockOnce(); + } + + private synchronized void unlockOnce() + { + if ( null != m_lock ) { + m_lock.unlock(); + m_lock = null; + } } public boolean busy() @@ -746,9 +755,18 @@ public class JNIThread extends Thread { m_jniGamePtr.release(); m_jniGamePtr = null; } + + unlockOnce(); Log.d( TAG, "run() finished" ); } // run + @Override + public void finalize() throws java.lang.Throwable + { + Assert.assertTrue( null == m_lock || !BuildConfig.DEBUG ); + super.finalize(); + } + public void handleBkgrnd( JNICmd cmd, Object... args ) { // DbgUtils.logf( "adding: %s", cmd.toString() ); @@ -823,6 +841,12 @@ public class JNIThread extends Thread { } } + @Override + public void close() + { + release(); + } + public static JNIThread getRetained( long rowid ) { return getRetained( rowid, null ); diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java index f033ff4eb..a3d56077f 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java @@ -467,7 +467,6 @@ public class XwJNI { release(); super.finalize(); } - } public static native boolean dict_tilesAreSame( int dict1, int dict2 ); diff --git a/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/CrashTrack.java b/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/CrashTrack.java index 97af2f750..f9376612c 100644 --- a/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/CrashTrack.java +++ b/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/CrashTrack.java @@ -24,5 +24,5 @@ import android.content.Context; public class CrashTrack { public static void init( Context context ) {} // does nothing here - public static void logAndSend( String msg ) {} + public static void logAndSend( String tag, String msg ) {} } diff --git a/xwords4/android/app/src/xw4d/java/org/eehouse/android/xw4/CrashTrack.java b/xwords4/android/app/src/xw4d/java/org/eehouse/android/xw4/CrashTrack.java index 8d4521804..e5fefe067 100644 --- a/xwords4/android/app/src/xw4d/java/org/eehouse/android/xw4/CrashTrack.java +++ b/xwords4/android/app/src/xw4d/java/org/eehouse/android/xw4/CrashTrack.java @@ -61,9 +61,12 @@ public class CrashTrack { } } - public static void logAndSend( String msg ) + public static void logAndSend( String tag, String msg ) { Crashlytics.log( msg ); + Log.e( tag, msg ); + + // Now crash so Crashlytics will upload the log new Thread( new Runnable() { @Override public void run() {