diff options
Diffstat (limited to 'services/java/com/android/server/NsdService.java')
| -rw-r--r-- | services/java/com/android/server/NsdService.java | 468 |
1 files changed, 324 insertions, 144 deletions
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java index 8014e27..f33bf8b 100644 --- a/services/java/com/android/server/NsdService.java +++ b/services/java/com/android/server/NsdService.java @@ -17,6 +17,8 @@ package com.android.server; import android.content.Context; +import android.content.ContentResolver; +import android.content.Intent; import android.content.pm.PackageManager; import android.net.nsd.DnsSdServiceInfo; import android.net.nsd.DnsSdTxtRecord; @@ -28,6 +30,7 @@ import android.os.HandlerThread; import android.os.Message; import android.os.Messenger; import android.os.IBinder; +import android.provider.Settings; import android.util.Slog; import java.io.FileDescriptor; @@ -41,6 +44,9 @@ import java.util.concurrent.CountDownLatch; import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; import com.android.server.am.BatteryStatsService; import com.android.server.NativeDaemonConnector.Command; import com.android.internal.R; @@ -58,6 +64,8 @@ public class NsdService extends INsdManager.Stub { private static final boolean DBG = true; private Context mContext; + private ContentResolver mContentResolver; + private NsdStateMachine mNsdStateMachine; /** * Clients receiving asynchronous messages @@ -69,189 +77,342 @@ public class NsdService extends INsdManager.Stub { private int INVALID_ID = 0; private int mUniqueId = 1; - /** - * Handles client(app) connections - */ - private class AsyncServiceHandler extends Handler { + private static final int BASE = Protocol.BASE_NSD_MANAGER; + private static final int CMD_TO_STRING_COUNT = NsdManager.STOP_RESOLVE - BASE + 1; + private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; + + static { + sCmdToString[NsdManager.DISCOVER_SERVICES - BASE] = "DISCOVER"; + sCmdToString[NsdManager.STOP_DISCOVERY - BASE] = "STOP-DISCOVER"; + sCmdToString[NsdManager.REGISTER_SERVICE - BASE] = "REGISTER"; + sCmdToString[NsdManager.UNREGISTER_SERVICE - BASE] = "UNREGISTER"; + sCmdToString[NsdManager.RESOLVE_SERVICE - BASE] = "RESOLVE"; + sCmdToString[NsdManager.STOP_RESOLVE - BASE] = "STOP-RESOLVE"; + } - AsyncServiceHandler(android.os.Looper looper) { - super(looper); + private static String cmdToString(int cmd) { + cmd -= BASE; + if ((cmd >= 0) && (cmd < sCmdToString.length)) { + return sCmdToString[cmd]; + } else { + return null; } + } + + private class NsdStateMachine extends StateMachine { + + private DefaultState mDefaultState = new DefaultState(); + private DisabledState mDisabledState = new DisabledState(); + private EnabledState mEnabledState = new EnabledState(); @Override - public void handleMessage(Message msg) { - ClientInfo clientInfo; - DnsSdServiceInfo servInfo; - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - AsyncChannel c = (AsyncChannel) msg.obj; - if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); - c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); - ClientInfo cInfo = new ClientInfo(c, msg.replyTo); - if (mClients.size() == 0) { - startMDnsDaemon(); + protected String getMessageInfo(Message msg) { + return cmdToString(msg.what); + } + + NsdStateMachine(String name) { + super(name); + addState(mDefaultState); + addState(mDisabledState, mDefaultState); + addState(mEnabledState, mDefaultState); + if (isNsdEnabled()) { + setInitialState(mEnabledState); + } else { + setInitialState(mDisabledState); + } + setProcessedMessagesSize(25); + } + + class DefaultState extends State { + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + AsyncChannel c = (AsyncChannel) msg.obj; + if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); + c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); + ClientInfo cInfo = new ClientInfo(c, msg.replyTo); + mClients.put(msg.replyTo, cInfo); + } else { + Slog.e(TAG, "Client connection failure, error=" + msg.arg1); } - mClients.put(msg.replyTo, cInfo); - } else { - Slog.e(TAG, "Client connection failure, error=" + msg.arg1); - } - break; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { - Slog.e(TAG, "Send failed, client connection lost"); - } else { - if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); - } - mClients.remove(msg.replyTo); - if (mClients.size() == 0) { - stopMDnsDaemon(); - } - break; - case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: - AsyncChannel ac = new AsyncChannel(); - ac.connect(mContext, this, msg.replyTo); - break; - case NsdManager.DISCOVER_SERVICES: - if (DBG) Slog.d(TAG, "Discover services"); - servInfo = (DnsSdServiceInfo) msg.obj; - clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mDiscoveryId != INVALID_ID) { - //discovery already in progress - if (DBG) Slog.d(TAG, "discovery in progress"); - mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.ALREADY_ACTIVE); break; - } - clientInfo.mDiscoveryId = getUniqueId(); - if (discoverServices(clientInfo.mDiscoveryId, servInfo.getServiceType())) { - mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED); - } else { + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { + Slog.e(TAG, "Send failed, client connection lost"); + } else { + if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); + } + mClients.remove(msg.replyTo); + break; + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: + AsyncChannel ac = new AsyncChannel(); + ac.connect(mContext, getHandler(), msg.replyTo); + break; + case NsdManager.DISCOVER_SERVICES: mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.ERROR); - clientInfo.mDiscoveryId = INVALID_ID; - } - break; - case NsdManager.STOP_DISCOVERY: - if (DBG) Slog.d(TAG, "Stop service discovery"); - clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mDiscoveryId == INVALID_ID) { - //already stopped - if (DBG) Slog.d(TAG, "discovery already stopped"); - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.ALREADY_ACTIVE); + NsdManager.BUSY); + break; + case NsdManager.STOP_DISCOVERY: + mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, + NsdManager.ERROR); break; - } - if (stopServiceDiscovery(clientInfo.mDiscoveryId)) { - clientInfo.mDiscoveryId = INVALID_ID; - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); - } else { - mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.ERROR); - } - break; - case NsdManager.REGISTER_SERVICE: - if (DBG) Slog.d(TAG, "Register service"); - clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mRegisteredIds.size() >= ClientInfo.MAX_REG) { - if (DBG) Slog.d(TAG, "register service exceeds limit"); - mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.MAX_REGS_REACHED); - } - - int id = getUniqueId(); - if (registerService(id, (DnsSdServiceInfo) msg.obj)) { - clientInfo.mRegisteredIds.add(id); - } else { + case NsdManager.REGISTER_SERVICE: mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, NsdManager.ERROR); - } - break; - case NsdManager.UNREGISTER_SERVICE: - if (DBG) Slog.d(TAG, "unregister service"); - clientInfo = mClients.get(msg.replyTo); - int regId = msg.arg1; - if (clientInfo.mRegisteredIds.remove(new Integer(regId)) && - unregisterService(regId)) { - mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED); - } else { + break; + case NsdManager.UNREGISTER_SERVICE: mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, NsdManager.ERROR); - } - break; - case NsdManager.UPDATE_SERVICE: - if (DBG) Slog.d(TAG, "Update service"); - //TODO: implement - mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED); - break; - case NsdManager.RESOLVE_SERVICE: - if (DBG) Slog.d(TAG, "Resolve service"); - servInfo = (DnsSdServiceInfo) msg.obj; - clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mResolveId != INVALID_ID) { - //first cancel existing resolve - stopResolveService(clientInfo.mResolveId); - } - - clientInfo.mResolveId = getUniqueId(); - if (!resolveService(clientInfo.mResolveId, servInfo)) { + break; + case NsdManager.RESOLVE_SERVICE: mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, NsdManager.ERROR); - clientInfo.mResolveId = INVALID_ID; - } - break; - case NsdManager.STOP_RESOLVE: - if (DBG) Slog.d(TAG, "Stop resolve"); - clientInfo = mClients.get(msg.replyTo); - if (clientInfo.mResolveId == INVALID_ID) { - //already stopped - if (DBG) Slog.d(TAG, "resolve already stopped"); - mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, - NsdManager.ALREADY_ACTIVE); break; - } - if (stopResolveService(clientInfo.mResolveId)) { - clientInfo.mResolveId = INVALID_ID; - mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_SUCCEEDED); - } else { + case NsdManager.STOP_RESOLVE: mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, NsdManager.ERROR); - } - break; - default: - Slog.d(TAG, "NsdServicehandler.handleMessage ignoring msg=" + msg); - break; + break; + default: + Slog.e(TAG, "Unhandled " + msg); + return NOT_HANDLED; + } + return HANDLED; } } + + class DisabledState extends State { + @Override + public void enter() { + sendNsdStateChangeBroadcast(false); + } + + @Override + public boolean processMessage(Message msg) { + switch (msg.what) { + case NsdManager.ENABLE: + transitionTo(mEnabledState); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class EnabledState extends State { + @Override + public void enter() { + sendNsdStateChangeBroadcast(true); + if (mClients.size() > 0) { + startMDnsDaemon(); + } + } + + @Override + public void exit() { + if (mClients.size() > 0) { + stopMDnsDaemon(); + } + } + + @Override + public boolean processMessage(Message msg) { + ClientInfo clientInfo; + DnsSdServiceInfo servInfo; + boolean result = HANDLED; + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + //First client + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL && + mClients.size() == 0) { + startMDnsDaemon(); + } + result = NOT_HANDLED; + break; + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + //Last client + if (mClients.size() == 1) { + stopMDnsDaemon(); + } + result = NOT_HANDLED; + break; + case NsdManager.DISABLE: + //TODO: cleanup clients + transitionTo(mDisabledState); + break; + case NsdManager.DISCOVER_SERVICES: + if (DBG) Slog.d(TAG, "Discover services"); + servInfo = (DnsSdServiceInfo) msg.obj; + clientInfo = mClients.get(msg.replyTo); + if (clientInfo.mDiscoveryId != INVALID_ID) { + //discovery already in progress + if (DBG) Slog.d(TAG, "discovery in progress"); + mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + NsdManager.ALREADY_ACTIVE); + break; + } + clientInfo.mDiscoveryId = getUniqueId(); + if (discoverServices(clientInfo.mDiscoveryId, servInfo.getServiceType())) { + mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED); + } else { + mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + NsdManager.ERROR); + clientInfo.mDiscoveryId = INVALID_ID; + } + break; + case NsdManager.STOP_DISCOVERY: + if (DBG) Slog.d(TAG, "Stop service discovery"); + clientInfo = mClients.get(msg.replyTo); + if (clientInfo.mDiscoveryId == INVALID_ID) { + //already stopped + if (DBG) Slog.d(TAG, "discovery already stopped"); + mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, + NsdManager.ALREADY_ACTIVE); + break; + } + if (stopServiceDiscovery(clientInfo.mDiscoveryId)) { + clientInfo.mDiscoveryId = INVALID_ID; + mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); + } else { + mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, + NsdManager.ERROR); + } + break; + case NsdManager.REGISTER_SERVICE: + if (DBG) Slog.d(TAG, "Register service"); + clientInfo = mClients.get(msg.replyTo); + if (clientInfo.mRegisteredIds.size() >= ClientInfo.MAX_REG) { + if (DBG) Slog.d(TAG, "register service exceeds limit"); + mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, + NsdManager.MAX_REGS_REACHED); + } + + int id = getUniqueId(); + if (registerService(id, (DnsSdServiceInfo) msg.obj)) { + clientInfo.mRegisteredIds.add(id); + } else { + mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, + NsdManager.ERROR); + } + break; + case NsdManager.UNREGISTER_SERVICE: + if (DBG) Slog.d(TAG, "unregister service"); + clientInfo = mClients.get(msg.replyTo); + int regId = msg.arg1; + if (clientInfo.mRegisteredIds.remove(new Integer(regId)) && + unregisterService(regId)) { + mReplyChannel.replyToMessage(msg, + NsdManager.UNREGISTER_SERVICE_SUCCEEDED); + } else { + mReplyChannel.replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, + NsdManager.ERROR); + } + break; + case NsdManager.UPDATE_SERVICE: + if (DBG) Slog.d(TAG, "Update service"); + //TODO: implement + mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED); + break; + case NsdManager.RESOLVE_SERVICE: + if (DBG) Slog.d(TAG, "Resolve service"); + servInfo = (DnsSdServiceInfo) msg.obj; + clientInfo = mClients.get(msg.replyTo); + if (clientInfo.mResolveId != INVALID_ID) { + //first cancel existing resolve + stopResolveService(clientInfo.mResolveId); + } + + clientInfo.mResolveId = getUniqueId(); + if (!resolveService(clientInfo.mResolveId, servInfo)) { + mReplyChannel.replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, + NsdManager.ERROR); + clientInfo.mResolveId = INVALID_ID; + } + break; + case NsdManager.STOP_RESOLVE: + if (DBG) Slog.d(TAG, "Stop resolve"); + clientInfo = mClients.get(msg.replyTo); + if (clientInfo.mResolveId == INVALID_ID) { + //already stopped + if (DBG) Slog.d(TAG, "resolve already stopped"); + mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, + NsdManager.ALREADY_ACTIVE); + break; + } + if (stopResolveService(clientInfo.mResolveId)) { + clientInfo.mResolveId = INVALID_ID; + mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_SUCCEEDED); + } else { + mReplyChannel.replyToMessage(msg, NsdManager.STOP_RESOLVE_FAILED, + NsdManager.ERROR); + } + break; + default: + result = NOT_HANDLED; + break; + } + return result; + } + } } - private AsyncServiceHandler mAsyncServiceHandler; private NativeDaemonConnector mNativeConnector; private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1); private NsdService(Context context) { mContext = context; - - HandlerThread nsdThread = new HandlerThread("NsdService"); - nsdThread.start(); - mAsyncServiceHandler = new AsyncServiceHandler(nsdThread.getLooper()); + mContentResolver = context.getContentResolver(); mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10, MDNS_TAG, 25); + + mNsdStateMachine = new NsdStateMachine(TAG); + mNsdStateMachine.start(); + Thread th = new Thread(mNativeConnector, MDNS_TAG); th.start(); } public static NsdService create(Context context) throws InterruptedException { NsdService service = new NsdService(context); - /* service.mNativeDaemonConnected.await(); */ + service.mNativeDaemonConnected.await(); return service; } public Messenger getMessenger() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); - return new Messenger(mAsyncServiceHandler); + return new Messenger(mNsdStateMachine.getHandler()); + } + + public void setEnabled(boolean enable) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL, + "NsdService"); + Settings.Secure.putInt(mContentResolver, Settings.Secure.NSD_ON, enable ? 1 : 0); + if (enable) { + mNsdStateMachine.sendMessage(NsdManager.ENABLE); + } else { + mNsdStateMachine.sendMessage(NsdManager.DISABLE); + } + } + + private void sendNsdStateChangeBroadcast(boolean enabled) { + final Intent intent = new Intent(NsdManager.ACTION_NSD_STATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (enabled) { + intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_ENABLED); + } else { + intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED); + } + mContext.sendStickyBroadcast(intent); + } + + private boolean isNsdEnabled() { + boolean ret = Settings.Secure.getInt(mContentResolver, Settings.Secure.NSD_ON, 1) == 1; + if (DBG) Slog.d(TAG, "Network service discovery enabled " + ret); + return ret; } private int getUniqueId() { @@ -522,7 +683,7 @@ public class NsdService extends INsdManager.Stub { } @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid=" @@ -531,7 +692,12 @@ public class NsdService extends INsdManager.Stub { return; } - pw.println("Internal state:"); + for (ClientInfo client : mClients.values()) { + pw.println("Client Info"); + pw.println(client); + } + + mNsdStateMachine.dump(fd, pw, args); } private ClientInfo getClientByDiscovery(int discoveryId) { @@ -579,5 +745,19 @@ public class NsdService extends INsdManager.Stub { mDiscoveryId = mResolveId = INVALID_ID; if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("mChannel ").append(mChannel).append("\n"); + sb.append("mMessenger ").append(mMessenger).append("\n"); + sb.append("mDiscoveryId ").append(mDiscoveryId).append("\n"); + sb.append("mResolveId ").append(mResolveId).append("\n"); + sb.append("mResolvedService ").append(mResolvedService).append("\n"); + for(int regId : mRegisteredIds) { + sb.append("regId ").append(regId).append("\n"); + } + return sb.toString(); + } } } |
