Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c95d2db4 authored by Ivan Chiang's avatar Ivan Chiang
Browse files

[PM] Change LauncherIconConfig (2/N)

- Parse the two attributes alternateIcons and alternateLabels
- The maximum of the length of TypedArray is 500. Only parse and
  reserve first 500 ids.

Flag: android.content.pm.change_launcher_badging
Test: atest AndroidPackageTest
Bug: 374004639
Change-Id: Iec04e961d55adadb5de425887f5de95a2fa4425a
parent 4a6db9da
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -410,6 +410,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
    private int mLocaleConfigRes;
    private boolean mAllowCrossUidActivitySwitchFromBelow;

    @Nullable
    private int[] mAlternateLauncherIconResIds;
    @Nullable
    private int[] mAlternateLauncherLabelResIds;

    private List<AndroidPackageSplit> mSplits;

    @NonNull
@@ -874,6 +879,18 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        return adoptPermissions;
    }

    @Nullable
    @Override
    public int[] getAlternateLauncherIconResIds() {
        return mAlternateLauncherIconResIds;
    }

    @Nullable
    @Override
    public int[] getAlternateLauncherLabelResIds() {
        return mAlternateLauncherLabelResIds;
    }

    @NonNull
    @Override
    public List<ParsedApexSystemService> getApexSystemServices() {
@@ -1887,6 +1904,19 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        return setBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING, value);
    }

    @Override
    public PackageImpl setAlternateLauncherIconResIds(@Nullable int[] alternateLauncherIconResIds) {
        this.mAlternateLauncherIconResIds = alternateLauncherIconResIds;
        return this;
    }

    @Override
    public PackageImpl setAlternateLauncherLabelResIds(
            @Nullable int[] alternateLauncherLabelResIds) {
        this.mAlternateLauncherLabelResIds = alternateLauncherLabelResIds;
        return this;
    }

    @Override
    public PackageImpl setTaskReparentingAllowed(boolean value) {
        return setBoolean(Booleans.ALLOW_TASK_REPARENTING, value);
@@ -3273,6 +3303,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        dest.writeLong(this.mBooleans2);
        dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
        dest.writeInt(this.mIntentMatchingFlags);
        dest.writeIntArray(this.mAlternateLauncherIconResIds);
        dest.writeIntArray(this.mAlternateLauncherLabelResIds);
    }

    private void writeFeatureFlagState(@NonNull Parcel dest) {
@@ -3465,6 +3497,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        this.mBooleans2 = in.readLong();
        this.mAllowCrossUidActivitySwitchFromBelow = in.readBoolean();
        this.mIntentMatchingFlags = in.readInt();
        this.mAlternateLauncherIconResIds = in.createIntArray();
        this.mAlternateLauncherLabelResIds = in.createIntArray();

        assignDerivedFields();
        assignDerivedFields2();
+12 −0
Original line number Diff line number Diff line
@@ -413,6 +413,18 @@ public interface ParsingPackage {

    ParsingPackage setOnBackInvokedCallbackEnabled(boolean enableOnBackInvokedCallback);

    /**
     * Set the drawable resources id array of the alternate icons that are parsing from the
     * AndroidManifest file
     */
    ParsingPackage setAlternateLauncherIconResIds(int[] alternateLauncherIconResIds);

    /**
     * Set the string resources id array of the alternate labels that are parsing from the
     * AndroidManifest file
     */
    ParsingPackage setAlternateLauncherLabelResIds(int[] alternateLauncherLabelResIds);

    @CallSuper
    ParsedPackage hideAsParsed();

+115 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
@@ -154,6 +155,13 @@ public class ParsingPackageUtils {

    private static final String TAG = ParsingUtils.TAG;

    // It is the maximum length of the typedArray of {@link android.R.attr#alternateIcons}
    // and {@link android.R.attr#alternateLabels}.
    private static final int MAXIMUM_LAUNCHER_ALTERNATE_IDS_LENGTH = 500;

    private static final String TYPE_STRING = "string";
    private static final String TYPE_DRAWABLE = "drawable";

    public static final boolean DEBUG_JAR = false;
    public static final boolean DEBUG_BACKUP = false;
    public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
@@ -2021,6 +2029,24 @@ public class ParsingPackageUtils {
                pkg.setManageSpaceActivityName(manageSpaceActivityName);
            }

            if (Flags.changeLauncherBadging()) {
                ParseResult<int[]> result = drawableResIdArray(input, sa, res,
                        R.styleable.AndroidManifestApplication_alternateLauncherIcons,
                        MAXIMUM_LAUNCHER_ALTERNATE_IDS_LENGTH);
                if (result.isError()) {
                    return input.error(result);
                }
                pkg.setAlternateLauncherIconResIds(result.getResult());

                result = stringResIdArray(input, sa, res,
                        R.styleable.AndroidManifestApplication_alternateLauncherLabels,
                        MAXIMUM_LAUNCHER_ALTERNATE_IDS_LENGTH);
                if (result.isError()) {
                    return input.error(result);
                }
                pkg.setAlternateLauncherLabelResIds(result.getResult());
            }

            if (pkg.isBackupAllowed()) {
                // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
                // and restoreAnyVersion are only relevant if backup is possible for the
@@ -3395,6 +3421,95 @@ public class ParsingPackageUtils {
        return sa.getResourceId(attribute, 0);
    }

    /**
     * Parse the drawable resource id array in the typed array {@code resourceId}
     * if available. If {@code maxSize} is not zero, only parse and preserve at most
     * {@code maxSize} ids.
     */
    private static ParseResult<int[]> drawableResIdArray(ParseInput input, @NonNull TypedArray sa,
            @NonNull Resources res, int resourceId, int maxSize) {
        return resIdArray(input, sa, res, resourceId, TYPE_DRAWABLE, maxSize);
    }

    /**
     * Parse the string resource id array in the typed array {@code resourceId}
     * if available. If {@code maxSize} is not zero, only parse and preserve at most
     * {@code maxSize} ids.
     */
    private static ParseResult<int[]> stringResIdArray(ParseInput input, @NonNull TypedArray sa,
            @NonNull Resources res, int resourceId, int maxSize) {
        return resIdArray(input, sa, res, resourceId, TYPE_STRING, maxSize);
    }

    /**
     * Parse the resource id array in the typed array {@code resourceId}
     * if available. If {@code maxSize} is larger than zero, only parse and preserve
     * at most {@code maxSize} ids that type is matched to the {@code expectedTypeName}.
     * Because the TypedArray allows mixed types in an array, if {@code expectedTypeName}
     * is null, it means don't check the type.
     */
    private static ParseResult<int[]> resIdArray(ParseInput input, @NonNull TypedArray sa,
            @NonNull Resources res, int resourceId, @Nullable String expectedTypeName,
            int maxSize) {
        if (!sa.hasValue(resourceId)) {
            return input.success(null);
        }

        final int typeArrayResId = sa.getResourceId(resourceId, /* defValue= */ 0);
        if (typeArrayResId == 0) {
            return input.success(null);
        }

        // Parse the typedArray
        try (TypedArray typedArray = res.obtainTypedArray(typeArrayResId)) {
            final String typedArrayName = res.getResourceName(typeArrayResId);
            final int length = typedArray.length();
            if (maxSize > 0 && length > maxSize) {
                return input.error(TextUtils.formatSimple(
                        "The length of the typedArray (%s) is larger than %d.",
                        typedArrayName, maxSize));
            }
            Set<Integer> resourceIdSet = new ArraySet<>();
            for (int i = 0; i < length; i++) {
                final int id = typedArray.getResourceId(i, /* defValue= */ 0);
                // Add the id when the conditions are all matched:
                // 1. The resource Id is not 0
                // 2. The type is the expected type
                // 3. The id is not duplicated
                if (id == 0) {
                    return input.error(TextUtils.formatSimple(
                            "There is an item that is not a resource id in the typedArray (%s).",
                            typedArrayName));
                }

                try {
                    if (resourceIdSet.contains(id)) {
                        return input.error(TextUtils.formatSimple(
                                "There is a duplicated resource (%s) in the typedArray (%s).",
                                res.getResourceName(id), typedArrayName));
                    }
                    final String typeName = res.getResourceTypeName(id);
                    if (expectedTypeName != null
                            && !TextUtils.equals(typeName, expectedTypeName)) {
                        return input.error(TextUtils.formatSimple(
                                "There is a resource (%s) in the typedArray (%s) that is not a"
                                        + " %s type.", res.getResourceName(id), typedArrayName,
                                expectedTypeName));
                    }
                } catch (Resources.NotFoundException e) {
                    return input.error(TextUtils.formatSimple(
                            "There is a resource in the typedArray (%s) that is not found in"
                                    + " the app resources.", typedArrayName));
                }
                resourceIdSet.add(id);
            }
            if (resourceIdSet.isEmpty()) {
                return input.success(null);
            }
            return input.success(resourceIdSet.stream().mapToInt(i -> i).toArray());
        }
    }

    private static String string(@StyleableRes int attribute, TypedArray sa) {
        return sa.getString(attribute);
    }
+22 −0
Original line number Diff line number Diff line
@@ -90,6 +90,28 @@ import java.util.UUID;
@Immutable
public interface AndroidPackage {

    /**
     * An array containing the drawable resources that used for the launcher
     * activity icons.
     *
     * @see R.attr#alternateLauncherIcons
     * @hide
     */
    @Immutable.Ignore
    @Nullable
    int[] getAlternateLauncherIconResIds();

    /**
     * An array containing the string resources that used for the launcher
     * activity labels.
     *
     * @see R.attr#alternateLauncherLabels
     * @hide
     */
    @Immutable.Ignore
    @Nullable
    int[] getAlternateLauncherLabelResIds();

    /**
     * @see ApplicationInfo#className
     * @see R.styleable#AndroidManifestApplication_name
+28 −0
Original line number Diff line number Diff line
@@ -582,6 +582,34 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
            PackageImpl::setSystemExt,
            true
        ),
        getSetByValue(
            AndroidPackage::getAlternateLauncherIconResIds,
            PackageImpl::setAlternateLauncherIconResIds,
            intArrayOf(3, 5, 7),
            compare = { first, second ->
                equalBy(
                    first, second,
                    { it.size },
                    { it[0] },
                    { it[1] },
                    { it[2] }
                )
            }
        ),
        getSetByValue(
            AndroidPackage::getAlternateLauncherLabelResIds,
            PackageImpl::setAlternateLauncherLabelResIds,
            intArrayOf(3, 5, 7),
            compare = { first, second ->
                equalBy(
                    first, second,
                    { it.size },
                    { it[0] },
                    { it[1] },
                    { it[2] }
                )
            }
        ),
    )

    override fun initialObject() = PackageImpl.forParsing(