1 /* 2 * Copyright (C) 2013 4th Line GmbH, Switzerland 3 * 4 * The contents of this file are subject to the terms of either the GNU 5 * Lesser General Public License Version 2 or later ("LGPL") or the 6 * Common Development and Distribution License Version 1 or later 7 * ("CDDL") (collectively, the "License"). You may not use this file 8 * except in compliance with the License. See LICENSE.txt for more 9 * information. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 */ 15 16 package org.fourthline.cling.model.types; 17 18 import org.fourthline.cling.model.ModelUtil; 19 20 21 import java.io.UnsupportedEncodingException; 22 import java.util.UUID; 23 import java.security.MessageDigest; 24 import java.math.BigInteger; 25 import java.util.logging.Logger; 26 27 /** 28 * A unique device name. 29 * <p> 30 * UDA 1.0 does not specify a UUID format, however, UDA 1.1 specifies a format that is compatible 31 * with <tt>java.util.UUID</tt> variant 4. You can use any identifier string you like. 32 * </p> 33 * <p> 34 * You'll most likely need the {@link #uniqueSystemIdentifier(String)} method sooner or later. 35 * </p> 36 * 37 * @author Christian Bauer 38 */ 39 public class UDN { 40 41 final private static Logger log = Logger.getLogger(UDN.class.getName()); 42 43 public static final String PREFIX = "uuid:"; 44 45 private String identifierString; 46 47 /** 48 * @param identifierString The identifier string without the "uuid:" prefix. 49 */ 50 public UDN(String identifierString) { 51 this.identifierString = identifierString; 52 } 53 54 public UDN(UUID uuid) { 55 this.identifierString = uuid.toString(); 56 } 57 58 public boolean isUDA11Compliant() { 59 try { 60 UUID.fromString(identifierString); 61 return true; 62 } catch (IllegalArgumentException ex) { 63 return false; 64 } 65 } 66 67 public String getIdentifierString() { 68 return identifierString; 69 } 70 71 public static UDN valueOf(String udnString) { 72 return new UDN(udnString.startsWith(PREFIX) ? udnString.substring(PREFIX.length()) : udnString); 73 } 74 75 /** 76 * Generates a global unique identifier that is the same every time this method is invoked on the same machine with 77 * the same argument. 78 * <p> 79 * This method combines the first non-loopback network interface's MAC address with given salt to generate a 80 * globally unique identifier. In other words, every time you call this method with the same salt on the same 81 * machine, you get the same identifier. If you use the same salt on a different machine, a different identifier 82 * will be generated. 83 * </p> 84 * <p> 85 * Note for Android users: This method does not generate unique identifiers on Android devices and will 86 * throw an exception. We can't get details such as the hostname or MAC address on Android. Instead, 87 * construct a UDN with <code>new UDN(UUID)</code>. When your application is first started, generate all 88 * UUIDs needed for your UPnP devices and store them in your Android preferences. Then, use the stored 89 * UUID to create a UDN every time your application starts. 90 * </p> 91 * <p> 92 * Control points can remember your device's identifier, it will and should be the same every time 93 * your device is powered up. 94 * </p> 95 * 96 * @param salt An arbitrary string that uniquely identifies the device on the current system, e.g. "MyMediaServer". 97 * @return A global unique identifier, stable for the current system and salt. 98 */ 99 public static UDN uniqueSystemIdentifier(String salt) { 100 StringBuilder systemSalt = new StringBuilder(); 101 102 // Bug: On Android, NetworkInterface.isLoopback() isn't implemented 103 if (!ModelUtil.ANDROID_RUNTIME) { 104 try { 105 systemSalt.append(new String(ModelUtil.getFirstNetworkInterfaceHardwareAddress(), "UTF-8")); 106 } catch (UnsupportedEncodingException ex) { 107 // If your JVM doesn't support utf-8, you have bigger problems 108 throw new RuntimeException(ex); 109 } 110 } else { 111 throw new RuntimeException( 112 "This method does not create a unique identifier on Android, see the Javadoc and " + 113 "use new UDN(UUID) instead!" 114 ); 115 } 116 117 try { 118 byte[] hash = MessageDigest.getInstance("MD5").digest(systemSalt.toString().getBytes("UTF-8")); 119 return new UDN( 120 new UUID( 121 new BigInteger(-1, hash).longValue(), 122 salt.hashCode() 123 ) 124 ); 125 } catch (Exception ex) { 126 throw new RuntimeException(ex); 127 } 128 } 129 130 @Override 131 public String toString() { 132 return PREFIX + getIdentifierString(); 133 } 134 135 @Override 136 public boolean equals(Object o) { 137 if (this == o) return true; 138 if (o == null || !(o instanceof UDN)) return false; 139 UDN udn = (UDN) o; 140 return identifierString.equals(udn.identifierString); 141 } 142 143 @Override 144 public int hashCode() { 145 return identifierString.hashCode(); 146 } 147 148 }