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

Commit ebefe4f8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "System server should pass through the GD as is" into main

parents 2fe03850 d6c951b4
Loading
Loading
Loading
Loading
+76 −19
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package android.app.appfunctions;

import android.annotation.Nullable;
import android.app.appsearch.GenericDocument;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.MathUtils;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;

import java.util.Objects;
@@ -31,24 +34,33 @@ import java.util.Objects;
 * <p>{#link {@link Parcel#writeBlob(byte[])}} could take care of whether to pass data via binder
 * directly or Android shared memory if the data is large.
 *
 * <p>This class performs lazy unparcelling. The `GenericDocument` is only unparcelled
 * from the underlying `Parcel` when {@link #getValue()} is called. This optimization
 * allows the system server to pass through the generic document, without unparcel and parcel it.
 *
 * @hide
 * @see Parcel#writeBlob(byte[])
 */
public final class GenericDocumentWrapper implements Parcelable {
    @Nullable
    @GuardedBy("mLock")
    private GenericDocument mGenericDocument;
    @GuardedBy("mLock")
    @Nullable private Parcel mParcel;
    private final Object mLock = new Object();

    public static final Creator<GenericDocumentWrapper> CREATOR =
            new Creator<>() {
                @Override
                public GenericDocumentWrapper createFromParcel(Parcel in) {
                    byte[] dataBlob = Objects.requireNonNull(in.readBlob());
                    Parcel unmarshallParcel = Parcel.obtain();
                    try {
                        unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length);
                        unmarshallParcel.setDataPosition(0);
                        return new GenericDocumentWrapper(
                                GenericDocument.createFromParcel(unmarshallParcel));
                    } finally {
                        unmarshallParcel.recycle();
                    }
                    int length = in.readInt();
                    int offset = in.dataPosition();
                    in.setDataPosition(MathUtils.addOrThrow(offset, length));

                    Parcel p = Parcel.obtain();
                    p.appendFrom(in, offset, length);
                    p.setDataPosition(0);
                    return new GenericDocumentWrapper(p);
                }

                @Override
@@ -56,16 +68,42 @@ public final class GenericDocumentWrapper implements Parcelable {
                    return new GenericDocumentWrapper[size];
                }
            };
    @NonNull private final GenericDocument mGenericDocument;

    public GenericDocumentWrapper(@NonNull GenericDocument genericDocument) {
        mGenericDocument = Objects.requireNonNull(genericDocument);
        mParcel = null;
    }

    public GenericDocumentWrapper(@NonNull Parcel parcel) {
        mGenericDocument = null;
        mParcel = Objects.requireNonNull(parcel);
    }

    /** Returns the wrapped {@link android.app.appsearch.GenericDocument} */
    @NonNull
    public GenericDocument getValue() {
        return mGenericDocument;
        unparcel();
        synchronized (mLock) {
            return Objects.requireNonNull(mGenericDocument);
        }
    }

    private void unparcel() {
        synchronized (mLock) {
            if (mGenericDocument != null) {
                return;
            }
            byte[] dataBlob = Objects.requireNonNull(Objects.requireNonNull(mParcel).readBlob());
            Parcel unmarshallParcel = Parcel.obtain();
            try {
                unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length);
                unmarshallParcel.setDataPosition(0);
                mGenericDocument = GenericDocument.createFromParcel(unmarshallParcel);
                mParcel = null;
            } finally {
                unmarshallParcel.recycle();
            }
        }
    }

    @Override
@@ -75,13 +113,32 @@ public final class GenericDocumentWrapper implements Parcelable {

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        Parcel parcel = Parcel.obtain();
        synchronized (mLock) {
            if (mGenericDocument != null) {
                int lengthPos = dest.dataPosition();
                // write a placeholder for length
                dest.writeInt(-1);
                Parcel tempParcel = Parcel.obtain();
                byte[] bytes;
                try {
            mGenericDocument.writeToParcel(parcel, flags);
            byte[] bytes = parcel.marshall();
            dest.writeBlob(bytes);
                    mGenericDocument.writeToParcel(tempParcel, flags);
                    bytes = tempParcel.marshall();
                } finally {
            parcel.recycle();
                    tempParcel.recycle();
                }
                int startPos = dest.dataPosition();
                dest.writeBlob(bytes);
                int endPos = dest.dataPosition();
                dest.setDataPosition(lengthPos);
                // Overwrite the length placeholder
                dest.writeInt(endPos - startPos);
                dest.setDataPosition(endPos);

            } else {
                Parcel originalParcel = Objects.requireNonNull(mParcel);
                dest.writeInt(originalParcel.dataSize());
                dest.appendFrom(originalParcel, 0, originalParcel.dataSize());
            }
        }
    }
}
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.app.appfunctions

import android.app.appsearch.GenericDocument
import android.os.Parcel
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4


@RunWith(JUnit4::class)
class GenericDocumentWrapperTest {

    @Test
    fun parcelUnparcel() {
        val doc =
            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                .setPropertyLong("test", 42)
                .build()
        val wrapper = GenericDocumentWrapper(doc)

        val recovered = parcelUnparcel(wrapper)

        assertThat(recovered.value.getPropertyLong("test")).isEqualTo(42)
    }

    @Test
    fun parcelUnparcel_afterGetValue() {
        val doc =
            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                .setPropertyLong("test", 42)
                .build()
        val wrapper = GenericDocumentWrapper(doc)
        assertThat(wrapper.value.getPropertyLong("test")).isEqualTo(42)

        val recovered = parcelUnparcel(wrapper)

        assertThat(recovered.value.getPropertyLong("test")).isEqualTo(42)
    }


    @Test
    fun getValue() {
        val doc =
            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                .setPropertyLong("test", 42)
                .build()
        val wrapper = GenericDocumentWrapper(doc)

        assertThat(wrapper.value.getPropertyLong("test")).isEqualTo(42)
    }

    private fun parcelUnparcel(obj: GenericDocumentWrapper): GenericDocumentWrapper {
        val parcel = Parcel.obtain()
        try {
            obj.writeToParcel(parcel, 0)
            parcel.setDataPosition(0)
            return GenericDocumentWrapper.CREATOR.createFromParcel(parcel)
        } finally {
            parcel.recycle()
        }
    }
}
 No newline at end of file