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

Commit 9591eace authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "PackageManager lock reduction utilities"

parents 0612ad79 e3c68fa8
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.utils;

import android.annotation.NonNull;

/**
 * A class that implements Snappable can generate a read-only copy its instances.  A
 * snapshot is like a clone except that it is only required to support read-only class
 * methods.  Snapshots are immutable.  Attempts to modify the state of a snapshot throw
 * {@link UnsupporteOperationException}.
 * @param <T> The type returned by the snapshot() method.
 */
public interface Snappable<T> {

    /**
     * Create an immutable copy of the object, suitable for read-only methods.  A snapshot
     * is free to omit state that is only needed for mutating methods.
     */
    @NonNull T snapshot();
}
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.utils;

import android.annotation.NonNull;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;

/**
 * A collection of useful methods for manipulating Snapshot classes.  This is similar to
 * java.util.Objects or java.util.Arrays.
 */
public class Snapshots {

    /**
     * Return the snapshot of an object, if the object extends {@link Snapper}, or the object
     * itself.
     * @param o The object to be copied
     * @return A snapshot of the object, if the object extends {@link Snapper}
     */
    public static <T> T maybeSnapshot(T o) {
        if (o instanceof Snappable) {
            return ((Snappable<T>) o).snapshot();
        } else {
            return o;
        }
    }

    /**
     * Copy a SparseArray in a manner suitable for a snapshot.  The destination must be
     * empty.  This is not a snapshot because the elements are copied by reference even if
     * they are {@link Snappable}.
     * @param dst The destination array.  It must be empty.
     * @param src The source array
     */
    public <E> void copy(@NonNull SparseArray<E> dst, @NonNull SparseArray<E> src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("copy destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            dst.put(src.keyAt(i), src.valueAt(i));
        }
    }

    /**
     * Copy a SparseSetArray in a manner suitable for a snapshot.  The destination must be
     * empty.  This is not a snapshot because the elements are copied by reference even if
     * they are {@link Snappable}.
     * @param dst The destination array.  It must be empty.
     * @param src The source array
     */
    public static <E> void copy(@NonNull SparseSetArray<E> dst, @NonNull SparseSetArray<E> src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("copy destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            final int size = src.sizeAt(i);
            for (int j = 0; j < size; j++) {
                dst.add(src.keyAt(i), src.valueAt(i, j));
            }
        }
    }

    /**
     * Make <dst> a snapshot of <src> .
     * @param dst The destination array.  It must be empty.
     * @param src The source array
     */
    public void snapshot(@NonNull SparseIntArray dst, @NonNull SparseIntArray src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("snapshot destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            dst.put(src.keyAt(i), src.valueAt(i));
        }
    }

    /**
     * Make <dst> a "snapshot" of <src>.  <dst> mst be empty.  The destination is just a
     * copy of the source except that if the source elements implement Snappable, then
     * the elements in the destination will be snapshots of elements from the source.
     * @param dst The destination array.  It must be empty.
     * @param src The source array
     */
    public static <E extends Snappable<E>> void snapshot(@NonNull SparseArray<E> dst,
            @NonNull SparseArray<E> src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("snapshot destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            dst.put(src.keyAt(i), src.valueAt(i).snapshot());
        }
    }

    /**
     * Make <dst> a "snapshot" of <src>.  <dst> mst be empty.  The destination is a
     * copy of the source except that snapshots are taken of the elements.
     * @param dst The destination array.  It must be empty.
     * @param src The source array
     */
    public static <E extends Snappable<E>> void snapshot(@NonNull SparseSetArray<E> dst,
            @NonNull SparseSetArray<E> src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("snapshot destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            final int size = src.sizeAt(i);
            for (int j = 0; j < size; j++) {
                dst.add(src.keyAt(i), src.valueAt(i, j).snapshot());
            }
        }
    }
}
+32 −1
Original line number Diff line number Diff line
@@ -19,11 +19,15 @@ package com.android.server.utils;
import android.annotation.NonNull;
import android.annotation.Nullable;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.Objects;

/**
 * A concrete implementation of {@link Watchable}
 * A concrete implementation of {@link Watchable}.  This includes one commonly needed feature:
 * the Watchable may be sealed, so that it throws an {@link IllegalStateException} if
 * a change is detected.
 */
public class WatchableImpl implements Watchable {
    /**
@@ -78,10 +82,37 @@ public class WatchableImpl implements Watchable {
    @Override
    public void dispatchChange(@Nullable Watchable what) {
        synchronized (mObservers) {
            if (mSealed) {
                throw new IllegalStateException("attempt to change a sealed object");
            }
            final int end = mObservers.size();
            for (int i = 0; i < end; i++) {
                mObservers.get(i).onChange(what);
            }
        }
    }

    /**
     * True if the object is sealed.
     */
    @GuardedBy("mObservers")
    private boolean mSealed = false;

    /**
     * Freeze the {@link Watchable}.
     **/
    public void seal() {
        synchronized (mObservers) {
            mSealed = true;
        }
    }

    /**
     * Return the sealed state.
     */
    public boolean isFrozen() {
        synchronized (mObservers) {
            return mSealed;
        }
    }
}
+39 −4
Original line number Diff line number Diff line
@@ -18,9 +18,7 @@ package com.android.server.utils;

import android.annotation.NonNull;
import android.annotation.Nullable;

import android.util.ArrayMap;
import android.util.Log;

import java.util.Collection;
import java.util.Collections;
@@ -31,14 +29,17 @@ import java.util.Set;
 * WatchedArrayMap is an {@link android.util.ArrayMap} that can report changes to itself.  If its
 * values are {@link Watchable} then the WatchedArrayMap will also report changes to the values.
 * A {@link Watchable} is notified only once, no matter how many times it is stored in the array.
 * @param <K> The key type.
 * @param <V> The value type.
 */
public class WatchedArrayMap<K, V> extends WatchableImpl implements Map<K, V> {
public class WatchedArrayMap<K, V> extends WatchableImpl
        implements Map<K, V>, Snappable {

    // The storage
    private final ArrayMap<K, V> mStorage;

    // If true, the array is watching its children
    private boolean mWatching = false;
    private volatile boolean mWatching = false;

    // The local observer
    private final Watcher mObserver = new Watcher() {
@@ -386,4 +387,38 @@ public class WatchedArrayMap<K, V> extends WatchableImpl implements Map<K, V> {
        onChanged();
        return result;
    }

    /**
     * Create a copy of the array.  If the element is a subclass of Snapper then the copy
     * contains snapshots of the elements.  Otherwise the copy contains references to the
     * elements.  The returned snapshot is immutable.
     * @return A new array whose elements are the elements of <this>.
     */
    public WatchedArrayMap<K, V> snapshot() {
        WatchedArrayMap<K, V> l = new WatchedArrayMap<>();
        snapshot(l, this);
        return l;
    }

    /**
     * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
     * copy contains snapshots of the elements.  Otherwise the copy contains references to the
     * elements.  The destination must be initially empty.  Upon return, the destination is
     * immutable.
     * @param dst The destination array.  It must be empty.
     * @param src The source array.  It is not modified.
     */
    public static <K, V> void snapshot(@NonNull WatchedArrayMap<K, V> dst,
            @NonNull WatchedArrayMap<K, V> src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("snapshot destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            final V val = Snapshots.maybeSnapshot(src.valueAt(i));
            final K key = src.keyAt(i);
            dst.put(key, val);
        }
        dst.seal();
    }
}
+39 −3
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.utils;

import android.annotation.NonNull;
import android.annotation.Nullable;

import android.util.SparseArray;

import java.util.ArrayList;
@@ -28,14 +27,16 @@ import java.util.ArrayList;
 * array registers with the {@link Watchable}.  The array registers only once with each
 * {@link Watchable} no matter how many times the {@link Watchable} is stored in the
 * array.
 * @param <E> The element type, stored in the array.
 */
public class WatchedSparseArray<E> extends WatchableImpl {
public class WatchedSparseArray<E> extends WatchableImpl
        implements Snappable {

    // The storage
    private final SparseArray<E> mStorage;

    // If true, the array is watching its children
    private boolean mWatching = false;
    private volatile boolean mWatching = false;

    // The local observer
    private final Watcher mObserver = new Watcher() {
@@ -398,4 +399,39 @@ public class WatchedSparseArray<E> extends WatchableImpl {
    public String toString() {
        return mStorage.toString();
    }

    /**
     * Create a copy of the array.  If the element is a subclass of Snapper then the copy
     * contains snapshots of the elements.  Otherwise the copy contains references to the
     * elements.  The returned snapshot is immutable.
     * @return A new array whose elements are the elements of <this>.
     */
    public WatchedSparseArray<E> snapshot() {
        WatchedSparseArray<E> l = new WatchedSparseArray<>();
        snapshot(l, this);
        return l;
    }

    /**
     * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
     * copy contains snapshots of the elements.  Otherwise the copy contains references to the
     * elements.  The destination must be initially empty.  Upon return, the destination is
     * immutable.
     * @param dst The destination array.  It must be empty.
     * @param src The source array.  It is not modified.
     */
    public static <E> void snapshot(@NonNull WatchedSparseArray<E> dst,
            @NonNull WatchedSparseArray<E> src) {
        if (dst.size() != 0) {
            throw new IllegalArgumentException("snapshot destination is not empty");
        }
        final int end = src.size();
        for (int i = 0; i < end; i++) {
            final E val = Snapshots.maybeSnapshot(src.valueAt(i));
            final int key = src.keyAt(i);
            dst.put(key, val);
        }
        dst.seal();
    }

}
Loading