Refactoring again. Make importer handle ServiceConnection properly.
Bug: 2733143
Change-Id: I3189ca396da4d661a05530c02a3c46df6db24701
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
index e021457..f263f7b 100644
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -70,12 +70,12 @@
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Queue;
import java.util.Set;
import java.util.Vector;
-
-
/**
* The class letting users to import vCard. This includes the UI part for letting them select
* an Account and posssibly a file if there's no Uri is given from its caller Activity.
@@ -120,18 +120,92 @@
private String mErrorMessage;
- private Messenger mMessenger;
+ private class CustomConnection implements ServiceConnection {
+ private Messenger mMessenger;
+ /**
+ * Stores {@link RequestParameter} objects until actual connection is established.
+ */
+ private Queue<RequestParameter> mPendingRequests =
+ new LinkedList<RequestParameter>();
- private final ServiceConnection mConnection = new ServiceConnection() {
+ private boolean mConnected = false;
+ private boolean mNeedFinish = false;
+
+ public void doBindService() {
+ // Log.d("@@@", "doBindService");
+ bindService(new Intent(ImportVCardActivity.this,
+ ImportVCardService.class), this, Context.BIND_AUTO_CREATE);
+ }
+
+ public void setNeedFinish() {
+ synchronized (this) {
+ mNeedFinish = true;
+ if (mConnected) {
+ unbindService(this);
+ finish();
+ }
+ }
+ }
+
+ public synchronized void requestSend(final RequestParameter parameter) {
+ // Log.d("@@@", "requestSend(): " + (mMessenger != null) + ", "
+ // + mPendingRequests.size());
+ if (mMessenger != null) {
+ sendMessage(parameter);
+ } else {
+ mPendingRequests.add(parameter);
+ }
+ }
+
+ private void sendMessage(final RequestParameter parameter) {
+ // Log.d("@@@", "sendMessage()");
+ try {
+ mMessenger.send(Message.obtain(null,
+ ImportVCardService.IMPORT_REQUEST,
+ parameter));
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "RemoteException is thrown when trying to import vCard");
+ runOnUIThread(new DialogDisplayer(
+ getString(R.string.fail_reason_unknown)));
+ }
+ }
+
public void onServiceConnected(ComponentName name, IBinder service) {
- mMessenger = new Messenger(service);
+ // Log.d("@@@", "onServiceConnected()");
+ synchronized (this) {
+ mMessenger = new Messenger(service);
+ // Send pending requests thrown from this Activity before an actual connection
+ // is established.
+ while (!mPendingRequests.isEmpty()) {
+ final RequestParameter parameter = mPendingRequests.poll();
+ if (parameter == null) {
+ throw new NullPointerException();
+ }
+ sendMessage(parameter);
+ }
+ mConnected = true;
+ if (mNeedFinish) {
+ unbindService(this);
+ finish();
+ }
+ }
}
public void onServiceDisconnected(ComponentName name) {
- mMessenger = null;
- finish();
+ // Log.d("@@@", "onServiceDisconnected()");
+ synchronized (this) {
+ if (!mPendingRequests.isEmpty()) {
+ Log.w(LOG_TAG, "Some request(s) are dropped.");
+ }
+ // Set to null so that we can detect inappropriate re-connection toward
+ // the Service via NullPointerException;
+ mPendingRequests = null;
+ mMessenger = null;
+ }
}
- };
+ }
+
+ private final CustomConnection mConnection = new CustomConnection();
private static class VCardFile {
private final String mName;
@@ -206,8 +280,8 @@
mSourceUris = sourceUris;
final int length = sourceUris.length;
final Context context = ImportVCardActivity.this;
- PowerManager powerManager = (PowerManager)context.getSystemService(
- Context.POWER_SERVICE);
+ final PowerManager powerManager =
+ (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK |
PowerManager.ON_AFTER_RELEASE, LOG_TAG);
@@ -226,11 +300,10 @@
final ContentResolver resolver = context.getContentResolver();
String errorMessage = null;
mWakeLock.acquire();
- boolean successful = false;
+ boolean needFinish = true;
try {
clearOldCache();
- bindService(new Intent(ImportVCardActivity.this,
- ImportVCardService.class), mConnection, Context.BIND_AUTO_CREATE);
+ mConnection.doBindService();
final int length = mSourceUris.length;
// Uris given from caller applications may not be opened twice: consider when
@@ -247,6 +320,7 @@
for (int i = 0; i < length; i++) {
final Uri sourceUri = mSourceUris[i];
final Uri localDataUri = copyToLocal(sourceUri, i);
+ // Log.d("@@@", "source: " + sourceUri);
if (mCanceled) {
break;
}
@@ -258,38 +332,32 @@
if (mCanceled) {
return;
}
- mMessenger.send(Message.obtain(null,
- ImportVCardService.IMPORT_REQUEST,
- parameter));
-
+ mConnection.requestSend(parameter);
}
-
- successful = true;
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "RemoteException is thrown when trying to import vCard");
- runOnUIThread(new DialogDisplayer(getString(R.string.fail_reason_unknown)));
} catch (OutOfMemoryError e) {
Log.e(LOG_TAG, "OutOfMemoryError");
// We should take care of this case since Android devices may have
// smaller memory than we usually expect.
System.gc();
+ needFinish = false;
+ unbindService(mConnection);
runOnUIThread(new DialogDisplayer(
getString(R.string.fail_reason_io_error) +
": " + e.getLocalizedMessage()));
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage());
+ needFinish = false;
+ unbindService(mConnection);
runOnUIThread(new DialogDisplayer(
getString(R.string.fail_reason_io_error) +
": " + e.getLocalizedMessage()));
} finally {
mWakeLock.release();
mProgressDialogForCacheVCard.dismiss();
- }
-
- if (successful) {
- finish();
- } else {
- // finish() should be called via DialogDisplayer().
+ // Log.d("@@@", "before setNeedFinish: " + needFinish);
+ if (needFinish) {
+ mConnection.setNeedFinish();
+ }
}
}
@@ -657,9 +725,7 @@
}
private void importVCardFromSDCard(final VCardFile vcardFile) {
- String[] uriStrings = new String[1];
- uriStrings[0] = "file://" + vcardFile.getCanonicalPath();
- importVCard(uriStrings);
+ importVCard(new Uri[] {Uri.parse("file://" + vcardFile.getCanonicalPath())});
}
private void importVCard(final Uri uri) {
diff --git a/src/com/android/contacts/ImportVCardService.java b/src/com/android/contacts/ImportVCardService.java
index f940761..e79bf82 100644
--- a/src/com/android/contacts/ImportVCardService.java
+++ b/src/com/android/contacts/ImportVCardService.java
@@ -119,9 +119,11 @@
public class ImportRequestHandler extends Handler {
@Override
public void handleMessage(Message msg) {
+ Log.d("@@@", "handleMessange: " + msg.what);
switch (msg.what) {
- case IMPORT_REQUEST:
- RequestParameter parameter = (RequestParameter)msg.obj;
+ case IMPORT_REQUEST: {
+ Log.d("@@@", "IMPORT_REQUEST");
+ final RequestParameter parameter = (RequestParameter)msg.obj;
Toast.makeText(ImportVCardService.this,
getString(R.string.vcard_importer_start_message),
Toast.LENGTH_LONG).show();
@@ -143,6 +145,7 @@
mThread.start();
}
break;
+ }
default:
Log.e(LOG_TAG, "Unknown request type: " + msg.what);
super.hasMessages(msg.what);
@@ -167,10 +170,10 @@
private ImportVCardService mService;
private ContentResolver mResolver;
private NotificationManager mNotificationManager;
- private ProgressNotifier mProgressNotifier;
- private final List<Uri> mFailedUris;
- private final List<Uri> mCreatedUris;
+ private final List<Uri> mFailedUris = new ArrayList<Uri>();
+ private final List<Uri> mCreatedUris = new ArrayList<Uri>();
+ private ProgressNotifier mProgressNotifier = new ProgressNotifier();
private VCardParser mVCardParser;
@@ -190,15 +193,6 @@
private final Queue<RequestParameter> mPendingRequests =
new LinkedList<RequestParameter>();
- public RequestHandler() {
- // We cannot set Service here since Service is not fully ready at this point.
- // TODO: refactor this class.
-
- mFailedUris = new ArrayList<Uri>();
- mCreatedUris = new ArrayList<Uri>();
- mProgressNotifier = new ProgressNotifier();
- }
-
public void init(ImportVCardService service) {
// TODO: Based on fragile fact. fix this.
mService = service;
@@ -502,6 +496,7 @@
@Override
public IBinder onBind(Intent intent) {
+ Log.d("@@@", "onBind");
return mMessenger.getBinder();
}
}