Loading services/core/java/com/android/server/utils/Snappable.java 0 → 100644 +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(); } services/core/java/com/android/server/utils/Snapshots.java 0 → 100644 +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()); } } } } services/core/java/com/android/server/utils/WatchableImpl.java +32 −1 Original line number Diff line number Diff line Loading @@ -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 { /** Loading Loading @@ -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; } } } services/core/java/com/android/server/utils/WatchedArrayMap.java +39 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { Loading Loading @@ -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(); } } services/core/java/com/android/server/utils/WatchedSparseArray.java +39 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { Loading Loading @@ -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
services/core/java/com/android/server/utils/Snappable.java 0 → 100644 +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(); }
services/core/java/com/android/server/utils/Snapshots.java 0 → 100644 +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()); } } } }
services/core/java/com/android/server/utils/WatchableImpl.java +32 −1 Original line number Diff line number Diff line Loading @@ -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 { /** Loading Loading @@ -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; } } }
services/core/java/com/android/server/utils/WatchedArrayMap.java +39 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { Loading Loading @@ -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(); } }
services/core/java/com/android/server/utils/WatchedSparseArray.java +39 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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() { Loading Loading @@ -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(); } }