1 /*
2  * Copyright (C) 2019 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 
17 package android.net;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.net.ipmemorystore.Blob;
23 import android.net.ipmemorystore.NetworkAttributes;
24 import android.net.ipmemorystore.OnBlobRetrievedListener;
25 import android.net.ipmemorystore.OnL2KeyResponseListener;
26 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
27 import android.net.ipmemorystore.OnSameL3NetworkResponseListener;
28 import android.net.ipmemorystore.OnStatusListener;
29 import android.net.ipmemorystore.Status;
30 import android.os.RemoteException;
31 import android.util.Log;
32 
33 import java.util.concurrent.ExecutionException;
34 import java.util.function.Consumer;
35 
36 /**
37  * service used to communicate with the ip memory store service in network stack,
38  * which is running in a separate module.
39  * @hide
40  */
41 public abstract class IpMemoryStoreClient {
42     private static final String TAG = IpMemoryStoreClient.class.getSimpleName();
43     private final Context mContext;
44 
IpMemoryStoreClient(@onNull final Context context)45     public IpMemoryStoreClient(@NonNull final Context context) {
46         if (context == null) throw new IllegalArgumentException("missing context");
47         mContext = context;
48     }
49 
runWhenServiceReady(Consumer<IIpMemoryStore> cb)50     protected abstract void runWhenServiceReady(Consumer<IIpMemoryStore> cb)
51             throws ExecutionException;
52 
53     @FunctionalInterface
54     private interface ThrowingRunnable {
run()55         void run() throws RemoteException;
56     }
57 
ignoringRemoteException(ThrowingRunnable r)58     private void ignoringRemoteException(ThrowingRunnable r) {
59         ignoringRemoteException("Failed to execute remote procedure call", r);
60     }
61 
ignoringRemoteException(String message, ThrowingRunnable r)62     private void ignoringRemoteException(String message, ThrowingRunnable r) {
63         try {
64             r.run();
65         } catch (RemoteException e) {
66             Log.e(TAG, message, e);
67         }
68     }
69 
70     /**
71      * Store network attributes for a given L2 key.
72      * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
73      * calling findL2Key with the attributes and storing in the returned value.
74      *
75      * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
76      *              key and only care about grouping can pass a unique ID here like the ones
77      *              generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
78      *              relevance of such a network will lead to it being evicted soon if it's not
79      *              refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
80      * @param attributes The attributes for this network.
81      * @param listener A listener that will be invoked to inform of the completion of this call,
82      *                 or null if the client is not interested in learning about success/failure.
83      * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
84      * If the call failed, the L2 key will be null.
85      */
storeNetworkAttributes(@onNull final String l2Key, @NonNull final NetworkAttributes attributes, @Nullable final OnStatusListener listener)86     public void storeNetworkAttributes(@NonNull final String l2Key,
87             @NonNull final NetworkAttributes attributes,
88             @Nullable final OnStatusListener listener) {
89         try {
90             runWhenServiceReady(service -> ignoringRemoteException(
91                     () -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(),
92                             OnStatusListener.toAIDL(listener))));
93         } catch (ExecutionException m) {
94             ignoringRemoteException("Error storing network attributes",
95                     () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN)));
96         }
97     }
98 
99     /**
100      * Store a binary blob associated with an L2 key and a name.
101      *
102      * @param l2Key The L2 key for this network.
103      * @param clientId The ID of the client.
104      * @param name The name of this data.
105      * @param data The data to store.
106      * @param listener A listener to inform of the completion of this call, or null if the client
107      *        is not interested in learning about success/failure.
108      * Through the listener, returns a status to indicate success or failure.
109      */
storeBlob(@onNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final Blob data, @Nullable final OnStatusListener listener)110     public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
111             @NonNull final String name, @NonNull final Blob data,
112             @Nullable final OnStatusListener listener) {
113         try {
114             runWhenServiceReady(service -> ignoringRemoteException(
115                     () -> service.storeBlob(l2Key, clientId, name, data,
116                             OnStatusListener.toAIDL(listener))));
117         } catch (ExecutionException m) {
118             ignoringRemoteException("Error storing blob",
119                     () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN)));
120         }
121     }
122 
123     /**
124      * Returns the best L2 key associated with the attributes.
125      *
126      * This will find a record that would be in the same group as the passed attributes. This is
127      * useful to choose the key for storing a sample or private data when the L2 key is not known.
128      * If multiple records are group-close to these attributes, the closest match is returned.
129      * If multiple records have the same closeness, the one with the smaller (unicode codepoint
130      * order) L2 key is returned.
131      * If no record matches these attributes, null is returned.
132      *
133      * @param attributes The attributes of the network to find.
134      * @param listener The listener that will be invoked to return the answer.
135      * Through the listener, returns the L2 key if one matched, or null.
136      */
findL2Key(@onNull final NetworkAttributes attributes, @NonNull final OnL2KeyResponseListener listener)137     public void findL2Key(@NonNull final NetworkAttributes attributes,
138             @NonNull final OnL2KeyResponseListener listener) {
139         try {
140             runWhenServiceReady(service -> ignoringRemoteException(
141                     () -> service.findL2Key(attributes.toParcelable(),
142                             OnL2KeyResponseListener.toAIDL(listener))));
143         } catch (ExecutionException m) {
144             ignoringRemoteException("Error finding L2 Key",
145                     () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null));
146         }
147     }
148 
149     /**
150      * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
151      * to the same L3 network. Group-closeness is used to determine this.
152      *
153      * @param l2Key1 The key for the first network.
154      * @param l2Key2 The key for the second network.
155      * @param listener The listener that will be invoked to return the answer.
156      * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
157      */
isSameNetwork(@onNull final String l2Key1, @NonNull final String l2Key2, @NonNull final OnSameL3NetworkResponseListener listener)158     public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
159             @NonNull final OnSameL3NetworkResponseListener listener) {
160         try {
161             runWhenServiceReady(service -> ignoringRemoteException(
162                     () -> service.isSameNetwork(l2Key1, l2Key2,
163                             OnSameL3NetworkResponseListener.toAIDL(listener))));
164         } catch (ExecutionException m) {
165             ignoringRemoteException("Error checking for network sameness",
166                     () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null));
167         }
168     }
169 
170     /**
171      * Retrieve the network attributes for a key.
172      * If no record is present for this key, this will return null attributes.
173      *
174      * @param l2Key The key of the network to query.
175      * @param listener The listener that will be invoked to return the answer.
176      * Through the listener, returns the network attributes and the L2 key associated with
177      *         the query.
178      */
retrieveNetworkAttributes(@onNull final String l2Key, @NonNull final OnNetworkAttributesRetrievedListener listener)179     public void retrieveNetworkAttributes(@NonNull final String l2Key,
180             @NonNull final OnNetworkAttributesRetrievedListener listener) {
181         try {
182             runWhenServiceReady(service -> ignoringRemoteException(
183                     () -> service.retrieveNetworkAttributes(l2Key,
184                             OnNetworkAttributesRetrievedListener.toAIDL(listener))));
185         } catch (ExecutionException m) {
186             ignoringRemoteException("Error retrieving network attributes",
187                     () -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN),
188                             null, null));
189         }
190     }
191 
192     /**
193      * Retrieve previously stored private data.
194      * If no data was stored for this L2 key and name this will return null.
195      *
196      * @param l2Key The L2 key.
197      * @param clientId The id of the client that stored this data.
198      * @param name The name of the data.
199      * @param listener The listener that will be invoked to return the answer.
200      * Through the listener, returns the private data (or null), with the L2 key
201      *         and the name of the data associated with the query.
202      */
retrieveBlob(@onNull final String l2Key, @NonNull final String clientId, @NonNull final String name, @NonNull final OnBlobRetrievedListener listener)203     public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
204             @NonNull final String name, @NonNull final OnBlobRetrievedListener listener) {
205         try {
206             runWhenServiceReady(service -> ignoringRemoteException(
207                     () -> service.retrieveBlob(l2Key, clientId, name,
208                             OnBlobRetrievedListener.toAIDL(listener))));
209         } catch (ExecutionException m) {
210             ignoringRemoteException("Error retrieving blob",
211                     () -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN),
212                             null, null, null));
213         }
214     }
215 
216     /**
217      * Wipe the data in the database upon network factory reset.
218      */
factoryReset()219     public void factoryReset() {
220         try {
221             runWhenServiceReady(service -> ignoringRemoteException(
222                     () -> service.factoryReset()));
223         } catch (ExecutionException m) {
224             Log.e(TAG, "Error executing factory reset", m);
225         }
226     }
227 }
228