OSDN Git Service

am 937141cb: am 0cae8381: (-s ours) am b5173241: am e54c195a: am 0a8197b1: am 1cf21e4...
[android-x86/frameworks-base.git] / core / java / com / android / internal / app / ChooserActivity.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.internal.app;
18
19 import android.app.Activity;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentSender;
24 import android.content.IntentSender.SendIntentException;
25 import android.content.ServiceConnection;
26 import android.content.pm.ActivityInfo;
27 import android.content.pm.LabeledIntent;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.NameNotFoundException;
30 import android.content.pm.ResolveInfo;
31 import android.database.DataSetObserver;
32 import android.graphics.drawable.Drawable;
33 import android.graphics.drawable.Icon;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.Message;
38 import android.os.Parcelable;
39 import android.os.RemoteException;
40 import android.os.ResultReceiver;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.service.chooser.ChooserTarget;
44 import android.service.chooser.ChooserTargetService;
45 import android.service.chooser.IChooserTargetResult;
46 import android.service.chooser.IChooserTargetService;
47 import android.text.TextUtils;
48 import android.util.Log;
49 import android.util.Slog;
50 import android.view.LayoutInflater;
51 import android.view.View;
52 import android.view.View.OnClickListener;
53 import android.view.View.OnLongClickListener;
54 import android.view.ViewGroup;
55 import android.widget.AbsListView;
56 import android.widget.BaseAdapter;
57 import android.widget.ListView;
58 import com.android.internal.R;
59 import com.android.internal.logging.MetricsLogger;
60
61 import java.util.ArrayList;
62 import java.util.Collections;
63 import java.util.Comparator;
64 import java.util.List;
65
66 public class ChooserActivity extends ResolverActivity {
67     private static final String TAG = "ChooserActivity";
68
69     private static final boolean DEBUG = false;
70
71     private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
72     private static final int WATCHDOG_TIMEOUT_MILLIS = 5000;
73
74     private Bundle mReplacementExtras;
75     private IntentSender mChosenComponentSender;
76     private IntentSender mRefinementIntentSender;
77     private RefinementResultReceiver mRefinementResultReceiver;
78
79     private Intent mReferrerFillInIntent;
80
81     private ChooserListAdapter mChooserListAdapter;
82
83     private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
84
85     private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
86     private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2;
87
88     private final Handler mChooserHandler = new Handler() {
89         @Override
90         public void handleMessage(Message msg) {
91             switch (msg.what) {
92                 case CHOOSER_TARGET_SERVICE_RESULT:
93                     if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT");
94                     if (isDestroyed()) break;
95                     final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
96                     if (!mServiceConnections.contains(sri.connection)) {
97                         Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
98                                 + " returned after being removed from active connections."
99                                 + " Have you considered returning results faster?");
100                         break;
101                     }
102                     if (sri.resultTargets != null) {
103                         mChooserListAdapter.addServiceResults(sri.originalTarget,
104                                 sri.resultTargets);
105                     }
106                     unbindService(sri.connection);
107                     sri.connection.destroy();
108                     mServiceConnections.remove(sri.connection);
109                     if (mServiceConnections.isEmpty()) {
110                         mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
111                         sendVoiceChoicesIfNeeded();
112                     }
113                     break;
114
115                 case CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT:
116                     if (DEBUG) {
117                         Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services");
118                     }
119                     unbindRemainingServices();
120                     sendVoiceChoicesIfNeeded();
121                     break;
122
123                 default:
124                     super.handleMessage(msg);
125             }
126         }
127     };
128
129     @Override
130     protected void onCreate(Bundle savedInstanceState) {
131         Intent intent = getIntent();
132         Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
133         if (!(targetParcelable instanceof Intent)) {
134             Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable);
135             finish();
136             super.onCreate(null);
137             return;
138         }
139         Intent target = (Intent) targetParcelable;
140         if (target != null) {
141             modifyTargetIntent(target);
142         }
143         Parcelable[] targetsParcelable
144                 = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS);
145         if (targetsParcelable != null) {
146             final boolean offset = target == null;
147             Intent[] additionalTargets =
148                     new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length];
149             for (int i = 0; i < targetsParcelable.length; i++) {
150                 if (!(targetsParcelable[i] instanceof Intent)) {
151                     Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: "
152                             + targetsParcelable[i]);
153                     finish();
154                     super.onCreate(null);
155                     return;
156                 }
157                 final Intent additionalTarget = (Intent) targetsParcelable[i];
158                 if (i == 0 && target == null) {
159                     target = additionalTarget;
160                     modifyTargetIntent(target);
161                 } else {
162                     additionalTargets[offset ? i - 1 : i] = additionalTarget;
163                     modifyTargetIntent(additionalTarget);
164                 }
165             }
166             setAdditionalTargets(additionalTargets);
167         }
168
169         mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
170         CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
171         int defaultTitleRes = 0;
172         if (title == null) {
173             defaultTitleRes = com.android.internal.R.string.chooseActivity;
174         }
175         Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
176         Intent[] initialIntents = null;
177         if (pa != null) {
178             initialIntents = new Intent[pa.length];
179             for (int i=0; i<pa.length; i++) {
180                 if (!(pa[i] instanceof Intent)) {
181                     Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]);
182                     finish();
183                     super.onCreate(null);
184                     return;
185                 }
186                 final Intent in = (Intent) pa[i];
187                 modifyTargetIntent(in);
188                 initialIntents[i] = in;
189             }
190         }
191
192         mReferrerFillInIntent = new Intent().putExtra(Intent.EXTRA_REFERRER, getReferrer());
193
194         mChosenComponentSender = intent.getParcelableExtra(
195                 Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
196         mRefinementIntentSender = intent.getParcelableExtra(
197                 Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
198         setSafeForwardingMode(true);
199         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
200                 null, false);
201
202         MetricsLogger.action(this, MetricsLogger.ACTION_ACTIVITY_CHOOSER_SHOWN);
203     }
204
205     @Override
206     protected void onDestroy() {
207         super.onDestroy();
208         if (mRefinementResultReceiver != null) {
209             mRefinementResultReceiver.destroy();
210             mRefinementResultReceiver = null;
211         }
212         unbindRemainingServices();
213         mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
214     }
215
216     @Override
217     public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
218         Intent result = defIntent;
219         if (mReplacementExtras != null) {
220             final Bundle replExtras = mReplacementExtras.getBundle(aInfo.packageName);
221             if (replExtras != null) {
222                 result = new Intent(defIntent);
223                 result.putExtras(replExtras);
224             }
225         }
226         if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER)
227                 || aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE)) {
228             result = Intent.createChooser(result,
229                     getIntent().getCharSequenceExtra(Intent.EXTRA_TITLE));
230         }
231         return result;
232     }
233
234     @Override
235     void onActivityStarted(TargetInfo cti) {
236         if (mChosenComponentSender != null) {
237             final ComponentName target = cti.getResolvedComponentName();
238             if (target != null) {
239                 final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target);
240                 try {
241                     mChosenComponentSender.sendIntent(this, Activity.RESULT_OK, fillIn, null, null);
242                 } catch (IntentSender.SendIntentException e) {
243                     Slog.e(TAG, "Unable to launch supplied IntentSender to report "
244                             + "the chosen component: " + e);
245                 }
246             }
247         }
248     }
249
250     @Override
251     void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
252             boolean alwaysUseOption) {
253         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
254         mChooserListAdapter = (ChooserListAdapter) adapter;
255         adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter));
256         if (listView != null) {
257             listView.setItemsCanFocus(true);
258         }
259     }
260
261     @Override
262     int getLayoutResource() {
263         return R.layout.chooser_grid;
264     }
265
266     @Override
267     boolean shouldGetActivityMetadata() {
268         return true;
269     }
270
271     @Override
272     boolean shouldAutoLaunchSingleChoice() {
273         return false;
274     }
275
276     private void modifyTargetIntent(Intent in) {
277         final String action = in.getAction();
278         if (Intent.ACTION_SEND.equals(action) ||
279                 Intent.ACTION_SEND_MULTIPLE.equals(action)) {
280             in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
281                     Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
282         }
283     }
284
285     @Override
286     protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
287         if (mRefinementIntentSender != null) {
288             final Intent fillIn = new Intent();
289             final List<Intent> sourceIntents = target.getAllSourceIntents();
290             if (!sourceIntents.isEmpty()) {
291                 fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0));
292                 if (sourceIntents.size() > 1) {
293                     final Intent[] alts = new Intent[sourceIntents.size() - 1];
294                     for (int i = 1, N = sourceIntents.size(); i < N; i++) {
295                         alts[i - 1] = sourceIntents.get(i);
296                     }
297                     fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts);
298                 }
299                 if (mRefinementResultReceiver != null) {
300                     mRefinementResultReceiver.destroy();
301                 }
302                 mRefinementResultReceiver = new RefinementResultReceiver(this, target, null);
303                 fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER,
304                         mRefinementResultReceiver);
305                 try {
306                     mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null);
307                     return false;
308                 } catch (SendIntentException e) {
309                     Log.e(TAG, "Refinement IntentSender failed to send", e);
310                 }
311             }
312         }
313         return super.onTargetSelected(target, alwaysCheck);
314     }
315
316     @Override
317     void startSelected(int which, boolean always, boolean filtered) {
318         super.startSelected(which, always, filtered);
319
320         if (mChooserListAdapter != null) {
321             // Log the index of which type of target the user picked.
322             // Lower values mean the ranking was better.
323             int cat = 0;
324             int value = which;
325             switch (mChooserListAdapter.getPositionTargetType(which)) {
326                 case ChooserListAdapter.TARGET_CALLER:
327                     cat = MetricsLogger.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
328                     break;
329                 case ChooserListAdapter.TARGET_SERVICE:
330                     cat = MetricsLogger.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET;
331                     value -= mChooserListAdapter.getCallerTargetCount();
332                     break;
333                 case ChooserListAdapter.TARGET_STANDARD:
334                     cat = MetricsLogger.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET;
335                     value -= mChooserListAdapter.getCallerTargetCount()
336                             + mChooserListAdapter.getServiceTargetCount();
337                     break;
338             }
339
340             if (cat != 0) {
341                 MetricsLogger.action(this, cat, value);
342             }
343         }
344     }
345
346     void queryTargetServices(ChooserListAdapter adapter) {
347         final PackageManager pm = getPackageManager();
348         int targetsToQuery = 0;
349         for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
350             final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
351             final ActivityInfo ai = dri.getResolveInfo().activityInfo;
352             final Bundle md = ai.metaData;
353             final String serviceName = md != null ? convertServiceName(ai.packageName,
354                     md.getString(ChooserTargetService.META_DATA_NAME)) : null;
355             if (serviceName != null) {
356                 final ComponentName serviceComponent = new ComponentName(
357                         ai.packageName, serviceName);
358                 final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE)
359                         .setComponent(serviceComponent);
360
361                 if (DEBUG) {
362                     Log.d(TAG, "queryTargets found target with service " + serviceComponent);
363                 }
364
365                 try {
366                     final String perm = pm.getServiceInfo(serviceComponent, 0).permission;
367                     if (!ChooserTargetService.BIND_PERMISSION.equals(perm)) {
368                         Log.w(TAG, "ChooserTargetService " + serviceComponent + " does not require"
369                                 + " permission " + ChooserTargetService.BIND_PERMISSION
370                                 + " - this service will not be queried for ChooserTargets."
371                                 + " add android:permission=\""
372                                 + ChooserTargetService.BIND_PERMISSION + "\""
373                                 + " to the <service> tag for " + serviceComponent
374                                 + " in the manifest.");
375                         continue;
376                     }
377                 } catch (NameNotFoundException e) {
378                     Log.e(TAG, "Could not look up service " + serviceComponent, e);
379                     continue;
380                 }
381
382                 final ChooserTargetServiceConnection conn =
383                         new ChooserTargetServiceConnection(this, dri);
384                 if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
385                         UserHandle.CURRENT)) {
386                     if (DEBUG) {
387                         Log.d(TAG, "Binding service connection for target " + dri
388                                 + " intent " + serviceIntent);
389                     }
390                     mServiceConnections.add(conn);
391                     targetsToQuery++;
392                 }
393             }
394             if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
395                 if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
396                         + QUERY_TARGET_SERVICE_LIMIT);
397                 break;
398             }
399         }
400
401         if (!mServiceConnections.isEmpty()) {
402             if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for "
403                     + WATCHDOG_TIMEOUT_MILLIS + "ms");
404             mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT,
405                     WATCHDOG_TIMEOUT_MILLIS);
406         } else {
407             sendVoiceChoicesIfNeeded();
408         }
409     }
410
411     private String convertServiceName(String packageName, String serviceName) {
412         if (TextUtils.isEmpty(serviceName)) {
413             return null;
414         }
415
416         final String fullName;
417         if (serviceName.startsWith(".")) {
418             // Relative to the app package. Prepend the app package name.
419             fullName = packageName + serviceName;
420         } else if (serviceName.indexOf('.') >= 0) {
421             // Fully qualified package name.
422             fullName = serviceName;
423         } else {
424             fullName = null;
425         }
426         return fullName;
427     }
428
429     void unbindRemainingServices() {
430         if (DEBUG) {
431             Log.d(TAG, "unbindRemainingServices, " + mServiceConnections.size() + " left");
432         }
433         for (int i = 0, N = mServiceConnections.size(); i < N; i++) {
434             final ChooserTargetServiceConnection conn = mServiceConnections.get(i);
435             if (DEBUG) Log.d(TAG, "unbinding " + conn);
436             unbindService(conn);
437             conn.destroy();
438         }
439         mServiceConnections.clear();
440         mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
441     }
442
443     void onSetupVoiceInteraction() {
444         // Do nothing. We'll send the voice stuff ourselves.
445     }
446
447     void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
448         if (mRefinementResultReceiver != null) {
449             mRefinementResultReceiver.destroy();
450             mRefinementResultReceiver = null;
451         }
452
453         if (selectedTarget == null) {
454             Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
455         } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
456             Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
457                     + " cannot match refined source intent " + matchingIntent);
458         } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) {
459             finish();
460             return;
461         }
462         onRefinementCanceled();
463     }
464
465     void onRefinementCanceled() {
466         if (mRefinementResultReceiver != null) {
467             mRefinementResultReceiver.destroy();
468             mRefinementResultReceiver = null;
469         }
470         finish();
471     }
472
473     boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) {
474         final List<Intent> targetIntents = target.getAllSourceIntents();
475         for (int i = 0, N = targetIntents.size(); i < N; i++) {
476             final Intent targetIntent = targetIntents.get(i);
477             if (targetIntent.filterEquals(matchingIntent)) {
478                 return true;
479             }
480         }
481         return false;
482     }
483
484     void filterServiceTargets(String packageName, List<ChooserTarget> targets) {
485         if (targets == null) {
486             return;
487         }
488
489         final PackageManager pm = getPackageManager();
490         for (int i = targets.size() - 1; i >= 0; i--) {
491             final ChooserTarget target = targets.get(i);
492             final ComponentName targetName = target.getComponentName();
493             if (packageName != null && packageName.equals(targetName.getPackageName())) {
494                 // Anything from the original target's package is fine.
495                 continue;
496             }
497
498             boolean remove;
499             try {
500                 final ActivityInfo ai = pm.getActivityInfo(targetName, 0);
501                 remove = !ai.exported || ai.permission != null;
502             } catch (NameNotFoundException e) {
503                 Log.e(TAG, "Target " + target + " returned by " + packageName
504                         + " component not found");
505                 remove = true;
506             }
507
508             if (remove) {
509                 targets.remove(i);
510             }
511         }
512     }
513
514     @Override
515     ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
516             Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
517             boolean filterLastUsed) {
518         final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents,
519                 initialIntents, rList, launchedFromUid, filterLastUsed);
520         if (DEBUG) Log.d(TAG, "Adapter created; querying services");
521         queryTargetServices(adapter);
522         return adapter;
523     }
524
525     final class ChooserTargetInfo implements TargetInfo {
526         private final DisplayResolveInfo mSourceInfo;
527         private final ResolveInfo mBackupResolveInfo;
528         private final ChooserTarget mChooserTarget;
529         private Drawable mBadgeIcon = null;
530         private CharSequence mBadgeContentDescription;
531         private Drawable mDisplayIcon;
532         private final Intent mFillInIntent;
533         private final int mFillInFlags;
534         private final float mModifiedScore;
535
536         public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget,
537                 float modifiedScore) {
538             mSourceInfo = sourceInfo;
539             mChooserTarget = chooserTarget;
540             mModifiedScore = modifiedScore;
541             if (sourceInfo != null) {
542                 final ResolveInfo ri = sourceInfo.getResolveInfo();
543                 if (ri != null) {
544                     final ActivityInfo ai = ri.activityInfo;
545                     if (ai != null && ai.applicationInfo != null) {
546                         final PackageManager pm = getPackageManager();
547                         mBadgeIcon = pm.getApplicationIcon(ai.applicationInfo);
548                         mBadgeContentDescription = pm.getApplicationLabel(ai.applicationInfo);
549                     }
550                 }
551             }
552             final Icon icon = chooserTarget.getIcon();
553             // TODO do this in the background
554             mDisplayIcon = icon != null ? icon.loadDrawable(ChooserActivity.this) : null;
555
556             if (sourceInfo != null) {
557                 mBackupResolveInfo = null;
558             } else {
559                 mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0);
560             }
561
562             mFillInIntent = null;
563             mFillInFlags = 0;
564         }
565
566         private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) {
567             mSourceInfo = other.mSourceInfo;
568             mBackupResolveInfo = other.mBackupResolveInfo;
569             mChooserTarget = other.mChooserTarget;
570             mBadgeIcon = other.mBadgeIcon;
571             mBadgeContentDescription = other.mBadgeContentDescription;
572             mDisplayIcon = other.mDisplayIcon;
573             mFillInIntent = fillInIntent;
574             mFillInFlags = flags;
575             mModifiedScore = other.mModifiedScore;
576         }
577
578         public float getModifiedScore() {
579             return mModifiedScore;
580         }
581
582         @Override
583         public Intent getResolvedIntent() {
584             if (mSourceInfo != null) {
585                 return mSourceInfo.getResolvedIntent();
586             }
587             return getTargetIntent();
588         }
589
590         @Override
591         public ComponentName getResolvedComponentName() {
592             if (mSourceInfo != null) {
593                 return mSourceInfo.getResolvedComponentName();
594             } else if (mBackupResolveInfo != null) {
595                 return new ComponentName(mBackupResolveInfo.activityInfo.packageName,
596                         mBackupResolveInfo.activityInfo.name);
597             }
598             return null;
599         }
600
601         private Intent getBaseIntentToSend() {
602             Intent result = mSourceInfo != null
603                     ? mSourceInfo.getResolvedIntent() : getTargetIntent();
604             if (result == null) {
605                 Log.e(TAG, "ChooserTargetInfo: no base intent available to send");
606             } else {
607                 result = new Intent(result);
608                 if (mFillInIntent != null) {
609                     result.fillIn(mFillInIntent, mFillInFlags);
610                 }
611                 result.fillIn(mReferrerFillInIntent, 0);
612             }
613             return result;
614         }
615
616         @Override
617         public boolean start(Activity activity, Bundle options) {
618             throw new RuntimeException("ChooserTargets should be started as caller.");
619         }
620
621         @Override
622         public boolean startAsCaller(Activity activity, Bundle options, int userId) {
623             final Intent intent = getBaseIntentToSend();
624             if (intent == null) {
625                 return false;
626             }
627             intent.setComponent(mChooserTarget.getComponentName());
628             intent.putExtras(mChooserTarget.getIntentExtras());
629             activity.startActivityAsCaller(intent, options, true, userId);
630             return true;
631         }
632
633         @Override
634         public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
635             throw new RuntimeException("ChooserTargets should be started as caller.");
636         }
637
638         @Override
639         public ResolveInfo getResolveInfo() {
640             return mSourceInfo != null ? mSourceInfo.getResolveInfo() : mBackupResolveInfo;
641         }
642
643         @Override
644         public CharSequence getDisplayLabel() {
645             return mChooserTarget.getTitle();
646         }
647
648         @Override
649         public CharSequence getExtendedInfo() {
650             // ChooserTargets have badge icons, so we won't show the extended info to disambiguate.
651             return null;
652         }
653
654         @Override
655         public Drawable getDisplayIcon() {
656             return mDisplayIcon;
657         }
658
659         @Override
660         public Drawable getBadgeIcon() {
661             return mBadgeIcon;
662         }
663
664         @Override
665         public CharSequence getBadgeContentDescription() {
666             return mBadgeContentDescription;
667         }
668
669         @Override
670         public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
671             return new ChooserTargetInfo(this, fillInIntent, flags);
672         }
673
674         @Override
675         public List<Intent> getAllSourceIntents() {
676             final List<Intent> results = new ArrayList<>();
677             if (mSourceInfo != null) {
678                 // We only queried the service for the first one in our sourceinfo.
679                 results.add(mSourceInfo.getAllSourceIntents().get(0));
680             }
681             return results;
682         }
683     }
684
685     public class ChooserListAdapter extends ResolveListAdapter {
686         public static final int TARGET_BAD = -1;
687         public static final int TARGET_CALLER = 0;
688         public static final int TARGET_SERVICE = 1;
689         public static final int TARGET_STANDARD = 2;
690
691         private static final int MAX_SERVICE_TARGETS = 8;
692
693         private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
694         private final List<TargetInfo> mCallerTargets = new ArrayList<>();
695
696         private float mLateFee = 1.f;
697
698         private final BaseChooserTargetComparator mBaseTargetComparator
699                 = new BaseChooserTargetComparator();
700
701         public ChooserListAdapter(Context context, List<Intent> payloadIntents,
702                 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
703                 boolean filterLastUsed) {
704             // Don't send the initial intents through the shared ResolverActivity path,
705             // we want to separate them into a different section.
706             super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed);
707
708             if (initialIntents != null) {
709                 final PackageManager pm = getPackageManager();
710                 for (int i = 0; i < initialIntents.length; i++) {
711                     final Intent ii = initialIntents[i];
712                     if (ii == null) {
713                         continue;
714                     }
715                     final ActivityInfo ai = ii.resolveActivityInfo(pm, 0);
716                     if (ai == null) {
717                         Log.w(TAG, "No activity found for " + ii);
718                         continue;
719                     }
720                     ResolveInfo ri = new ResolveInfo();
721                     ri.activityInfo = ai;
722                     UserManager userManager =
723                             (UserManager) getSystemService(Context.USER_SERVICE);
724                     if (ii instanceof LabeledIntent) {
725                         LabeledIntent li = (LabeledIntent)ii;
726                         ri.resolvePackageName = li.getSourcePackage();
727                         ri.labelRes = li.getLabelResource();
728                         ri.nonLocalizedLabel = li.getNonLocalizedLabel();
729                         ri.icon = li.getIconResource();
730                         ri.iconResourceId = ri.icon;
731                     }
732                     if (userManager.isManagedProfile()) {
733                         ri.noResourceId = true;
734                         ri.icon = 0;
735                     }
736                     mCallerTargets.add(new DisplayResolveInfo(ii, ri,
737                             ri.loadLabel(pm), null, ii));
738                 }
739             }
740         }
741
742         @Override
743         public boolean showsExtendedInfo(TargetInfo info) {
744             // We have badges so we don't need this text shown.
745             return false;
746         }
747
748         @Override
749         public View onCreateView(ViewGroup parent) {
750             return mInflater.inflate(
751                     com.android.internal.R.layout.resolve_grid_item, parent, false);
752         }
753
754         @Override
755         public void onListRebuilt() {
756             if (mServiceTargets != null) {
757                 pruneServiceTargets();
758             }
759         }
760
761         @Override
762         public boolean shouldGetResolvedFilter() {
763             return true;
764         }
765
766         @Override
767         public int getCount() {
768             return super.getCount() + getServiceTargetCount() + getCallerTargetCount();
769         }
770
771         @Override
772         public int getUnfilteredCount() {
773             return super.getUnfilteredCount() + getServiceTargetCount() + getCallerTargetCount();
774         }
775
776         public int getCallerTargetCount() {
777             return mCallerTargets.size();
778         }
779
780         public int getServiceTargetCount() {
781             return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS);
782         }
783
784         public int getStandardTargetCount() {
785             return super.getCount();
786         }
787
788         public int getPositionTargetType(int position) {
789             int offset = 0;
790
791             final int callerTargetCount = getCallerTargetCount();
792             if (position < callerTargetCount) {
793                 return TARGET_CALLER;
794             }
795             offset += callerTargetCount;
796
797             final int serviceTargetCount = getServiceTargetCount();
798             if (position - offset < serviceTargetCount) {
799                 return TARGET_SERVICE;
800             }
801             offset += serviceTargetCount;
802
803             final int standardTargetCount = super.getCount();
804             if (position - offset < standardTargetCount) {
805                 return TARGET_STANDARD;
806             }
807
808             return TARGET_BAD;
809         }
810
811         @Override
812         public TargetInfo getItem(int position) {
813             return targetInfoForPosition(position, true);
814         }
815
816         @Override
817         public TargetInfo targetInfoForPosition(int position, boolean filtered) {
818             int offset = 0;
819
820             final int callerTargetCount = getCallerTargetCount();
821             if (position < callerTargetCount) {
822                 return mCallerTargets.get(position);
823             }
824             offset += callerTargetCount;
825
826             final int serviceTargetCount = getServiceTargetCount();
827             if (position - offset < serviceTargetCount) {
828                 return mServiceTargets.get(position - offset);
829             }
830             offset += serviceTargetCount;
831
832             return filtered ? super.getItem(position - offset)
833                     : getDisplayInfoAt(position - offset);
834         }
835
836         public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets) {
837             if (DEBUG) Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
838                     + " targets");
839             final float parentScore = getScore(origTarget);
840             Collections.sort(targets, mBaseTargetComparator);
841             float lastScore = 0;
842             for (int i = 0, N = targets.size(); i < N; i++) {
843                 final ChooserTarget target = targets.get(i);
844                 float targetScore = target.getScore();
845                 targetScore *= parentScore;
846                 targetScore *= mLateFee;
847                 if (i > 0 && targetScore >= lastScore) {
848                     // Apply a decay so that the top app can't crowd out everything else.
849                     // This incents ChooserTargetServices to define what's truly better.
850                     targetScore = lastScore * 0.95f;
851                 }
852                 insertServiceTarget(new ChooserTargetInfo(origTarget, target, targetScore));
853
854                 if (DEBUG) {
855                     Log.d(TAG, " => " + target.toString() + " score=" + targetScore
856                             + " base=" + target.getScore()
857                             + " lastScore=" + lastScore
858                             + " parentScore=" + parentScore
859                             + " lateFee=" + mLateFee);
860                 }
861
862                 lastScore = targetScore;
863             }
864
865             mLateFee *= 0.95f;
866
867             notifyDataSetChanged();
868         }
869
870         private void insertServiceTarget(ChooserTargetInfo chooserTargetInfo) {
871             final float newScore = chooserTargetInfo.getModifiedScore();
872             for (int i = 0, N = mServiceTargets.size(); i < N; i++) {
873                 final ChooserTargetInfo serviceTarget = mServiceTargets.get(i);
874                 if (newScore > serviceTarget.getModifiedScore()) {
875                     mServiceTargets.add(i, chooserTargetInfo);
876                     return;
877                 }
878             }
879             mServiceTargets.add(chooserTargetInfo);
880         }
881
882         private void pruneServiceTargets() {
883             if (DEBUG) Log.d(TAG, "pruneServiceTargets");
884             for (int i = mServiceTargets.size() - 1; i >= 0; i--) {
885                 final ChooserTargetInfo cti = mServiceTargets.get(i);
886                 if (!hasResolvedTarget(cti.getResolveInfo())) {
887                     if (DEBUG) Log.d(TAG, " => " + i + " " + cti);
888                     mServiceTargets.remove(i);
889                 }
890             }
891         }
892     }
893
894     static class BaseChooserTargetComparator implements Comparator<ChooserTarget> {
895         @Override
896         public int compare(ChooserTarget lhs, ChooserTarget rhs) {
897             // Descending order
898             return (int) Math.signum(lhs.getScore() - rhs.getScore());
899         }
900     }
901
902     class ChooserRowAdapter extends BaseAdapter {
903         private ChooserListAdapter mChooserListAdapter;
904         private final LayoutInflater mLayoutInflater;
905         private final int mColumnCount = 4;
906
907         public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
908             mChooserListAdapter = wrappedAdapter;
909             mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
910
911             wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
912                 @Override
913                 public void onChanged() {
914                     super.onChanged();
915                     notifyDataSetChanged();
916                 }
917
918                 @Override
919                 public void onInvalidated() {
920                     super.onInvalidated();
921                     notifyDataSetInvalidated();
922                 }
923             });
924         }
925
926         @Override
927         public int getCount() {
928             return (int) (
929                     Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount)
930                     + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount)
931                     + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
932             );
933         }
934
935         @Override
936         public Object getItem(int position) {
937             // We have nothing useful to return here.
938             return position;
939         }
940
941         @Override
942         public long getItemId(int position) {
943             return position;
944         }
945
946         @Override
947         public View getView(int position, View convertView, ViewGroup parent) {
948             final View[] holder;
949             if (convertView == null) {
950                 holder = createViewHolder(parent);
951             } else {
952                 holder = (View[]) convertView.getTag();
953             }
954             bindViewHolder(position, holder);
955
956             // We keep the actual list item view as the last item in the holder array
957             return holder[mColumnCount];
958         }
959
960         View[] createViewHolder(ViewGroup parent) {
961             final View[] holder = new View[mColumnCount + 1];
962
963             final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
964                     parent, false);
965             for (int i = 0; i < mColumnCount; i++) {
966                 holder[i] = mChooserListAdapter.createView(row);
967                 row.addView(holder[i]);
968             }
969             row.setTag(holder);
970             holder[mColumnCount] = row;
971             return holder;
972         }
973
974         void bindViewHolder(int rowPosition, View[] holder) {
975             final int start = getFirstRowPosition(rowPosition);
976             final int startType = mChooserListAdapter.getPositionTargetType(start);
977
978             int end = start + mColumnCount - 1;
979             while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) {
980                 end--;
981             }
982
983             final ViewGroup row = (ViewGroup) holder[mColumnCount];
984
985             if (startType == ChooserListAdapter.TARGET_SERVICE) {
986                 row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color));
987             } else {
988                 row.setBackground(null);
989             }
990
991             for (int i = 0; i < mColumnCount; i++) {
992                 final View v = holder[i];
993                 if (start + i <= end) {
994                     v.setVisibility(View.VISIBLE);
995                     final int itemIndex = start + i;
996                     mChooserListAdapter.bindView(itemIndex, v);
997                     v.setOnClickListener(new OnClickListener() {
998                         @Override
999                         public void onClick(View v) {
1000                             startSelected(itemIndex, false, true);
1001                         }
1002                     });
1003                     v.setOnLongClickListener(new OnLongClickListener() {
1004                         @Override
1005                         public boolean onLongClick(View v) {
1006                             showAppDetails(
1007                                     mChooserListAdapter.resolveInfoForPosition(itemIndex, true));
1008                             return true;
1009                         }
1010                     });
1011                 } else {
1012                     v.setVisibility(View.GONE);
1013                 }
1014             }
1015         }
1016
1017         int getFirstRowPosition(int row) {
1018             final int callerCount = mChooserListAdapter.getCallerTargetCount();
1019             final int callerRows = (int) Math.ceil((float) callerCount / mColumnCount);
1020
1021             if (row < callerRows) {
1022                 return row * mColumnCount;
1023             }
1024
1025             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
1026             final int serviceRows = (int) Math.ceil((float) serviceCount / mColumnCount);
1027
1028             if (row < callerRows + serviceRows) {
1029                 return callerCount + (row - callerRows) * mColumnCount;
1030             }
1031
1032             return callerCount + serviceCount
1033                     + (row - callerRows - serviceRows) * mColumnCount;
1034         }
1035     }
1036
1037     static class ChooserTargetServiceConnection implements ServiceConnection {
1038         private final DisplayResolveInfo mOriginalTarget;
1039         private ComponentName mConnectedComponent;
1040         private ChooserActivity mChooserActivity;
1041         private final Object mLock = new Object();
1042
1043         private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
1044             @Override
1045             public void sendResult(List<ChooserTarget> targets) throws RemoteException {
1046                 synchronized (mLock) {
1047                     if (mChooserActivity == null) {
1048                         Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from "
1049                                 + mConnectedComponent + "; ignoring...");
1050                         return;
1051                     }
1052                     mChooserActivity.filterServiceTargets(
1053                             mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
1054                     final Message msg = Message.obtain();
1055                     msg.what = CHOOSER_TARGET_SERVICE_RESULT;
1056                     msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
1057                             ChooserTargetServiceConnection.this);
1058                     mChooserActivity.mChooserHandler.sendMessage(msg);
1059                 }
1060             }
1061         };
1062
1063         public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
1064                 DisplayResolveInfo dri) {
1065             mChooserActivity = chooserActivity;
1066             mOriginalTarget = dri;
1067         }
1068
1069         @Override
1070         public void onServiceConnected(ComponentName name, IBinder service) {
1071             if (DEBUG) Log.d(TAG, "onServiceConnected: " + name);
1072             synchronized (mLock) {
1073                 if (mChooserActivity == null) {
1074                     Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected");
1075                     return;
1076                 }
1077
1078                 final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
1079                 try {
1080                     icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
1081                             mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
1082                 } catch (RemoteException e) {
1083                     Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
1084                     mChooserActivity.unbindService(this);
1085                     destroy();
1086                     mChooserActivity.mServiceConnections.remove(this);
1087                 }
1088             }
1089         }
1090
1091         @Override
1092         public void onServiceDisconnected(ComponentName name) {
1093             if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
1094             synchronized (mLock) {
1095                 if (mChooserActivity == null) {
1096                     Log.e(TAG,
1097                             "destroyed ChooserTargetServiceConnection got onServiceDisconnected");
1098                     return;
1099                 }
1100
1101                 mChooserActivity.unbindService(this);
1102                 destroy();
1103                 mChooserActivity.mServiceConnections.remove(this);
1104                 if (mChooserActivity.mServiceConnections.isEmpty()) {
1105                     mChooserActivity.mChooserHandler.removeMessages(
1106                             CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
1107                     mChooserActivity.sendVoiceChoicesIfNeeded();
1108                 }
1109                 mConnectedComponent = null;
1110             }
1111         }
1112
1113         public void destroy() {
1114             synchronized (mLock) {
1115                 mChooserActivity = null;
1116             }
1117         }
1118
1119         @Override
1120         public String toString() {
1121             return "ChooserTargetServiceConnection{service="
1122                     + mConnectedComponent + ", activity="
1123                     + mOriginalTarget.getResolveInfo().activityInfo.toString() + "}";
1124         }
1125     }
1126
1127     static class ServiceResultInfo {
1128         public final DisplayResolveInfo originalTarget;
1129         public final List<ChooserTarget> resultTargets;
1130         public final ChooserTargetServiceConnection connection;
1131
1132         public ServiceResultInfo(DisplayResolveInfo ot, List<ChooserTarget> rt,
1133                 ChooserTargetServiceConnection c) {
1134             originalTarget = ot;
1135             resultTargets = rt;
1136             connection = c;
1137         }
1138     }
1139
1140     static class RefinementResultReceiver extends ResultReceiver {
1141         private ChooserActivity mChooserActivity;
1142         private TargetInfo mSelectedTarget;
1143
1144         public RefinementResultReceiver(ChooserActivity host, TargetInfo target,
1145                 Handler handler) {
1146             super(handler);
1147             mChooserActivity = host;
1148             mSelectedTarget = target;
1149         }
1150
1151         @Override
1152         protected void onReceiveResult(int resultCode, Bundle resultData) {
1153             if (mChooserActivity == null) {
1154                 Log.e(TAG, "Destroyed RefinementResultReceiver received a result");
1155                 return;
1156             }
1157             if (resultData == null) {
1158                 Log.e(TAG, "RefinementResultReceiver received null resultData");
1159                 return;
1160             }
1161
1162             switch (resultCode) {
1163                 case RESULT_CANCELED:
1164                     mChooserActivity.onRefinementCanceled();
1165                     break;
1166                 case RESULT_OK:
1167                     Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT);
1168                     if (intentParcelable instanceof Intent) {
1169                         mChooserActivity.onRefinementResult(mSelectedTarget,
1170                                 (Intent) intentParcelable);
1171                     } else {
1172                         Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent"
1173                                 + " in resultData with key Intent.EXTRA_INTENT");
1174                     }
1175                     break;
1176                 default:
1177                     Log.w(TAG, "Unknown result code " + resultCode
1178                             + " sent to RefinementResultReceiver");
1179                     break;
1180             }
1181         }
1182
1183         public void destroy() {
1184             mChooserActivity = null;
1185             mSelectedTarget = null;
1186         }
1187     }
1188 }