package com.android.server.backup.transport;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
+import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
*/
public class TransportClient {
private static final String TAG = "TransportClient";
+ private static final int LOG_BUFFER_SIZE = 5;
private final Context mContext;
private final Intent mBindIntent;
private final Handler mListenerHandler;
private final String mPrefixForLog;
private final Object mStateLock = new Object();
+ private final Object mLogBufferLock = new Object();
+
+ @GuardedBy("mLogBufferLock")
+ private final List<String> mLogBuffer = new LinkedList<>();
@GuardedBy("mStateLock")
private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>();
// For logging
String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", "");
- mPrefixForLog = classNameForLog + "#" + mIdentifier + ": ";
+ mPrefixForLog = classNameForLog + "#" + mIdentifier + ":";
}
public ComponentName getTransportComponent() {
switch (mState) {
case State.UNUSABLE:
- log(Log.DEBUG, caller, "Async connect: UNUSABLE client");
+ log(Log.WARN, caller, "Async connect: UNUSABLE client");
notifyListener(listener, null, caller);
break;
case State.IDLE:
IBackupTransport transport = mTransport;
if (transport != null) {
- log(Log.DEBUG, caller, "Sync connect: reusing transport");
+ log(Log.INFO, caller, "Sync connect: reusing transport");
return transport;
}
// If it's already UNUSABLE we return straight away, no need to go to main-thread
synchronized (mStateLock) {
if (mState == State.UNUSABLE) {
- log(Log.DEBUG, caller, "Sync connect: UNUSABLE client");
+ log(Log.WARN, caller, "Sync connect: UNUSABLE client");
return null;
}
}
}
private void notifyListener(
- TransportConnectionListener listener, IBackupTransport transport, String caller) {
- log(Log.VERBOSE, caller, "Notifying listener of transport = " + transport);
+ TransportConnectionListener listener,
+ @Nullable IBackupTransport transport,
+ String caller) {
+ String transportString = (transport != null) ? "IBackupTransport" : "null";
+ log(Log.INFO, "Notifying [" + caller + "] transport = " + transportString);
mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this));
}
@GuardedBy("mStateLock")
- private void notifyListenersAndClearLocked(IBackupTransport transport) {
+ private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) {
for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) {
TransportConnectionListener listener = entry.getKey();
String caller = entry.getValue();
}
private void log(int priority, String message) {
- TransportUtils.log(priority, TAG, message);
+ TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message));
+ saveLogEntry(formatMessage(null, null, message));
}
- private void log(int priority, String caller, String msg) {
- TransportUtils.log(priority, TAG, mPrefixForLog, caller, msg);
- // TODO(brufino): Log in internal list for dump
- // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+ private void log(int priority, String caller, String message) {
+ TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message));
+ saveLogEntry(formatMessage(null, caller, message));
+ }
+
+ private void saveLogEntry(String message) {
+ CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+ message = time + " " + message;
+ synchronized (mLogBufferLock) {
+ if (mLogBuffer.size() == LOG_BUFFER_SIZE) {
+ mLogBuffer.remove(mLogBuffer.size() - 1);
+ }
+ mLogBuffer.add(0, message);
+ }
+ }
+
+ List<String> getLogBuffer() {
+ synchronized (mLogBufferLock) {
+ return Collections.unmodifiableList(mLogBuffer);
+ }
}
@IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
package com.android.server.backup.transport;
import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
-
import com.android.server.backup.TransportManager;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Manages the creation and disposal of {@link TransportClient}s. The only class that should use
* this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
- *
- * <p>TODO(brufino): Implement pool of TransportClients
*/
public class TransportClientManager {
private static final String TAG = "TransportClientManager";
private final Context mContext;
private final Object mTransportClientsLock = new Object();
private int mTransportClientsCreated = 0;
+ private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
public TransportClientManager(Context context) {
mContext = context;
bindIntent,
transportComponent,
Integer.toString(mTransportClientsCreated));
+ mTransportClientsCallerMap.put(transportClient, caller);
mTransportClientsCreated++;
- TransportUtils.log(Log.DEBUG, TAG, caller, "Retrieving " + transportClient);
+ TransportUtils.log(
+ Log.DEBUG, TAG, formatMessage(null, caller, "Retrieving " + transportClient));
return transportClient;
}
}
* details.
*/
public void disposeOfTransportClient(TransportClient transportClient, String caller) {
- TransportUtils.log(Log.DEBUG, TAG, caller, "Disposing of " + transportClient);
transportClient.unbind(caller);
+ synchronized (mTransportClientsLock) {
+ TransportUtils.log(
+ Log.DEBUG, TAG, formatMessage(null, caller, "Disposing of " + transportClient));
+ mTransportClientsCallerMap.remove(transportClient);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("Transport clients created: " + mTransportClientsCreated);
+ synchronized (mTransportClientsLock) {
+ pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
+ for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
+ String caller = mTransportClientsCallerMap.get(transportClient);
+ pw.println(" " + transportClient + " [" + caller + "]");
+ for (String logEntry : transportClient.getLogBuffer()) {
+ pw.println(" " + logEntry);
+ }
+ }
+ }
}
}
}
static void log(int priority, String tag, String message) {
- log(priority, tag, null, message);
- }
-
- static void log(int priority, String tag, @Nullable String caller, String message) {
- log(priority, tag, "", caller, message);
+ if (Log.isLoggable(tag, priority)) {
+ Slog.println(priority, tag, message);
+ }
}
- static void log(
- int priority, String tag, String prefix, @Nullable String caller, String message) {
- if (Log.isLoggable(tag, priority)) {
- if (caller != null) {
- prefix += "[" + caller + "] ";
- }
- Slog.println(priority, tag, prefix + message);
+ static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) {
+ StringBuilder string = new StringBuilder();
+ if (prefix != null) {
+ string.append(prefix).append(" ");
+ }
+ if (caller != null) {
+ string.append("[").append(caller).append("] ");
}
+ return string.append(message).toString();
}
private TransportUtils() {}