1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.net.module.util;
17 
18 import android.system.ErrnoException;
19 
20 import androidx.annotation.NonNull;
21 import androidx.annotation.Nullable;
22 
23 import java.io.IOException;
24 import java.util.NoSuchElementException;
25 
26 /**
27  * The interface of BpfMap. This could be used to inject for testing.
28  * So the testing code won't load the JNI and update the entries to kernel.
29  *
30  * @param <K> the key of the map.
31  * @param <V> the value of the map.
32  */
33 public interface IBpfMap<K extends Struct, V extends Struct> extends AutoCloseable {
34     /** Update an existing or create a new key -> value entry in an eBbpf map. */
updateEntry(K key, V value)35     void updateEntry(K key, V value) throws ErrnoException;
36 
37     /** If the key does not exist in the map, insert key -> value entry into eBpf map. */
insertEntry(K key, V value)38     void insertEntry(K key, V value) throws ErrnoException, IllegalStateException;
39 
40     /** If the key already exists in the map, replace its value. */
replaceEntry(K key, V value)41     void replaceEntry(K key, V value) throws ErrnoException, NoSuchElementException;
42 
43     /**
44      * Update an existing or create a new key -> value entry in an eBbpf map. Returns true if
45      * inserted, false if replaced. (use updateEntry() if you don't care whether insert or replace
46      * happened).
47      */
insertOrReplaceEntry(K key, V value)48     boolean insertOrReplaceEntry(K key, V value) throws ErrnoException;
49 
50     /** Remove existing key from eBpf map. Return true if something was deleted. */
deleteEntry(K key)51     boolean deleteEntry(K key) throws ErrnoException;
52 
53     /** Get the key after the passed-in key. */
getNextKey(@onNull K key)54     K getNextKey(@NonNull K key) throws ErrnoException;
55 
56     /** Get the first key of the eBpf map. */
getFirstKey()57     K getFirstKey() throws ErrnoException;
58 
59     /** Returns {@code true} if this map contains no elements. */
isEmpty()60     default boolean isEmpty() throws ErrnoException {
61         return getFirstKey() == null;
62     }
63 
64     /** Check whether a key exists in the map. */
containsKey(@onNull K key)65     boolean containsKey(@NonNull K key) throws ErrnoException;
66 
67     /** Retrieve a value from the map. */
getValue(@onNull K key)68     V getValue(@NonNull K key) throws ErrnoException;
69 
70     public interface ThrowingBiConsumer<T,U> {
accept(T t, U u)71         void accept(T t, U u) throws ErrnoException;
72     }
73 
74     /**
75      * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer.
76      * The given BiConsumer may to delete the passed-in entry, but is not allowed to perform any
77      * other structural modifications to the map, such as adding entries or deleting other entries.
78      * Otherwise, iteration will result in undefined behaviour.
79      */
forEach(ThrowingBiConsumer<K, V> action)80     default public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException {
81         @Nullable K nextKey = getFirstKey();
82 
83         while (nextKey != null) {
84             @NonNull final K curKey = nextKey;
85             @NonNull final V value = getValue(curKey);
86 
87             nextKey = getNextKey(curKey);
88             action.accept(curKey, value);
89         }
90     }
91 
92     /**
93      * Clears the map. The map may already be empty.
94      *
95      * @throws ErrnoException if the map is already closed, if an error occurred during iteration,
96      *                        or if a non-ENOENT error occurred when deleting a key.
97      */
clear()98     default public void clear() throws ErrnoException {
99         K key = getFirstKey();
100         while (key != null) {
101             deleteEntry(key);  // ignores ENOENT.
102             key = getFirstKey();
103         }
104     }
105 
106     /** Close for AutoCloseable. */
107     @Override
close()108     default void close() throws IOException {
109     };
110 }
111