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