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