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
17package android.content.pm;
18
19import android.content.ComponentName;
20import android.content.IntentFilter;
21import android.graphics.drawable.Drawable;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.os.UserHandle;
25import android.text.TextUtils;
26import android.util.Printer;
27import android.util.Slog;
28
29import java.text.Collator;
30import 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 */
38public 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} */
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     */
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     */
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     */
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     */
275    public final int getIconResource() {
276        if (noResourceId) return 0;
277        return getIconResourceInternal();
278    }
279
280    public void dump(Printer pw, String prefix) {
281        dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL);
282    }
283
284    /** @hide */
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
315    public ResolveInfo() {
316        targetUserId = UserHandle.USER_CURRENT;
317    }
318
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
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
366    public int describeContents() {
367        return 0;
368    }
369
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
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> {
455        public DisplayNameComparator(PackageManager pm) {
456            mPM = pm;
457            mCollator.setStrength(Collator.PRIMARY);
458        }
459
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