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