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

Commit 9ec6cdde authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Optimize memory use of IntentResolver.

Use raw arrays instead of ArrayList for data structures.

Temporarily includes a copy of the old intent resolver for
validating the new implementation.

Change-Id: I988925669b6686ac73b779be6cd6fe3a9fd86660
parent 619e4dca
Loading
Loading
Loading
Loading
+209 −81
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
import android.util.StringBuilderPrinter;

import android.content.Intent;
import android.content.IntentFilter;
@@ -65,11 +66,17 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
            register_intent_filter(f, f.actionsIterator(),
                    mTypedActionToFilter, "      TypedAction: ");
        }

        mOldResolver.addFilter(f);
        verifyDataStructures(f);
    }

    public void removeFilter(F f) {
        removeFilterInternal(f);
        mFilters.remove(f);

        mOldResolver.removeFilter(f);
        verifyDataStructures(f);
    }

    void removeFilterInternal(F f) {
@@ -93,18 +100,18 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
    }

    boolean dumpMap(PrintWriter out, String titlePrefix, String title,
            String prefix, Map<String, ArrayList<F>> map, String packageName,
            String prefix, Map<String, F[]> map, String packageName,
            boolean printFilter) {
        String eprefix = prefix + "  ";
        String fprefix = prefix + "    ";
        boolean printedSomething = false;
        Printer printer = null;
        for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
            ArrayList<F> a = e.getValue();
            final int N = a.size();
        for (Map.Entry<String, F[]> e : map.entrySet()) {
            F[] a = e.getValue();
            final int N = a.length;
            boolean printedHeader = false;
            for (int i=0; i<N; i++) {
                F filter = a.get(i);
            F filter;
            for (int i=0; i<N && (filter=a[i]) != null; i++) {
                if (packageName != null && !packageName.equals(packageForFilter(filter))) {
                    continue;
                }
@@ -201,7 +208,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
    }

    public List<R> queryIntentFromList(Intent intent, String resolvedType, 
            boolean defaultOnly, ArrayList<ArrayList<F>> listCut, int userId) {
            boolean defaultOnly, ArrayList<F[]> listCut, int userId) {
        ArrayList<R> resultList = new ArrayList<R>();

        final boolean debug = localLOGV ||
@@ -231,10 +238,10 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
            TAG, "Resolving type " + resolvedType + " scheme " + scheme
            + " of intent " + intent);

        ArrayList<F> firstTypeCut = null;
        ArrayList<F> secondTypeCut = null;
        ArrayList<F> thirdTypeCut = null;
        ArrayList<F> schemeCut = null;
        F[] firstTypeCut = null;
        F[] secondTypeCut = null;
        F[] thirdTypeCut = null;
        F[] schemeCut = null;

        // If the intent includes a MIME type, then we want to collect all of
        // the filters that match that MIME type.
@@ -307,6 +314,14 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
        }
        sortResults(finalList);

        List<R> oldList = mOldResolver.queryIntent(intent, resolvedType, defaultOnly, userId);
        if (oldList.size() != finalList.size()) {
            ValidationFailure here = new ValidationFailure();
            here.fillInStackTrace();
            Log.wtf(TAG, "Query result " + intent + " size is " + finalList.size()
                    + "; old implementation is " + oldList.size(), here);
        }

        if (debug) {
            Slog.v(TAG, "Final result list:");
            for (R r : finalList) {
@@ -341,6 +356,8 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
     */
    protected abstract String packageForFilter(F filter);

    protected abstract F[] newArray(int size);

    @SuppressWarnings("unchecked")
    protected R newResult(F filter, int match, int userId) {
        return (R)filter;
@@ -355,6 +372,29 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
        out.print(prefix); out.println(filter);
    }

    private final void addFilter(HashMap<String, F[]> map, String name, F filter) {
        F[] array = map.get(name);
        if (array == null) {
            array = newArray(2);
            map.put(name,  array);
            array[0] = filter;
        } else {
            final int N = array.length;
            int i = N;
            while (i > 0 && array[i-1] == null) {
                i--;
            }
            if (i < N) {
                array[i] = filter;
            } else {
                F[] newa = newArray((N*3)/2);
                System.arraycopy(array, 0, newa, 0, N);
                newa[N] = filter;
                map.put(name, newa);
            }
        }
    }

    private final int register_mime_types(F filter, String prefix) {
        final Iterator<String> i = filter.typesIterator();
        if (i == null) {
@@ -374,30 +414,12 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
                name = name + "/*";
            }

            ArrayList<F> array = mTypeToFilter.get(name);
            if (array == null) {
                //Slog.v(TAG, "Creating new array for " + name);
                array = new ArrayList<F>();
                mTypeToFilter.put(name, array);
            }
            array.add(filter);
            addFilter(mTypeToFilter, name, filter);

            if (slashpos > 0) {
                array = mBaseTypeToFilter.get(baseName);
                if (array == null) {
                    //Slog.v(TAG, "Creating new array for " + name);
                    array = new ArrayList<F>();
                    mBaseTypeToFilter.put(baseName, array);
                }
                array.add(filter);
                addFilter(mBaseTypeToFilter, baseName, filter);
            } else {
                array = mWildTypeToFilter.get(baseName);
                if (array == null) {
                    //Slog.v(TAG, "Creating new array for " + name);
                    array = new ArrayList<F>();
                    mWildTypeToFilter.put(baseName, array);
                }
                array.add(filter);
                addFilter(mWildTypeToFilter, baseName, filter);
            }
        }

@@ -423,25 +445,19 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
                name = name + "/*";
            }

            if (!remove_all_objects(mTypeToFilter.get(name), filter)) {
                mTypeToFilter.remove(name);
            }
            remove_all_objects(mTypeToFilter, name, filter);

            if (slashpos > 0) {
                if (!remove_all_objects(mBaseTypeToFilter.get(baseName), filter)) {
                    mBaseTypeToFilter.remove(baseName);
                }
                remove_all_objects(mBaseTypeToFilter, baseName, filter);
            } else {
                if (!remove_all_objects(mWildTypeToFilter.get(baseName), filter)) {
                    mWildTypeToFilter.remove(baseName);
                }
                remove_all_objects(mWildTypeToFilter, baseName, filter);
            }
        }
        return num;
    }

    private final int register_intent_filter(F filter, Iterator<String> i,
            HashMap<String, ArrayList<F>> dest, String prefix) {
            HashMap<String, F[]> dest, String prefix) {
        if (i == null) {
            return 0;
        }
@@ -451,19 +467,13 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
            String name = i.next();
            num++;
            if (localLOGV) Slog.v(TAG, prefix + name);
            ArrayList<F> array = dest.get(name);
            if (array == null) {
                //Slog.v(TAG, "Creating new array for " + name);
                array = new ArrayList<F>();
                dest.put(name, array);
            }
            array.add(filter);
            addFilter(dest, name, filter);
        }
        return num;
    }

    private final int unregister_intent_filter(F filter, Iterator<String> i,
            HashMap<String, ArrayList<F>> dest, String prefix) {
            HashMap<String, F[]> dest, String prefix) {
        if (i == null) {
            return 0;
        }
@@ -473,26 +483,37 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
            String name = i.next();
            num++;
            if (localLOGV) Slog.v(TAG, prefix + name);
            if (!remove_all_objects(dest.get(name), filter)) {
                dest.remove(name);
            }
            remove_all_objects(dest, name, filter);
        }
        return num;
    }

    private final boolean remove_all_objects(List<F> list, Object object) {
        if (list != null) {
            int N = list.size();
            for (int idx=0; idx<N; idx++) {
                if (list.get(idx) == object) {
                    list.remove(idx);
                    idx--;
                    N--;
    private final void remove_all_objects(HashMap<String, F[]> map, String name,
            Object object) {
        F[] array = map.get(name);
        if (array != null) {
            int LAST = array.length-1;
            while (LAST >= 0 && array[LAST] == null) {
                LAST--;
            }
            for (int idx=LAST; idx>=0; idx--) {
                if (array[idx] == object) {
                    final int remain = LAST - idx;
                    if (remain > 0) {
                        System.arraycopy(array, idx+1, array, idx, remain);
                    }
                    array[LAST] = null;
                    LAST--;
                }
            }
            if (LAST < 0) {
                map.remove(name);
            } else if (LAST < (array.length/2)) {
                F[] newa = newArray(LAST+2);
                System.arraycopy(array, 0, newa, 0, LAST+1);
                map.put(name, newa);
            }
            return N > 0;
        }
        return false;
    }

    private static FastImmutableArraySet<String> getFastIntentCategories(Intent intent) {
@@ -505,18 +526,18 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {

    private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
            boolean debug, boolean defaultOnly,
            String resolvedType, String scheme, List<F> src, List<R> dest, int userId) {
            String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
        final String action = intent.getAction();
        final Uri data = intent.getData();
        final String packageName = intent.getPackage();

        final boolean excludingStopped = intent.isExcludingStopped();

        final int N = src != null ? src.size() : 0;
        final int N = src != null ? src.length : 0;
        boolean hasNonDefaults = false;
        int i;
        for (i=0; i<N; i++) {
            F filter = src.get(i);
        F filter;
        for (i=0; i<N && (filter=src[i]) != null; i++) {
            int match;
            if (debug) Slog.v(TAG, "Matching against filter " + filter);

@@ -585,6 +606,120 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
        }
    };

    static class ValidationFailure extends RuntimeException {
    }

    private void verifyDataStructures(IntentFilter src) {
        compareMaps(src, "mTypeToFilter", mTypeToFilter, mOldResolver.mTypeToFilter);
        compareMaps(src, "mBaseTypeToFilter", mBaseTypeToFilter, mOldResolver.mBaseTypeToFilter);
        compareMaps(src, "mWildTypeToFilter", mWildTypeToFilter, mOldResolver.mWildTypeToFilter);
        compareMaps(src, "mSchemeToFilter", mSchemeToFilter, mOldResolver.mSchemeToFilter);
        compareMaps(src, "mActionToFilter", mActionToFilter, mOldResolver.mActionToFilter);
        compareMaps(src, "mTypedActionToFilter", mTypedActionToFilter, mOldResolver.mTypedActionToFilter);
    }

    private void compareMaps(IntentFilter src, String name, HashMap<String, F[]> cur,
            HashMap<String, ArrayList<F>> old) {
        if (cur.size() != old.size()) {
            StringBuilder missing = new StringBuilder(128);
            for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) {
                final F[] curArray = cur.get(e.getKey());
                if (curArray == null) {
                    if (missing.length() > 0) {
                        missing.append(' ');
                    }
                    missing.append(e.getKey());
                }
            }
            StringBuilder extra = new StringBuilder(128);
            for (Map.Entry<String, F[]> e : cur.entrySet()) {
                if (old.get(e.getKey()) == null) {
                    if (extra.length() > 0) {
                        extra.append(' ');
                    }
                    extra.append(e.getKey());
                }
            }
            StringBuilder srcStr = new StringBuilder(1024);
            StringBuilderPrinter printer = new StringBuilderPrinter(srcStr);
            src.dump(printer, "");
            ValidationFailure here = new ValidationFailure();
            here.fillInStackTrace();
            Log.wtf(TAG, "New map " + name + " size is " + cur.size()
                    + "; old implementation is " + old.size()
                    + "; missing: " + missing.toString()
                    + "; extra: " + extra.toString()
                    + "; src: " + srcStr.toString(), here);
            return;
        }
        for (Map.Entry<String, ArrayList<F>> e : old.entrySet()) {
            final F[] curArray = cur.get(e.getKey());
            int curLen = curArray != null ? curArray.length : 0;
            if (curLen == 0) {
                ValidationFailure here = new ValidationFailure();
                here.fillInStackTrace();
                Log.wtf(TAG, "New map " + name + " doesn't contain expected key "
                        + e.getKey() + " (array=" + curArray + ")");
                return;
            }
            while (curLen > 0 && curArray[curLen-1] == null) {
                curLen--;
            }
            final ArrayList<F> oldArray = e.getValue();
            final int oldLen = oldArray.size();
            if (curLen != oldLen) {
                ValidationFailure here = new ValidationFailure();
                here.fillInStackTrace();
                Log.wtf(TAG, "New map " + name + " entry " + e.getKey() + " size is "
                        + curLen + "; old implementation is " + oldLen, here);
                return;
            }
            for (int i=0; i<oldLen; i++) {
                F f = oldArray.get(i);
                boolean found = false;
                for (int j=0; j<curLen; j++) {
                    if (curArray[j] == f) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    ValidationFailure here = new ValidationFailure();
                    here.fillInStackTrace();
                    Log.wtf(TAG, "New map " + name + " entry + " + e.getKey()
                            + " doesn't contain expected filter " + f, here);
                }
            }
            for (int i=0; i<curLen; i++) {
                if (curArray[i] == null) {
                    ValidationFailure here = new ValidationFailure();
                    here.fillInStackTrace();
                    Log.wtf(TAG, "New map " + name + " entry + " + e.getKey()
                            + " has unexpected null at " + i + "; array: " + curArray, here);
                    break;
                }
            }
        }
    }

    private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
        @Override protected String packageForFilter(F filter) {
            return IntentResolver.this.packageForFilter(filter);
        }
        @Override protected boolean allowFilterResult(F filter, List<R> dest) {
            return IntentResolver.this.allowFilterResult(filter, dest);
        }
        @Override protected boolean isFilterStopped(F filter, int userId) {
            return IntentResolver.this.isFilterStopped(filter, userId);
        }
        @Override protected R newResult(F filter, int match, int userId) {
            return IntentResolver.this.newResult(filter, match, userId);
        }
        @Override protected void sortResults(List<R> results) {
            IntentResolver.this.sortResults(results);
        }
    };

    /**
     * All filters that have been registered.
     */
@@ -594,16 +729,14 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
     * All of the MIME types that have been registered, such as "image/jpeg",
     * "image/*", or "{@literal *}/*".
     */
    private final HashMap<String, ArrayList<F>> mTypeToFilter
            = new HashMap<String, ArrayList<F>>();
    private final HashMap<String, F[]> mTypeToFilter = new HashMap<String, F[]>();

    /**
     * The base names of all of all fully qualified MIME types that have been
     * registered, such as "image" or "*".  Wild card MIME types such as
     * "image/*" will not be here.
     */
    private final HashMap<String, ArrayList<F>> mBaseTypeToFilter
            = new HashMap<String, ArrayList<F>>();
    private final HashMap<String, F[]> mBaseTypeToFilter = new HashMap<String, F[]>();

    /**
     * The base names of all of the MIME types with a sub-type wildcard that
@@ -612,26 +745,21 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
     * included here.  This also includes the "*" for the "{@literal *}/*"
     * MIME type.
     */
    private final HashMap<String, ArrayList<F>> mWildTypeToFilter
            = new HashMap<String, ArrayList<F>>();
    private final HashMap<String, F[]> mWildTypeToFilter = new HashMap<String, F[]>();

    /**
     * All of the URI schemes (such as http) that have been registered.
     */
    private final HashMap<String, ArrayList<F>> mSchemeToFilter
            = new HashMap<String, ArrayList<F>>();
    private final HashMap<String, F[]> mSchemeToFilter = new HashMap<String, F[]>();

    /**
     * All of the actions that have been registered, but only those that did
     * not specify data.
     */
    private final HashMap<String, ArrayList<F>> mActionToFilter
            = new HashMap<String, ArrayList<F>>();
    private final HashMap<String, F[]> mActionToFilter = new HashMap<String, F[]>();

    /**
     * All of the actions that have been registered and specified a MIME type.
     */
    private final HashMap<String, ArrayList<F>> mTypedActionToFilter
            = new HashMap<String, ArrayList<F>>();
    private final HashMap<String, F[]> mTypedActionToFilter = new HashMap<String, F[]>();
}
+638 −0

File added.

Preview size limit exceeded, changes collapsed.

+5 −0
Original line number Diff line number Diff line
@@ -512,6 +512,11 @@ public final class ActivityManagerService extends ActivityManagerNative
            return true;
        }
        @Override
        protected BroadcastFilter[] newArray(int size) {
            return new BroadcastFilter[size];
        }
        @Override
        protected String packageForFilter(BroadcastFilter filter) {
            return filter.packageName;
+23 −6
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -4737,14 +4738,17 @@ public class PackageManagerService extends IPackageManager.Stub {
            mFlags = flags;
            final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
            final int N = packageActivities.size();
            ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut =
                new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N);
            ArrayList<PackageParser.ActivityIntentInfo[]> listCut =
                new ArrayList<PackageParser.ActivityIntentInfo[]>(N);

            ArrayList<PackageParser.ActivityIntentInfo> intentFilters;
            for (int i = 0; i < N; ++i) {
                intentFilters = packageActivities.get(i).intents;
                if (intentFilters != null && intentFilters.size() > 0) {
                    listCut.add(intentFilters);
                    PackageParser.ActivityIntentInfo[] array =
                            new PackageParser.ActivityIntentInfo[intentFilters.size()];
                    intentFilters.toArray(array);
                    listCut.add(array);
                }
            }
            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
@@ -4811,6 +4815,11 @@ public class PackageManagerService extends IPackageManager.Stub {
            return true;
        }

        @Override
        protected ActivityIntentInfo[] newArray(int size) {
            return new ActivityIntentInfo[size];
        }

        @Override
        protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
            if (!sUserManager.exists(userId)) return true;
@@ -4925,14 +4934,17 @@ public class PackageManagerService extends IPackageManager.Stub {
            mFlags = flags;
            final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
            final int N = packageServices.size();
            ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut =
                new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N);
            ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
                new ArrayList<PackageParser.ServiceIntentInfo[]>(N);

            ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
            for (int i = 0; i < N; ++i) {
                intentFilters = packageServices.get(i).intents;
                if (intentFilters != null && intentFilters.size() > 0) {
                    listCut.add(intentFilters);
                    PackageParser.ServiceIntentInfo[] array =
                            new PackageParser.ServiceIntentInfo[intentFilters.size()];
                    intentFilters.toArray(array);
                    listCut.add(array);
                }
            }
            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
@@ -4994,6 +5006,11 @@ public class PackageManagerService extends IPackageManager.Stub {
            return true;
        }

        @Override
        protected PackageParser.ServiceIntentInfo[] newArray(int size) {
            return new PackageParser.ServiceIntentInfo[size];
        }

        @Override
        protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) {
            if (!sUserManager.exists(userId)) return true;
+4 −0
Original line number Diff line number Diff line
@@ -121,6 +121,10 @@ final class Settings {
    final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
                new IntentResolver<PreferredActivity, PreferredActivity>() {
        @Override
        protected PreferredActivity[] newArray(int size) {
            return new PreferredActivity[size];
        }
        @Override
        protected String packageForFilter(PreferredActivity filter) {
            return filter.mPref.mComponent.getPackageName();
        }