clean logging

This commit is contained in:
ryanzhao
2021-02-03 12:22:40 +11:00
parent 0a952bcb85
commit 307da3b2be
290 changed files with 360 additions and 549 deletions

View File

@@ -44,6 +44,7 @@ android {
}
dependencies {
implementation "androidx.annotation:annotation:1.1.0"
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
implementation "com.googlecode.libphonenumber:libphonenumber:8.10.7"

View File

@@ -9,7 +9,7 @@ package org.session.libsignal.libsignal;
import org.session.libsignal.libsignal.ecc.Curve;
import org.session.libsignal.libsignal.ecc.ECKeyPair;
import org.session.libsignal.libsignal.ecc.ECPublicKey;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.protocol.PreKeySignalMessage;
import org.session.libsignal.libsignal.protocol.SignalMessage;
import org.session.libsignal.libsignal.ratchet.AliceSignalProtocolParameters;

View File

@@ -1,94 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.logging;
import org.session.libsignal.libsignal.logging.SignalProtocolLogger;
import org.session.libsignal.libsignal.logging.SignalProtocolLoggerProvider;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.UnknownHostException;
public class Log {
private Log() {}
public static void v(String tag, String msg) {
log(SignalProtocolLogger.VERBOSE, tag, msg);
}
public static void v(String tag, String msg, Throwable tr) {
log(SignalProtocolLogger.VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
}
public static void d(String tag, String msg) {
log(SignalProtocolLogger.DEBUG, tag, msg);
}
public static void d(String tag, String msg, Throwable tr) {
log(SignalProtocolLogger.DEBUG, tag, msg + '\n' + getStackTraceString(tr));
}
public static void i(String tag, String msg) {
log(SignalProtocolLogger.INFO, tag, msg);
}
public static void i(String tag, String msg, Throwable tr) {
log(SignalProtocolLogger.INFO, tag, msg + '\n' + getStackTraceString(tr));
}
public static void w(String tag, String msg) {
log(SignalProtocolLogger.WARN, tag, msg);
}
public static void w(String tag, String msg, Throwable tr) {
log(SignalProtocolLogger.WARN, tag, msg + '\n' + getStackTraceString(tr));
}
public static void w(String tag, Throwable tr) {
log(SignalProtocolLogger.WARN, tag, getStackTraceString(tr));
}
public static void e(String tag, String msg) {
log(SignalProtocolLogger.ERROR, tag, msg);
}
public static void e(String tag, String msg, Throwable tr) {
log(SignalProtocolLogger.ERROR, tag, msg + '\n' + getStackTraceString(tr));
}
private static String getStackTraceString(Throwable tr) {
if (tr == null) {
return "";
}
// This is to reduce the amount of log spew that apps do in the non-error
// condition of the network being unavailable.
Throwable t = tr;
while (t != null) {
if (t instanceof UnknownHostException) {
return "";
}
t = t.getCause();
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
tr.printStackTrace(pw);
pw.flush();
return sw.toString();
}
private static void log(int priority, String tag, String msg) {
SignalProtocolLogger logger = SignalProtocolLoggerProvider.getProvider();
if (logger != null) {
logger.log(priority, tag, msg);
}
}
}

View File

@@ -1,18 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.logging;
public interface SignalProtocolLogger {
public static final int VERBOSE = 2;
public static final int DEBUG = 3;
public static final int INFO = 4;
public static final int WARN = 5;
public static final int ERROR = 6;
public static final int ASSERT = 7;
public void log(int priority, String tag, String message);
}

View File

@@ -1,19 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.session.libsignal.libsignal.logging;
public class SignalProtocolLoggerProvider {
private static SignalProtocolLogger provider;
public static SignalProtocolLogger getProvider() {
return provider;
}
public static void setProvider(SignalProtocolLogger provider) {
SignalProtocolLoggerProvider.provider = provider;
}
}

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.libsignal.loki
import com.google.protobuf.ByteString
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.libsignal.protocol.CiphertextMessage
import org.session.libsignal.libsignal.protocol.SignalProtos

View File

@@ -17,7 +17,7 @@ import org.session.libsignal.libsignal.ecc.ECKeyPair;
import org.session.libsignal.libsignal.ecc.ECPrivateKey;
import org.session.libsignal.libsignal.ecc.ECPublicKey;
import org.session.libsignal.libsignal.kdf.HKDF;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.ratchet.ChainKey;
import org.session.libsignal.libsignal.ratchet.MessageKeys;
import org.session.libsignal.libsignal.ratchet.RootKey;

View File

@@ -15,7 +15,7 @@ import org.session.libsignal.libsignal.IdentityKey;
import org.session.libsignal.libsignal.IdentityKeyPair;
import org.session.libsignal.libsignal.InvalidKeyException;
import org.session.libsignal.libsignal.ecc.ECPublicKey;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.state.PreKeyRecord;
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
import org.session.libsignal.libsignal.util.Pair;

View File

@@ -10,7 +10,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import org.jetbrains.annotations.Nullable;
import org.session.libsignal.libsignal.ecc.ECKeyPair;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.loki.SessionResetProtocol;
import org.session.libsignal.libsignal.state.SignalProtocolStore;
import org.session.libsignal.libsignal.util.guava.Optional;

View File

@@ -9,7 +9,7 @@ package org.session.libsignal.service.api.messages;
import com.google.protobuf.ByteString;
import org.session.libsignal.libsignal.InvalidVersionException;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.service.api.push.SignalServiceAddress;
import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope;
import org.session.libsignal.utilities.Base64;

View File

@@ -9,7 +9,7 @@ package org.session.libsignal.service.api.messages.multidevice;
import org.session.libsignal.libsignal.IdentityKey;
import org.session.libsignal.libsignal.InvalidKeyException;
import org.session.libsignal.libsignal.InvalidMessageException;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
import org.session.libsignal.service.internal.push.SignalServiceProtos;

View File

@@ -11,8 +11,7 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.service.api.util.InvalidNumberException;
import org.session.libsignal.utilities.logging.Log;
import java.util.Locale;
import java.util.regex.Pattern;

View File

@@ -11,7 +11,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import org.session.libsignal.libsignal.IdentityKey;
import org.session.libsignal.libsignal.ecc.ECPublicKey;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.state.PreKeyBundle;
import org.session.libsignal.libsignal.state.PreKeyRecord;
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;

View File

@@ -7,7 +7,7 @@
package org.session.libsignal.service.internal.push;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
public class PushTransportDetails {

View File

@@ -2,7 +2,7 @@ package org.session.libsignal.service.internal.websocket;
import com.google.protobuf.InvalidProtocolBufferException;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.util.Pair;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.push.TrustStore;

View File

@@ -8,7 +8,7 @@ import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.Request
import okhttp3.RequestBody
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.libsignal.loki.DiffieHellman
import org.session.libsignal.service.api.crypto.ProfileCipherOutputStream
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException

View File

@@ -2,7 +2,7 @@ package org.session.libsignal.service.loki.api
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.loki.api.crypto.ProofOfWork
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.loki.api
import com.google.protobuf.ByteString
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.internal.websocket.WebSocketProtos.WebSocketMessage

View File

@@ -2,7 +2,7 @@ package org.session.libsignal.service.loki.api
import nl.komponents.kovenant.*
import nl.komponents.kovenant.functional.bind
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import java.security.SecureRandom

View File

@@ -2,7 +2,7 @@ package org.session.libsignal.service.loki.api
import nl.komponents.kovenant.functional.map
import okhttp3.*
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
import org.session.libsignal.service.loki.utilities.retryIfNeeded

View File

@@ -6,7 +6,7 @@ import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.task
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.internal.push.SignalServiceProtos.Envelope
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI

View File

@@ -5,7 +5,7 @@ import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.task
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.loki.api.utilities.HTTP
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.utilities.ThreadUtils

View File

@@ -1,6 +1,6 @@
package org.session.libsignal.service.loki.api.crypto
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.loki.api.SnodeAPI
import java.math.BigInteger

View File

@@ -3,7 +3,7 @@ package org.session.libsignal.service.loki.api.fileserver
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map
import okhttp3.Request
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.service.loki.api.LokiDotNetAPI

View File

@@ -6,7 +6,7 @@ import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import okhttp3.Request
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.service.loki.api.*

View File

@@ -5,7 +5,7 @@ import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.then
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Hex
import org.session.libsignal.utilities.JsonUtil

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.loki.api.opengroups
import org.whispersystems.curve25519.Curve25519
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Hex
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.loki.api.utilities
import okhttp3.*
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.JsonUtil
import java.security.SecureRandom
import java.security.cert.X509Certificate

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.loki.protocol.closedgroups
import com.google.protobuf.ByteString
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Hex
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.utilities.JsonUtil

View File

@@ -3,7 +3,7 @@ package org.session.libsignal.service.loki.protocol.closedgroups
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.libsignal.util.ByteUtil
import org.session.libsignal.utilities.Hex
import org.session.libsignal.service.internal.util.Util

View File

@@ -1,16 +1,12 @@
package org.session.libsignal.service.loki.protocol.sessionmanagement
import org.session.libsignal.libsignal.SignalProtocolAddress
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.libsignal.loki.SessionResetProtocol
import org.session.libsignal.libsignal.loki.SessionResetStatus
import org.session.libsignal.libsignal.state.SignalProtocolStore
import org.session.libsignal.libsignal.util.guava.Optional
import org.session.libsignal.service.api.SignalServiceMessageSender
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
import org.session.libsignal.service.api.push.SignalServiceAddress
import org.session.libsignal.service.loki.api.SnodeAPI
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol
public class SessionManagementProtocol(private val sessionResetImpl: SessionResetProtocol, private val sskDatabase: SharedSenderKeysDatabaseProtocol,

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.loki.protocol.shelved.multidevice
import org.whispersystems.curve25519.Curve25519
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Hex
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded

View File

@@ -2,7 +2,7 @@ package org.session.libsignal.service.loki.utilities
import okhttp3.HttpUrl
import okhttp3.Request
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.service.api.messages.SignalServiceAttachment
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException
import org.session.libsignal.service.api.push.exceptions.PushNetworkException

View File

@@ -16,8 +16,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.session.libsignal.libsignal.IdentityKey;
import org.session.libsignal.libsignal.InvalidKeyException;
import org.session.libsignal.libsignal.logging.Log;
import org.session.libsignal.utilities.Base64;
import org.session.libsignal.utilities.logging.Log;
import java.io.IOException;
import java.io.InputStream;

View File

@@ -6,7 +6,7 @@ import nl.komponents.kovenant.Kovenant
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.jvm.asDispatcher
import org.session.libsignal.libsignal.logging.Log
import org.session.libsignal.utilities.logging.Log
import java.util.concurrent.Executors
import java.util.concurrent.TimeoutException

View File

@@ -0,0 +1,55 @@
package org.session.libsignal.utilities.externalstorage
import android.content.Context
import android.os.Environment
import java.io.File
object ExternalStorageUtil {
const val DIRECTORY_BACKUPS = "Backups"
/** @see Context.getExternalFilesDir
*/
@Throws(NoExternalStorageException::class)
fun getDir(context: Context, type: String?): File {
return context.getExternalFilesDir(type)
?: throw NoExternalStorageException("External storage dir is currently unavailable: $type")
}
@Throws(NoExternalStorageException::class)
fun getBackupDir(context: Context): File {
return getDir(context, DIRECTORY_BACKUPS)
}
@Throws(NoExternalStorageException::class)
fun getVideoDir(context: Context): File {
return getDir(context, Environment.DIRECTORY_MOVIES)
}
@Throws(NoExternalStorageException::class)
fun getAudioDir(context: Context): File {
return getDir(context, Environment.DIRECTORY_MUSIC)
}
@JvmStatic
@Throws(NoExternalStorageException::class)
fun getImageDir(context: Context): File {
return getDir(context, Environment.DIRECTORY_PICTURES)
}
@Throws(NoExternalStorageException::class)
fun getDownloadDir(context: Context): File {
return getDir(context, Environment.DIRECTORY_DOWNLOADS)
}
fun getCacheDir(context: Context): File? {
return context.externalCacheDir
}
@JvmStatic
fun getCleanFileName(fileName: String?): String? {
var fileName = fileName ?: return null
fileName = fileName.replace('\u202D', '\uFFFD')
fileName = fileName.replace('\u202E', '\uFFFD')
return fileName
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.session.libsignal.utilities.externalstorage;
public class NoExternalStorageException extends Exception {
public NoExternalStorageException() {
// TODO Auto-generated constructor stub
}
public NoExternalStorageException(String detailMessage) {
super(detailMessage);
// TODO Auto-generated constructor stub
}
public NoExternalStorageException(Throwable throwable) {
super(throwable);
// TODO Auto-generated constructor stub
}
public NoExternalStorageException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
// TODO Auto-generated constructor stub
}
}

View File

@@ -0,0 +1,147 @@
package org.session.libsignal.utilities.logging;
import androidx.annotation.MainThread;
public class Log {
private static Logger[] loggers;
@MainThread
public static void initialize(Logger... loggers) {
Log.loggers = loggers;
}
public static void v(String tag, String message) {
v(tag, message, null);
}
public static void d(String tag, String message) {
d(tag, message, null);
}
public static void i(String tag, String message) {
i(tag, message, null);
}
public static void w(String tag, String message) {
w(tag, message, null);
}
public static void e(String tag, String message) {
e(tag, message, null);
}
public static void wtf(String tag, String message) {
wtf(tag, message, null);
}
public static void v(String tag, Throwable t) {
v(tag, null, t);
}
public static void d(String tag, Throwable t) {
d(tag, null, t);
}
public static void i(String tag, Throwable t) {
i(tag, null, t);
}
public static void w(String tag, Throwable t) {
w(tag, null, t);
}
public static void e(String tag, Throwable t) {
e(tag, null, t);
}
public static void wtf(String tag, Throwable t) {
wtf(tag, null, t);
}
public static void v(String tag, String message, Throwable t) {
if (loggers != null) {
for (Logger logger : loggers) {
logger.v(tag, message, t);
}
} else {
android.util.Log.v(tag, message, t);
}
}
public static void d(String tag, String message, Throwable t) {
if (loggers != null) {
for (Logger logger : loggers) {
logger.d(tag, message, t);
}
} else {
android.util.Log.d(tag, message, t);
}
}
public static void i(String tag, String message, Throwable t) {
if (loggers != null) {
for (Logger logger : loggers) {
logger.i(tag, message, t);
}
} else {
android.util.Log.i(tag, message, t);
}
}
public static void w(String tag, String message, Throwable t) {
if (loggers != null) {
for (Logger logger : loggers) {
logger.w(tag, message, t);
}
} else {
android.util.Log.w(tag, message, t);
}
}
public static void e(String tag, String message, Throwable t) {
if (loggers != null) {
for (Logger logger : loggers) {
logger.e(tag, message, t);
}
} else {
android.util.Log.e(tag, message, t);
}
}
public static void wtf(String tag, String message, Throwable t) {
if (loggers != null) {
for (Logger logger : loggers) {
logger.wtf(tag, message, t);
}
} else {
android.util.Log.wtf(tag, message, t);
}
}
public static String tag(Class<?> clazz) {
String simpleName = clazz.getSimpleName();
if (simpleName.length() > 23) {
return simpleName.substring(0, 23);
}
return simpleName;
}
public static void blockUntilAllWritesFinished() {
if (loggers != null) {
for (Logger logger : loggers) {
logger.blockUntilAllWritesFinished();
}
}
}
public static abstract class Logger {
public abstract void v(String tag, String message, Throwable t);
public abstract void d(String tag, String message, Throwable t);
public abstract void i(String tag, String message, Throwable t);
public abstract void w(String tag, String message, Throwable t);
public abstract void e(String tag, String message, Throwable t);
public abstract void wtf(String tag, String message, Throwable t);
public abstract void blockUntilAllWritesFinished();
}
}