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

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

Merge "[Companion] Dont store duplicate association records"

parents eb5a5714 612311ef
Loading
Loading
Loading
Loading
+105 −1
Original line number Diff line number Diff line
@@ -21,13 +21,16 @@ import static com.android.internal.util.ArrayUtils.isEmpty;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArraySet;
import android.util.ExceptionUtils;

import com.android.internal.util.FunctionalUtils.ThrowingConsumer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.*;
import java.util.stream.Stream;

/**
@@ -57,6 +60,32 @@ public class CollectionUtils {
        return emptyIfNull(result);
    }

    /**
     * @see #filter(List, java.util.function.Predicate)
     */
    public static @NonNull <T> Set<T> filter(@Nullable Set<T> set,
            java.util.function.Predicate<? super T> predicate) {
        if (set == null || set.size() == 0) return Collections.emptySet();
        ArraySet<T> result = null;
        if (set instanceof ArraySet) {
            ArraySet<T> arraySet = (ArraySet<T>) set;
            int size = arraySet.size();
            for (int i = 0; i < size; i++) {
                final T item = arraySet.valueAt(i);
                if (predicate.test(item)) {
                    result = ArrayUtils.add(result, item);
                }
            }
        } else {
            for (T item : set) {
                if (predicate.test(item)) {
                    result = ArrayUtils.add(result, item);
                }
            }
        }
        return emptyIfNull(result);
    }

    /**
     * Returns a list of items resulting from applying the given function to each element of the
     * provided list.
@@ -76,6 +105,27 @@ public class CollectionUtils {
        return result;
    }

    /**
     * @see #map(List, Function)
     */
    public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
            Function<? super I, ? extends O> f) {
        if (isEmpty(cur)) return Collections.emptySet();
        ArraySet<O> result = new ArraySet<>();
        if (cur instanceof ArraySet) {
            ArraySet<I> arraySet = (ArraySet<I>) cur;
            int size = arraySet.size();
            for (int i = 0; i < size; i++) {
                result.add(f.apply(arraySet.valueAt(i)));
            }
        } else {
            for (I item : cur) {
                result.add(f.apply(item));
            }
        }
        return result;
    }

    /**
     * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
     *
@@ -179,6 +229,17 @@ public class CollectionUtils {
        return cur;
    }

    /**
     * @see #add(List, Object)
     */
    public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
        if (cur == null || cur == Collections.emptySet()) {
            cur = new ArraySet<>();
        }
        cur.add(val);
        return cur;
    }

    /**
     * Similar to {@link List#remove}, but with support for list values of {@code null} and
     * {@link Collections#emptyList}
@@ -191,10 +252,53 @@ public class CollectionUtils {
        return cur;
    }

    /**
     * @see #remove(List, Object)
     */
    public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) {
        if (isEmpty(cur)) {
            return emptyIfNull(cur);
        }
        cur.remove(val);
        return cur;
    }

    /**
     * @return a list that will not be affected by mutations to the given original list.
     */
    public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
        return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
    }

    /**
     * @return a list that will not be affected by mutations to the given original list.
     */
    public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
        return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur);
    }

    /**
     * Applies {@code action} to each element in {@code cur}
     *
     * This avoids creating an iterator if the given set is an {@link ArraySet}
     */
    public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) {
        if (cur == null || action == null) return;
        int size = cur.size();
        if (size == 0) return;
        try {
            if (cur instanceof ArraySet) {
                ArraySet<T> arraySet = (ArraySet<T>) cur;
                for (int i = 0; i < size; i++) {
                    action.accept(arraySet.valueAt(i));
                }
            } else {
                for (T t : cur) {
                    action.accept(t);
                }
            }
        } catch (Exception e) {
            throw ExceptionUtils.propagate(e);
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -45,4 +45,15 @@ public class FunctionalUtils {
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    /**
     * An equivalent of {@link java.util.function.Consumer} that allows throwing checked exceptions
     *
     * This can be used to specify a lambda argument without forcing all the checked exceptions
     * to be handled within it
     */
    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }
}
+21 −22
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.text.BidiFormatter;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
import android.util.Log;
@@ -83,6 +84,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@@ -247,9 +249,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                throws RemoteException {
            checkCallerIsSystemOr(callingPackage, userId);
            checkUsesFeature(callingPackage, getCallingUserId());
            return CollectionUtils.map(
            return new ArrayList<>(CollectionUtils.map(
                    readAllAssociations(userId, callingPackage),
                    a -> a.deviceAddress);
                    a -> a.deviceAddress));
        }

        //TODO also revoke notification access
@@ -495,20 +497,20 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                new Association(userId, deviceAddress, priviledgedPackage)));
    }

    private void updateAssociations(Function<List<Association>, List<Association>> update) {
    private void updateAssociations(Function<Set<Association>, Set<Association>> update) {
        updateAssociations(update, getCallingUserId());
    }

    private void updateAssociations(Function<List<Association>, List<Association>> update,
    private void updateAssociations(Function<Set<Association>, Set<Association>> update,
            int userId) {
        final AtomicFile file = getStorageFileForUser(userId);
        synchronized (file) {
            List<Association> associations = readAllAssociations(userId);
            final List<Association> old = CollectionUtils.copyOf(associations);
            Set<Association> associations = readAllAssociations(userId);
            final Set<Association> old = CollectionUtils.copyOf(associations);
            associations = update.apply(associations);
            if (size(old) == size(associations)) return;

            List<Association> finalAssociations = associations;
            Set<Association> finalAssociations = associations;
            file.write((out) -> {
                XmlSerializer xml = Xml.newSerializer();
                try {
@@ -517,13 +519,12 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
                    xml.startDocument(null, true);
                    xml.startTag(null, XML_TAG_ASSOCIATIONS);

                    for (int i = 0; i < size(finalAssociations); i++) {
                        Association association = finalAssociations.get(i);
                    CollectionUtils.forEach(finalAssociations, association -> {
                        xml.startTag(null, XML_TAG_ASSOCIATION)
                                .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
                                .attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
                                .endTag(null, XML_TAG_ASSOCIATION);
                    }
                    });

                    xml.endTag(null, XML_TAG_ASSOCIATIONS);
                    xml.endDocument();
@@ -545,17 +546,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
    }

    @Nullable
    private ArrayList<Association> readAllAssociations(int userId) {
    private Set<Association> readAllAssociations(int userId) {
        return readAllAssociations(userId, null);
    }

    @Nullable
    private ArrayList<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
    private Set<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
        final AtomicFile file = getStorageFileForUser(userId);

        if (!file.getBaseFile().exists()) return null;

        ArrayList<Association> result = null;
        ArraySet<Association> result = null;
        final XmlPullParser parser = Xml.newPullParser();
        synchronized (file) {
            try (FileInputStream in = file.openRead()) {
@@ -627,12 +628,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
        public int onCommand(String cmd) {
            switch (cmd) {
                case "list": {
                    ArrayList<Association> associations = readAllAssociations(getNextArgInt());
                    for (int i = 0; i < size(associations); i++) {
                        Association a = associations.get(i);
                        getOutPrintWriter()
                                .println(a.companionAppPackage + " " + a.deviceAddress);
                    }
                    CollectionUtils.forEach(
                            readAllAssociations(getNextArgInt()),
                            a -> getOutPrintWriter()
                                    .println(a.companionAppPackage + " " + a.deviceAddress));
                } break;

                case "associate": {