Home | History | Annotate | Download | only in hotspot2
      1 package com.android.hotspot2;
      2 
      3 import android.content.Context;
      4 import android.content.SharedPreferences;
      5 import android.net.wifi.WifiManager;
      6 import android.os.SystemProperties;
      7 import android.telephony.TelephonyManager;
      8 import android.text.TextUtils;
      9 import android.util.Log;
     10 
     11 import com.android.anqp.eap.EAP;
     12 import com.android.hotspot2.omadm.MOTree;
     13 import com.android.hotspot2.omadm.OMAConstants;
     14 import com.android.hotspot2.omadm.OMAConstructed;
     15 import com.android.hotspot2.osu.OSUManager;
     16 
     17 import java.io.IOException;
     18 import java.nio.charset.StandardCharsets;
     19 import java.util.ArrayList;
     20 import java.util.Arrays;
     21 import java.util.HashMap;
     22 import java.util.List;
     23 import java.util.Locale;
     24 import java.util.Map;
     25 import java.util.concurrent.atomic.AtomicInteger;
     26 
     27 import static com.android.anqp.eap.NonEAPInnerAuth.NonEAPType;
     28 import static com.android.anqp.eap.NonEAPInnerAuth.mapInnerType;
     29 
     30 public class OMADMAdapter {
     31     private final Context mContext;
     32     private final String mImei;
     33     private final String mImsi;
     34     private final String mDevID;
     35     private final List<PathAccessor> mDevInfo;
     36     private final List<PathAccessor> mDevDetail;
     37 
     38     private static final int IMEI_Length = 14;
     39 
     40     private static final String[] ExtWiFiPath = {"DevDetail", "Ext", "org.wi-fi", "Wi-Fi"};
     41 
     42     private static final Map<String, String> RTProps = new HashMap<>();
     43 
     44     private MOTree mDevInfoTree;
     45     private MOTree mDevDetailTree;
     46 
     47     private static OMADMAdapter sInstance;
     48 
     49     static {
     50         RTProps.put(ExtWiFiPath[2], "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0");
     51     }
     52 
     53     private static abstract class PathAccessor {
     54         private final String[] mPath;
     55         private final int mHashCode;
     56 
     57         protected PathAccessor(Object... path) {
     58             int length = 0;
     59             for (Object o : path) {
     60                 if (o.getClass() == String[].class) {
     61                     length += ((String[]) o).length;
     62                 } else {
     63                     length++;
     64                 }
     65             }
     66             mPath = new String[length];
     67             int n = 0;
     68             for (Object o : path) {
     69                 if (o.getClass() == String[].class) {
     70                     for (String element : (String[]) o) {
     71                         mPath[n++] = element;
     72                     }
     73                 } else if (o.getClass() == Integer.class) {
     74                     mPath[n++] = "x" + o.toString();
     75                 } else {
     76                     mPath[n++] = o.toString();
     77                 }
     78             }
     79             mHashCode = Arrays.hashCode(mPath);
     80         }
     81 
     82         @Override
     83         public int hashCode() {
     84             return mHashCode;
     85         }
     86 
     87         @Override
     88         public boolean equals(Object thatObject) {
     89             return thatObject == this || (thatObject instanceof ConstPathAccessor &&
     90                     Arrays.equals(mPath, ((PathAccessor) thatObject).mPath));
     91         }
     92 
     93         private String[] getPath() {
     94             return mPath;
     95         }
     96 
     97         protected abstract Object getValue();
     98     }
     99 
    100     private static class ConstPathAccessor<T> extends PathAccessor {
    101         private final T mValue;
    102 
    103         protected ConstPathAccessor(T value, Object... path) {
    104             super(path);
    105             mValue = value;
    106         }
    107 
    108         protected Object getValue() {
    109             return mValue;
    110         }
    111     }
    112 
    113     public static OMADMAdapter getInstance(Context context) {
    114         synchronized (OMADMAdapter.class) {
    115             if (sInstance == null) {
    116                 sInstance = new OMADMAdapter(context);
    117             }
    118             return sInstance;
    119         }
    120     }
    121 
    122     private OMADMAdapter(Context context) {
    123         mContext = context;
    124 
    125         TelephonyManager tm = (TelephonyManager) context
    126                 .getSystemService(Context.TELEPHONY_SERVICE);
    127         String simOperator = tm.getSimOperator();
    128         mImsi = tm.getSubscriberId();
    129         mImei = tm.getImei();
    130         String strDevId;
    131 
    132         /* Use MEID for sprint */
    133         if ("310120".equals(simOperator) || (mImsi != null && mImsi.startsWith("310120"))) {
    134                 /* MEID is 14 digits. If IMEI is returned as DevId, MEID can be extracted by taking
    135                  * first 14 characters. This is not always true but should be the case for sprint */
    136             strDevId = tm.getDeviceId().toUpperCase(Locale.US);
    137             if (strDevId != null && strDevId.length() >= IMEI_Length) {
    138                 strDevId = strDevId.substring(0, IMEI_Length);
    139             } else {
    140                 Log.w(OSUManager.TAG, "MEID cannot be extracted from DeviceId " + strDevId);
    141             }
    142         } else {
    143             if (isPhoneTypeLTE()) {
    144                 strDevId = mImei;
    145             } else {
    146                 strDevId = tm.getDeviceId();
    147             }
    148             if (strDevId == null) {
    149                 strDevId = "unknown";
    150             }
    151             strDevId = strDevId.toUpperCase(Locale.US);
    152 
    153             if (!isPhoneTypeLTE()) {
    154                 strDevId = strDevId.substring(0, IMEI_Length);
    155             }
    156         }
    157         mDevID = strDevId;
    158 
    159         mDevInfo = new ArrayList<>();
    160         mDevInfo.add(new ConstPathAccessor<>(strDevId, "DevInfo", "DevID"));
    161         mDevInfo.add(new ConstPathAccessor<>(getProperty(context,
    162                 "Man", "ro.product.manufacturer", "unknown"), "DevInfo", "Man"));
    163         mDevInfo.add(new ConstPathAccessor<>(getProperty(context,
    164                 "Mod", "ro.product.model", "generic"), "DevInfo", "Mod"));
    165         mDevInfo.add(new ConstPathAccessor<>(getLocale(context), "DevInfo", "Lang"));
    166         mDevInfo.add(new ConstPathAccessor<>("1.2", "DevInfo", "DmV"));
    167 
    168         mDevDetail = new ArrayList<>();
    169         mDevDetail.add(new ConstPathAccessor<>(getDeviceType(), "DevDetail", "DevType"));
    170         mDevDetail.add(new ConstPathAccessor<>(SystemProperties.get("ro.product.brand"),
    171                 "DevDetail", "OEM"));
    172         mDevDetail.add(new ConstPathAccessor<>(getVersion(context, false), "DevDetail", "FwV"));
    173         mDevDetail.add(new ConstPathAccessor<>(getVersion(context, true), "DevDetail", "SwV"));
    174         mDevDetail.add(new ConstPathAccessor<>(getHwV(), "DevDetail", "HwV"));
    175         mDevDetail.add(new ConstPathAccessor<>("TRUE", "DevDetail", "LrgObj"));
    176 
    177         mDevDetail.add(new ConstPathAccessor<>(32, "DevDetail", "URI", "MaxDepth"));
    178         mDevDetail.add(new ConstPathAccessor<>(2048, "DevDetail", "URI", "MaxTotLen"));
    179         mDevDetail.add(new ConstPathAccessor<>(64, "DevDetail", "URI", "MaxSegLen"));
    180 
    181         AtomicInteger index = new AtomicInteger(1);
    182         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TTLS, ExtWiFiPath,
    183                 "EAPMethodList", index, "EAPType"));
    184         mDevDetail.add(new ConstPathAccessor<>(mapInnerType(NonEAPType.MSCHAPv2), ExtWiFiPath,
    185                 "EAPMethodList", index, "InnerMethod"));
    186 
    187         index.incrementAndGet();
    188         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TTLS, ExtWiFiPath,
    189                 "EAPMethodList", index, "EAPType"));
    190         mDevDetail.add(new ConstPathAccessor<>(mapInnerType(NonEAPType.PAP), ExtWiFiPath,
    191                 "EAPMethodList", index, "InnerMethod"));
    192 
    193         index.incrementAndGet();
    194         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TTLS, ExtWiFiPath,
    195                 "EAPMethodList", index, "EAPType"));
    196         mDevDetail.add(new ConstPathAccessor<>(mapInnerType(NonEAPType.MSCHAP), ExtWiFiPath,
    197                 "EAPMethodList", index, "InnerMethod"));
    198 
    199         index.incrementAndGet();
    200         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TLS, ExtWiFiPath,
    201                 "EAPMethodList", index, "EAPType"));
    202         index.incrementAndGet();
    203         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_AKA, ExtWiFiPath,
    204                 "EAPMethodList", index, "EAPType"));
    205         index.incrementAndGet();
    206         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_AKAPrim, ExtWiFiPath,
    207                 "EAPMethodList", index, "EAPType"));
    208         index.incrementAndGet();
    209         mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_SIM, ExtWiFiPath,
    210                 "EAPMethodList", index, "EAPType"));
    211 
    212         mDevDetail.add(new ConstPathAccessor<>("FALSE", ExtWiFiPath, "ManufacturingCertificate"));
    213         mDevDetail.add(new ConstPathAccessor<>(mImsi, ExtWiFiPath, "IMSI"));
    214         mDevDetail.add(new ConstPathAccessor<>(mImei, ExtWiFiPath, "IMEI_MEID"));
    215         mDevDetail.add(new PathAccessor(ExtWiFiPath, "Wi-FiMACAddress") {
    216             @Override
    217             protected String getValue() {
    218                 return getMAC();
    219             }
    220         });
    221     }
    222 
    223     private static void buildNode(PathAccessor pathAccessor, int depth, OMAConstructed parent)
    224             throws IOException {
    225         String[] path = pathAccessor.getPath();
    226         String name = path[depth];
    227         if (depth < path.length - 1) {
    228             OMAConstructed node = (OMAConstructed) parent.getChild(name);
    229             if (node == null) {
    230                 node = (OMAConstructed) parent.addChild(name, RTProps.get(name),
    231                         null, null);
    232             }
    233             buildNode(pathAccessor, depth + 1, node);
    234         } else if (pathAccessor.getValue() != null) {
    235             parent.addChild(name, null, pathAccessor.getValue().toString(), null);
    236         }
    237     }
    238 
    239     public String getMAC() {
    240         WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
    241         return wifiManager != null ?
    242                 String.format("%012x",
    243                         Utils.parseMac(wifiManager.getConnectionInfo().getMacAddress())) :
    244                 null;
    245     }
    246 
    247     public String getImei() {
    248         return mImei;
    249     }
    250 
    251     public byte[] getMeid() {
    252         return Arrays.copyOf(mImei.getBytes(StandardCharsets.ISO_8859_1), IMEI_Length);
    253     }
    254 
    255     public String getDevID() {
    256         return mDevID;
    257     }
    258 
    259     public MOTree getMO(String urn) {
    260         try {
    261             switch (urn) {
    262                 case OMAConstants.DevInfoURN:
    263                     if (mDevInfoTree == null) {
    264                         OMAConstructed root = new OMAConstructed(null, "DevInfo", urn);
    265                         for (PathAccessor pathAccessor : mDevInfo) {
    266                             buildNode(pathAccessor, 1, root);
    267                         }
    268                         mDevInfoTree = MOTree.buildMgmtTree(OMAConstants.DevInfoURN,
    269                                 OMAConstants.OMAVersion, root);
    270                     }
    271                     return mDevInfoTree;
    272                 case OMAConstants.DevDetailURN:
    273                     if (mDevDetailTree == null) {
    274                         OMAConstructed root = new OMAConstructed(null, "DevDetail", urn);
    275                         for (PathAccessor pathAccessor : mDevDetail) {
    276                             buildNode(pathAccessor, 1, root);
    277                         }
    278                         mDevDetailTree = MOTree.buildMgmtTree(OMAConstants.DevDetailURN,
    279                                 OMAConstants.OMAVersion, root);
    280                     }
    281                     return mDevDetailTree;
    282                 default:
    283                     throw new IllegalArgumentException(urn);
    284             }
    285         } catch (IOException ioe) {
    286             Log.e(OSUManager.TAG, "Caught exception building OMA Tree: " + ioe, ioe);
    287             return null;
    288         }
    289 
    290         /*
    291         switch (urn) {
    292             case DevInfoURN: return DevInfo;
    293             case DevDetailURN: return DevDetail;
    294             default: throw new IllegalArgumentException(urn);
    295         }
    296         */
    297     }
    298 
    299     // TODO: For now, assume the device supports LTE.
    300     private static boolean isPhoneTypeLTE() {
    301         return true;
    302     }
    303 
    304     private static String getHwV() {
    305         try {
    306             return SystemProperties.get("ro.hardware", "Unknown")
    307                     + "." + SystemProperties.get("ro.revision", "Unknown");
    308         } catch (RuntimeException e) {
    309             return "Unknown";
    310         }
    311     }
    312 
    313     private static String getDeviceType() {
    314         String devicetype = SystemProperties.get("ro.build.characteristics");
    315         if ((((TextUtils.isEmpty(devicetype)) || (!devicetype.equals("tablet"))))) {
    316             devicetype = "phone";
    317         }
    318         return devicetype;
    319     }
    320 
    321     private static String getVersion(Context context, boolean swv) {
    322         String version;
    323         try {
    324             if (!isSprint(context) && swv) {
    325                 return "Android " + SystemProperties.get("ro.build.version.release");
    326             } else {
    327                 version = SystemProperties.get("ro.build.version.full");
    328                 if (null == version || version.equals("")) {
    329                     return SystemProperties.get("ro.build.id", null) + "~"
    330                             + SystemProperties.get("ro.build.config.version", null) + "~"
    331                             + SystemProperties.get("gsm.version.baseband", null) + "~"
    332                             + SystemProperties.get("ro.gsm.flexversion", null);
    333                 }
    334             }
    335         } catch (RuntimeException e) {
    336             return "Unknown";
    337         }
    338         return version;
    339     }
    340 
    341     private static boolean isSprint(Context context) {
    342         TelephonyManager tm = (TelephonyManager) context
    343                 .getSystemService(Context.TELEPHONY_SERVICE);
    344         String simOperator = tm.getSimOperator();
    345         String imsi = tm.getSubscriberId();
    346         /* Use MEID for sprint */
    347         if ("310120".equals(simOperator) || (imsi != null && imsi.startsWith("310120"))) {
    348             return true;
    349         } else {
    350             return false;
    351         }
    352     }
    353 
    354     private static String getLocale(Context context) {
    355         String strLang = readValueFromFile(context, "Lang");
    356         if (strLang == null) {
    357             strLang = Locale.getDefault().toString();
    358         }
    359         return strLang;
    360     }
    361 
    362     private static String getProperty(Context context, String key, String propKey, String dflt) {
    363         String strMan = readValueFromFile(context, key);
    364         if (strMan == null) {
    365             strMan = SystemProperties.get(propKey, dflt);
    366         }
    367         return strMan;
    368     }
    369 
    370     private static String readValueFromFile(Context context, String propName) {
    371         String ret = null;
    372         // use preference instead of the system property
    373         SharedPreferences prefs = context.getSharedPreferences("dmconfig", 0);
    374         if (prefs.contains(propName)) {
    375             ret = prefs.getString(propName, "");
    376             if (ret.length() == 0) {
    377                 ret = null;
    378             }
    379         }
    380         return ret;
    381     }
    382 
    383     private static final String DevDetail =
    384             "<MgmtTree>" +
    385                     "<VerDTD>1.2</VerDTD>" +
    386                     "<Node>" +
    387                     "<NodeName>DevDetail</NodeName>" +
    388                     "<RTProperties>" +
    389                     "<Type>" +
    390                     "<DDFName>urn:oma:mo:oma-dm-devdetail:1.0</DDFName>" +
    391                     "</Type>" +
    392                     "</RTProperties>" +
    393                     "<Node>" +
    394                     "<NodeName>Ext</NodeName>" +
    395                     "<Node>" +
    396                     "<NodeName>org.wi-fi</NodeName>" +
    397                     "<RTProperties>" +
    398                     "<Type>" +
    399                     "<DDFName>" +
    400                     "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext :1.0" +
    401                     "</DDFName>" +
    402                     "</Type>" +
    403                     "</RTProperties>" +
    404                     "<Node>" +
    405                     "<NodeName>Wi-Fi</NodeName>" +
    406                     "<Node>" +
    407                     "<NodeName>EAPMethodList</NodeName>" +
    408                     "<Node>" +
    409                     "<NodeName>Method01</NodeName>" +
    410                     "<!-- EAP-TTLS/MS-CHAPv2 -->" +
    411                     "<Node>" +
    412                     "<NodeName>EAPType</NodeName>" +
    413                     "<Value>21</Value>" +
    414                     "</Node>" +
    415                     "<Node>" +
    416                     "<NodeName>InnerMethod</NodeName>" +
    417                     "<Value>MS-CHAP-V2</Value>" +
    418                     "</Node>" +
    419                     "</Node>" +
    420                     "<Node>" +
    421                     "<NodeName>Method02</NodeName>" +
    422                     "<!-- EAP-TLS -->" +
    423                     "<Node>" +
    424                     "<NodeName>EAPType</NodeName>" +
    425                     "<Value>13</Value>" +
    426                     "</Node>" +
    427                     "</Node>" +
    428                     "<Node>" +
    429                     "<NodeName>Method03</NodeName>" +
    430                     "<!-- EAP-SIM -->" +
    431                     "<Node>" +
    432                     "<NodeName>EAPType</NodeName>" +
    433                     "<Value>18</Value>" +
    434                     "</Node>" +
    435                     "</Node>" +
    436                     "<Node>" +
    437                     "<NodeName>Method04</NodeName>" +
    438                     "<!-- EAP-AKA -->" +
    439                     "<Node>" +
    440                     "<NodeName>EAPType</NodeName>" +
    441                     "<Value>23</Value>" +
    442                     "</Node>" +
    443                     "</Node>" +
    444                     "<Node>" +
    445                     "<NodeName>Method05</NodeName>" +
    446                     "<!-- EAP-AKA' -->" +
    447                     "<Node>" +
    448                     "<NodeName>EAPType</NodeName>" +
    449                     "<Value>50</Value>" +
    450                     "</Node>" +
    451                     "</Node>" +
    452                     "<Node>" +
    453                     "<NodeName>Method06</NodeName>" +
    454                     "<!-- Supported method (EAP-TTLS/PAP) not mandated by Hotspot2.0-->" +
    455                     "<Node>" +
    456                     "<NodeName>EAPType</NodeName>" +
    457                     "<Value>21</Value>" +
    458                     "</Node>" +
    459                     "<Node>" +
    460                     "<NodeName>InnerMethod</NodeName>" +
    461                     "<Value>PAP</Value>" +
    462                     "</Node>" +
    463                     "</Node>" +
    464                     "<Node>" +
    465                     "<NodeName>Method07</NodeName>" +
    466                     "<!-- Supported method (PEAP/EAP-GTC) not mandated by Hotspot 2.0-->" +
    467                     "<Node>" +
    468                     "<NodeName>EAPType</NodeName>" +
    469                     "<Value>25</Value>" +
    470                     "</Node>" +
    471                     "<Node>" +
    472                     "<NodeName>InnerEAPType</NodeName>" +
    473                     "<Value>6</Value>" +
    474                     "</Node>" +
    475                     "</Node>" +
    476                     "</Node>" +
    477                     "<Node>" +
    478                     "<NodeName>SPCertificate</NodeName>" +
    479                     "<Node>" +
    480                     "<NodeName>Cert01</NodeName>" +
    481                     "<Node>" +
    482                     "<NodeName>CertificateIssuerName</NodeName>" +
    483                     "<Value>CN=RuckusCA</Value>" +
    484                     "</Node>" +
    485                     "</Node>" +
    486                     "</Node>" +
    487                     "<Node>" +
    488                     "<NodeName>ManufacturingCertificate</NodeName>" +
    489                     "<Value>FALSE</Value>" +
    490                     "</Node>" +
    491                     "<Node>" +
    492                     "<NodeName>Wi-FiMACAddress</NodeName>" +
    493                     "<Value>001d2e112233</Value>" +
    494                     "</Node>" +
    495                     "<Node>" +
    496                     "<NodeName>ClientTriggerRedirectURI</NodeName>" +
    497                     "<Value>http://127.0.0.1:12345/index.htm</Value>" +
    498                     "</Node>" +
    499                     "<Node>" +
    500                     "<NodeName>Ops</NodeName>" +
    501                     "<Node>" +
    502                     "<NodeName>launchBrowserToURI</NodeName>" +
    503                     "<Value></Value>" +
    504                     "</Node>" +
    505                     "<Node>" +
    506                     "<NodeName>negotiateClientCertTLS</NodeName>" +
    507                     "<Value></Value>" +
    508                     "</Node>" +
    509                     "<Node>" +
    510                     "<NodeName>getCertificate</NodeName>" +
    511                     "<Value></Value>" +
    512                     "</Node>" +
    513                     "</Node>" +
    514                     "</Node>" +
    515                     "<!-- End of Wi-Fi node -->" +
    516                     "</Node>" +
    517                     "<!-- End of org.wi-fi node -->" +
    518                     "</Node>" +
    519                     "<!-- End of Ext node -->" +
    520                     "<Node>" +
    521                     "<NodeName>URI</NodeName>" +
    522                     "<Node>" +
    523                     "<NodeName>MaxDepth</NodeName>" +
    524                     "<Value>32</Value>" +
    525                     "</Node>" +
    526                     "<Node>" +
    527                     "<NodeName>MaxTotLen</NodeName>" +
    528                     "<Value>2048</Value>" +
    529                     "</Node>" +
    530                     "<Node>" +
    531                     "<NodeName>MaxSegLen</NodeName>" +
    532                     "<Value>64</Value>" +
    533                     "</Node>" +
    534                     "</Node>" +
    535                     "<Node>" +
    536                     "<NodeName>DevType</NodeName>" +
    537                     "<Value>Smartphone</Value>" +
    538                     "</Node>" +
    539                     "<Node>" +
    540                     "<NodeName>OEM</NodeName>" +
    541                     "<Value>ACME</Value>" +
    542                     "</Node>" +
    543                     "<Node>" +
    544                     "<NodeName>FwV</NodeName>" +
    545                     "<Value>1.2.100.5</Value>" +
    546                     "</Node>" +
    547                     "<Node>" +
    548                     "<NodeName>SwV</NodeName>" +
    549                     "<Value>9.11.130</Value>" +
    550                     "</Node>" +
    551                     "<Node>" +
    552                     "<NodeName>HwV</NodeName>" +
    553                     "<Value>1.0</Value>" +
    554                     "</Node>" +
    555                     "<Node>" +
    556                     "<NodeName>LrgObj</NodeName>" +
    557                     "<Value>TRUE</Value>" +
    558                     "</Node>" +
    559                     "</Node>" +
    560                     "</MgmtTree>";
    561 
    562 
    563     private static final String DevInfo =
    564             "<MgmtTree>" +
    565                     "<VerDTD>1.2</VerDTD>" +
    566                     "<Node>" +
    567                     "<NodeName>DevInfo</NodeName>" +
    568                     "<RTProperties>" +
    569                     "<Type>" +
    570                     "<DDFName>urn:oma:mo:oma-dm-devinfo:1.0" +
    571                     "</DDFName>" +
    572                     "</Type>" +
    573                     "</RTProperties>" +
    574                     "</Node>" +
    575                     "<Node>" +
    576                     "<NodeName>DevID</NodeName>" +
    577                     "<Path>DevInfo</Path>" +
    578                     "<Value>urn:acme:00-11-22-33-44-55</Value>" +
    579                     "</Node>" +
    580                     "<Node>" +
    581                     "<NodeName>Man</NodeName>" +
    582                     "<Path>DevInfo</Path>" +
    583                     "<Value>ACME</Value>" +
    584                     "</Node>" +
    585                     "<Node>" +
    586                     "<NodeName>Mod</NodeName>" +
    587                     "<Path>DevInfo</Path>" +
    588                     "<Value>HS2.0-01</Value>" +
    589                     "</Node>" +
    590                     "<Node>" +
    591                     "<NodeName>DmV</NodeName>" +
    592                     "<Path>DevInfo</Path>" +
    593                     "<Value>1.2</Value>" +
    594                     "</Node>" +
    595                     "<Node>" +
    596                     "<NodeName>Lang</NodeName>" +
    597                     "<Path>DevInfo</Path>" +
    598                     "<Value>en-US</Value>" +
    599                     "</Node>" +
    600                     "</MgmtTree>";
    601 }
    602