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      * Retrieve the current graphical icon associated with this resolution.  This
226      * will call back on the given PackageManager to load the icon from
227      * the application.
228      *
229      * @param pm A PackageManager from which the icon can be loaded; usually
230      * the PackageManager from which you originally retrieved this item.
231      *
232      * @return Returns a Drawable containing the resolution's icon.  If the
233      * item does not have an icon, the default activity icon is returned.
234      */
loadIcon(PackageManager pm)235     public Drawable loadIcon(PackageManager pm) {
236         Drawable dr = null;
237         if (resolvePackageName != null && iconResourceId != 0) {
238             dr = pm.getDrawable(resolvePackageName, iconResourceId, null);
239         }
240         ComponentInfo ci = getComponentInfo();
241         if (dr == null && iconResourceId != 0) {
242             ApplicationInfo ai = ci.applicationInfo;
243             dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
244         }
245         if (dr != null) {
246             return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId()));
247         }
248         return ci.loadIcon(pm);
249     }
250 
251     /**
252      * Return the icon resource identifier to use for this match.  If the
253      * match defines an icon, that is used; else if the activity defines
254      * an icon, that is used; else, the application icon is used.
255      * This function does not check noResourceId flag.
256      *
257      * @return The icon associated with this match.
258      */
getIconResourceInternal()259     final int getIconResourceInternal() {
260         if (iconResourceId != 0) return iconResourceId;
261         final ComponentInfo ci = getComponentInfo();
262         if (ci != null) {
263             return ci.getIconResource();
264         }
265         return 0;
266     }
267 
268     /**
269      * Return the icon resource identifier to use for this match.  If the
270      * match defines an icon, that is used; else if the activity defines
271      * an icon, that is used; else, the application icon is used.
272      *
273      * @return The icon associated with this match.
274      */
getIconResource()275     public final int getIconResource() {
276         if (noResourceId) return 0;
277         return getIconResourceInternal();
278     }
279 
dump(Printer pw, String prefix)280     public void dump(Printer pw, String prefix) {
281         dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL);
282     }
283 
284     /** @hide */
dump(Printer pw, String prefix, int flags)285     public void dump(Printer pw, String prefix, int flags) {
286         if (filter != null) {
287             pw.println(prefix + "Filter:");
288             filter.dump(pw, prefix + "  ");
289         }
290         pw.println(prefix + "priority=" + priority
291                 + " preferredOrder=" + preferredOrder
292                 + " match=0x" + Integer.toHexString(match)
293                 + " specificIndex=" + specificIndex
294                 + " isDefault=" + isDefault);
295         if (resolvePackageName != null) {
296             pw.println(prefix + "resolvePackageName=" + resolvePackageName);
297         }
298         if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) {
299             pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
300                     + " nonLocalizedLabel=" + nonLocalizedLabel
301                     + " icon=0x" + Integer.toHexString(icon));
302         }
303         if (activityInfo != null) {
304             pw.println(prefix + "ActivityInfo:");
305             activityInfo.dump(pw, prefix + "  ", flags);
306         } else if (serviceInfo != null) {
307             pw.println(prefix + "ServiceInfo:");
308             serviceInfo.dump(pw, prefix + "  ", flags);
309         } else if (providerInfo != null) {
310             pw.println(prefix + "ProviderInfo:");
311             providerInfo.dump(pw, prefix + "  ", flags);
312         }
313     }
314 
ResolveInfo()315     public ResolveInfo() {
316         targetUserId = UserHandle.USER_CURRENT;
317     }
318 
ResolveInfo(ResolveInfo orig)319     public ResolveInfo(ResolveInfo orig) {
320         activityInfo = orig.activityInfo;
321         serviceInfo = orig.serviceInfo;
322         providerInfo = orig.providerInfo;
323         filter = orig.filter;
324         priority = orig.priority;
325         preferredOrder = orig.preferredOrder;
326         match = orig.match;
327         specificIndex = orig.specificIndex;
328         labelRes = orig.labelRes;
329         nonLocalizedLabel = orig.nonLocalizedLabel;
330         icon = orig.icon;
331         resolvePackageName = orig.resolvePackageName;
332         noResourceId = orig.noResourceId;
333         iconResourceId = orig.iconResourceId;
334         system = orig.system;
335         targetUserId = orig.targetUserId;
336         handleAllWebDataURI = orig.handleAllWebDataURI;
337         isInstantAppAvailable = orig.isInstantAppAvailable;
338         instantAppAvailable = isInstantAppAvailable;
339     }
340 
toString()341     public String toString() {
342         final ComponentInfo ci = getComponentInfo();
343         StringBuilder sb = new StringBuilder(128);
344         sb.append("ResolveInfo{");
345         sb.append(Integer.toHexString(System.identityHashCode(this)));
346         sb.append(' ');
347         ComponentName.appendShortString(sb, ci.packageName, ci.name);
348         if (priority != 0) {
349             sb.append(" p=");
350             sb.append(priority);
351         }
352         if (preferredOrder != 0) {
353             sb.append(" o=");
354             sb.append(preferredOrder);
355         }
356         sb.append(" m=0x");
357         sb.append(Integer.toHexString(match));
358         if (targetUserId != UserHandle.USER_CURRENT) {
359             sb.append(" targetUserId=");
360             sb.append(targetUserId);
361         }
362         sb.append('}');
363         return sb.toString();
364     }
365 
describeContents()366     public int describeContents() {
367         return 0;
368     }
369 
writeToParcel(Parcel dest, int parcelableFlags)370     public void writeToParcel(Parcel dest, int parcelableFlags) {
371         if (activityInfo != null) {
372             dest.writeInt(1);
373             activityInfo.writeToParcel(dest, parcelableFlags);
374         } else if (serviceInfo != null) {
375             dest.writeInt(2);
376             serviceInfo.writeToParcel(dest, parcelableFlags);
377         } else if (providerInfo != null) {
378             dest.writeInt(3);
379             providerInfo.writeToParcel(dest, parcelableFlags);
380         } else {
381             dest.writeInt(0);
382         }
383         if (filter != null) {
384             dest.writeInt(1);
385             filter.writeToParcel(dest, parcelableFlags);
386         } else {
387             dest.writeInt(0);
388         }
389         dest.writeInt(priority);
390         dest.writeInt(preferredOrder);
391         dest.writeInt(match);
392         dest.writeInt(specificIndex);
393         dest.writeInt(labelRes);
394         TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
395         dest.writeInt(icon);
396         dest.writeString(resolvePackageName);
397         dest.writeInt(targetUserId);
398         dest.writeInt(system ? 1 : 0);
399         dest.writeInt(noResourceId ? 1 : 0);
400         dest.writeInt(iconResourceId);
401         dest.writeInt(handleAllWebDataURI ? 1 : 0);
402         dest.writeInt(isInstantAppAvailable ? 1 : 0);
403     }
404 
405     public static final Creator<ResolveInfo> CREATOR
406             = new Creator<ResolveInfo>() {
407         public ResolveInfo createFromParcel(Parcel source) {
408             return new ResolveInfo(source);
409         }
410         public ResolveInfo[] newArray(int size) {
411             return new ResolveInfo[size];
412         }
413     };
414 
ResolveInfo(Parcel source)415     private ResolveInfo(Parcel source) {
416         activityInfo = null;
417         serviceInfo = null;
418         providerInfo = null;
419         switch (source.readInt()) {
420             case 1:
421                 activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
422                 break;
423             case 2:
424                 serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
425                 break;
426             case 3:
427                 providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
428                 break;
429             default:
430                 Slog.w(TAG, "Missing ComponentInfo!");
431                 break;
432         }
433         if (source.readInt() != 0) {
434             filter = IntentFilter.CREATOR.createFromParcel(source);
435         }
436         priority = source.readInt();
437         preferredOrder = source.readInt();
438         match = source.readInt();
439         specificIndex = source.readInt();
440         labelRes = source.readInt();
441         nonLocalizedLabel
442                 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
443         icon = source.readInt();
444         resolvePackageName = source.readString();
445         targetUserId = source.readInt();
446         system = source.readInt() != 0;
447         noResourceId = source.readInt() != 0;
448         iconResourceId = source.readInt();
449         handleAllWebDataURI = source.readInt() != 0;
450         instantAppAvailable = isInstantAppAvailable = source.readInt() != 0;
451     }
452 
453     public static class DisplayNameComparator
454             implements Comparator<ResolveInfo> {
DisplayNameComparator(PackageManager pm)455         public DisplayNameComparator(PackageManager pm) {
456             mPM = pm;
457             mCollator.setStrength(Collator.PRIMARY);
458         }
459 
compare(ResolveInfo a, ResolveInfo b)460         public final int compare(ResolveInfo a, ResolveInfo b) {
461             // We want to put the one targeted to another user at the end of the dialog.
462             if (a.targetUserId != UserHandle.USER_CURRENT) {
463                 return 1;
464             }
465             if (b.targetUserId != UserHandle.USER_CURRENT) {
466                 return -1;
467             }
468             CharSequence  sa = a.loadLabel(mPM);
469             if (sa == null) sa = a.activityInfo.name;
470             CharSequence  sb = b.loadLabel(mPM);
471             if (sb == null) sb = b.activityInfo.name;
472 
473             return mCollator.compare(sa.toString(), sb.toString());
474         }
475 
476         private final Collator   mCollator = Collator.getInstance();
477         private PackageManager   mPM;
478     }
479 }
480