431 lines
15 KiB
Java
431 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.content;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.compat.annotation.UnsupportedAppUsage;
|
|
import android.os.Build;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.text.TextUtils;
|
|
import android.util.proto.ProtoOutputStream;
|
|
|
|
import java.io.PrintWriter;
|
|
|
|
/**
|
|
* Identifier for a specific application component
|
|
* ({@link android.app.Activity}, {@link android.app.Service},
|
|
* {@link android.content.BroadcastReceiver}, or
|
|
* {@link android.content.ContentProvider}) that is available. Two
|
|
* pieces of information, encapsulated here, are required to identify
|
|
* a component: the package (a String) it exists in, and the class (a String)
|
|
* name inside of that package.
|
|
*
|
|
*/
|
|
@android.ravenwood.annotation.RavenwoodKeepWholeClass
|
|
public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
|
|
private final String mPackage;
|
|
private final String mClass;
|
|
|
|
/**
|
|
* Create a new component identifier where the class name may be specified
|
|
* as either absolute or relative to the containing package.
|
|
*
|
|
* <p>Relative package names begin with a <code>'.'</code> character. For a package
|
|
* <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
|
|
* will return a ComponentName with the package <code>"com.example"</code>and class name
|
|
* <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
|
|
* permitted.</p>
|
|
*
|
|
* @param pkg the name of the package the component exists in
|
|
* @param cls the name of the class inside of <var>pkg</var> that implements
|
|
* the component
|
|
* @return the new ComponentName
|
|
*/
|
|
public static @NonNull ComponentName createRelative(@NonNull String pkg, @NonNull String cls) {
|
|
if (TextUtils.isEmpty(cls)) {
|
|
throw new IllegalArgumentException("class name cannot be empty");
|
|
}
|
|
|
|
final String fullName;
|
|
if (cls.charAt(0) == '.') {
|
|
// Relative to the package. Prepend the package name.
|
|
fullName = pkg + cls;
|
|
} else {
|
|
// Fully qualified package name.
|
|
fullName = cls;
|
|
}
|
|
return new ComponentName(pkg, fullName);
|
|
}
|
|
|
|
/**
|
|
* Create a new component identifier where the class name may be specified
|
|
* as either absolute or relative to the containing package.
|
|
*
|
|
* <p>Relative package names begin with a <code>'.'</code> character. For a package
|
|
* <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
|
|
* will return a ComponentName with the package <code>"com.example"</code>and class name
|
|
* <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
|
|
* permitted.</p>
|
|
*
|
|
* @param pkg a Context for the package implementing the component
|
|
* @param cls the name of the class inside of <var>pkg</var> that implements
|
|
* the component
|
|
* @return the new ComponentName
|
|
*/
|
|
public static @NonNull ComponentName createRelative(@NonNull Context pkg, @NonNull String cls) {
|
|
return createRelative(pkg.getPackageName(), cls);
|
|
}
|
|
|
|
/**
|
|
* Create a new component identifier.
|
|
*
|
|
* @param pkg The name of the package that the component exists in. Can
|
|
* not be null.
|
|
* @param cls The name of the class inside of <var>pkg</var> that
|
|
* implements the component. Can not be null.
|
|
*/
|
|
public ComponentName(@NonNull String pkg, @NonNull String cls) {
|
|
if (pkg == null) throw new NullPointerException("package name is null");
|
|
if (cls == null) throw new NullPointerException("class name is null");
|
|
mPackage = pkg;
|
|
mClass = cls;
|
|
}
|
|
|
|
/**
|
|
* Create a new component identifier from a Context and class name.
|
|
*
|
|
* @param pkg A Context for the package implementing the component,
|
|
* from which the actual package name will be retrieved.
|
|
* @param cls The name of the class inside of <var>pkg</var> that
|
|
* implements the component.
|
|
*/
|
|
public ComponentName(@NonNull Context pkg, @NonNull String cls) {
|
|
if (cls == null) throw new NullPointerException("class name is null");
|
|
mPackage = pkg.getPackageName();
|
|
mClass = cls;
|
|
}
|
|
|
|
/**
|
|
* Create a new component identifier from a Context and Class object.
|
|
*
|
|
* @param pkg A Context for the package implementing the component, from
|
|
* which the actual package name will be retrieved.
|
|
* @param cls The Class object of the desired component, from which the
|
|
* actual class name will be retrieved.
|
|
*/
|
|
public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) {
|
|
mPackage = pkg.getPackageName();
|
|
mClass = cls.getName();
|
|
}
|
|
|
|
public ComponentName clone() {
|
|
return new ComponentName(mPackage, mClass);
|
|
}
|
|
|
|
/**
|
|
* Return the package name of this component.
|
|
*/
|
|
public @NonNull String getPackageName() {
|
|
return mPackage;
|
|
}
|
|
|
|
/**
|
|
* Return the class name of this component.
|
|
*/
|
|
public @NonNull String getClassName() {
|
|
return mClass;
|
|
}
|
|
|
|
/**
|
|
* Return the class name, either fully qualified or in a shortened form
|
|
* (with a leading '.') if it is a suffix of the package.
|
|
*/
|
|
public String getShortClassName() {
|
|
if (mClass.startsWith(mPackage)) {
|
|
int PN = mPackage.length();
|
|
int CN = mClass.length();
|
|
if (CN > PN && mClass.charAt(PN) == '.') {
|
|
return mClass.substring(PN, CN);
|
|
}
|
|
}
|
|
return mClass;
|
|
}
|
|
|
|
private static void appendShortClassName(StringBuilder sb, String packageName,
|
|
String className) {
|
|
if (className.startsWith(packageName)) {
|
|
int PN = packageName.length();
|
|
int CN = className.length();
|
|
if (CN > PN && className.charAt(PN) == '.') {
|
|
sb.append(className, PN, CN);
|
|
return;
|
|
}
|
|
}
|
|
sb.append(className);
|
|
}
|
|
|
|
private static void printShortClassName(PrintWriter pw, String packageName,
|
|
String className) {
|
|
if (className.startsWith(packageName)) {
|
|
int PN = packageName.length();
|
|
int CN = className.length();
|
|
if (CN > PN && className.charAt(PN) == '.') {
|
|
pw.write(className, PN, CN-PN);
|
|
return;
|
|
}
|
|
}
|
|
pw.print(className);
|
|
}
|
|
|
|
/**
|
|
* Helper to get {@link #flattenToShortString()} in a {@link ComponentName} reference that can
|
|
* be {@code null}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Nullable
|
|
public static String flattenToShortString(@Nullable ComponentName componentName) {
|
|
return componentName == null ? null : componentName.flattenToShortString();
|
|
}
|
|
|
|
/**
|
|
* Return a String that unambiguously describes both the package and
|
|
* class names contained in the ComponentName. You can later recover
|
|
* the ComponentName from this string through
|
|
* {@link #unflattenFromString(String)}.
|
|
*
|
|
* @return Returns a new String holding the package and class names. This
|
|
* is represented as the package name, concatenated with a '/' and then the
|
|
* class name.
|
|
*
|
|
* @see #unflattenFromString(String)
|
|
*/
|
|
public @NonNull String flattenToString() {
|
|
return mPackage + "/" + mClass;
|
|
}
|
|
|
|
/**
|
|
* The same as {@link #flattenToString()}, but abbreviates the class
|
|
* name if it is a suffix of the package. The result can still be used
|
|
* with {@link #unflattenFromString(String)}.
|
|
*
|
|
* @return Returns a new String holding the package and class names. This
|
|
* is represented as the package name, concatenated with a '/' and then the
|
|
* class name.
|
|
*
|
|
* @see #unflattenFromString(String)
|
|
*/
|
|
public @NonNull String flattenToShortString() {
|
|
StringBuilder sb = new StringBuilder(mPackage.length() + mClass.length());
|
|
appendShortString(sb, mPackage, mClass);
|
|
return sb.toString();
|
|
}
|
|
|
|
/** @hide */
|
|
public void appendShortString(StringBuilder sb) {
|
|
appendShortString(sb, mPackage, mClass);
|
|
}
|
|
|
|
/** @hide */
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
public static void appendShortString(StringBuilder sb, String packageName, String className) {
|
|
sb.append(packageName).append('/');
|
|
appendShortClassName(sb, packageName, className);
|
|
}
|
|
|
|
/** @hide */
|
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
|
public static void printShortString(PrintWriter pw, String packageName, String className) {
|
|
pw.print(packageName);
|
|
pw.print('/');
|
|
printShortClassName(pw, packageName, className);
|
|
}
|
|
|
|
/**
|
|
* Recover a ComponentName from a String that was previously created with
|
|
* {@link #flattenToString()}. It splits the string at the first '/',
|
|
* taking the part before as the package name and the part after as the
|
|
* class name. As a special convenience (to use, for example, when
|
|
* parsing component names on the command line), if the '/' is immediately
|
|
* followed by a '.' then the final class name will be the concatenation
|
|
* of the package name with the string following the '/'. Thus
|
|
* "com.foo/.Blah" becomes package="com.foo" class="com.foo.Blah".
|
|
*
|
|
* @param str The String that was returned by flattenToString().
|
|
* @return Returns a new ComponentName containing the package and class
|
|
* names that were encoded in <var>str</var>
|
|
*
|
|
* @see #flattenToString()
|
|
*/
|
|
public static @Nullable ComponentName unflattenFromString(@NonNull String str) {
|
|
int sep = str.indexOf('/');
|
|
if (sep < 0 || (sep+1) >= str.length()) {
|
|
return null;
|
|
}
|
|
String pkg = str.substring(0, sep);
|
|
String cls = str.substring(sep+1);
|
|
if (cls.length() > 0 && cls.charAt(0) == '.') {
|
|
cls = pkg + cls;
|
|
}
|
|
return new ComponentName(pkg, cls);
|
|
}
|
|
|
|
/**
|
|
* Return string representation of this class without the class's name
|
|
* as a prefix.
|
|
*/
|
|
public String toShortString() {
|
|
return "{" + mPackage + "/" + mClass + "}";
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "ComponentInfo{" + mPackage + "/" + mClass + "}";
|
|
}
|
|
|
|
/** Put this here so that individual services don't have to reimplement this. @hide */
|
|
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
|
|
final long token = proto.start(fieldId);
|
|
proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
|
|
proto.write(ComponentNameProto.CLASS_NAME, mClass);
|
|
proto.end(token);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* <p>Two components are considered to be equal if the packages in which they reside have the
|
|
* same name, and if the classes that implement each component also have the same name.
|
|
*/
|
|
@Override
|
|
public boolean equals(@Nullable Object obj) {
|
|
if (obj instanceof ComponentName) {
|
|
ComponentName other = (ComponentName) obj;
|
|
// mPackage and mClass can never be null.
|
|
return mPackage.equals(other.mPackage)
|
|
&& mClass.equals(other.mClass);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return mPackage.hashCode() + mClass.hashCode();
|
|
}
|
|
|
|
public int compareTo(ComponentName that) {
|
|
int v;
|
|
v = this.mPackage.compareTo(that.mPackage);
|
|
if (v != 0) {
|
|
return v;
|
|
}
|
|
return this.mClass.compareTo(that.mClass);
|
|
}
|
|
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
public void writeToParcel(Parcel out, int flags) {
|
|
// WARNING: If you modify this function, also update
|
|
// frameworks/base/libs/services/src/content/ComponentName.cpp.
|
|
out.writeString(mPackage);
|
|
out.writeString(mClass);
|
|
}
|
|
|
|
/**
|
|
* Write a ComponentName to a Parcel, handling null pointers. Must be
|
|
* read with {@link #readFromParcel(Parcel)}.
|
|
*
|
|
* @param c The ComponentName to be written.
|
|
* @param out The Parcel in which the ComponentName will be placed.
|
|
*
|
|
* @see #readFromParcel(Parcel)
|
|
*/
|
|
public static void writeToParcel(ComponentName c, Parcel out) {
|
|
if (c != null) {
|
|
c.writeToParcel(out, 0);
|
|
} else {
|
|
out.writeString(null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a ComponentName from a Parcel that was previously written
|
|
* with {@link #writeToParcel(ComponentName, Parcel)}, returning either
|
|
* a null or new object as appropriate.
|
|
*
|
|
* @param in The Parcel from which to read the ComponentName
|
|
* @return Returns a new ComponentName matching the previously written
|
|
* object, or null if a null had been written.
|
|
*
|
|
* @see #writeToParcel(ComponentName, Parcel)
|
|
*/
|
|
public static ComponentName readFromParcel(Parcel in) {
|
|
String pkg = in.readString();
|
|
return pkg != null ? new ComponentName(pkg, in) : null;
|
|
}
|
|
|
|
public static final @android.annotation.NonNull Parcelable.Creator<ComponentName> CREATOR
|
|
= new Parcelable.Creator<ComponentName>() {
|
|
public ComponentName createFromParcel(Parcel in) {
|
|
return new ComponentName(in);
|
|
}
|
|
|
|
public ComponentName[] newArray(int size) {
|
|
return new ComponentName[size];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Instantiate a new ComponentName from the data in a Parcel that was
|
|
* previously written with {@link #writeToParcel(Parcel, int)}. Note that you
|
|
* must not use this with data written by
|
|
* {@link #writeToParcel(ComponentName, Parcel)} since it is not possible
|
|
* to handle a null ComponentObject here.
|
|
*
|
|
* @param in The Parcel containing the previously written ComponentName,
|
|
* positioned at the location in the buffer where it was written.
|
|
*/
|
|
public ComponentName(Parcel in) {
|
|
mPackage = in.readString();
|
|
if (mPackage == null) throw new NullPointerException(
|
|
"package name is null");
|
|
mClass = in.readString();
|
|
if (mClass == null) throw new NullPointerException(
|
|
"class name is null");
|
|
}
|
|
|
|
private ComponentName(String pkg, Parcel in) {
|
|
mPackage = pkg;
|
|
mClass = in.readString();
|
|
}
|
|
|
|
/**
|
|
* Interface for classes associated with a component name.
|
|
* @hide
|
|
*/
|
|
@FunctionalInterface
|
|
public interface WithComponentName {
|
|
/** Return the associated component name. */
|
|
ComponentName getComponentName();
|
|
}
|
|
}
|