blob: d4ee328aacdae941e6b5794c30d361af8402c142 [file] [log] [blame]
/*
* Copyright (C) 2022 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.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Class holding the properties of a user that derive mostly from its user type.
*/
public final class UserProperties implements Parcelable {
private static final String LOG_TAG = UserProperties.class.getSimpleName();
// Attribute strings for reading/writing properties to/from XML.
private static final String ATTR_SHOW_IN_LAUNCHER = "showInLauncher";
private static final String ATTR_START_WITH_PARENT = "startWithParent";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
INDEX_SHOW_IN_LAUNCHER,
INDEX_START_WITH_PARENT,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
}
private static final int INDEX_SHOW_IN_LAUNCHER = 0;
private static final int INDEX_START_WITH_PARENT = 1;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
/**
* Possible values for whether or how to show this user in the Launcher.
* @hide
*/
@IntDef(prefix = "SHOW_IN_LAUNCHER_", value = {
SHOW_IN_LAUNCHER_WITH_PARENT,
SHOW_IN_LAUNCHER_SEPARATE,
SHOW_IN_LAUNCHER_NO,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShowInLauncher {
}
/**
* Suggests that the launcher should show this user's apps in the main tab.
* That is, either this user is a full user, so its apps should be presented accordingly, or, if
* this user is a profile, then its apps should be shown alongside its parent's apps.
*/
public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0;
/**
* Suggests that the launcher should show this user's apps, but separately from the apps of this
* user's parent.
*/
public static final int SHOW_IN_LAUNCHER_SEPARATE = 1;
/**
* Suggests that the launcher should not show this user.
*/
public static final int SHOW_IN_LAUNCHER_NO = 2;
/**
* Reference to the default user properties for this user's user type.
* <li>If non-null, then any absent property will use the default property from here instead.
* <li>If null, then any absent property indicates that the caller lacks permission to see it,
* so attempting to get that property will trigger a SecurityException.
*/
private final @Nullable UserProperties mDefaultProperties;
/**
* Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
* default properties, which it uses for any property not subsequently set.
* @hide
*/
public UserProperties(@NonNull UserProperties defaultProperties) {
mDefaultProperties = defaultProperties;
mPropertiesPresent = 0;
}
/**
* Copies the given UserProperties, excluding any information that doesn't satisfy the specified
* permissions.
* Can only be used on the original version (one that won't throw on permission errors).
* Note that, internally, this does not perform an exact copy.
* @hide
*/
public UserProperties(UserProperties orig,
boolean exposeAllFields,
boolean hasManagePermission,
boolean hasQueryPermission) {
if (orig.mDefaultProperties == null) {
throw new IllegalArgumentException("Attempting to copy a non-original UserProperties.");
}
this.mDefaultProperties = null;
// NOTE: Copy each property using getters to ensure default values are copied if needed.
if (exposeAllFields) {
setStartWithParent(orig.getStartWithParent());
}
if (hasManagePermission) {
// Add any items that require this permission.
}
if (hasQueryPermission) {
// Add any items that require this permission.
}
// Add any items that require no permissions at all.
setShowInLauncher(orig.getShowInLauncher());
}
/**
* Indicates that the given property is being stored explicitly in this object.
* If false, it means that either
* <li>the default property for the user type should be used instead (for SystemServer callers)
* <li>the caller lacks permission to see this property (for all other callers)
*/
private boolean isPresent(@PropertyIndex long index) {
return (mPropertiesPresent & (1L << index)) != 0;
}
/** Indicates that the given property is henceforth being explicitly stored in this object. */
private void setPresent(@PropertyIndex long index) {
mPropertiesPresent |= (1L << index);
}
/** @hide Returns the internal mPropertiesPresent value. Only for testing purposes. */
@VisibleForTesting
public long getPropertiesPresent() {
return mPropertiesPresent;
}
/**
* Returns whether, and how, a user should be shown in the Launcher.
* This is generally inapplicable for non-profile users.
*
* Possible return values include
* {@link #SHOW_IN_LAUNCHER_WITH_PARENT}},
* {@link #SHOW_IN_LAUNCHER_SEPARATE},
* and {@link #SHOW_IN_LAUNCHER_NO}.
*
* @return whether, and how, a profile should be shown in the Launcher.
*/
public @ShowInLauncher int getShowInLauncher() {
if (isPresent(INDEX_SHOW_IN_LAUNCHER)) return mShowInLauncher;
if (mDefaultProperties != null) return mDefaultProperties.mShowInLauncher;
throw new SecurityException("You don't have permission to query showInLauncher");
}
/** @hide */
public void setShowInLauncher(@ShowInLauncher int val) {
this.mShowInLauncher = val;
setPresent(INDEX_SHOW_IN_LAUNCHER);
}
private @ShowInLauncher int mShowInLauncher;
/**
* Returns whether a profile should be started when its parent starts (unless in quiet mode).
* This only applies for users that have parents (i.e. for profiles).
* @hide
*/
public boolean getStartWithParent() {
if (isPresent(INDEX_START_WITH_PARENT)) return mStartWithParent;
if (mDefaultProperties != null) return mDefaultProperties.mStartWithParent;
throw new SecurityException("You don't have permission to query startWithParent");
}
/** @hide */
public void setStartWithParent(boolean val) {
this.mStartWithParent = val;
setPresent(INDEX_START_WITH_PARENT);
}
private boolean mStartWithParent;
@Override
public String toString() {
// Please print in increasing order of PropertyIndex.
return "UserProperties{"
+ "mPropertiesPresent=" + Long.toBinaryString(mPropertiesPresent)
+ ", mShowInLauncher=" + getShowInLauncher()
+ ", mStartWithParent=" + getStartWithParent()
+ "}";
}
/**
* Print the UserProperties to the given PrintWriter.
* @hide
*/
public void println(PrintWriter pw, String prefix) {
// Please print in increasing order of PropertyIndex.
pw.println(prefix + "UserProperties:");
pw.println(prefix + " mPropertiesPresent=" + Long.toBinaryString(mPropertiesPresent));
pw.println(prefix + " mShowInLauncher=" + getShowInLauncher());
pw.println(prefix + " mStartWithParent=" + getStartWithParent());
}
/**
* Reads in a UserProperties from an xml file, for use by the SystemServer.
*
* The serializer should already be inside a tag from which to read the user properties.
*
* @param defaultUserPropertiesReference the default UserProperties to use for this user type.
* @see #writeToXml
* @hide
*/
public UserProperties(
TypedXmlPullParser parser,
@NonNull UserProperties defaultUserPropertiesReference)
throws IOException, XmlPullParserException {
this(defaultUserPropertiesReference);
updateFromXml(parser);
}
/**
* Parses the given xml file and updates this UserProperties with its data.
* I.e., if a piece of data is present in the xml, it will overwrite whatever was
* previously stored in this UserProperties.
* @hide
*/
public void updateFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
final int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
final String attributeName = parser.getAttributeName(i);
switch(attributeName) {
case ATTR_SHOW_IN_LAUNCHER:
setShowInLauncher(parser.getAttributeInt(i));
break;
case ATTR_START_WITH_PARENT:
setStartWithParent(parser.getAttributeBoolean(i));
break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
}
}
/**
* Writes the UserProperties, as used by the SystemServer, to the xml file.
*
* The serializer should already be inside a tag in which to write the user properties.
*
* @see #UserProperties(TypedXmlPullParser, UserProperties)
* @hide
*/
public void writeToXml(TypedXmlSerializer serializer)
throws IOException, XmlPullParserException {
if (isPresent(INDEX_SHOW_IN_LAUNCHER)) {
serializer.attributeInt(null, ATTR_SHOW_IN_LAUNCHER, mShowInLauncher);
}
if (isPresent(INDEX_START_WITH_PARENT)) {
serializer.attributeBoolean(null, ATTR_START_WITH_PARENT, mStartWithParent);
}
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@Override
public void writeToParcel(@NonNull Parcel dest, int parcelableFlags) {
dest.writeLong(mPropertiesPresent);
dest.writeInt(mShowInLauncher);
dest.writeBoolean(mStartWithParent);
}
/**
* Reads a UserProperties object from the parcel.
* Not suitable for the canonical SystemServer version since it lacks mDefaultProperties.
*/
private UserProperties(@NonNull Parcel source) {
mDefaultProperties = null;
mPropertiesPresent = source.readLong();
mShowInLauncher = source.readInt();
mStartWithParent = source.readBoolean();
}
@Override
public int describeContents() {
return 0;
}
public static final @android.annotation.NonNull Parcelable.Creator<UserProperties> CREATOR
= new Parcelable.Creator<UserProperties>() {
public UserProperties createFromParcel(Parcel source) {
return new UserProperties(source);
}
public UserProperties[] newArray(int size) {
return new UserProperties[size];
}
};
/**
* Builder for the SystemServer's {@link UserProperties}; see that class for documentation.
* Intended for building default values (and so all properties are present in the built object).
* @hide
*/
public static final class Builder {
// UserProperties fields and their default values.
private @ShowInLauncher int mShowInLauncher = SHOW_IN_LAUNCHER_WITH_PARENT;
private boolean mStartWithParent = false;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
return this;
}
public Builder setStartWithParent(boolean startWithParent) {
mStartWithParent = startWithParent;
return this;
}
/** Builds a UserProperties object with *all* values populated. */
public UserProperties build() {
return new UserProperties(
mShowInLauncher,
mStartWithParent);
}
} // end Builder
/** Creates a UserProperties with the given properties. Intended for building default values. */
private UserProperties(
@ShowInLauncher int showInLauncher,
boolean startWithParent) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
}
}