• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
RTProps.put(ExtWiFiPath[2], "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0")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 
PathAccessor(Object... path)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
hashCode()83         public int hashCode() {
84             return mHashCode;
85         }
86 
87         @Override
equals(Object thatObject)88         public boolean equals(Object thatObject) {
89             return thatObject == this || (thatObject instanceof ConstPathAccessor &&
90                     Arrays.equals(mPath, ((PathAccessor) thatObject).mPath));
91         }
92 
getPath()93         private String[] getPath() {
94             return mPath;
95         }
96 
getValue()97         protected abstract Object getValue();
98     }
99 
100     private static class ConstPathAccessor<T> extends PathAccessor {
101         private final T mValue;
102 
ConstPathAccessor(T value, Object... path)103         protected ConstPathAccessor(T value, Object... path) {
104             super(path);
105             mValue = value;
106         }
107 
getValue()108         protected Object getValue() {
109             return mValue;
110         }
111     }
112 
getInstance(Context context)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 
OMADMAdapter(Context context)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 
buildNode(PathAccessor pathAccessor, int depth, OMAConstructed parent)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 
getMAC()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 
getImei()247     public String getImei() {
248         return mImei;
249     }
250 
getMeid()251     public byte[] getMeid() {
252         return Arrays.copyOf(mImei.getBytes(StandardCharsets.ISO_8859_1), IMEI_Length);
253     }
254 
getDevID()255     public String getDevID() {
256         return mDevID;
257     }
258 
getMO(String urn)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.
isPhoneTypeLTE()300     private static boolean isPhoneTypeLTE() {
301         return true;
302     }
303 
getHwV()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 
getDeviceType()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 
getVersion(Context context, boolean swv)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 
isSprint(Context context)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 
getLocale(Context context)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 
getProperty(Context context, String key, String propKey, String dflt)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 
readValueFromFile(Context context, String propName)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