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

Commit abaa0695 authored by Robin Lee's avatar Robin Lee
Browse files

Delete ParcelableString, add StringParceledListSlice

Both inherit from package private BaseParceledListSlice.

This is still bad, but it's not as bad. The existing code that uses
this can just do Foo.bar().getList() now instead of having to marshal
to and from an oddball type at either end as well.

In the longer term ParceledListSlice<> should be eliminated, but it's
not clear how far into the future that is going to happen.

Test: runtest -x services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
Test: runtest -x core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
Change-Id: Ie69b96b5215d6e04990f6d31345772cdfee21d78
parent c98c16de
Loading
Loading
Loading
Loading
+201 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2011 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.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Transfer a large list of Parcelable objects across an IPC.  Splits into
 * multiple transactions if needed.
 *
 * Caveat: for efficiency and security, all elements must be the same concrete type.
 * In order to avoid writing the class name of each object, we must ensure that
 * each object is the same type, or else unparceling then reparceling the data may yield
 * a different result if the class name encoded in the Parcelable is a Base type.
 * See b/17671747.
 *
 * @hide
 */
abstract class BaseParceledListSlice<T> implements Parcelable {
    private static String TAG = "ParceledListSlice";
    private static boolean DEBUG = false;

    /*
     * TODO get this number from somewhere else. For now set it to a quarter of
     * the 1MB limit.
     */
    private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE;

    private final List<T> mList;

    public BaseParceledListSlice(List<T> list) {
        mList = list;
    }

    @SuppressWarnings("unchecked")
    BaseParceledListSlice(Parcel p, ClassLoader loader) {
        final int N = p.readInt();
        mList = new ArrayList<T>(N);
        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
        if (N <= 0) {
            return;
        }

        Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
        Class<?> listElementClass = null;

        int i = 0;
        while (i < N) {
            if (p.readInt() == 0) {
                break;
            }

            final T parcelable = readCreator(creator, p, loader);
            if (listElementClass == null) {
                listElementClass = parcelable.getClass();
            } else {
                verifySameType(listElementClass, parcelable.getClass());
            }

            mList.add(parcelable);

            if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
            i++;
        }
        if (i >= N) {
            return;
        }
        final IBinder retriever = p.readStrongBinder();
        while (i < N) {
            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInt(i);
            try {
                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
            } catch (RemoteException e) {
                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
                return;
            }
            while (i < N && reply.readInt() != 0) {
                final T parcelable = reply.readCreator(creator, loader);
                verifySameType(listElementClass, parcelable.getClass());

                mList.add(parcelable);

                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
                i++;
            }
            reply.recycle();
            data.recycle();
        }
    }

    private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
            Parcelable.ClassLoaderCreator<?> classLoaderCreator =
                    (Parcelable.ClassLoaderCreator<?>) creator;
            return (T) classLoaderCreator.createFromParcel(p, loader);
        }
        return (T) creator.createFromParcel(p);
    }

    private static void verifySameType(final Class<?> expected, final Class<?> actual) {
        if (!actual.equals(expected)) {
            throw new IllegalArgumentException("Can't unparcel type "
                    + actual.getName() + " in list of type "
                    + expected.getName());
        }
    }

    public List<T> getList() {
        return mList;
    }

    /**
     * Write this to another Parcel. Note that this discards the internal Parcel
     * and should not be used anymore. This is so we can pass this to a Binder
     * where we won't have a chance to call recycle on this.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        final int N = mList.size();
        final int callFlags = flags;
        dest.writeInt(N);
        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
        if (N > 0) {
            final Class<?> listElementClass = mList.get(0).getClass();
            writeParcelableCreator(mList.get(0), dest);
            int i = 0;
            while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
                dest.writeInt(1);

                final T parcelable = mList.get(i);
                verifySameType(listElementClass, parcelable.getClass());
                writeElement(parcelable, dest, callFlags);

                if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
                i++;
            }
            if (i < N) {
                dest.writeInt(0);
                Binder retriever = new Binder() {
                    @Override
                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                            throws RemoteException {
                        if (code != FIRST_CALL_TRANSACTION) {
                            return super.onTransact(code, data, reply, flags);
                        }
                        int i = data.readInt();
                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
                            reply.writeInt(1);

                            final T parcelable = mList.get(i);
                            verifySameType(listElementClass, parcelable.getClass());
                            writeElement(parcelable, reply, callFlags);

                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
                            i++;
                        }
                        if (i < N) {
                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
                            reply.writeInt(0);
                        }
                        return true;
                    }
                };
                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
                dest.writeStrongBinder(retriever);
            }
        }
    }

    protected abstract void writeElement(T parcelable, Parcel reply, int callFlags);

    protected abstract void writeParcelableCreator(T parcelable, Parcel dest);

    protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
}
+21 −150
Original line number Original line Diff line number Diff line
@@ -16,14 +16,9 @@


package android.content.pm;
package android.content.pm;


import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.List;


@@ -31,171 +26,46 @@ import java.util.List;
 * Transfer a large list of Parcelable objects across an IPC.  Splits into
 * Transfer a large list of Parcelable objects across an IPC.  Splits into
 * multiple transactions if needed.
 * multiple transactions if needed.
 *
 *
 * Caveat: for efficiency and security, all elements must be the same concrete type.
 * @see BaseParceledListSlice
 * In order to avoid writing the class name of each object, we must ensure that
 * each object is the same type, or else unparceling then reparceling the data may yield
 * a different result if the class name encoded in the Parcelable is a Base type.
 * See b/17671747.
 *
 *
 * @hide
 * @hide
 */
 */
public class ParceledListSlice<T extends Parcelable> implements Parcelable {
public class ParceledListSlice<T extends Parcelable> extends BaseParceledListSlice<T> {
    private static String TAG = "ParceledListSlice";
    private static boolean DEBUG = false;

    /*
     * TODO get this number from somewhere else. For now set it to a quarter of
     * the 1MB limit.
     */
    private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE;

    private final List<T> mList;

    public static <T extends Parcelable> ParceledListSlice<T> emptyList() {
        return new ParceledListSlice<T>(Collections.<T> emptyList());
    }

    public ParceledListSlice(List<T> list) {
    public ParceledListSlice(List<T> list) {
        mList = list;
        super(list);
    }

    @SuppressWarnings("unchecked")
    private ParceledListSlice(Parcel p, ClassLoader loader) {
        final int N = p.readInt();
        mList = new ArrayList<T>(N);
        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
        if (N <= 0) {
            return;
        }

        Parcelable.Creator<?> creator = p.readParcelableCreator(loader);
        Class<?> listElementClass = null;

        int i = 0;
        while (i < N) {
            if (p.readInt() == 0) {
                break;
            }

            final T parcelable = p.readCreator(creator, loader);
            if (listElementClass == null) {
                listElementClass = parcelable.getClass();
            } else {
                verifySameType(listElementClass, parcelable.getClass());
            }

            mList.add(parcelable);

            if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
            i++;
        }
        if (i >= N) {
            return;
        }
        final IBinder retriever = p.readStrongBinder();
        while (i < N) {
            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInt(i);
            try {
                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
            } catch (RemoteException e) {
                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
                return;
    }
    }
            while (i < N && reply.readInt() != 0) {
                final T parcelable = reply.readCreator(creator, loader);
                verifySameType(listElementClass, parcelable.getClass());


                mList.add(parcelable);
    private ParceledListSlice(Parcel in, ClassLoader loader) {

        super(in, loader);
                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
                i++;
            }
            reply.recycle();
            data.recycle();
        }
    }

    private static void verifySameType(final Class<?> expected, final Class<?> actual) {
        if (!actual.equals(expected)) {
            throw new IllegalArgumentException("Can't unparcel type "
                    + actual.getName() + " in list of type "
                    + expected.getName());
        }
    }
    }


    public List<T> getList() {
    public static <T extends Parcelable> ParceledListSlice<T> emptyList() {
        return mList;
        return new ParceledListSlice<T>(Collections.<T> emptyList());
    }
    }


    @Override
    @Override
    public int describeContents() {
    public int describeContents() {
        int contents = 0;
        int contents = 0;
        for (int i=0; i<mList.size(); i++) {
        final List<T> list = getList();
            contents |= mList.get(i).describeContents();
        for (int i=0; i<list.size(); i++) {
            contents |= list.get(i).describeContents();
        }
        }
        return contents;
        return contents;
    }
    }


    /**
     * Write this to another Parcel. Note that this discards the internal Parcel
     * and should not be used anymore. This is so we can pass this to a Binder
     * where we won't have a chance to call recycle on this.
     */
    @Override
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    protected void writeElement(T parcelable, Parcel dest, int callFlags) {
        final int N = mList.size();
        final int callFlags = flags;
        dest.writeInt(N);
        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
        if (N > 0) {
            final Class<?> listElementClass = mList.get(0).getClass();
            dest.writeParcelableCreator(mList.get(0));
            int i = 0;
            while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
                dest.writeInt(1);

                final T parcelable = mList.get(i);
                verifySameType(listElementClass, parcelable.getClass());
        parcelable.writeToParcel(dest, callFlags);
        parcelable.writeToParcel(dest, callFlags);

                if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
                i++;
    }
    }
            if (i < N) {

                dest.writeInt(0);
                Binder retriever = new Binder() {
    @Override
    @Override
                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    protected void writeParcelableCreator(T parcelable, Parcel dest) {
                            throws RemoteException {
        dest.writeParcelableCreator((Parcelable) parcelable);
                        if (code != FIRST_CALL_TRANSACTION) {
                            return super.onTransact(code, data, reply, flags);
    }
    }
                        int i = data.readInt();
                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
                            reply.writeInt(1);

                            final T parcelable = mList.get(i);
                            verifySameType(listElementClass, parcelable.getClass());
                            parcelable.writeToParcel(reply, callFlags);


                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
    @Override
                            i++;
    protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
                        }
        return from.readParcelableCreator(loader);
                        if (i < N) {
                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
                            reply.writeInt(0);
                        }
                        return true;
                    }
                };
                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
                dest.writeStrongBinder(retriever);
            }
        }
    }
    }


    @SuppressWarnings("unchecked")
    @SuppressWarnings("unchecked")
@@ -210,6 +80,7 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
            return new ParceledListSlice(in, loader);
            return new ParceledListSlice(in, loader);
        }
        }


        @Override
        public ParceledListSlice[] newArray(int size) {
        public ParceledListSlice[] newArray(int size) {
            return new ParceledListSlice[size];
            return new ParceledListSlice[size];
        }
        }
+19 −0
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright 2014, The Android Open Source Project
 * Copyright 2017, The Android Open Source Project
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may not use this file except in compliance with the License.
@@ -14,39 +14,6 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


package com.android.internal.util;
package android.content.pm;


import android.os.Parcel;
parcelable StringParceledListSlice;
import android.os.Parcelable;

/**
 * Helper class to adapt a simple String to cases where a Parcelable is expected.
 * @hide
 */
public class ParcelableString implements Parcelable {
    public String string;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeString(string);
    }

    public static final Parcelable.Creator<ParcelableString> CREATOR =
            new Parcelable.Creator<ParcelableString>() {
                @Override
                public ParcelableString createFromParcel(Parcel in) {
                    ParcelableString ret = new ParcelableString();
                    ret.string = in.readString();
                    return ret;
                }
                @Override
                public ParcelableString[] newArray(int size) {
                    return new ParcelableString[size];
                }
    };
}
 No newline at end of file
+83 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.os.Parcel;
import android.os.Parcelable;

import java.util.Collections;
import java.util.List;

/**
 * Transfer a large list of Parcelable objects across an IPC.  Splits into
 * multiple transactions if needed.
 *
 * @see BaseParceledListSlice
 *
 * @hide
 */
public class StringParceledListSlice extends BaseParceledListSlice<String> {
    public StringParceledListSlice(List<String> list) {
        super(list);
    }

    private StringParceledListSlice(Parcel in, ClassLoader loader) {
        super(in, loader);
    }

    public static StringParceledListSlice emptyList() {
        return new StringParceledListSlice(Collections.<String> emptyList());
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    protected void writeElement(String parcelable, Parcel reply, int callFlags) {
        reply.writeString(parcelable);
    }

    @Override
    protected void writeParcelableCreator(String parcelable, Parcel dest) {
        return;
    }

    @Override
    protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
        return Parcel.STRING_CREATOR;
    }

    @SuppressWarnings("unchecked")
    public static final Parcelable.ClassLoaderCreator<StringParceledListSlice> CREATOR =
            new Parcelable.ClassLoaderCreator<StringParceledListSlice>() {
        public StringParceledListSlice createFromParcel(Parcel in) {
            return new StringParceledListSlice(in, null);
        }

        @Override
        public StringParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
            return new StringParceledListSlice(in, loader);
        }

        @Override
        public StringParceledListSlice[] newArray(int size) {
            return new StringParceledListSlice[size];
        }
    };
}
+22 −0
Original line number Original line Diff line number Diff line
@@ -91,6 +91,28 @@ public class ParceledListSliceTest extends TestCase {
        }
        }
    }
    }


    public void testStringList() throws Exception {
        final int objectCount = 400;
        List<String> list = new ArrayList<String>();
        for (long i = 0; i < objectCount; i++) {
            list.add(Long.toString(i * (6 - i)));
        }

        StringParceledListSlice slice;
        Parcel parcel = Parcel.obtain();
        try {
            parcel.writeParcelable(new StringParceledListSlice(list), 0);
            parcel.setDataPosition(0);
            slice = parcel.readParcelable(getClass().getClassLoader());
        } finally {
            parcel.recycle();
        }

        assertNotNull(slice);
        assertNotNull(slice.getList());
        assertEquals(list, slice.getList());
    }

    /**
    /**
     * Test that only homogeneous elements may be unparceled.
     * Test that only homogeneous elements may be unparceled.
     */
     */
Loading