summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java67
-rw-r--r--src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java57
-rw-r--r--src/com/android/server/connectivity/ipmemorystore/StatusAndCount.java29
3 files changed, 152 insertions, 1 deletions
diff --git a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
index be338e5..0b05a5b 100644
--- a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
@@ -602,6 +602,64 @@ public class IpMemoryStoreDatabase {
return bestKey;
}
+ /**
+ * Delete a single entry by key.
+ *
+ * If |needWipe| is true, the data will be wiped from disk immediately. Otherwise, it will
+ * only be marked deleted, and overwritten by subsequent writes or reclaimed during the next
+ * maintenance window.
+ * Note that wiping data is a very expensive operation. This is meant for clients that need
+ * this data gone from disk immediately for security reasons. Functionally it makes no
+ * difference at all.
+ */
+ static StatusAndCount delete(@NonNull final SQLiteDatabase db, @NonNull final String l2key,
+ final boolean needWipe) {
+ return deleteEntriesWithColumn(db,
+ NetworkAttributesContract.COLNAME_L2KEY, l2key, needWipe);
+ }
+
+ /**
+ * Delete all entries that have a particular cluster value.
+ *
+ * If |needWipe| is true, the data will be wiped from disk immediately. Otherwise, it will
+ * only be marked deleted, and overwritten by subsequent writes or reclaimed during the next
+ * maintenance window.
+ * Note that wiping data is a very expensive operation. This is meant for clients that need
+ * this data gone from disk immediately for security reasons. Functionally it makes no
+ * difference at all.
+ */
+ static StatusAndCount deleteCluster(@NonNull final SQLiteDatabase db,
+ @NonNull final String cluster, final boolean needWipe) {
+ return deleteEntriesWithColumn(db,
+ NetworkAttributesContract.COLNAME_CLUSTER, cluster, needWipe);
+ }
+
+ // Delete all entries where the given column has the given value.
+ private static StatusAndCount deleteEntriesWithColumn(@NonNull final SQLiteDatabase db,
+ @NonNull final String column, @NonNull final String value, final boolean needWipe) {
+ db.beginTransaction();
+ int deleted = 0;
+ try {
+ deleted = db.delete(NetworkAttributesContract.TABLENAME,
+ column + "= ?", new String[] { value });
+ db.setTransactionSuccessful();
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Could not delete from the memory store", e);
+ // Unclear what might have happened ; deleting records is not supposed to be able
+ // to fail barring a syntax error in the SQL query.
+ return new StatusAndCount(Status.ERROR_UNKNOWN, 0);
+ } finally {
+ db.endTransaction();
+ }
+
+ if (needWipe) {
+ final int vacuumStatus = vacuum(db);
+ // This is a problem for the client : return the failure
+ if (Status.SUCCESS != vacuumStatus) return new StatusAndCount(vacuumStatus, deleted);
+ }
+ return new StatusAndCount(Status.SUCCESS, deleted);
+ }
+
// Drops all records that are expired. Relevance has decayed to zero of these records. Returns
// an int out of Status.{SUCCESS, ERROR_*}
static int dropAllExpiredRecords(@NonNull final SQLiteDatabase db) {
@@ -708,4 +766,13 @@ public class IpMemoryStoreDatabase {
final int columnIndex = cursor.getColumnIndex(columnName);
return (columnIndex >= 0) ? cursor.getLong(columnIndex) : defaultValue;
}
+ private static int vacuum(@NonNull final SQLiteDatabase db) {
+ try {
+ db.execSQL("VACUUM");
+ return Status.SUCCESS;
+ } catch (SQLiteException e) {
+ // Vacuuming may fail from lack of storage, because it makes a copy of the database.
+ return Status.ERROR_STORAGE;
+ }
+ }
}
diff --git a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
index cd29e0d..ae9c875 100644
--- a/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
+++ b/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
@@ -33,6 +33,7 @@ import android.net.ipmemorystore.IOnBlobRetrievedListener;
import android.net.ipmemorystore.IOnL2KeyResponseListener;
import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
+import android.net.ipmemorystore.IOnStatusAndCountListener;
import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
@@ -116,7 +117,11 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
// TODO : investigate replacing this scheme with a scheme where each thread has its own
// instance of the database, as it may be faster. It is likely however that IpMemoryStore
// operations are mostly IO-bound anyway, and additional contention is unlikely to bring
- // benefits. Alternatively, a read-write lock might increase throughput.
+ // benefits. Alternatively, a read-write lock might increase throughput. Also if doing
+ // this work, care must be taken around the privacy-preserving VACUUM operations as
+ // VACUUM will fail if there are other open transactions at the same time, and using
+ // multiple threads will open the possibility of this failure happening, threatening
+ // the privacy guarantees.
mExecutor = Executors.newSingleThreadExecutor();
RegularMaintenanceJobService.schedule(mContext, this);
}
@@ -405,6 +410,56 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
}
/**
+ * Delete a single entry.
+ *
+ * @param l2Key The L2 key of the entry to delete.
+ * @param needWipe Whether the data must be wiped from disk immediately for security reasons.
+ * This is very expensive and makes no functional difference ; only pass
+ * true if security requires this data must be removed from disk immediately.
+ * @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.
+ * returns (through the listener) A status to indicate success and the number of deleted records
+ */
+ public void delete(@NonNull final String l2Key, final boolean needWipe,
+ @Nullable final IOnStatusAndCountListener listener) {
+ mExecutor.execute(() -> {
+ try {
+ final StatusAndCount res = IpMemoryStoreDatabase.delete(mDb, l2Key, needWipe);
+ if (null != listener) listener.onComplete(makeStatus(res.status), res.count);
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
+ }
+
+ /**
+ * Delete all entries in a cluster.
+ *
+ * This method will delete all entries in the memory store that have the cluster attribute
+ * passed as an argument.
+ *
+ * @param cluster The cluster to delete.
+ * @param needWipe Whether the data must be wiped from disk immediately for security reasons.
+ * This is very expensive and makes no functional difference ; only pass
+ * true if security requires this data must be removed from disk immediately.
+ * @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.
+ * returns (through the listener) A status to indicate success and the number of deleted records
+ */
+ public void deleteCluster(@NonNull final String cluster, final boolean needWipe,
+ @Nullable final IOnStatusAndCountListener listener) {
+ mExecutor.execute(() -> {
+ try {
+ final StatusAndCount res =
+ IpMemoryStoreDatabase.deleteCluster(mDb, cluster, needWipe);
+ if (null != listener) listener.onComplete(makeStatus(res.status), res.count);
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
+ }
+
+ /**
* Wipe the data in IpMemoryStore database upon network factory reset.
*/
@Override
diff --git a/src/com/android/server/connectivity/ipmemorystore/StatusAndCount.java b/src/com/android/server/connectivity/ipmemorystore/StatusAndCount.java
new file mode 100644
index 0000000..2cbe843
--- /dev/null
+++ b/src/com/android/server/connectivity/ipmemorystore/StatusAndCount.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.android.server.connectivity.ipmemorystore;
+
+/**
+ * Small data class to wrap a Status and an int.
+ */
+public class StatusAndCount {
+ public final int status;
+ public final int count;
+ public StatusAndCount(final int status, final int count) {
+ this.status = status;
+ this.count = count;
+ }
+}