| Index: components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/DevToolsBridgeServer.java
|
| diff --git a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/DevToolsBridgeServer.java b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/DevToolsBridgeServer.java
|
| index abc3dd56aace1d392b53c4384c974fe2b722a6d4..076fe50417f0205a1a32266f3325b26ebc8461f7 100644
|
| --- a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/DevToolsBridgeServer.java
|
| +++ b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/DevToolsBridgeServer.java
|
| @@ -4,13 +4,10 @@
|
|
|
| package org.chromium.components.devtools_bridge;
|
|
|
| -import android.app.Service;
|
| import android.content.Context;
|
| import android.content.Intent;
|
| import android.content.SharedPreferences;
|
| -import android.os.PowerManager;
|
|
|
| -import org.chromium.components.devtools_bridge.ui.ServiceUIFactory;
|
| import org.chromium.components.devtools_bridge.util.LooperExecutor;
|
|
|
| import java.util.HashMap;
|
| @@ -18,53 +15,57 @@ import java.util.List;
|
| import java.util.Map;
|
|
|
| /**
|
| - * Android service mixin implementing DevTools Bridge features that not depend on
|
| - * WebRTC signaling. Ability to host this class in different service classes allows:
|
| - * 1. Parametrization.
|
| - * 2. Simplified signaling for tests.
|
| - *
|
| - * Service starts foreground once any remote client starts a debugging session. Stops when all
|
| - * remote clients disconnect.
|
| - *
|
| - * Must be called on service's main thread.
|
| + * Responsibility of DevToolsBridgeServer consists of handling commands and managing sessions.
|
| + * It designed to live in DevToolsBridgeServiceBase but also may live separately (in tests).
|
| */
|
| public class DevToolsBridgeServer implements SignalingReceiver {
|
| - public final int NOTIFICATION_ID = 1;
|
| - public final String DISCONNECT_ALL_CLIENTS_ACTION =
|
| - "action.DISCONNECT_ALL_CLIENTS_ACTION";
|
| -
|
| - public final String WAKELOCK_KEY = "wake_lock.DevToolsBridgeServer";
|
| -
|
| - private final Service mHost;
|
| - private final String mSocketName;
|
| - private final ServiceUIFactory mServiceUIFactory;
|
| private final LooperExecutor mExecutor;
|
| private final SessionDependencyFactory mFactory = SessionDependencyFactory.newInstance();
|
| private final Map<String, ServerSession> mSessions = new HashMap<String, ServerSession>();
|
| private final GCDNotificationHandler mHandler;
|
| - private PowerManager.WakeLock mWakeLock;
|
| - private Runnable mForegroundCompletionCallback;
|
| -
|
| - public DevToolsBridgeServer(Service host, String socketName, ServiceUIFactory uiFactory) {
|
| - mHost = host;
|
| - mSocketName = socketName;
|
| - mServiceUIFactory = uiFactory;
|
| - mExecutor = LooperExecutor.newInstanceForMainLooper(mHost);
|
| - mHandler = new GCDNotificationHandler(this);
|
| + private final Delegate mDelegate;
|
|
|
| - checkCalledOnHostServiceThread();
|
| + /**
|
| + * Callback for finding DevTools socket asynchronously. Needed in multiprocess
|
| + * scenario when socket name is variable. May be called synchronously.
|
| + */
|
| + public interface QuerySocketCallback {
|
| + void onSuccess(String socketName);
|
| + void onFailure();
|
| + }
|
| +
|
| + /**
|
| + * Delegate abstracts Server from service lifetime management and UI.
|
| + */
|
| + public interface Delegate {
|
| + Context getContext();
|
| +
|
| + // When runs in a service this service should not die when |sessionCount| > 0.
|
| + void onSessionCountChange(int sessionCount);
|
| +
|
| + // Lets query a socket name when starting a new session. Result may change
|
| + // (in multiprocess scenario: when browser process stops and then starts again).
|
| + void querySocketName(QuerySocketCallback callback);
|
| + }
|
| +
|
| + public DevToolsBridgeServer(Delegate delegate) {
|
| + assert delegate != null;
|
| +
|
| + mExecutor = LooperExecutor.newInstanceForMainLooper(delegate.getContext());
|
| + mHandler = new GCDNotificationHandler(this);
|
| + mDelegate = delegate;
|
| }
|
|
|
| private void checkCalledOnHostServiceThread() {
|
| assert mExecutor.isCalledOnSessionThread();
|
| }
|
|
|
| - public Service getContext() {
|
| - return mHost;
|
| + public Context getContext() {
|
| + return mDelegate.getContext();
|
| }
|
|
|
| public SharedPreferences getPreferences() {
|
| - return getPreferences(mHost);
|
| + return getPreferences(getContext());
|
| }
|
|
|
| public static SharedPreferences getPreferences(Context context) {
|
| @@ -72,15 +73,18 @@ public class DevToolsBridgeServer implements SignalingReceiver {
|
| DevToolsBridgeServer.class.getName(), Context.MODE_PRIVATE);
|
| }
|
|
|
| - public void onStartCommand(Intent intent) {
|
| - String action = intent.getAction();
|
| - if (DISCONNECT_ALL_CLIENTS_ACTION.equals(action)) {
|
| - closeAllSessions();
|
| - } else if (mHandler.isNotification(intent)) {
|
| - mHandler.onNotification(intent, startSticky());
|
| + public void handleCloudMessage(Intent cloudMessage, Runnable completionHandler) {
|
| + if (mHandler.isNotification(cloudMessage)) {
|
| + mHandler.onNotification(cloudMessage, completionHandler);
|
| + } else {
|
| + completionHandler.run();
|
| }
|
| }
|
|
|
| + public void updateCloudMessagesId(String channelId, Runnable completionHandler) {
|
| + mHandler.updateCloudMessagesId(channelId, completionHandler);
|
| + }
|
| +
|
| /**
|
| * Should be called in service's onDestroy.
|
| */
|
| @@ -96,22 +100,31 @@ public class DevToolsBridgeServer implements SignalingReceiver {
|
|
|
| @Override
|
| public void startSession(
|
| - String sessionId,
|
| - RTCConfiguration config,
|
| - String offer,
|
| - SessionBase.NegotiationCallback callback) {
|
| + final String sessionId,
|
| + final RTCConfiguration config,
|
| + final String offer,
|
| + final SessionBase.NegotiationCallback callback) {
|
| checkCalledOnHostServiceThread();
|
| if (mSessions.containsKey(sessionId)) {
|
| - callback.onFailure("Session already exists");
|
| + callback.onFailure("Session " + sessionId + " already exists");
|
| return;
|
| }
|
|
|
| - ServerSession session = new ServerSession(mFactory, mExecutor, mSocketName);
|
| - session.setEventListener(new SessionEventListener(sessionId));
|
| - mSessions.put(sessionId, session);
|
| - session.startSession(config, offer, callback);
|
| - if (mSessions.size() == 1)
|
| - startForeground();
|
| + mDelegate.querySocketName(new QuerySocketCallback() {
|
| + @Override
|
| + public void onSuccess(String socketName) {
|
| + ServerSession session = new ServerSession(mFactory, mExecutor, socketName);
|
| + session.setEventListener(new SessionEventListener(sessionId));
|
| + mSessions.put(sessionId, session);
|
| + session.startSession(config, offer, callback);
|
| + mDelegate.onSessionCountChange(mSessions.size());
|
| + }
|
| +
|
| + @Override
|
| + public void onFailure() {
|
| + callback.onFailure("Socket not available");
|
| + }
|
| + });
|
| }
|
|
|
| @Override
|
| @@ -142,25 +155,6 @@ public class DevToolsBridgeServer implements SignalingReceiver {
|
| session.iceExchange(clientCandidates, callback);
|
| }
|
|
|
| - protected void startForeground() {
|
| - mForegroundCompletionCallback = startSticky();
|
| - checkCalledOnHostServiceThread();
|
| - mHost.startForeground(
|
| - NOTIFICATION_ID,
|
| - mServiceUIFactory.newForegroundNotification(mHost, DISCONNECT_ALL_CLIENTS_ACTION));
|
| - }
|
| -
|
| - protected void stopForeground() {
|
| - checkCalledOnHostServiceThread();
|
| - mHost.stopForeground(true);
|
| - mForegroundCompletionCallback.run();
|
| - mForegroundCompletionCallback = null;
|
| - }
|
| -
|
| - public void postOnServiceThread(Runnable runnable) {
|
| - mExecutor.postOnSessionThread(0, runnable);
|
| - }
|
| -
|
| private class SessionEventListener implements SessionBase.EventListener {
|
| private final String mSessionId;
|
|
|
| @@ -172,62 +166,16 @@ public class DevToolsBridgeServer implements SignalingReceiver {
|
| checkCalledOnHostServiceThread();
|
|
|
| mSessions.remove(mSessionId);
|
| - if (mSessions.size() == 0) {
|
| - stopForeground();
|
| - }
|
| + mDelegate.onSessionCountChange(mSessions.size());
|
| }
|
| }
|
|
|
| - private void closeAllSessions() {
|
| + public void closeAllSessions() {
|
| if (mSessions.isEmpty()) return;
|
| for (ServerSession session : mSessions.values()) {
|
| session.stop();
|
| }
|
| mSessions.clear();
|
| - stopForeground();
|
| - }
|
| -
|
| - /**
|
| - * TODO(serya): Move service lifetime management to DevToolsBridgeServiceBase.
|
| - * Helper method for doing background tasks. Usage:
|
| - *
|
| - * int onStartCommand(...) {
|
| - * if (..*) {
|
| - * startWorkInBackground(startSticky());
|
| - * return START_STICKY;
|
| - * }
|
| - * ...
|
| - * }
|
| - *
|
| - * void doWorkInBackground(final Runable completionHandler) {
|
| - * ... start background task
|
| - * @Override
|
| - * void run() {
|
| - * ...
|
| - * completionHandler.run();
|
| - * }
|
| - * }
|
| - */
|
| - public Runnable startSticky() {
|
| - checkCalledOnHostServiceThread();
|
| - if (mWakeLock == null) {
|
| - PowerManager pm = (PowerManager) mHost.getSystemService(Context.POWER_SERVICE);
|
| - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
|
| - }
|
| - mWakeLock.acquire();
|
| - return new StartStickyCompletionHandler();
|
| - }
|
| -
|
| - private class StartStickyCompletionHandler implements Runnable {
|
| - @Override
|
| - public void run() {
|
| - postOnServiceThread(new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - mWakeLock.release();
|
| - if (!mWakeLock.isHeld()) mHost.stopSelf();
|
| - }
|
| - });
|
| - }
|
| + mDelegate.onSessionCountChange(mSessions.size());
|
| }
|
| }
|
|
|