1 /*
2  * Copyright (C) 2007 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 android.content.pm;
18 
19 import android.content.ComponentName;
20 import android.content.IntentFilter;
21 import android.graphics.drawable.Drawable;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.UserHandle;
25 import android.text.TextUtils;
26 import android.util.Printer;
27 import android.util.Slog;
28 
29 import java.text.Collator;
30 import java.util.Comparator;
31 
32 /**
33  * Information that is returned from resolving an intent
34  * against an IntentFilter. This partially corresponds to
35  * information collected from the AndroidManifest.xml's
36  * <intent> tags.
37  */
38 public class ResolveInfo implements Parcelable {
39     private static final String TAG = "ResolveInfo";
40 
41     /**
42      * The activity or broadcast receiver that corresponds to this resolution
43      * match, if this resolution is for an activity or broadcast receiver.
44      * Exactly one of {@link #activityInfo}, {@link #serviceInfo}, or
45      * {@link #providerInfo} will be non-null.
46      */
47     public ActivityInfo activityInfo;
48 
49     /**
50      * The service that corresponds to this resolution match, if this resolution
51      * is for a service. Exactly one of {@link #activityInfo},
52      * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
53      */
54     public ServiceInfo serviceInfo;
55 
56     /**
57      * The provider that corresponds to this resolution match, if this
58      * resolution is for a provider. Exactly one of {@link #activityInfo},
59      * {@link #serviceInfo}, or {@link #providerInfo} will be non-null.
60      */
61     public ProviderInfo providerInfo;
62 
63     /**
64      * An auxiliary response that may modify the resolved information. This is
65      * only set under certain circumstances; such as when resolving instant apps
66      * or components defined in un-installed splits.
67      * @hide
68      */
69     public AuxiliaryResolveInfo auxiliaryInfo;
70 
71     /**
72      * Whether or not an instant app is available for the resolved intent.
73      */
74     public boolean isInstantAppAvailable;
75 
76     /** @removed */
77     @Deprecated
78     public boolean instantAppAvailable;
79 
80     /**
81      * The IntentFilter that was matched for this ResolveInfo.
82      */
83     public IntentFilter filter;
84 
85     /**
86      * The declared priority of this match.  Comes from the "priority"
87      * attribute or, if not set, defaults to 0.  Higher values are a higher
88      * priority.
89      */
90     public int priority;
91 
92     /**
93      * Order of result according to the user's preference.  If the user
94      * has not set a preference for this result, the value is 0; higher
95      * values are a higher priority.
96      */
97     public int preferredOrder;
98 
99     /**
100      * The system's evaluation of how well the activity matches the
101      * IntentFilter.  This is a match constant, a combination of
102      * {@link IntentFilter#MATCH_CATEGORY_MASK IntentFilter.MATCH_CATEGORY_MASK}
103      * and {@link IntentFilter#MATCH_ADJUSTMENT_MASK IntentFiler.MATCH_ADJUSTMENT_MASK}.
104      */
105     public int match;
106 
107     /**
108      * Only set when returned by
109      * {@link PackageManager#queryIntentActivityOptions}, this tells you
110      * which of the given specific intents this result came from.  0 is the
111      * first in the list, < 0 means it came from the generic Intent query.
112      */
113     public int specificIndex = -1;
114 
115     /**
116      * This filter has specified the Intent.CATEGORY_DEFAULT, meaning it
117      * would like to be considered a default action that the user can
118      * perform on this data.
119      */
120     public boolean isDefault;
121 
122     /**
123      * A string resource identifier (in the package's resources) of this
124      * match's label.  From the "label" attribute or, if not set, 0.
125      */
126     public int labelRes;
127 
128     /**
129      * The actual string retrieve from <var>labelRes</var> or null if none
130      * was provided.
131      */
132     public CharSequence nonLocalizedLabel;
133 
134     /**
135      * A drawable resource identifier (in the package's resources) of this
136      * match's icon.  From the "icon" attribute or, if not set, 0. It is
137      * set only if the icon can be obtained by resource id alone.
138      */
139     public int icon;
140 
141     /**
142      * Optional -- if non-null, the {@link #labelRes} and {@link #icon}
143      * resources will be loaded from this package, rather than the one
144      * containing the resolved component.
145      */
146     public String resolvePackageName;
147 
148     /**
149      * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user.
150      * @hide
151      */
152     public int targetUserId;
153 
154     /**
155      * Set to true if the icon cannot be obtained by resource ids alone.
156      * It is set to true for ResolveInfos from the managed profile: They need to
157      * have their icon badged, so it cannot be obtained by resource ids alone.
158      * @hide
159      */
160     public boolean noResourceId;
161 
162     /**
163      * Same as {@link #icon} but it will always correspond to "icon" attribute
164      * regardless of {@link #noResourceId} value.
165      * @hide
166      */
167     public int iconResourceId;
168 
169     /**
170      * @hide Target comes from system process?
171      */
172     public boolean system;
173 
174     /**
175      * @hide Does the associated IntentFilter comes from a Browser ?
176      */
177     public boolean handleAllWebDataURI;
178 
179     /** {@hide} */
getComponentInfo()180     public ComponentInfo getComponentInfo() {
181         if (activityInfo != null) return activityInfo;
182         if (serviceInfo != null) return serviceInfo;
183         if (providerInfo != null) return providerInfo;
184         throw new IllegalStateException("Missing ComponentInfo!");
185     }
186 
187     /**
188      * Retrieve the current textual label associated with this resolution.  This
189      * will call back on the given PackageManager to load the label from
190      * the application.
191      *
192      * @param pm A PackageManager from which the label can be loaded; usually
193      * the PackageManager from which you originally retrieved this item.
194      *
195      * @return Returns a CharSequence containing the resolutions's label.  If the
196      * item does not have a label, its name is returned.
197      */
loadLabel(PackageManager pm)198     public CharSequence loadLabel(PackageManager pm) {
199         if (nonLocalizedLabel != null) {
200             return nonLocalizedLabel;
201         }
202         CharSequence label;
203         if (resolvePackageName != null && labelRes != 0) {
204             label = pm.getText(resolvePackageName, labelRes, null);
205             if (label != null) {
206                 return label.toString().trim();
207             }
208         }
209         ComponentInfo ci = getComponentInfo();
210         ApplicationInfo ai = ci.applicationInfo;
211         if (labelRes != 0) {
212             label = pm.getText(ci.packageName, labelRes, ai);
213             if (label != null) {
214                 return label.toString().trim();
215             }
216         }
217 
218         CharSequence data = ci.loadLabel(pm);
219         // Make the data safe
220         if (data != null) data = data.toString().trim();
221         return data;
222     }
223 
224     /**
225      * @return The resource that would be used when loading
226      * the label for this resolve info.
227      *
228      * @hide
229      */
resolveLabelResId()230     public int resolveLabelResId() {
231         if (labelRes != 0) {
232             return labelRes;
233         }
234         final ComponentInfo componentInfo = getComponentInfo();
235         if (componentInfo.labelRes != 0) {
236             return componentInfo.labelRes;
237         }
238         return componentInfo.applicationInfo.labelRes;
239     }
240 
241     /**
242      * @return The resource that would be used when loading
243      * the icon for this resolve info.
244      *
245      * @hide
246      */
resolveIconResId()247     public int resolveIconResId() {
248         if (icon != 0) {
249             return icon;
250         }
251         final ComponentInfo componentInfo = getComponentInfo();
252         if (componentInfo.icon != 0) {
253             return componentInfo.icon;
254         }
255         return componentInfo.applicationInfo.icon;
256     }
257 
258     /**
259      * Retrieve the current graphical icon associated with this resolution.  This
260      * will call back on the given PackageManager to load the icon from
261      * the application.
262      *
263      * @param pm A PackageManager from which the icon can be loaded; usually
264      * the PackageManager from which you originally retrieved this item.
265      *
266      * @return Returns a Drawable containing the resolution's icon.  If the
267      * item does not have an icon, the default activity icon is returned.
268      */
loadIcon(PackageManager pm)269     public Drawable loadIcon(PackageManager pm) {
270         Drawable dr = null;
271         if (resolvePackageName != null && iconResourceId != 0) {
272             dr = pm.getDrawable(resolvePackageName, iconResourceId, null);
273         }
274         ComponentInfo ci = getComponentInfo();
275         if (dr == null && iconResourceId != 0) {
276             ApplicationInfo ai = ci.applicationInfo;
277             dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
278         }
279         if (dr != null) {
280             return pm.getUserBadgedIcon(dr, new UserHandle(pm.getUserId()));
281         }
282         return ci.loadIcon(pm);
283     }
284 
285     /**
286      * Return the icon resource identifier to use for this match.  If the
287      * match defines an icon, that is used; else if the activity defines
288      * an icon, that is used; else, the application icon is used.
289      * This function does not check noResourceId flag.
290      *
291      * @return The icon associated with this match.
292      */
getIconResourceInternal()293     final int getIconResourceInternal() {
294         if (iconResourceId != 0) return iconResourceId;
295         final ComponentInfo ci = getComponentInfo();
296         if (ci != null) {
297             return ci.getIconResource();
298         }
299         return 0;
300     }
301 
302     /**
303      * Return the icon resource identifier to use for this match.  If the
304      * match defines an icon, that is used; else if the activity defines
305      * an icon, that is used; else, the application icon is used.
306      *
307      * @return The icon associated with this match.
308      */
getIconResource()309     public final int getIconResource() {
310         if (noResourceId) return 0;
311         return getIconResourceInternal();
312     }
313 
dump(Printer pw, String prefix)314     public void dump(Printer pw, String prefix) {
315         dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL);
316     }
317 
318     /** @hide */
dump(Printer pw, String prefix, int dumpFlags)319     public void dump(Printer pw, String prefix, int dumpFlags) {
320         if (filter != null) {
321             pw.println(prefix + "Filter:");
322             filter.dump(pw, prefix + "  ");
323         }
324         pw.println(prefix + "priority=" + priority
325                 + " preferredOrder=" + preferredOrder
326                 + " match=0x" + Integer.toHexString(match)
327                 + " specificIndex=" + specificIndex
328                 + " isDefault=" + isDefault);
329         if (resolvePackageName != null) {
330             pw.println(prefix + "resolvePackageName=" + resolvePackageName);
331         }
332         if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) {
333             pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
334                     + " nonLocalizedLabel=" + nonLocalizedLabel
335                     + " icon=0x" + Integer.toHexString(icon));
336         }
337         if (activityInfo != null) {
338             pw.println(prefix + "ActivityInfo:");
339             activityInfo.dump(pw, prefix + "  ", dumpFlags);
340         } else if (serviceInfo != null) {
341             pw.println(prefix + "ServiceInfo:");
342             serviceInfo.dump(pw, prefix + "  ", dumpFlags);
343         } else if (providerInfo != null) {
344             pw.println(prefix + "ProviderInfo:");
345             providerInfo.dump(pw, prefix + "  ", dumpFlags);
346         }
347     }
348 
ResolveInfo()349     public ResolveInfo() {
350         targetUserId = UserHandle.USER_CURRENT;
351     }
352 
ResolveInfo(ResolveInfo orig)353     public ResolveInfo(ResolveInfo orig) {
354         activityInfo = orig.activityInfo;
355         serviceInfo = orig.serviceInfo;
356         providerInfo = orig.providerInfo;
357         filter = orig.filter;
358         priority = orig.priority;
359         preferredOrder = orig.preferredOrder;
360         match = orig.match;
361         specificIndex = orig.specificIndex;
362         labelRes = orig.labelRes;
363         nonLocalizedLabel = orig.nonLocalizedLabel;
364         icon = orig.icon;
365         resolvePackageName = orig.resolvePackageName;
366         noResourceId = orig.noResourceId;
367         iconResourceId = orig.iconResourceId;
368         system = orig.system;
369         targetUserId = orig.targetUserId;
370         handleAllWebDataURI = orig.handleAllWebDataURI;
371         isInstantAppAvailable = orig.isInstantAppAvailable;
372         instantAppAvailable = isInstantAppAvailable;
373     }
374 
toString()375     public String toString() {
376         final ComponentInfo ci = getComponentInfo();
377         StringBuilder sb = new StringBuilder(128);
378         sb.append("ResolveInfo{");
379         sb.append(Integer.toHexString(System.identityHashCode(this)));
380         sb.append(' ');
381         ComponentName.appendShortString(sb, ci.packageName, ci.name);
382         if (priority != 0) {
383             sb.append(" p=");
384             sb.append(priority);
385         }
386         if (preferredOrder != 0) {
387             sb.append(" o=");
388             sb.append(preferredOrder);
389         }
390         sb.append(" m=0x");
391         sb.append(Integer.toHexString(match));
392         if (targetUserId != UserHandle.USER_CURRENT) {
393             sb.append(" targetUserId=");
394             sb.append(targetUserId);
395         }
396         sb.append('}');
397         return sb.toString();
398     }
399 
describeContents()400     public int describeContents() {
401         return 0;
402     }
403 
writeToParcel(Parcel dest, int parcelableFlags)404     public void writeToParcel(Parcel dest, int parcelableFlags) {
405         if (activityInfo != null) {
406             dest.writeInt(1);
407             activityInfo.writeToParcel(dest, parcelableFlags);
408         } else if (serviceInfo != null) {
409             dest.writeInt(2);
410             serviceInfo.writeToParcel(dest, parcelableFlags);
411         } else if (providerInfo != null) {
412             dest.writeInt(3);
413             providerInfo.writeToParcel(dest, parcelableFlags);
414         } else {
415             dest.writeInt(0);
416         }
417         if (filter != null) {
418             dest.writeInt(1);
419             filter.writeToParcel(dest, parcelableFlags);
420         } else {
421             dest.writeInt(0);
422         }
423         dest.writeInt(priority);
424         dest.writeInt(preferredOrder);
425         dest.writeInt(match);
426         dest.writeInt(specificIndex);
427         dest.writeInt(labelRes);
428         TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
429         dest.writeInt(icon);
430         dest.writeString(resolvePackageName);
431         dest.writeInt(targetUserId);
432         dest.writeInt(system ? 1 : 0);
433         dest.writeInt(noResourceId ? 1 : 0);
434         dest.writeInt(iconResourceId);
435         dest.writeInt(handleAllWebDataURI ? 1 : 0);
436         dest.writeInt(isInstantAppAvailable ? 1 : 0);
437     }
438 
439     public static final Creator<ResolveInfo> CREATOR
440             = new Creator<ResolveInfo>() {
441         public ResolveInfo createFromParcel(Parcel source) {
442             return new ResolveInfo(source);
443         }
444         public ResolveInfo[] newArray(int size) {
445             return new ResolveInfo[size];
446         }
447     };
448 
ResolveInfo(Parcel source)449     private ResolveInfo(Parcel source) {
450         activityInfo = null;
451         serviceInfo = null;
452         providerInfo = null;
453         switch (source.readInt()) {
454             case 1:
455                 activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
456                 break;
457             case 2:
458                 serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
459                 break;
460             case 3:
461                 providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
462                 break;
463             default:
464                 Slog.w(TAG, "Missing ComponentInfo!");
465                 break;
466         }
467         if (source.readInt() != 0) {
468             filter = IntentFilter.CREATOR.createFromParcel(source);
469         }
470         priority = source.readInt();
471         preferredOrder = source.readInt();
472         match = source.readInt();
473         specificIndex = source.readInt();
474         labelRes = source.readInt();
475         nonLocalizedLabel
476                 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
477         icon = source.readInt();
478         resolvePackageName = source.readString();
479         targetUserId = source.readInt();
480         system = source.readInt() != 0;
481         noResourceId = source.readInt() != 0;
482         iconResourceId = source.readInt();
483         handleAllWebDataURI = source.readInt() != 0;
484         instantAppAvailable = isInstantAppAvailable = source.readInt() != 0;
485     }
486 
487     public static class DisplayNameComparator
488             implements Comparator<ResolveInfo> {
DisplayNameComparator(PackageManager pm)489         public DisplayNameComparator(PackageManager pm) {
490             mPM = pm;
491             mCollator.setStrength(Collator.PRIMARY);
492         }
493 
compare(ResolveInfo a, ResolveInfo b)494         public final int compare(ResolveInfo a, ResolveInfo b) {
495             // We want to put the one targeted to another user at the end of the dialog.
496             if (a.targetUserId != UserHandle.USER_CURRENT) {
497                 return 1;
498             }
499             if (b.targetUserId != UserHandle.USER_CURRENT) {
500                 return -1;
501             }
502             CharSequence  sa = a.loadLabel(mPM);
503             if (sa == null) sa = a.activityInfo.name;
504             CharSequence  sb = b.loadLabel(mPM);
505             if (sb == null) sb = b.activityInfo.name;
506 
507             return mCollator.compare(sa.toString(), sb.toString());
508         }
509 
510         private final Collator   mCollator = Collator.getInstance();
511         private PackageManager   mPM;
512     }
513 }
514