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