Home | History | Annotate | Download | only in content
      1 /*
      2  * Copyright (C) 2006 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;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.annotation.UnsupportedAppUsage;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.text.TextUtils;
     25 import android.util.proto.ProtoOutputStream;
     26 
     27 import java.io.PrintWriter;
     28 
     29 /**
     30  * Identifier for a specific application component
     31  * ({@link android.app.Activity}, {@link android.app.Service},
     32  * {@link android.content.BroadcastReceiver}, or
     33  * {@link android.content.ContentProvider}) that is available.  Two
     34  * pieces of information, encapsulated here, are required to identify
     35  * a component: the package (a String) it exists in, and the class (a String)
     36  * name inside of that package.
     37  *
     38  */
     39 public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
     40     private final String mPackage;
     41     private final String mClass;
     42 
     43     /**
     44      * Create a new component identifier where the class name may be specified
     45      * as either absolute or relative to the containing package.
     46      *
     47      * <p>Relative package names begin with a <code>'.'</code> character. For a package
     48      * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
     49      * will return a ComponentName with the package <code>"com.example"</code>and class name
     50      * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
     51      * permitted.</p>
     52      *
     53      * @param pkg the name of the package the component exists in
     54      * @param cls the name of the class inside of <var>pkg</var> that implements
     55      *            the component
     56      * @return the new ComponentName
     57      */
     58     public static @NonNull ComponentName createRelative(@NonNull String pkg, @NonNull String cls) {
     59         if (TextUtils.isEmpty(cls)) {
     60             throw new IllegalArgumentException("class name cannot be empty");
     61         }
     62 
     63         final String fullName;
     64         if (cls.charAt(0) == '.') {
     65             // Relative to the package. Prepend the package name.
     66             fullName = pkg + cls;
     67         } else {
     68             // Fully qualified package name.
     69             fullName = cls;
     70         }
     71         return new ComponentName(pkg, fullName);
     72     }
     73 
     74     /**
     75      * Create a new component identifier where the class name may be specified
     76      * as either absolute or relative to the containing package.
     77      *
     78      * <p>Relative package names begin with a <code>'.'</code> character. For a package
     79      * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
     80      * will return a ComponentName with the package <code>"com.example"</code>and class name
     81      * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
     82      * permitted.</p>
     83      *
     84      * @param pkg a Context for the package implementing the component
     85      * @param cls the name of the class inside of <var>pkg</var> that implements
     86      *            the component
     87      * @return the new ComponentName
     88      */
     89     public static @NonNull ComponentName createRelative(@NonNull Context pkg, @NonNull String cls) {
     90         return createRelative(pkg.getPackageName(), cls);
     91     }
     92 
     93     /**
     94      * Create a new component identifier.
     95      *
     96      * @param pkg The name of the package that the component exists in.  Can
     97      * not be null.
     98      * @param cls The name of the class inside of <var>pkg</var> that
     99      * implements the component.  Can not be null.
    100      */
    101     public ComponentName(@NonNull String pkg, @NonNull String cls) {
    102         if (pkg == null) throw new NullPointerException("package name is null");
    103         if (cls == null) throw new NullPointerException("class name is null");
    104         mPackage = pkg;
    105         mClass = cls;
    106     }
    107 
    108     /**
    109      * Create a new component identifier from a Context and class name.
    110      *
    111      * @param pkg A Context for the package implementing the component,
    112      * from which the actual package name will be retrieved.
    113      * @param cls The name of the class inside of <var>pkg</var> that
    114      * implements the component.
    115      */
    116     public ComponentName(@NonNull Context pkg, @NonNull String cls) {
    117         if (cls == null) throw new NullPointerException("class name is null");
    118         mPackage = pkg.getPackageName();
    119         mClass = cls;
    120     }
    121 
    122     /**
    123      * Create a new component identifier from a Context and Class object.
    124      *
    125      * @param pkg A Context for the package implementing the component, from
    126      * which the actual package name will be retrieved.
    127      * @param cls The Class object of the desired component, from which the
    128      * actual class name will be retrieved.
    129      */
    130     public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) {
    131         mPackage = pkg.getPackageName();
    132         mClass = cls.getName();
    133     }
    134 
    135     public ComponentName clone() {
    136         return new ComponentName(mPackage, mClass);
    137     }
    138 
    139     /**
    140      * Return the package name of this component.
    141      */
    142     public @NonNull String getPackageName() {
    143         return mPackage;
    144     }
    145 
    146     /**
    147      * Return the class name of this component.
    148      */
    149     public @NonNull String getClassName() {
    150         return mClass;
    151     }
    152 
    153     /**
    154      * Return the class name, either fully qualified or in a shortened form
    155      * (with a leading '.') if it is a suffix of the package.
    156      */
    157     public String getShortClassName() {
    158         if (mClass.startsWith(mPackage)) {
    159             int PN = mPackage.length();
    160             int CN = mClass.length();
    161             if (CN > PN && mClass.charAt(PN) == '.') {
    162                 return mClass.substring(PN, CN);
    163             }
    164         }
    165         return mClass;
    166     }
    167 
    168     private static void appendShortClassName(StringBuilder sb, String packageName,
    169             String className) {
    170         if (className.startsWith(packageName)) {
    171             int PN = packageName.length();
    172             int CN = className.length();
    173             if (CN > PN && className.charAt(PN) == '.') {
    174                 sb.append(className, PN, CN);
    175                 return;
    176             }
    177         }
    178         sb.append(className);
    179     }
    180 
    181     private static void printShortClassName(PrintWriter pw, String packageName,
    182             String className) {
    183         if (className.startsWith(packageName)) {
    184             int PN = packageName.length();
    185             int CN = className.length();
    186             if (CN > PN && className.charAt(PN) == '.') {
    187                 pw.write(className, PN, CN-PN);
    188                 return;
    189             }
    190         }
    191         pw.print(className);
    192     }
    193 
    194     /**
    195      * Helper to get {@link #flattenToShortString()} in a {@link ComponentName} reference that can
    196      * be {@code null}.
    197      *
    198      * @hide
    199      */
    200     @Nullable
    201     public static String flattenToShortString(@Nullable ComponentName componentName) {
    202         return componentName == null ? null : componentName.flattenToShortString();
    203     }
    204 
    205     /**
    206      * Return a String that unambiguously describes both the package and
    207      * class names contained in the ComponentName.  You can later recover
    208      * the ComponentName from this string through
    209      * {@link #unflattenFromString(String)}.
    210      *
    211      * @return Returns a new String holding the package and class names.  This
    212      * is represented as the package name, concatenated with a '/' and then the
    213      * class name.
    214      *
    215      * @see #unflattenFromString(String)
    216      */
    217     public @NonNull String flattenToString() {
    218         return mPackage + "/" + mClass;
    219     }
    220 
    221     /**
    222      * The same as {@link #flattenToString()}, but abbreviates the class
    223      * name if it is a suffix of the package.  The result can still be used
    224      * with {@link #unflattenFromString(String)}.
    225      *
    226      * @return Returns a new String holding the package and class names.  This
    227      * is represented as the package name, concatenated with a '/' and then the
    228      * class name.
    229      *
    230      * @see #unflattenFromString(String)
    231      */
    232     public @NonNull String flattenToShortString() {
    233         StringBuilder sb = new StringBuilder(mPackage.length() + mClass.length());
    234         appendShortString(sb, mPackage, mClass);
    235         return sb.toString();
    236     }
    237 
    238     /** @hide */
    239     public void appendShortString(StringBuilder sb) {
    240         appendShortString(sb, mPackage, mClass);
    241     }
    242 
    243     /** @hide */
    244     @UnsupportedAppUsage
    245     public static void appendShortString(StringBuilder sb, String packageName, String className) {
    246         sb.append(packageName).append('/');
    247         appendShortClassName(sb, packageName, className);
    248     }
    249 
    250     /** @hide */
    251     @UnsupportedAppUsage
    252     public static void printShortString(PrintWriter pw, String packageName, String className) {
    253         pw.print(packageName);
    254         pw.print('/');
    255         printShortClassName(pw, packageName, className);
    256     }
    257 
    258     /**
    259      * Recover a ComponentName from a String that was previously created with
    260      * {@link #flattenToString()}.  It splits the string at the first '/',
    261      * taking the part before as the package name and the part after as the
    262      * class name.  As a special convenience (to use, for example, when
    263      * parsing component names on the command line), if the '/' is immediately
    264      * followed by a '.' then the final class name will be the concatenation
    265      * of the package name with the string following the '/'.  Thus
    266      * "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah".
    267      *
    268      * @param str The String that was returned by flattenToString().
    269      * @return Returns a new ComponentName containing the package and class
    270      * names that were encoded in <var>str</var>
    271      *
    272      * @see #flattenToString()
    273      */
    274     public static @Nullable ComponentName unflattenFromString(@NonNull String str) {
    275         int sep = str.indexOf('/');
    276         if (sep < 0 || (sep+1) >= str.length()) {
    277             return null;
    278         }
    279         String pkg = str.substring(0, sep);
    280         String cls = str.substring(sep+1);
    281         if (cls.length() > 0 && cls.charAt(0) == '.') {
    282             cls = pkg + cls;
    283         }
    284         return new ComponentName(pkg, cls);
    285     }
    286 
    287     /**
    288      * Return string representation of this class without the class's name
    289      * as a prefix.
    290      */
    291     public String toShortString() {
    292         return "{" + mPackage + "/" + mClass + "}";
    293     }
    294 
    295     @Override
    296     public String toString() {
    297         return "ComponentInfo{" + mPackage + "/" + mClass + "}";
    298     }
    299 
    300     /** Put this here so that individual services don't have to reimplement this. @hide */
    301     public void writeToProto(ProtoOutputStream proto, long fieldId) {
    302         final long token = proto.start(fieldId);
    303         proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
    304         proto.write(ComponentNameProto.CLASS_NAME, mClass);
    305         proto.end(token);
    306     }
    307 
    308     @Override
    309     public boolean equals(Object obj) {
    310         try {
    311             if (obj != null) {
    312                 ComponentName other = (ComponentName)obj;
    313                 // Note: no null checks, because mPackage and mClass can
    314                 // never be null.
    315                 return mPackage.equals(other.mPackage)
    316                         && mClass.equals(other.mClass);
    317             }
    318         } catch (ClassCastException e) {
    319         }
    320         return false;
    321     }
    322 
    323     @Override
    324     public int hashCode() {
    325         return mPackage.hashCode() + mClass.hashCode();
    326     }
    327 
    328     public int compareTo(ComponentName that) {
    329         int v;
    330         v = this.mPackage.compareTo(that.mPackage);
    331         if (v != 0) {
    332             return v;
    333         }
    334         return this.mClass.compareTo(that.mClass);
    335     }
    336 
    337     public int describeContents() {
    338         return 0;
    339     }
    340 
    341     public void writeToParcel(Parcel out, int flags) {
    342         // WARNING: If you modify this function, also update
    343         // frameworks/base/libs/services/src/content/ComponentName.cpp.
    344         out.writeString(mPackage);
    345         out.writeString(mClass);
    346     }
    347 
    348     /**
    349      * Write a ComponentName to a Parcel, handling null pointers.  Must be
    350      * read with {@link #readFromParcel(Parcel)}.
    351      *
    352      * @param c The ComponentName to be written.
    353      * @param out The Parcel in which the ComponentName will be placed.
    354      *
    355      * @see #readFromParcel(Parcel)
    356      */
    357     public static void writeToParcel(ComponentName c, Parcel out) {
    358         if (c != null) {
    359             c.writeToParcel(out, 0);
    360         } else {
    361             out.writeString(null);
    362         }
    363     }
    364 
    365     /**
    366      * Read a ComponentName from a Parcel that was previously written
    367      * with {@link #writeToParcel(ComponentName, Parcel)}, returning either
    368      * a null or new object as appropriate.
    369      *
    370      * @param in The Parcel from which to read the ComponentName
    371      * @return Returns a new ComponentName matching the previously written
    372      * object, or null if a null had been written.
    373      *
    374      * @see #writeToParcel(ComponentName, Parcel)
    375      */
    376     public static ComponentName readFromParcel(Parcel in) {
    377         String pkg = in.readString();
    378         return pkg != null ? new ComponentName(pkg, in) : null;
    379     }
    380 
    381     public static final @android.annotation.NonNull Parcelable.Creator<ComponentName> CREATOR
    382             = new Parcelable.Creator<ComponentName>() {
    383         public ComponentName createFromParcel(Parcel in) {
    384             return new ComponentName(in);
    385         }
    386 
    387         public ComponentName[] newArray(int size) {
    388             return new ComponentName[size];
    389         }
    390     };
    391 
    392     /**
    393      * Instantiate a new ComponentName from the data in a Parcel that was
    394      * previously written with {@link #writeToParcel(Parcel, int)}.  Note that you
    395      * must not use this with data written by
    396      * {@link #writeToParcel(ComponentName, Parcel)} since it is not possible
    397      * to handle a null ComponentObject here.
    398      *
    399      * @param in The Parcel containing the previously written ComponentName,
    400      * positioned at the location in the buffer where it was written.
    401      */
    402     public ComponentName(Parcel in) {
    403         mPackage = in.readString();
    404         if (mPackage == null) throw new NullPointerException(
    405                 "package name is null");
    406         mClass = in.readString();
    407         if (mClass == null) throw new NullPointerException(
    408                 "class name is null");
    409     }
    410 
    411     private ComponentName(String pkg, Parcel in) {
    412         mPackage = pkg;
    413         mClass = in.readString();
    414     }
    415 
    416     /**
    417      * Interface for classes associated with a component name.
    418      * @hide
    419      */
    420     @FunctionalInterface
    421     public interface WithComponentName {
    422         /** Return the associated component name. */
    423         ComponentName getComponentName();
    424     }
    425 }
    426