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