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

Commit a7d9c739 authored by Taras Antoshchuk's avatar Taras Antoshchuk
Browse files

Implement new API to modify MIME groups by adding/removing MIME types

MIME groups can now be modified via PackageManager.
MIME group modification will affect intent-filters
that were declared with that |mimeGroup| in manifest
in the same way, as if intent-filter was initially
declared with |mimeType| attributes that correspond to
MIME types in MIME group

Preferred activities will be handled in the next CL

Bug: 134736173
Bug: 136635677
Test: atest android.content.pm.PackageParserTest#testPackageWithIntentFilters*
Change-Id: I083a8794897e632aad5325a67311931193c69a3c
parent 26d3496c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -11938,6 +11938,7 @@ package android.content.pm {
    method @CheckResult public abstract int checkSignatures(@NonNull String, @NonNull String);
    method @CheckResult public abstract int checkSignatures(int, int);
    method public abstract void clearInstantAppCookie();
    method public void clearMimeGroup(@NonNull String);
    method @Deprecated public abstract void clearPackagePreferredActivities(@NonNull String);
    method public abstract String[] currentToCanonicalPackageNames(@NonNull String[]);
    method public abstract void extendVerificationTimeout(int, int, long);
@@ -11973,6 +11974,7 @@ package android.content.pm {
    method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
    method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String);
    method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String);
    method @Nullable public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
    method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
    method @Nullable public abstract String getNameForUid(int);
    method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int);
@@ -12035,6 +12037,7 @@ package android.content.pm {
    method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
    method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
    method public abstract void setInstallerPackageName(@NonNull String, @Nullable String);
    method public void setMimeGroup(@NonNull String, @NonNull java.util.Set<java.lang.String>);
    method public abstract void updateInstantAppCookie(@Nullable byte[]);
    method public abstract void verifyPendingInstall(int, int);
    field public static final int CERT_INPUT_RAW_X509 = 0; // 0x0
+31 −0
Original line number Diff line number Diff line
@@ -3334,4 +3334,35 @@ public class ApplicationPackageManager extends PackageManager {
            throw e.rethrowAsRuntimeException();
        }
    }

    public void setMimeGroup(String mimeGroup, Set<String> mimeTypes) {
        try {
            mPM.setMimeGroup(mContext.getPackageName(), mimeGroup,
                    new ArrayList<String>(mimeTypes));
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }

    @Override
    public void clearMimeGroup(String mimeGroup) {
        try {
            mPM.clearMimeGroup(mContext.getPackageName(), mimeGroup);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }

    @Override
    public Set<String> getMimeGroup(String group) {
        try {
            List<String> mimeGroup = mPM.getMimeGroup(mContext.getPackageName(), group);
            if (mimeGroup == null) {
                return null;
            }
            return new ArraySet<>(mimeGroup);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }
}
+210 −36
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.function.BiConsumer;

/**
 * Structured description of Intent values to be matched.  An IntentFilter can
@@ -153,6 +154,7 @@ public class IntentFilter implements Parcelable {
    private static final String AUTH_STR = "auth";
    private static final String SSP_STR = "ssp";
    private static final String SCHEME_STR = "scheme";
    private static final String STATIC_TYPE_STR = "staticType";
    private static final String TYPE_STR = "type";
    private static final String GROUP_STR = "group";
    private static final String CAT_STR = "cat";
@@ -281,9 +283,11 @@ public class IntentFilter implements Parcelable {
    private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
    private ArrayList<AuthorityEntry> mDataAuthorities = null;
    private ArrayList<PatternMatcher> mDataPaths = null;
    private ArrayList<String> mStaticDataTypes = null;
    private ArrayList<String> mDataTypes = null;
    private ArrayList<String> mMimeGroups = null;
    private boolean mHasPartialTypes = false;
    private boolean mHasStaticPartialTypes = false;
    private boolean mHasDynamicPartialTypes = false;

    private static final int STATE_VERIFY_AUTO         = 0x00000001;
    private static final int STATE_NEED_VERIFY         = 0x00000010;
@@ -456,6 +460,9 @@ public class IntentFilter implements Parcelable {
        if (o.mCategories != null) {
            mCategories = new ArrayList<String>(o.mCategories);
        }
        if (o.mStaticDataTypes != null) {
            mStaticDataTypes = new ArrayList<String>(o.mStaticDataTypes);
        }
        if (o.mDataTypes != null) {
            mDataTypes = new ArrayList<String>(o.mDataTypes);
        }
@@ -474,7 +481,8 @@ public class IntentFilter implements Parcelable {
        if (o.mMimeGroups != null) {
            mMimeGroups = new ArrayList<String>(o.mMimeGroups);
        }
        mHasPartialTypes = o.mHasPartialTypes;
        mHasStaticPartialTypes = o.mHasStaticPartialTypes;
        mHasDynamicPartialTypes = o.mHasDynamicPartialTypes;
        mVerifyState = o.mVerifyState;
        mInstantAppVisibility = o.mInstantAppVisibility;
    }
@@ -781,25 +789,108 @@ public class IntentFilter implements Parcelable {
     */
    public final void addDataType(String type)
        throws MalformedMimeTypeException {
        processMimeType(type, (internalType, isPartial) -> {
            if (mDataTypes == null) {
                mDataTypes = new ArrayList<>();
            }
            if (mStaticDataTypes == null) {
                mStaticDataTypes = new ArrayList<>();
            }

            if (mDataTypes.contains(internalType)) {
                return;
            }

            mDataTypes.add(internalType.intern());
            mStaticDataTypes.add(internalType.intern());
            mHasStaticPartialTypes = mHasStaticPartialTypes || isPartial;
        });
    }

    /**
     * Add a new Intent data type <em>from MIME group</em> to match against.  If any types are
     * included in the filter, then an Intent's data must be <em>either</em>
     * one of these types <em>or</em> a matching scheme.  If no data types
     * are included, then an Intent will only match if it specifies no data.
     *
     * <p><em>Note: MIME type matching in the Android framework is
     * case-sensitive, unlike formal RFC MIME types.  As a result,
     * you should always write your MIME types with lower case letters,
     * and any MIME types you receive from outside of Android should be
     * converted to lower case before supplying them here.</em></p>
     *
     * <p>Throws {@link MalformedMimeTypeException} if the given MIME type is
     * not syntactically correct.
     *
     * @param type Name of the data type to match, such as "vnd.android.cursor.dir/person".
     *
     * @see #clearDynamicDataTypes()
     * @hide
     */
    public final void addDynamicDataType(String type)
            throws MalformedMimeTypeException {
        processMimeType(type, (internalType, isPartial) -> {
            if (mDataTypes == null) {
                mDataTypes = new ArrayList<>();
            }

            if (!mDataTypes.contains(internalType)) {
                mDataTypes.add(internalType.intern());

                mHasDynamicPartialTypes = mHasDynamicPartialTypes || isPartial;
            }
        });
    }

    /**
     * Process mime type - convert to representation used internally and check if type is partial,
     * and then call provided action
     */
    private void processMimeType(String type, BiConsumer<String, Boolean> action)
            throws MalformedMimeTypeException {
        final int slashpos = type.indexOf('/');
        final int typelen = type.length();
        if (slashpos > 0 && typelen >= slashpos+2) {
            if (mDataTypes == null) mDataTypes = new ArrayList<String>();
            if (typelen == slashpos+2 && type.charAt(slashpos+1) == '*') {
                String str = type.substring(0, slashpos);
                if (!mDataTypes.contains(str)) {
                    mDataTypes.add(str.intern());
        if (slashpos <= 0 || typelen < slashpos + 2) {
            throw new MalformedMimeTypeException(type);
        }
                mHasPartialTypes = true;
            } else {
                if (!mDataTypes.contains(type)) {
                    mDataTypes.add(type.intern());

        String internalType = type;
        boolean isPartialType = false;
        if (typelen == slashpos + 2 && type.charAt(slashpos + 1) == '*') {
            internalType = type.substring(0, slashpos);
            isPartialType = true;
        }

        action.accept(internalType, isPartialType);
    }

    /**
     * Remove all previously added Intent data types from IntentFilter.
     *
     * @see #addDynamicDataType(String)
     * @hide
     */
    public final void clearDynamicDataTypes() {
        if (mDataTypes == null) {
            return;
        }

        throw new MalformedMimeTypeException(type);
        if (mStaticDataTypes != null) {
            mDataTypes.clear();
            mDataTypes.addAll(mStaticDataTypes);
        } else {
            mDataTypes = null;
        }

        mHasDynamicPartialTypes = false;
    }

    /**
     * Return the number of static data types in the filter.
     * @hide
     */
    public int countStaticDataTypes() {
        return mStaticDataTypes != null ? mStaticDataTypes.size() : 0;
    }

    /**
@@ -820,6 +911,16 @@ public class IntentFilter implements Parcelable {
        return mDataTypes != null && mDataTypes.contains(type);
    }

    /** @hide */
    public final boolean hasExactDynamicDataType(String type) {
        return hasExactDataType(type) && !hasExactStaticDataType(type);
    }

    /** @hide */
    public final boolean hasExactStaticDataType(String type) {
        return mStaticDataTypes != null && mStaticDataTypes.contains(type);
    }

    /**
     * Return the number of data types in the filter.
     */
@@ -1652,14 +1753,7 @@ public class IntentFilter implements Parcelable {
            serializer.attribute(null, NAME_STR, mCategories.get(i));
            serializer.endTag(null, CAT_STR);
        }
        N = countDataTypes();
        for (int i=0; i<N; i++) {
            serializer.startTag(null, TYPE_STR);
            String type = mDataTypes.get(i);
            if (type.indexOf('/') < 0) type = type + "/*";
            serializer.attribute(null, NAME_STR, type);
            serializer.endTag(null, TYPE_STR);
        }
        writeDataTypesToXml(serializer);
        N = countMimeGroups();
        for (int i=0; i<N; i++) {
            serializer.startTag(null, GROUP_STR);
@@ -1724,6 +1818,46 @@ public class IntentFilter implements Parcelable {
        }
    }

    /**
     * Write data types (both static and dynamic) to XML.
     * In implementation we rely on two facts:
     * - {@link #mStaticDataTypes} is subsequence of {@link #mDataTypes}
     * - both {@link #mStaticDataTypes} and {@link #mDataTypes} does not contain duplicates
     */
    private void writeDataTypesToXml(XmlSerializer serializer) throws IOException {
        if (mStaticDataTypes == null) {
            return;
        }

        int i = 0;
        for (String staticType: mStaticDataTypes) {
            while (!mDataTypes.get(i).equals(staticType)) {
                writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
                i++;
            }

            writeDataTypeToXml(serializer, staticType, STATIC_TYPE_STR);
            i++;
        }

        while (i < mDataTypes.size()) {
            writeDataTypeToXml(serializer, mDataTypes.get(i), TYPE_STR);
            i++;
        }
    }

    private void writeDataTypeToXml(XmlSerializer serializer, String type, String tag)
            throws IOException {
        serializer.startTag(null, tag);

        if (type.indexOf('/') < 0) {
            type = type + "/*";
        }

        serializer.attribute(null, NAME_STR, type);
        serializer.endTag(null, tag);
    }

    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
            IOException {
        String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
@@ -1750,7 +1884,7 @@ public class IntentFilter implements Parcelable {
                if (name != null) {
                    addCategory(name);
                }
            } else if (tagName.equals(TYPE_STR)) {
            } else if (tagName.equals(STATIC_TYPE_STR)) {
                String name = parser.getAttributeValue(null, NAME_STR);
                if (name != null) {
                    try {
@@ -1758,6 +1892,14 @@ public class IntentFilter implements Parcelable {
                    } catch (MalformedMimeTypeException e) {
                    }
                }
            } else if (tagName.equals(TYPE_STR)) {
                String name = parser.getAttributeValue(null, NAME_STR);
                if (name != null) {
                    try {
                        addDynamicDataType(name);
                    } catch (MalformedMimeTypeException e) {
                    }
                }
            } else if (tagName.equals(GROUP_STR)) {
                String name = parser.getAttributeValue(null, NAME_STR);
                if (name != null) {
@@ -1854,9 +1996,9 @@ public class IntentFilter implements Parcelable {
                proto.write(IntentFilterProto.MIME_GROUPS, it.next());
            }
        }
        if (mPriority != 0 || mHasPartialTypes) {
        if (mPriority != 0 || hasPartialTypes()) {
            proto.write(IntentFilterProto.PRIORITY, mPriority);
            proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, mHasPartialTypes);
            proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, hasPartialTypes());
        }
        proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
        proto.end(token);
@@ -1923,12 +2065,26 @@ public class IntentFilter implements Parcelable {
                du.println(sb.toString());
            }
        }
        if (mStaticDataTypes != null) {
            Iterator<String> it = mStaticDataTypes.iterator();
            while (it.hasNext()) {
                sb.setLength(0);
                sb.append(prefix); sb.append("StaticType: \"");
                sb.append(it.next()); sb.append("\"");
                du.println(sb.toString());
            }
        }
        if (mDataTypes != null) {
            Iterator<String> it = mDataTypes.iterator();
            while (it.hasNext()) {
                String dataType = it.next();
                if (hasExactStaticDataType(dataType)) {
                    continue;
                }

                sb.setLength(0);
                sb.append(prefix); sb.append("Type: \"");
                        sb.append(it.next()); sb.append("\"");
                sb.append(dataType); sb.append("\"");
                du.println(sb.toString());
            }
        }
@@ -1941,11 +2097,13 @@ public class IntentFilter implements Parcelable {
                du.println(sb.toString());
            }
        }
        if (mPriority != 0 || mOrder != 0 || mHasPartialTypes) {
        if (mPriority != 0 || mOrder != 0 || hasPartialTypes()) {
            sb.setLength(0);
            sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
            sb.append(prefix);
            sb.append("mPriority="); sb.append(mPriority);
            sb.append(", mOrder="); sb.append(mOrder);
                    sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
            sb.append(", mHasStaticPartialTypes="); sb.append(mHasStaticPartialTypes);
            sb.append(", mHasDynamicPartialTypes="); sb.append(mHasDynamicPartialTypes);
            du.println(sb.toString());
        }
        if (getAutoVerify()) {
@@ -1984,6 +2142,12 @@ public class IntentFilter implements Parcelable {
        } else {
            dest.writeInt(0);
        }
        if (mStaticDataTypes != null) {
            dest.writeInt(1);
            dest.writeStringList(mStaticDataTypes);
        } else {
            dest.writeInt(0);
        }
        if (mDataTypes != null) {
            dest.writeInt(1);
            dest.writeStringList(mDataTypes);
@@ -2024,7 +2188,8 @@ public class IntentFilter implements Parcelable {
            dest.writeInt(0);
        }
        dest.writeInt(mPriority);
        dest.writeInt(mHasPartialTypes ? 1 : 0);
        dest.writeInt(mHasStaticPartialTypes ? 1 : 0);
        dest.writeInt(mHasDynamicPartialTypes ? 1 : 0);
        dest.writeInt(getAutoVerify() ? 1 : 0);
        dest.writeInt(mInstantAppVisibility);
        dest.writeInt(mOrder);
@@ -2068,6 +2233,10 @@ public class IntentFilter implements Parcelable {
            mDataSchemes = new ArrayList<String>();
            source.readStringList(mDataSchemes);
        }
        if (source.readInt() != 0) {
            mStaticDataTypes = new ArrayList<String>();
            source.readStringList(mStaticDataTypes);
        }
        if (source.readInt() != 0) {
            mDataTypes = new ArrayList<String>();
            source.readStringList(mDataTypes);
@@ -2098,12 +2267,17 @@ public class IntentFilter implements Parcelable {
            }
        }
        mPriority = source.readInt();
        mHasPartialTypes = source.readInt() > 0;
        mHasStaticPartialTypes = source.readInt() > 0;
        mHasDynamicPartialTypes = source.readInt() > 0;
        setAutoVerify(source.readInt() > 0);
        setVisibilityToInstantApp(source.readInt());
        mOrder = source.readInt();
    }

    private boolean hasPartialTypes() {
        return mHasStaticPartialTypes || mHasDynamicPartialTypes;
    }

    private final boolean findMimeType(String type) {
        final ArrayList<String> t = mDataTypes;

@@ -2122,13 +2296,13 @@ public class IntentFilter implements Parcelable {
        }

        // Deal with this IntentFilter wanting to match every Intent type.
        if (mHasPartialTypes && t.contains("*")) {
        if (hasPartialTypes() && t.contains("*")) {
            return true;
        }

        final int slashpos = type.indexOf('/');
        if (slashpos > 0) {
            if (mHasPartialTypes && t.contains(type.substring(0, slashpos))) {
            if (hasPartialTypes() && t.contains(type.substring(0, slashpos))) {
                return true;
            }
            if (typeLength == slashpos+2 && type.charAt(slashpos+1) == '*') {
+7 −0
Original line number Diff line number Diff line
@@ -749,4 +749,11 @@ interface IPackageManager {
    // a large change that modifies many repos.
    //------------------------------------------------------------------------
    int checkUidPermission(String permName, int uid);

    void setMimeGroup(String packageName, String group, in List<String> mimeTypes);

    void clearMimeGroup(String packageName, String group);

    List<String> getMimeGroup(String packageName, String group);

}
+33 −0
Original line number Diff line number Diff line
@@ -7807,4 +7807,37 @@ public abstract class PackageManager {
        return resId == com.android.internal.R.drawable.sym_def_app_icon
                || resId == com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon;
    }

    /**
     * Sets MIME group's MIME types
     *
     * @param mimeGroup MIME group to modify
     * @param mimeTypes new MIME types contained by MIME group
     */
    public void setMimeGroup(@NonNull String mimeGroup, @NonNull Set<String> mimeTypes) {
        throw new UnsupportedOperationException(
                "setMimeGroup not implemented in subclass");
    }

    /**
     * Clears MIME group by removing all MIME types from it
     *
     * @param mimeGroup MIME group to clear
     */
    public void clearMimeGroup(@NonNull String mimeGroup) {
        throw new UnsupportedOperationException(
                "clearMimeGroup not implemented in subclass");
    }

    /**
     * Gets all MIME types that MIME group contains
     *
     * @return MIME types contained by the MIME group,
     *         or null if the MIME group was not declared in the manifest.
     */
    @Nullable
    public Set<String> getMimeGroup(@NonNull String mimeGroup) {
        throw new UnsupportedOperationException(
                "getMimeGroup not implemented in subclass");
    }
}
Loading