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