Prevent ProgressDialog from causing crash.
This is a minimal fix (or a workaround) for the bug 3129933:
SpecialCharSequenceMgr can crash Contacts app with a bad
Dialog.dismiss() call
Ideally we should merge two implementations into one and introduce
better cleanup mechanism for the whole class. Maybe we want to
instanciate the whole SpecialCharSequenceMgr, while it will cause
a lot of work. Note that this crash won't happen on Phone side,
so this kind of cleanup won't be needed there.
To keep the two implementations basically same, this change just has
*static* cleanup() method in Contacts part of the class.
Bug: 3129933
Change-Id: I99e4e1494f91f58f10f6ebbf814d2c8695e021f6
diff --git a/src/com/android/contacts/SpecialCharSequenceMgr.java b/src/com/android/contacts/SpecialCharSequenceMgr.java
index b5deab8..aa6faec 100644
--- a/src/com/android/contacts/SpecialCharSequenceMgr.java
+++ b/src/com/android/contacts/SpecialCharSequenceMgr.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Telephony.Intents;
@@ -54,6 +55,23 @@
private static final String TAG = "SpecialCharSequenceMgr";
private static final String MMI_IMEI_DISPLAY = "*#06#";
+ /**
+ * Remembers the previous {@link QueryHandler} and cancel the operation when needed, to
+ * prevent possible crash.
+ *
+ * QueryHandler may call {@link ProgressDialog#dismiss()} when the screen is already gone,
+ * which will cause the app crash. This variable enables the class to prevent the crash
+ * on {@link #cleanup()}.
+ *
+ * TODO: Remove this and replace it (and {@link #cleanup()}) with better implementation.
+ * One complication is that we have SpecialCharSequencMgr in Phone package too, which has
+ * *slightly* different implementation. Note that Phone package doesn't have this problem,
+ * so the class on Phone side doesn't have this functionality.
+ * Fundamental fix would be to have one shared implementation and resolve this corner case more
+ * gracefully.
+ */
+ private static QueryHandler sPreviousAdnQueryHandler;
+
/** This class is never instantiated. */
private SpecialCharSequenceMgr() {
}
@@ -83,6 +101,23 @@
}
/**
+ * Cleanup everything around this class. Must be run inside the main thread.
+ *
+ * This should be called when the screen becomes background.
+ */
+ public static void cleanup() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ Log.wtf(TAG, "cleanup() is called outside the main thread");
+ return;
+ }
+
+ if (sPreviousAdnQueryHandler != null) {
+ sPreviousAdnQueryHandler.cancel();
+ sPreviousAdnQueryHandler = null;
+ }
+ }
+
+ /**
* Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
* If a secret code is encountered an Intent is started with the android_secret_code://<code>
* URI.
@@ -164,6 +199,12 @@
// run the query.
handler.startQuery(ADN_QUERY_TOKEN, sc, Uri.parse("content://icc/adn"),
new String[]{ADN_PHONE_NUMBER_COLUMN_NAME}, null, null, null);
+
+ if (sPreviousAdnQueryHandler != null) {
+ // It is harmless to call cancel() even after the handler's gone.
+ sPreviousAdnQueryHandler.cancel();
+ }
+ sPreviousAdnQueryHandler = handler;
return true;
} catch (NumberFormatException ex) {
// Ignore
@@ -304,6 +345,8 @@
*/
private static class QueryHandler extends AsyncQueryHandler {
+ private boolean mCanceled;
+
public QueryHandler(ContentResolver cr) {
super(cr);
}
@@ -314,6 +357,11 @@
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor c) {
+ sPreviousAdnQueryHandler = null;
+ if (mCanceled) {
+ return;
+ }
+
SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
// close the progress dialog.
@@ -339,5 +387,12 @@
.show();
}
}
+
+ public void cancel() {
+ mCanceled = true;
+ // Ask AsyncQueryHandler to cancel the whole request. This will fails when the
+ // query already started.
+ cancelOperation(ADN_QUERY_TOKEN);
+ }
}
}