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.compat.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 */ createRelative(@onNull String pkg, @NonNull String cls)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 */ createRelative(@onNull Context pkg, @NonNull String cls)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 */ ComponentName(@onNull String pkg, @NonNull String cls)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 */ ComponentName(@onNull Context pkg, @NonNull String cls)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 */ ComponentName(@onNull Context pkg, @NonNull Class<?> cls)130 public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) { 131 mPackage = pkg.getPackageName(); 132 mClass = cls.getName(); 133 } 134 clone()135 public ComponentName clone() { 136 return new ComponentName(mPackage, mClass); 137 } 138 139 /** 140 * Return the package name of this component. 141 */ getPackageName()142 public @NonNull String getPackageName() { 143 return mPackage; 144 } 145 146 /** 147 * Return the class name of this component. 148 */ getClassName()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 */ getShortClassName()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 appendShortClassName(StringBuilder sb, String packageName, String className)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 printShortClassName(PrintWriter pw, String packageName, String className)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 flattenToShortString(@ullable ComponentName componentName)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 */ flattenToString()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 */ flattenToShortString()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 */ appendShortString(StringBuilder sb)239 public void appendShortString(StringBuilder sb) { 240 appendShortString(sb, mPackage, mClass); 241 } 242 243 /** @hide */ 244 @UnsupportedAppUsage appendShortString(StringBuilder sb, String packageName, String className)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 printShortString(PrintWriter pw, String packageName, String className)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 */ unflattenFromString(@onNull String str)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 */ toShortString()291 public String toShortString() { 292 return "{" + mPackage + "/" + mClass + "}"; 293 } 294 295 @Override toString()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 */ dumpDebug(ProtoOutputStream proto, long fieldId)301 public void dumpDebug(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 /** 309 * {@inheritDoc} 310 * 311 * <p>Two components are considered to be equal if the packages in which they reside have the 312 * same name, and if the classes that implement each component also have the same name. 313 */ 314 @Override equals(Object obj)315 public boolean equals(Object obj) { 316 try { 317 if (obj != null) { 318 ComponentName other = (ComponentName)obj; 319 // Note: no null checks, because mPackage and mClass can 320 // never be null. 321 return mPackage.equals(other.mPackage) 322 && mClass.equals(other.mClass); 323 } 324 } catch (ClassCastException e) { 325 } 326 return false; 327 } 328 329 @Override hashCode()330 public int hashCode() { 331 return mPackage.hashCode() + mClass.hashCode(); 332 } 333 compareTo(ComponentName that)334 public int compareTo(ComponentName that) { 335 int v; 336 v = this.mPackage.compareTo(that.mPackage); 337 if (v != 0) { 338 return v; 339 } 340 return this.mClass.compareTo(that.mClass); 341 } 342 describeContents()343 public int describeContents() { 344 return 0; 345 } 346 writeToParcel(Parcel out, int flags)347 public void writeToParcel(Parcel out, int flags) { 348 // WARNING: If you modify this function, also update 349 // frameworks/base/libs/services/src/content/ComponentName.cpp. 350 out.writeString(mPackage); 351 out.writeString(mClass); 352 } 353 354 /** 355 * Write a ComponentName to a Parcel, handling null pointers. Must be 356 * read with {@link #readFromParcel(Parcel)}. 357 * 358 * @param c The ComponentName to be written. 359 * @param out The Parcel in which the ComponentName will be placed. 360 * 361 * @see #readFromParcel(Parcel) 362 */ writeToParcel(ComponentName c, Parcel out)363 public static void writeToParcel(ComponentName c, Parcel out) { 364 if (c != null) { 365 c.writeToParcel(out, 0); 366 } else { 367 out.writeString(null); 368 } 369 } 370 371 /** 372 * Read a ComponentName from a Parcel that was previously written 373 * with {@link #writeToParcel(ComponentName, Parcel)}, returning either 374 * a null or new object as appropriate. 375 * 376 * @param in The Parcel from which to read the ComponentName 377 * @return Returns a new ComponentName matching the previously written 378 * object, or null if a null had been written. 379 * 380 * @see #writeToParcel(ComponentName, Parcel) 381 */ readFromParcel(Parcel in)382 public static ComponentName readFromParcel(Parcel in) { 383 String pkg = in.readString(); 384 return pkg != null ? new ComponentName(pkg, in) : null; 385 } 386 387 public static final @android.annotation.NonNull Parcelable.Creator<ComponentName> CREATOR 388 = new Parcelable.Creator<ComponentName>() { 389 public ComponentName createFromParcel(Parcel in) { 390 return new ComponentName(in); 391 } 392 393 public ComponentName[] newArray(int size) { 394 return new ComponentName[size]; 395 } 396 }; 397 398 /** 399 * Instantiate a new ComponentName from the data in a Parcel that was 400 * previously written with {@link #writeToParcel(Parcel, int)}. Note that you 401 * must not use this with data written by 402 * {@link #writeToParcel(ComponentName, Parcel)} since it is not possible 403 * to handle a null ComponentObject here. 404 * 405 * @param in The Parcel containing the previously written ComponentName, 406 * positioned at the location in the buffer where it was written. 407 */ ComponentName(Parcel in)408 public ComponentName(Parcel in) { 409 mPackage = in.readString(); 410 if (mPackage == null) throw new NullPointerException( 411 "package name is null"); 412 mClass = in.readString(); 413 if (mClass == null) throw new NullPointerException( 414 "class name is null"); 415 } 416 ComponentName(String pkg, Parcel in)417 private ComponentName(String pkg, Parcel in) { 418 mPackage = pkg; 419 mClass = in.readString(); 420 } 421 422 /** 423 * Interface for classes associated with a component name. 424 * @hide 425 */ 426 @FunctionalInterface 427 public interface WithComponentName { 428 /** Return the associated component name. */ getComponentName()429 ComponentName getComponentName(); 430 } 431 } 432