summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2015-09-08 17:01:49 -0700
committerAdam Powell <adamp@google.com>2015-09-16 19:54:24 +0000
commit39e94ebe22d4959facea4c3621a0a5377242a570 (patch)
tree312e9e9fe89c32ff28de2d14f5ec75933471e311 /core
parentbfc584df683a84dffd8e04b7b13f619606a0a42a (diff)
downloadframeworks_base-39e94ebe22d4959facea4c3621a0a5377242a570.zip
frameworks_base-39e94ebe22d4959facea4c3621a0a5377242a570.tar.gz
frameworks_base-39e94ebe22d4959facea4c3621a0a5377242a570.tar.bz2
Handle single-choice ChooserActivities and bound ChooserTargetServices
Fix cases where we could try to unbind from a ChooserTargetService that is not connected. This could happen if we still had stale replies coming back after the activity was destroyed. Always offer users an explicit choice in ChooserActivity, don't auto-start a single option. Make sure we don't allow a wedged ChooserTargetService to hold a hard reference to the ChooserActivity via its internal result callback. Bug 23152483 Change-Id: I7c8b1fc9559dcd477702ef582011b088b07d646b (cherry picked from commit 9761ab2a645a4643bd20619c7c148ef07e0dd39d)
Diffstat (limited to 'core')
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java97
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java20
2 files changed, 89 insertions, 28 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index d9faece..80f9b0f 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -104,6 +104,7 @@ public class ChooserActivity extends ResolverActivity {
sri.resultTargets);
}
unbindService(sri.connection);
+ sri.connection.destroy();
mServiceConnections.remove(sri.connection);
if (mServiceConnections.isEmpty()) {
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
@@ -208,6 +209,8 @@ public class ChooserActivity extends ResolverActivity {
mRefinementResultReceiver.destroy();
mRefinementResultReceiver = null;
}
+ unbindRemainingServices();
+ mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
}
@Override
@@ -265,6 +268,11 @@ public class ChooserActivity extends ResolverActivity {
return true;
}
+ @Override
+ boolean shouldAutoLaunchSingleChoice() {
+ return false;
+ }
+
private void modifyTargetIntent(Intent in) {
final String action = in.getAction();
if (Intent.ACTION_SEND.equals(action) ||
@@ -371,7 +379,8 @@ public class ChooserActivity extends ResolverActivity {
continue;
}
- final ChooserTargetServiceConnection conn = new ChooserTargetServiceConnection(dri);
+ final ChooserTargetServiceConnection conn =
+ new ChooserTargetServiceConnection(this, dri);
if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
UserHandle.CURRENT)) {
if (DEBUG) {
@@ -425,6 +434,7 @@ public class ChooserActivity extends ResolverActivity {
final ChooserTargetServiceConnection conn = mServiceConnections.get(i);
if (DEBUG) Log.d(TAG, "unbinding " + conn);
unbindService(conn);
+ conn.destroy();
}
mServiceConnections.clear();
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
@@ -1024,54 +1034,93 @@ public class ChooserActivity extends ResolverActivity {
}
}
- class ChooserTargetServiceConnection implements ServiceConnection {
+ static class ChooserTargetServiceConnection implements ServiceConnection {
private final DisplayResolveInfo mOriginalTarget;
+ private ComponentName mConnectedComponent;
+ private ChooserActivity mChooserActivity;
+ private final Object mLock = new Object();
private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
@Override
public void sendResult(List<ChooserTarget> targets) throws RemoteException {
- filterServiceTargets(mOriginalTarget.getResolveInfo().activityInfo.packageName,
- targets);
- final Message msg = Message.obtain();
- msg.what = CHOOSER_TARGET_SERVICE_RESULT;
- msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
- ChooserTargetServiceConnection.this);
- mChooserHandler.sendMessage(msg);
+ synchronized (mLock) {
+ if (mChooserActivity == null) {
+ Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from "
+ + mConnectedComponent + "; ignoring...");
+ return;
+ }
+ mChooserActivity.filterServiceTargets(
+ mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
+ final Message msg = Message.obtain();
+ msg.what = CHOOSER_TARGET_SERVICE_RESULT;
+ msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
+ ChooserTargetServiceConnection.this);
+ mChooserActivity.mChooserHandler.sendMessage(msg);
+ }
}
};
- public ChooserTargetServiceConnection(DisplayResolveInfo dri) {
+ public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
+ DisplayResolveInfo dri) {
+ mChooserActivity = chooserActivity;
mOriginalTarget = dri;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Log.d(TAG, "onServiceConnected: " + name);
- final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
- try {
- icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
- mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
- } catch (RemoteException e) {
- Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
- unbindService(this);
- mServiceConnections.remove(this);
+ synchronized (mLock) {
+ if (mChooserActivity == null) {
+ Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected");
+ return;
+ }
+
+ final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
+ try {
+ icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
+ mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
+ mChooserActivity.unbindService(this);
+ destroy();
+ mChooserActivity.mServiceConnections.remove(this);
+ }
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
- unbindService(this);
- mServiceConnections.remove(this);
- if (mServiceConnections.isEmpty()) {
- mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
- sendVoiceChoicesIfNeeded();
+ synchronized (mLock) {
+ if (mChooserActivity == null) {
+ Log.e(TAG,
+ "destroyed ChooserTargetServiceConnection got onServiceDisconnected");
+ return;
+ }
+
+ mChooserActivity.unbindService(this);
+ destroy();
+ mChooserActivity.mServiceConnections.remove(this);
+ if (mChooserActivity.mServiceConnections.isEmpty()) {
+ mChooserActivity.mChooserHandler.removeMessages(
+ CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
+ mChooserActivity.sendVoiceChoicesIfNeeded();
+ }
+ mConnectedComponent = null;
+ }
+ }
+
+ public void destroy() {
+ synchronized (mLock) {
+ mChooserActivity = null;
}
}
@Override
public String toString() {
- return mOriginalTarget.getResolveInfo().activityInfo.toString();
+ return "ChooserTargetServiceConnection{service="
+ + mConnectedComponent + ", activity="
+ + mOriginalTarget.getResolveInfo().activityInfo.toString() + "}";
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7dd3bed..9272193 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -234,7 +234,9 @@ public class ResolverActivity extends Activity {
mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
- configureContentView(mIntents, initialIntents, rList, alwaysUseOption);
+ if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
+ return;
+ }
// Prevent the Resolver window from becoming the top fullscreen window and thus from taking
// control of the system bars.
@@ -794,6 +796,10 @@ public class ResolverActivity extends Activity {
return false;
}
+ boolean shouldAutoLaunchSingleChoice() {
+ return true;
+ }
+
void showAppDetails(ResolveInfo ri) {
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
@@ -808,7 +814,10 @@ public class ResolverActivity extends Activity {
launchedFromUid, filterLastUsed);
}
- void configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
+ /**
+ * Returns true if the activity is finishing and creation should halt
+ */
+ boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
List<ResolveInfo> rList, boolean alwaysUseOption) {
// The last argument of createAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
@@ -828,7 +837,9 @@ public class ResolverActivity extends Activity {
mAlwaysUseOption = alwaysUseOption;
int count = mAdapter.getUnfilteredCount();
- if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
+ if ((!shouldAutoLaunchSingleChoice() && count > 0)
+ || count > 1
+ || (count == 1 && mAdapter.getOtherProfile() != null)) {
setContentView(layoutId);
mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
@@ -837,7 +848,7 @@ public class ResolverActivity extends Activity {
mPackageMonitor.unregister();
mRegistered = false;
finish();
- return;
+ return true;
} else {
setContentView(R.layout.resolver_list);
@@ -847,6 +858,7 @@ public class ResolverActivity extends Activity {
mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
mAdapterView.setVisibility(View.GONE);
}
+ return false;
}
void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,