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