Commit 7d024d37 authored by Irfan Sheriff's avatar Irfan Sheriff
Browse files

Add initial framework for DNS service discovery

Change-Id: I53c0b7ebfd75e520ebb7553612f1aa8413b6b79b
parent 26d4452a
......@@ -117,6 +117,7 @@ LOCAL_SRC_FILES += \
core/java/android/net/INetworkPolicyListener.aidl \
core/java/android/net/INetworkPolicyManager.aidl \
core/java/android/net/INetworkStatsService.aidl \
core/java/android/net/nsd/INsdManager.aidl \
core/java/android/nfc/INdefPushCallback.aidl \
core/java/android/nfc/INfcAdapter.aidl \
core/java/android/nfc/INfcAdapterExtras.aidl \
......
......@@ -59,6 +59,8 @@ import android.net.NetworkPolicyManager;
import android.net.ThrottleManager;
import android.net.IThrottleManager;
import android.net.Uri;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.IWifiP2pManager;
......@@ -372,6 +374,14 @@ class ContextImpl extends Context {
ctx.mMainThread.getHandler());
}});
registerService(NSD_SERVICE, new ServiceFetcher() {
@Override
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(NSD_SERVICE);
INsdManager service = INsdManager.Stub.asInterface(b);
return new NsdManager(service);
}});
// Note: this was previously cached in a static variable, but
// constructed using mMainThread.getHandler(), so converting
// it to be a regular Context-cached service...
......
......@@ -1768,6 +1768,18 @@ public abstract class Context {
*/
public static final String WIFI_P2P_SERVICE = "wifip2p";
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.NsdManager} for handling management of network service
* discovery
*
* @hide
* @see #getSystemService
* @see android.net.NsdManager
*/
public static final String NSD_SERVICE = "servicediscovery";
/**
* Use with {@link #getSystemService} to retrieve a
* {@link android.media.AudioManager} for handling management of volume,
......
......@@ -14,276 +14,292 @@
* See the License for the specific language governing permissions and
* limitations under the License.
To do:
- implement remove()
- fix set() to replace existing values
To do:
- implement remove()
- fix set() to replace existing values
*/
package android.net.nsd;
package com.apple.dnssd;
/**
Object used to construct and parse DNS-SD format TXT records.
For more info see <a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS-Based Service Discovery</a>, section 6.
*/
public class TXTRecord
{
/*
DNS-SD specifies that a TXT record corresponding to an SRV record consist of
a packed array of bytes, each preceded by a length byte. Each string
is an attribute-value pair.
The TXTRecord object stores the entire TXT data as a single byte array, traversing it
as need be to implement its various methods.
*/
static final protected byte kAttrSep = '=';
protected byte[] fBytes;
/** Constructs a new, empty TXT record. */
public TXTRecord()
{ fBytes = new byte[0]; }
/** Constructs a new TXT record from a byte array in the standard format. */
public TXTRecord( byte[] initBytes)
{ fBytes = (byte[]) initBytes.clone(); }
/** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
@param key
The key name. Must be ASCII, with no '=' characters.
<P>
@param value
Value to be encoded into bytes using the default platform character set.
*/
public void set( String key, String value)
{
byte[] valBytes = (value != null) ? value.getBytes() : null;
this.set( key, valBytes);
}
/** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
@param key
The key name. Must be ASCII, with no '=' characters.
<P>
@param value
Binary representation of the value.
*/
public void set( String key, byte[] value)
{
byte[] keyBytes;
int valLen = (value != null) ? value.length : 0;
try {
keyBytes = key.getBytes( "US-ASCII");
}
catch ( java.io.UnsupportedEncodingException uee) {
throw new IllegalArgumentException();
}
for ( int i=0; i < keyBytes.length; i++)
if ( keyBytes[i] == '=')
throw new IllegalArgumentException();
if ( keyBytes.length + valLen >= 255)
throw new ArrayIndexOutOfBoundsException();
int prevLoc = this.remove( key);
if ( prevLoc == -1)
prevLoc = this.size();
this.insert( keyBytes, value, prevLoc);
}
protected void insert( byte[] keyBytes, byte[] value, int index)
// Insert a key-value pair at index
{
byte[] oldBytes = fBytes;
int valLen = (value != null) ? value.length : 0;
int insertion = 0;
int newLen, avLen;
// locate the insertion point
for ( int i=0; i < index && insertion < fBytes.length; i++)
insertion += (0xFF & (fBytes[ insertion] + 1));
avLen = keyBytes.length + valLen + (value != null ? 1 : 0);
newLen = avLen + oldBytes.length + 1;
fBytes = new byte[ newLen];
System.arraycopy( oldBytes, 0, fBytes, 0, insertion);
int secondHalfLen = oldBytes.length - insertion;
System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen);
fBytes[ insertion] = ( byte) avLen;
System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length);
if ( value != null)
{
fBytes[ insertion + 1 + keyBytes.length] = kAttrSep;
System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen);
}
}
/** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */
public int remove( String key)
{
int avStart = 0;
for ( int i=0; avStart < fBytes.length; i++)
{
int avLen = fBytes[ avStart];
if ( key.length() <= avLen &&
( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep))
{
String s = new String( fBytes, avStart + 1, key.length());
if ( 0 == key.compareToIgnoreCase( s))
{
byte[] oldBytes = fBytes;
fBytes = new byte[ oldBytes.length - avLen - 1];
System.arraycopy( oldBytes, 0, fBytes, 0, avStart);
System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1);
return i;
}
}
avStart += (0xFF & (avLen + 1));
}
return -1;
}
/** Return the number of keys in the TXT record. */
public int size()
{
int i, avStart;
for ( i=0, avStart=0; avStart < fBytes.length; i++)
avStart += (0xFF & (fBytes[ avStart] + 1));
return i;
}
/** Return true if key is present in the TXT record, false if not. */
public boolean contains( String key)
{
String s = null;
for ( int i=0; null != ( s = this.getKey( i)); i++)
if ( 0 == key.compareToIgnoreCase( s))
return true;
return false;
}
/** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
public String getKey( int index)
{
int avStart = 0;
for ( int i=0; i < index && avStart < fBytes.length; i++)
avStart += fBytes[ avStart] + 1;
if ( avStart < fBytes.length)
{
int avLen = fBytes[ avStart];
int aLen = 0;
for ( aLen=0; aLen < avLen; aLen++)
if ( fBytes[ avStart + aLen + 1] == kAttrSep)
break;
return new String( fBytes, avStart + 1, aLen);
}
return null;
}
/**
Look up a key in the TXT record by zero-based index and return its value. <P>
Returns null if index exceeds the total number of keys.
Returns null if the key is present with no value.
*/
public byte[] getValue( int index)
{
int avStart = 0;
byte[] value = null;
for ( int i=0; i < index && avStart < fBytes.length; i++)
avStart += fBytes[ avStart] + 1;
if ( avStart < fBytes.length)
{
int avLen = fBytes[ avStart];
int aLen = 0;
for ( aLen=0; aLen < avLen; aLen++)
{
if ( fBytes[ avStart + aLen + 1] == kAttrSep)
{
value = new byte[ avLen - aLen - 1];
System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1);
break;
}
}
}
return value;
}
/** Converts the result of getValue() to a string in the platform default character set. */
public String getValueAsString( int index)
{
byte[] value = this.getValue( index);
return value != null ? new String( value) : null;
}
/** Get the value associated with a key. Will be null if the key is not defined.
Array will have length 0 if the key is defined with an = but no value.<P>
@param forKey
The left-hand side of the key-value pair.
<P>
@return The binary representation of the value.
*/
public byte[] getValue( String forKey)
{
String s = null;
int i;
for ( i=0; null != ( s = this.getKey( i)); i++)
if ( 0 == forKey.compareToIgnoreCase( s))
return this.getValue( i);
return null;
}
/** Converts the result of getValue() to a string in the platform default character set.<P>
@param forKey
The left-hand side of the key-value pair.
<P>
@return The value represented in the default platform character set.
*/
public String getValueAsString( String forKey)
{
byte[] val = this.getValue( forKey);
return val != null ? new String( val) : null;
}
/** Return the contents of the TXT record as raw bytes. */
public byte[] getRawBytes() { return (byte[]) fBytes.clone(); }
/** Return a string representation of the object. */
public String toString()
{
String a, result = null;
for ( int i=0; null != ( a = this.getKey( i)); i++)
{
String av = String.valueOf( i) + "={" + a;
String val = this.getValueAsString( i);
if ( val != null)
av += "=" + val + "}";
else
av += "}";
if ( result == null)
result = av;
else
result = result + ", " + av;
}
return result != null ? result : "";
}
import android.os.Parcelable;
import android.os.Parcel;
/**
* This class handles TXT record data for DNS based service discovery as specified at
* http://tools.ietf.org/html/draft-cheshire-dnsext-dns-sd-11
*
* DNS-SD specifies that a TXT record corresponding to an SRV record consist of
* a packed array of bytes, each preceded by a length byte. Each string
* is an attribute-value pair.
*
* The DnsSdTxtRecord object stores the entire TXT data as a single byte array, traversing it
* as need be to implement its various methods.
*
* @hide
*/
public class DnsSdTxtRecord implements Parcelable {
private static final byte mSeperator = '=';
private byte[] mData;
/** Constructs a new, empty TXT record. */
public DnsSdTxtRecord() {
mData = new byte[0];
}
/** Constructs a new TXT record from a byte array in the standard format. */
public DnsSdTxtRecord(byte[] data) {
mData = (byte[]) data.clone();
}
/** Copy constructor */
public DnsSdTxtRecord(DnsSdTxtRecord src) {
if (src != null && src.mData != null) {
mData = (byte[]) src.mData.clone();
}
}
/**
* Set a key/value pair. Setting an existing key will replace its value.
* @param key Must be ascii with no '='
* @param value matching value to key
*/
public void set(String key, String value) {
byte[] keyBytes;
byte[] valBytes;
int valLen;
if (value != null) {
valBytes = value.getBytes();
valLen = valBytes.length;
} else {
valBytes = null;
valLen = 0;
}
try {
keyBytes = key.getBytes("US-ASCII");
}
catch (java.io.UnsupportedEncodingException e) {
throw new IllegalArgumentException("key should be US-ASCII");
}
for (int i = 0; i < keyBytes.length; i++) {
if (keyBytes[i] == '=') {
throw new IllegalArgumentException("= is not a valid character in key");
}
}
if (keyBytes.length + valLen >= 255) {
throw new IllegalArgumentException("Key and Value length cannot exceed 255 bytes");
}
int currentLoc = remove(key);
if (currentLoc == -1)
currentLoc = keyCount();
insert(keyBytes, valBytes, currentLoc);
}
/**
* Get a value for a key
*
* @param key
* @return The value associated with the key
*/
public String get(String key) {
byte[] val = this.getValue(key);
return val != null ? new String(val) : null;
}
/** Remove a key/value pair. If found, returns the index or -1 if not found */
public int remove(String key) {
int avStart = 0;
for (int i=0; avStart < mData.length; i++) {
int avLen = mData[avStart];
if (key.length() <= avLen &&
(key.length() == avLen || mData[avStart + key.length() + 1] == mSeperator)) {
String s = new String(mData, avStart + 1, key.length());
if (0 == key.compareToIgnoreCase(s)) {
byte[] oldBytes = mData;
mData = new byte[oldBytes.length - avLen - 1];
System.arraycopy(oldBytes, 0, mData, 0, avStart);
System.arraycopy(oldBytes, avStart + avLen + 1, mData, avStart,
oldBytes.length - avStart - avLen - 1);
return i;
}
}
avStart += (0xFF & (avLen + 1));
}
return -1;
}
/** Return the count of keys */
public int keyCount() {
int count = 0, nextKey;
for (nextKey = 0; nextKey < mData.length; count++) {
nextKey += (0xFF & (mData[nextKey] + 1));
}
return count;
}
/** Return true if key is present, false if not. */
public boolean contains(String key) {
String s = null;
for (int i = 0; null != (s = this.getKey(i)); i++) {
if (0 == key.compareToIgnoreCase(s)) return true;
}
return false;
}
/* Gets the size in bytes */
public int size() {
return mData.length;
}
/* Gets the raw data in bytes */
public byte[] getRawData() {
return mData;
}
private void insert(byte[] keyBytes, byte[] value, int index) {
byte[] oldBytes = mData;
int valLen = (value != null) ? value.length : 0;
int insertion = 0;
int newLen, avLen;
for (int i = 0; i < index && insertion < mData.length; i++) {
insertion += (0xFF & (mData[insertion] + 1));
}
avLen = keyBytes.length + valLen + (value != null ? 1 : 0);
newLen = avLen + oldBytes.length + 1;
mData = new byte[newLen];
System.arraycopy(oldBytes, 0, mData, 0, insertion);
int secondHalfLen = oldBytes.length - insertion;
System.arraycopy(oldBytes, insertion, mData, newLen - secondHalfLen, secondHalfLen);
mData[insertion] = (byte) avLen;
System.arraycopy(keyBytes, 0, mData, insertion + 1, keyBytes.length);
if (value != null) {
mData[insertion + 1 + keyBytes.length] = mSeperator;
System.arraycopy(value, 0, mData, insertion + keyBytes.length + 2, valLen);
}
}
/** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
private String getKey(int index) {
int avStart = 0;
for (int i=0; i < index && avStart < mData.length; i++) {
avStart += mData[avStart] + 1;
}
if (avStart < mData.length) {
int avLen = mData[avStart];
int aLen = 0;
for (aLen=0; aLen < avLen; aLen++) {
if (mData[avStart + aLen + 1] == mSeperator) break;
}
return new String(mData, avStart + 1, aLen);
}
return null;
}
/**
* Look up a key in the TXT record by zero-based index and return its value.
* Returns null if index exceeds the total number of keys.
* Returns null if the key is present with no value.
*/
private byte[] getValue(int index) {
int avStart = 0;
byte[] value = null;
for (int i=0; i < index && avStart < mData.length; i++) {
avStart += mData[avStart] + 1;
}
if (avStart < mData.length) {
int avLen = mData[avStart];
int aLen = 0;
for (aLen=0; aLen < avLen; aLen++) {
if (mData[avStart + aLen + 1] == mSeperator) {
value = new byte[avLen - aLen - 1];
System.arraycopy(mData, avStart + aLen + 2, value, 0, avLen - aLen - 1);
break;
}
}
}
return value;
}
private String getValueAsString(int index) {
byte[] value = this.getValue(index);
return value != null ? new String(value) : null;
}
private byte[] getValue(String forKey) {
String s = null;
int i;
for (i = 0; null != (s = this.getKey(i)); i++) {
if (0 == forKey.compareToIgnoreCase(s)) {
return this.getValue(i);
}
}
return null;
}
/**
* Return a string representation.
* Example : {key1=value1},{key2=value2}..
*
* For a key say like "key3" with null value
* {key1=value1},{key2=value2}{key3}
*/
public String toString() {
String a, result = null;
for (int i = 0; null != (a = this.getKey(i)); i++) {
String av = "{" + a;
String val = this.getValueAsString(i);
if (val != null)
av += "=" + val + "}";
else
av += "}";
if (result == null)
result = av;
else
result = result + ", " + av;
}
return result != null ? result : "";
}
/** Implement the Parcelable interface */
public int describeContents() {
return 0;
}
/** Implement the Parcelable interface */
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(mData);
}
/** Implement the Parcelable interface */
public static final Creator<DnsSdTxtRecord> CREATOR =
new Creator<DnsSdTxtRecord>() {
public DnsSdTxtRecord createFromParcel(Parcel in) {
DnsSdTxtRecord info = new DnsSdTxtRecord();
in.readByteArray(info.mData);
return info;
}
public DnsSdTxtRecord[] newArray(int size) {
return new DnsSdTxtRecord[size];
}
};
}
/**
* Copyright (c) 2012, 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 android.net.nsd;
import android.os.Messenger;
/**
* Interface that NsdService implements
*
* {@hide}
*/
interface INsdManager
{
Messenger getMessenger();
}
/*
* Copyright (C) 2012 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 android.net.nsd;
/**
* Interface for a network service.
*
* {@hide}
*/
public interface NetworkServiceInfo {
String getServiceName();
void setServiceName(String s);
String getServiceType();
void setServiceType(String s);
}
/*
* Copyright (C) 2012 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 android.net.nsd;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.Messenger;
import android.util.Log;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
/**
* The Network Service Discovery Manager class provides the API for service
* discovery. Service discovery enables applications to discover and connect with services
* on a network. Example applications include a game application discovering another instance
* of the game application or a printer application discovering other printers on a network.
*
* <p> The API is asynchronous and responses to requests from an application are on listener
* callbacks provided by the application. The application needs to do an initialization with
* {@link #initialize} before doing any operation.
*
* <p> Android currently supports DNS based service discovery and it is limited to a local
* network with the use of multicast DNS. In future, this class will be
* extended to support other service discovery mechanisms.
*
* Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
* Context.getSystemService(Context.NSD_SERVICE)}.
* @hide
*
*/
public class NsdManager {
private static final String TAG = "NsdManager";
INsdManager mService;
private static final int BASE = Protocol.BASE_NSD_MANAGER;
/** @hide */
public static final int DISCOVER_SERVICES = BASE + 1;
/** @hide */
public static final int DISCOVER_SERVICES_STARTED = BASE + 2;
/** @hide */
public static final int DISCOVER_SERVICES_FAILED = BASE + 3;
/** @hide */
public static final int SERVICE_FOUND = BASE + 4;
/** @hide */
public static final int SERVICE_LOST = BASE + 5;
/** @hide */
public static final int STOP_DISCOVERY = BASE + 6;
/** @hide */
public static final int STOP_DISCOVERY_FAILED = BASE + 7;
/** @hide */
public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8;
/** @hide */
public static final int REGISTER_SERVICE = BASE + 9;
/** @hide */
public static final int REGISTER_SERVICE_FAILED = BASE + 10;
/** @hide */
public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
/** @hide */
public static final int UPDATE_SERVICE = BASE + 12;
/** @hide */
public static final int UPDATE_SERVICE_FAILED = BASE + 13;
/** @hide */
public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 14;
/** @hide */
public static final int RESOLVE_SERVICE = BASE + 15;
/** @hide */
public static final int RESOLVE_SERVICE_FAILED = BASE + 16;
/** @hide */
public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 17;
/**
* Create a new Nsd instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}.
* @param service the Binder interface
* @hide - hide this because it takes in a parameter of type INsdManager, which
* is a system private class.
*/
public NsdManager(INsdManager service) {
mService = service;
}
/**
* Indicates that the operation failed due to an internal error.
*/
public static final int ERROR = 0;
/**
* Indicates that the operation failed because service discovery is unsupported on the device.
*/
public static final int UNSUPPORTED = 1;
/**
* Indicates that the operation failed because the framework is busy and
* unable to service the request
*/
public static final int BUSY = 2;
/** Interface for callback invocation when framework channel is connected or lost */
public interface ChannelListener {
public void onChannelConnected(Channel c);
/**
* The channel to the framework has been disconnected.
* Application could try re-initializing using {@link #initialize}
*/
public void onChannelDisconnected();
}
public interface ActionListener {
public void onFailure(int errorCode);
public void onSuccess();
}
public interface DnsSdDiscoveryListener {
public void onFailure(int errorCode);
public void onStarted(String registrationType);
public void onServiceFound(DnsSdServiceInfo serviceInfo);
public void onServiceLost(DnsSdServiceInfo serviceInfo);
}
public interface DnsSdRegisterListener {
public void onFailure(int errorCode);
public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo);
}
public interface DnsSdUpdateRegistrationListener {
public void onFailure(int errorCode);
public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
}
public interface DnsSdResolveListener {
public void onFailure(int errorCode);
public void onServiceResolved(DnsSdServiceInfo serviceInfo);
}
/**
* A channel that connects the application to the NetworkService framework.
* Most service operations require a Channel as an argument. An instance of Channel is obtained
* by doing a call on {@link #initialize}
*/
public static class Channel {
Channel(Looper looper, ChannelListener l) {
mAsyncChannel = new AsyncChannel();
mHandler = new ServiceHandler(looper);
mChannelListener = l;
}
private ChannelListener mChannelListener;
private DnsSdDiscoveryListener mDnsSdDiscoveryListener;
private ActionListener mDnsSdStopDiscoveryListener;
private DnsSdRegisterListener mDnsSdRegisterListener;
private DnsSdUpdateRegistrationListener mDnsSdUpdateListener;
private DnsSdResolveListener mDnsSdResolveListener;
AsyncChannel mAsyncChannel;
ServiceHandler mHandler;
class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
if (mChannelListener != null) {
mChannelListener.onChannelConnected(Channel.this);
}
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
if (mChannelListener != null) {
mChannelListener.onChannelDisconnected();
mChannelListener = null;
}
break;
case DISCOVER_SERVICES_STARTED:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onStarted((String) message.obj);
}
break;
case DISCOVER_SERVICES_FAILED:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onFailure(message.arg1);
}
break;
case SERVICE_FOUND:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onServiceFound(
(DnsSdServiceInfo) message.obj);
}
break;
case SERVICE_LOST:
if (mDnsSdDiscoveryListener != null) {
mDnsSdDiscoveryListener.onServiceLost(
(DnsSdServiceInfo) message.obj);
}
break;
case STOP_DISCOVERY_FAILED:
if (mDnsSdStopDiscoveryListener != null) {
mDnsSdStopDiscoveryListener.onFailure(message.arg1);
}
break;
case STOP_DISCOVERY_SUCCEEDED:
if (mDnsSdStopDiscoveryListener != null) {
mDnsSdStopDiscoveryListener.onSuccess();
}
break;
case REGISTER_SERVICE_FAILED:
if (mDnsSdRegisterListener != null) {
mDnsSdRegisterListener.onFailure(message.arg1);
}
break;
case REGISTER_SERVICE_SUCCEEDED:
if (mDnsSdRegisterListener != null) {
mDnsSdRegisterListener.onServiceRegistered(message.arg1,
(DnsSdServiceInfo) message.obj);
}
break;
case UPDATE_SERVICE_FAILED:
if (mDnsSdUpdateListener != null) {
mDnsSdUpdateListener.onFailure(message.arg1);
}
break;
case UPDATE_SERVICE_SUCCEEDED:
if (mDnsSdUpdateListener != null) {
mDnsSdUpdateListener.onServiceUpdated(message.arg1,
(DnsSdTxtRecord) message.obj);
}
break;
case RESOLVE_SERVICE_FAILED:
if (mDnsSdResolveListener != null) {
mDnsSdResolveListener.onFailure(message.arg1);
}
break;
case RESOLVE_SERVICE_SUCCEEDED:
if (mDnsSdResolveListener != null) {
mDnsSdResolveListener.onServiceResolved(
(DnsSdServiceInfo) message.obj);
}
break;
default:
Log.d(TAG, "Ignored " + message);
break;
}
}
}
}
/**
* Registers the application with the service discovery framework. This function
* must be the first to be called before any other operations are performed. No service
* discovery operations must be performed until the ChannelListener callback notifies
* that the channel is connected
*
* @param srcContext is the context of the source
* @param srcLooper is the Looper on which the callbacks are receivied
* @param listener for callback at loss of framework communication.
*/
public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
Messenger messenger = getMessenger();
if (messenger == null) throw new RuntimeException("Failed to initialize");
if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null");
Channel c = new Channel(srcLooper, listener);
c.mAsyncChannel.connect(srcContext, c.mHandler, messenger);
}
/**
* Set the listener for service discovery. Can be null.
*/
public void setDiscoveryListener(Channel c, DnsSdDiscoveryListener b) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mDnsSdDiscoveryListener = b;
}
/**
* Set the listener for stop service discovery. Can be null.
*/
public void setStopDiscoveryListener(Channel c, ActionListener a) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mDnsSdStopDiscoveryListener = a;
}
/**
* Set the listener for service registration. Can be null.
*/
public void setRegisterListener(Channel c, DnsSdRegisterListener b) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mDnsSdRegisterListener = b;
}
/**
* Set the listener for service registration. Can be null.
*/
public void setUpdateRegistrationListener(Channel c, DnsSdUpdateRegistrationListener b) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mDnsSdUpdateListener = b;
}
/**
* Set the listener for service resolution. Can be null.
*/
public void setResolveListener(Channel c, DnsSdResolveListener b) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mDnsSdResolveListener = b;
}
public void registerService(Channel c, DnsSdServiceInfo serviceInfo) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo);
}
public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
}
public void discoverServices(Channel c, String serviceType) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
if (c.mDnsSdDiscoveryListener == null) throw new
IllegalStateException("Discovery listener needs to be set first");
DnsSdServiceInfo s = new DnsSdServiceInfo();
s.setServiceType(serviceType);
c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
}
public void stopServiceDiscovery(Channel c) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
c.mAsyncChannel.sendMessage(STOP_DISCOVERY);
}
public void resolveService(Channel c, DnsSdServiceInfo serviceInfo) {
if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
if (c.mDnsSdResolveListener == null) throw new
IllegalStateException("Resolve listener needs to be set first");
c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo);
}
/**
* Get a reference to NetworkService handler. This is used to establish
* an AsyncChannel communication with the service
*
* @return Messenger pointing to the NetworkService handler
*/
private Messenger getMessenger() {
try {
return mService.getMessenger();
} catch (RemoteException e) {
return null;
}
}
}
......@@ -49,7 +49,7 @@ public class Protocol {
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
public static final int BASE_DNS_PINGER = 0x00050000;
public static final int BASE_NSD_MANAGER = 0x00060000;
//TODO: define all used protocols
}
/*
* Copyright (C) 2010 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;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.nsd.DnsSdServiceInfo;
import android.net.nsd.DnsSdTxtRecord;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Messenger;
import android.os.IBinder;
import android.util.Slog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.List;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
import com.android.server.am.BatteryStatsService;
import com.android.server.NativeDaemonConnector.Command;
import com.android.internal.R;
/**
* Network Service Discovery Service handles remote service discovery operation requests by
* implementing the INsdManager interface.
*
* @hide
*/
public class NsdService extends INsdManager.Stub {
private static final String TAG = "NsdService";
private static final String MDNS_TAG = "mDnsConnector";
private static final boolean DBG = true;
private Context mContext;
/**
* Clients receiving asynchronous messages
*/
private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>();
private AsyncChannel mReplyChannel = new AsyncChannel();
/**
* Handles client(app) connections
*/
private class AsyncServiceHandler extends Handler {
AsyncServiceHandler(android.os.Looper looper) {
super(looper);
}
@Override
public void handleMessage(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);
mClients.add(c);
} 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((AsyncChannel) msg.obj);
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");
DnsSdServiceInfo s = (DnsSdServiceInfo) msg.obj;
discoverServices(1, s.getServiceType());
mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED);
break;
case NsdManager.STOP_DISCOVERY:
if (DBG) Slog.d(TAG, "Stop service discovery");
mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED);
break;
case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service");
mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED);
break;
case NsdManager.UPDATE_SERVICE:
if (DBG) Slog.d(TAG, "Update service");
mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED);
break;
default:
Slog.d(TAG, "NsdServicehandler.handleMessage ignoring msg=" + msg);
break;
}
}
}
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());
/*
mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
MDNS_TAG, 25);
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(); */
return service;
}
public Messenger getMessenger() {
return new Messenger(mAsyncServiceHandler);
}
/* These should be in sync with system/netd/mDnsResponseCode.h */
class NativeResponseCode {
public static final int SERVICE_FOUND = 101;
public static final int SERVICE_LOST = 102;
public static final int SERVICE_DISCOVERY_FAILED = 103;
public static final int SERVICE_REGISTERED = 104;
public static final int SERVICE_REGISTRATION_FAILED = 105;
public static final int SERVICE_UPDATED = 106;
public static final int SERVICE_UPDATE_FAILED = 107;
public static final int SERVICE_RESOLVED = 108;
public static final int SERVICE_RESOLUTION_FAILED = 109;
}
class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
public void onDaemonConnected() {
mNativeDaemonConnected.countDown();
}
public boolean onEvent(int code, String raw, String[] cooked) {
switch (code) {
case NativeResponseCode.SERVICE_FOUND:
/* NNN uniqueId serviceName regType */
break;
case NativeResponseCode.SERVICE_LOST:
/* NNN uniqueId serviceName regType */
break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
/* NNN uniqueId errorCode */
break;
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
break;
case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
/* NNN regId errorCode */
break;
case NativeResponseCode.SERVICE_UPDATED:
/* NNN regId */
break;
case NativeResponseCode.SERVICE_UPDATE_FAILED:
/* NNN regId errorCode */
break;
case NativeResponseCode.SERVICE_RESOLVED:
/* NNN resolveId fullName hostName port txtlen txtdata */
break;
case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
/* NNN resovleId errorCode */
break;
default:
break;
}
return false;
}
}
private void registerService(int regId, DnsSdServiceInfo service) {
try {
//Add txtlen and txtdata
mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
service.getServiceType(), service.getPort());
} catch(NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to execute registerService");
}
}
private void updateService(int regId, DnsSdTxtRecord t) {
try {
if (t == null) return;
mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
} catch(NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to updateServices");
}
}
private void discoverServices(int discoveryId, String serviceType) {
try {
mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
} catch(NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to discoverServices");
}
}
private void stopServiceDiscovery(int discoveryId) {
try {
mNativeConnector.execute("mdnssd", "stopdiscover", discoveryId);
} catch(NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to stopServiceDiscovery");
}
}
private void resolveService(DnsSdServiceInfo service) {
try {
mNativeConnector.execute("mdnssd", "resolve", service.getServiceName(),
service.getServiceType());
} catch(NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to resolveService");
}
}
@Override
protected 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="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
pw.println("Internal state:");
}
}
......@@ -120,6 +120,7 @@ class ServerThread extends Thread {
ConnectivityService connectivity = null;
WifiP2pService wifiP2p = null;
WifiService wifi = null;
NsdService serviceDiscovery= null;
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
......@@ -393,6 +394,15 @@ class ServerThread extends Thread {
reportWtf("starting Connectivity Service", e);
}
try {
Slog.i(TAG, "Network Service Discovery Service");
serviceDiscovery = NsdService.create(context);
ServiceManager.addService(
Context.NSD_SERVICE, serviceDiscovery);
} catch (Throwable e) {
reportWtf("starting Service Discovery Service", e);
}
try {
Slog.i(TAG, "Throttle Service");
throttle = new ThrottleService(context);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment