blob: 014b5289bace8925212a1b4398c392c51c5380e7 [file] [log] [blame]
/*
* Copyright (C) 2019 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.net;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.ipmemorystore.Blob;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.OnBlobRetrievedListener;
import android.net.ipmemorystore.OnL2KeyResponseListener;
import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
import android.net.ipmemorystore.OnSameL3NetworkResponseListener;
import android.net.ipmemorystore.OnStatusListener;
import android.net.ipmemorystore.Status;
import android.os.RemoteException;
import android.util.Log;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
/**
* service used to communicate with the ip memory store service in network stack,
* which is running in a separate module.
* @hide
*/
public abstract class IpMemoryStoreClient {
private static final String TAG = IpMemoryStoreClient.class.getSimpleName();
private final Context mContext;
public IpMemoryStoreClient(@NonNull final Context context) {
if (context == null) throw new IllegalArgumentException("missing context");
mContext = context;
}
protected abstract void runWhenServiceReady(Consumer<IIpMemoryStore> cb)
throws ExecutionException;
@FunctionalInterface
private interface ThrowingRunnable {
void run() throws RemoteException;
}
private void ignoringRemoteException(ThrowingRunnable r) {
ignoringRemoteException("Failed to execute remote procedure call", r);
}
private void ignoringRemoteException(String message, ThrowingRunnable r) {
try {
r.run();
} catch (RemoteException e) {
Log.e(TAG, message, e);
}
}
/**
* Store network attributes for a given L2 key.
* If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
* calling findL2Key with the attributes and storing in the returned value.
*
* @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
* key and only care about grouping can pass a unique ID here like the ones
* generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
* relevance of such a network will lead to it being evicted soon if it's not
* refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
* @param attributes The attributes for this network.
* @param listener A listener that will be invoked to inform of the completion of this call,
* or null if the client is not interested in learning about success/failure.
* Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
* If the call failed, the L2 key will be null.
*/
public void storeNetworkAttributes(@NonNull final String l2Key,
@NonNull final NetworkAttributes attributes,
@Nullable final OnStatusListener listener) {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(),
OnStatusListener.toAIDL(listener))));
} catch (ExecutionException m) {
ignoringRemoteException("Error storing network attributes",
() -> listener.onComplete(new Status(Status.ERROR_UNKNOWN)));
}
}
/**
* Store a binary blob associated with an L2 key and a name.
*
* @param l2Key The L2 key for this network.
* @param clientId The ID of the client.
* @param name The name of this data.
* @param data The data to store.
* @param listener A listener to inform of the completion of this call, or null if the client
* is not interested in learning about success/failure.
* Through the listener, returns a status to indicate success or failure.
*/
public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
@NonNull final String name, @NonNull final Blob data,
@Nullable final OnStatusListener listener) {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.storeBlob(l2Key, clientId, name, data,
OnStatusListener.toAIDL(listener))));
} catch (ExecutionException m) {
ignoringRemoteException("Error storing blob",
() -> listener.onComplete(new Status(Status.ERROR_UNKNOWN)));
}
}
/**
* Returns the best L2 key associated with the attributes.
*
* This will find a record that would be in the same group as the passed attributes. This is
* useful to choose the key for storing a sample or private data when the L2 key is not known.
* If multiple records are group-close to these attributes, the closest match is returned.
* If multiple records have the same closeness, the one with the smaller (unicode codepoint
* order) L2 key is returned.
* If no record matches these attributes, null is returned.
*
* @param attributes The attributes of the network to find.
* @param listener The listener that will be invoked to return the answer.
* Through the listener, returns the L2 key if one matched, or null.
*/
public void findL2Key(@NonNull final NetworkAttributes attributes,
@NonNull final OnL2KeyResponseListener listener) {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.findL2Key(attributes.toParcelable(),
OnL2KeyResponseListener.toAIDL(listener))));
} catch (ExecutionException m) {
ignoringRemoteException("Error finding L2 Key",
() -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null));
}
}
/**
* Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
* to the same L3 network. Group-closeness is used to determine this.
*
* @param l2Key1 The key for the first network.
* @param l2Key2 The key for the second network.
* @param listener The listener that will be invoked to return the answer.
* Through the listener, a SameL3NetworkResponse containing the answer and confidence.
*/
public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
@NonNull final OnSameL3NetworkResponseListener listener) {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.isSameNetwork(l2Key1, l2Key2,
OnSameL3NetworkResponseListener.toAIDL(listener))));
} catch (ExecutionException m) {
ignoringRemoteException("Error checking for network sameness",
() -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null));
}
}
/**
* Retrieve the network attributes for a key.
* If no record is present for this key, this will return null attributes.
*
* @param l2Key The key of the network to query.
* @param listener The listener that will be invoked to return the answer.
* Through the listener, returns the network attributes and the L2 key associated with
* the query.
*/
public void retrieveNetworkAttributes(@NonNull final String l2Key,
@NonNull final OnNetworkAttributesRetrievedListener listener) {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.retrieveNetworkAttributes(l2Key,
OnNetworkAttributesRetrievedListener.toAIDL(listener))));
} catch (ExecutionException m) {
ignoringRemoteException("Error retrieving network attributes",
() -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN),
null, null));
}
}
/**
* Retrieve previously stored private data.
* If no data was stored for this L2 key and name this will return null.
*
* @param l2Key The L2 key.
* @param clientId The id of the client that stored this data.
* @param name The name of the data.
* @param listener The listener that will be invoked to return the answer.
* Through the listener, returns the private data (or null), with the L2 key
* and the name of the data associated with the query.
*/
public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
@NonNull final String name, @NonNull final OnBlobRetrievedListener listener) {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.retrieveBlob(l2Key, clientId, name,
OnBlobRetrievedListener.toAIDL(listener))));
} catch (ExecutionException m) {
ignoringRemoteException("Error retrieving blob",
() -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN),
null, null, null));
}
}
/**
* Wipe the data in the database upon network factory reset.
*/
public void factoryReset() {
try {
runWhenServiceReady(service -> ignoringRemoteException(
() -> service.factoryReset()));
} catch (ExecutionException m) {
Log.e(TAG, "Error executing factory reset", m);
}
}
}