1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.wifi.p2p;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.net.MacAddress;
25 import android.net.wifi.OuiKeyedData;
26 import android.net.wifi.ParcelUtil;
27 import android.net.wifi.ScanResult;
28 import android.os.Build;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.util.Log;
32 
33 import androidx.annotation.RequiresApi;
34 
35 import com.android.modules.utils.build.SdkLevel;
36 import com.android.wifi.flags.Flags;
37 
38 import java.net.InetAddress;
39 import java.net.UnknownHostException;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
46 
47 /**
48  * A class representing a Wi-Fi p2p device
49  *
50  * Note that the operations are not thread safe
51  * {@see WifiP2pManager}
52  */
53 public class WifiP2pDevice implements Parcelable {
54 
55     private static final String TAG = "WifiP2pDevice";
56 
57     /**
58      * The device name is a user friendly string to identify a Wi-Fi p2p device
59      */
60     public String deviceName = "";
61 
62     /**
63      * The device MAC address uniquely identifies a Wi-Fi p2p device
64      */
65     public String deviceAddress = "";
66     /**
67      * The device interface MAC address. This field is valid when the device is a part of the group
68      */
69     @Nullable private MacAddress mInterfaceMacAddress;
70 
71     /**
72      * The IP address of the device. This field is valid when the device is a part of the group.
73      */
74     @Nullable private InetAddress mIpAddress;
75 
76     /**
77      * Primary device type identifies the type of device. For example, an application
78      * could filter the devices discovered to only display printers if the purpose is to
79      * enable a printing action from the user. See the Wi-Fi Direct technical specification
80      * for the full list of standard device types supported.
81      */
82     public String primaryDeviceType;
83 
84     /**
85      * Secondary device type is an optional attribute that can be provided by a device in
86      * addition to the primary device type.
87      */
88     public String secondaryDeviceType;
89 
90 
91     // These definitions match the ones in wpa_supplicant
92     /* WPS config methods supported */
93     private static final int WPS_CONFIG_DISPLAY         = 0x0008;
94     private static final int WPS_CONFIG_PUSHBUTTON      = 0x0080;
95     private static final int WPS_CONFIG_KEYPAD          = 0x0100;
96 
97     /* Device Capability bitmap */
98     private static final int DEVICE_CAPAB_SERVICE_DISCOVERY         = 1;
99     @SuppressWarnings("unused")
100     private static final int DEVICE_CAPAB_CLIENT_DISCOVERABILITY    = 1<<1;
101     @SuppressWarnings("unused")
102     private static final int DEVICE_CAPAB_CONCURRENT_OPER           = 1<<2;
103     @SuppressWarnings("unused")
104     private static final int DEVICE_CAPAB_INFRA_MANAGED             = 1<<3;
105     @SuppressWarnings("unused")
106     private static final int DEVICE_CAPAB_DEVICE_LIMIT              = 1<<4;
107     private static final int DEVICE_CAPAB_INVITATION_PROCEDURE      = 1<<5;
108 
109     /* Group Capability bitmap */
110     private static final int GROUP_CAPAB_GROUP_OWNER                = 1;
111     @SuppressWarnings("unused")
112     private static final int GROUP_CAPAB_PERSISTENT_GROUP           = 1<<1;
113     private static final int GROUP_CAPAB_GROUP_LIMIT                = 1<<2;
114     @SuppressWarnings("unused")
115     private static final int GROUP_CAPAB_INTRA_BSS_DIST             = 1<<3;
116     @SuppressWarnings("unused")
117     private static final int GROUP_CAPAB_CROSS_CONN                 = 1<<4;
118     @SuppressWarnings("unused")
119     private static final int GROUP_CAPAB_PERSISTENT_RECONN          = 1<<5;
120     @SuppressWarnings("unused")
121     private static final int GROUP_CAPAB_GROUP_FORMATION            = 1<<6;
122 
123     /**
124      * WPS config methods supported
125      * @hide
126      */
127     @UnsupportedAppUsage
128     public int wpsConfigMethodsSupported;
129 
130     /**
131      * Device capability
132      * @hide
133      */
134     @UnsupportedAppUsage
135     public int deviceCapability;
136 
137     /**
138      * Group capability
139      * @hide
140      */
141     @UnsupportedAppUsage
142     public int groupCapability;
143 
144     public static final int CONNECTED   = 0;
145     public static final int INVITED     = 1;
146     public static final int FAILED      = 2;
147     public static final int AVAILABLE   = 3;
148     public static final int UNAVAILABLE = 4;
149 
150     /** Device connection status */
151     public int status = UNAVAILABLE;
152 
153     /** @hide */
154     @UnsupportedAppUsage
155     public WifiP2pWfdInfo wfdInfo;
156 
157     /** This stores vendor-specific information element from the native side. */
158     private List<ScanResult.InformationElement> mVendorElements;
159 
160     /** Detailed device string pattern with WFD info
161      * Example:
162      *  P2P-DEVICE-FOUND 00:18:6b:de:a3:6e p2p_dev_addr=00:18:6b:de:a3:6e
163      *  pri_dev_type=1-0050F204-1 name='DWD-300-DEA36E' config_methods=0x188
164      *  dev_capab=0x21 group_capab=0x9
165      */
166     private static final Pattern detailedDevicePattern = Pattern.compile(
167             "((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) "
168             + "(\\d+ )?"
169             + "p2p_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) "
170             + "pri_dev_type=(\\d+-[0-9a-fA-F]+-\\d+) "
171             + "name='(.*)' "
172             + "config_methods=(0x[0-9a-fA-F]+) "
173             + "dev_capab=(0x[0-9a-fA-F]+) "
174             + "group_capab=(0x[0-9a-fA-F]+)"
175             + "( wfd_dev_info=0x([0-9a-fA-F]{12}))?"
176             + "( wfd_r2_dev_info=0x([0-9a-fA-F]{4}))?"
177     );
178 
179     /** 2 token device address pattern
180      * Example:
181      *  P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
182      *  AP-STA-DISCONNECTED 42:fc:89:a8:96:09
183      */
184     private static final Pattern twoTokenPattern = Pattern.compile(
185         "(p2p_dev_addr=)?((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
186     );
187 
188     /** 3 token device address pattern
189      * Example:
190      *  AP-STA-CONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=fa:7b:7a:42:02:13
191      *  AP-STA-DISCONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=fa:7b:7a:42:02:13
192      */
193     private static final Pattern threeTokenPattern = Pattern.compile(
194         "(?:[0-9a-f]{2}:){5}[0-9a-f]{2} p2p_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
195     );
196 
197     /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */
198     private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList();
199 
200     /**
201      * Return the vendor-provided configuration data, if it exists. See also {@link
202      * #setVendorData(List)}
203      *
204      * @return Vendor configuration data, or empty list if it does not exist.
205      * @hide
206      */
207     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
208     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
209     @SystemApi
210     @NonNull
getVendorData()211     public List<OuiKeyedData> getVendorData() {
212         if (!SdkLevel.isAtLeastV()) {
213             throw new UnsupportedOperationException();
214         }
215         return mVendorData;
216     }
217 
WifiP2pDevice()218     public WifiP2pDevice() {
219     }
220 
221     /**
222      * @param string formats supported include
223      *  P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13
224      *  pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27
225      *  group_capab=0x0 wfd_dev_info=000006015d022a0032
226      *
227      *  P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
228      *
229      *  AP-STA-CONNECTED 42:fc:89:a8:96:09 [p2p_dev_addr=02:90:4c:a0:92:54]
230      *
231      *  AP-STA-DISCONNECTED 42:fc:89:a8:96:09 [p2p_dev_addr=02:90:4c:a0:92:54]
232      *
233      *  fa:7b:7a:42:02:13
234      *
235      *  Note: The events formats can be looked up in the wpa_supplicant code
236      * @hide
237      */
238     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
WifiP2pDevice(String string)239     public WifiP2pDevice(String string) throws IllegalArgumentException {
240         String[] tokens = string.split("[ \n]");
241         Matcher match;
242 
243         if (tokens.length < 1) {
244             throw new IllegalArgumentException("Malformed supplicant event");
245         }
246 
247         switch (tokens.length) {
248             case 1:
249                 /* Just a device address */
250                 deviceAddress = string;
251                 return;
252             case 2:
253                 match = twoTokenPattern.matcher(string);
254                 if (!match.find()) {
255                     throw new IllegalArgumentException("Malformed supplicant event");
256                 }
257                 deviceAddress = match.group(2);
258                 return;
259             case 3:
260                 match = threeTokenPattern.matcher(string);
261                 if (!match.find()) {
262                     throw new IllegalArgumentException("Malformed supplicant event");
263                 }
264                 deviceAddress = match.group(1);
265                 return;
266             default:
267                 match = detailedDevicePattern.matcher(string);
268                 if (!match.find()) {
269                     throw new IllegalArgumentException("Malformed supplicant event");
270                 }
271 
272                 deviceAddress = match.group(3);
273                 primaryDeviceType = match.group(4);
274                 deviceName = match.group(5);
275                 wpsConfigMethodsSupported = parseHex(match.group(6));
276                 deviceCapability = parseHex(match.group(7));
277                 groupCapability = parseHex(match.group(8));
278                 if (match.group(9) != null) {
279                     String str = match.group(10);
280                     if (null == str) break;
281                     wfdInfo = new WifiP2pWfdInfo(parseHex(str.substring(0,4)),
282                             parseHex(str.substring(4,8)),
283                             parseHex(str.substring(8,12)));
284                     if (match.group(11) != null && SdkLevel.isAtLeastS()) {
285                         String r2str = match.group(12);
286                         if (null == r2str) break;
287                         wfdInfo.setR2DeviceType(parseHex(r2str.substring(0, 4)));
288                     }
289                 }
290                 break;
291         }
292 
293         if (tokens[0].startsWith("P2P-DEVICE-FOUND")) {
294             status = AVAILABLE;
295         }
296     }
297 
298     /** The Wifi Display information for this device, or null if unavailable. */
299     @Nullable
getWfdInfo()300     public WifiP2pWfdInfo getWfdInfo() {
301         return wfdInfo;
302     }
303 
304     /** Returns true if WPS push button configuration is supported */
wpsPbcSupported()305     public boolean wpsPbcSupported() {
306         return (wpsConfigMethodsSupported & WPS_CONFIG_PUSHBUTTON) != 0;
307     }
308 
309     /** Returns true if WPS keypad configuration is supported */
wpsKeypadSupported()310     public boolean wpsKeypadSupported() {
311         return (wpsConfigMethodsSupported & WPS_CONFIG_KEYPAD) != 0;
312     }
313 
314     /** Returns true if WPS display configuration is supported */
wpsDisplaySupported()315     public boolean wpsDisplaySupported() {
316         return (wpsConfigMethodsSupported & WPS_CONFIG_DISPLAY) != 0;
317     }
318 
319     /** Returns true if the device is capable of service discovery */
isServiceDiscoveryCapable()320     public boolean isServiceDiscoveryCapable() {
321         return (deviceCapability & DEVICE_CAPAB_SERVICE_DISCOVERY) != 0;
322     }
323 
324     /** Returns true if the device is capable of invitation {@hide}*/
isInvitationCapable()325     public boolean isInvitationCapable() {
326         return (deviceCapability & DEVICE_CAPAB_INVITATION_PROCEDURE) != 0;
327     }
328 
329     /** Returns true if the device reaches the limit. {@hide}*/
isDeviceLimit()330     public boolean isDeviceLimit() {
331         return (deviceCapability & DEVICE_CAPAB_DEVICE_LIMIT) != 0;
332     }
333 
334     /** Returns true if the device is a group owner */
isGroupOwner()335     public boolean isGroupOwner() {
336         return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
337     }
338 
339     /** Returns true if the group reaches the limit. {@hide}*/
isGroupLimit()340     public boolean isGroupLimit() {
341         return (groupCapability & GROUP_CAPAB_GROUP_LIMIT) != 0;
342     }
343 
344     /**
345      * Update this device's details using another {@link WifiP2pDevice} instance.
346      * This will throw an exception if the device address does not match.
347      *
348      * @param device another instance of {@link WifiP2pDevice} used to update this instance.
349      * @throws IllegalArgumentException if the device is null or the device address does not match
350      */
update(@onNull WifiP2pDevice device)351     public void update(@NonNull WifiP2pDevice device) {
352         updateSupplicantDetails(device);
353         status = device.status;
354     }
355 
356     /** Updates details obtained from supplicant @hide */
updateSupplicantDetails(WifiP2pDevice device)357     public void updateSupplicantDetails(WifiP2pDevice device) {
358         if (device == null) {
359             throw new IllegalArgumentException("device is null");
360         }
361         if (device.deviceAddress == null) {
362             throw new IllegalArgumentException("deviceAddress is null");
363         }
364         if (!deviceAddress.equals(device.deviceAddress)) {
365             throw new IllegalArgumentException("deviceAddress does not match");
366         }
367         mInterfaceMacAddress = device.mInterfaceMacAddress;
368         deviceName = device.deviceName;
369         primaryDeviceType = device.primaryDeviceType;
370         secondaryDeviceType = device.secondaryDeviceType;
371         wpsConfigMethodsSupported = device.wpsConfigMethodsSupported;
372         deviceCapability = device.deviceCapability;
373         groupCapability = device.groupCapability;
374         wfdInfo = device.wfdInfo;
375     }
376 
377     /**
378      * Set vendor-specific information elements.
379      * @hide
380      */
setVendorElements( List<ScanResult.InformationElement> vendorElements)381     public void setVendorElements(
382             List<ScanResult.InformationElement> vendorElements) {
383         if (vendorElements == null) {
384             mVendorElements = null;
385             return;
386         }
387         mVendorElements = new ArrayList<>(vendorElements);
388     }
389 
390     /**
391      * Set additional vendor-provided configuration data.
392      *
393      * @param vendorData List of {@link android.net.wifi.OuiKeyedData} containing the
394      *                   vendor-provided configuration data. Note that multiple elements with
395      *                   the same OUI are allowed.
396      * @hide
397      */
398     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
399     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
400     @SystemApi
setVendorData(@onNull List<OuiKeyedData> vendorData)401     public void setVendorData(@NonNull List<OuiKeyedData> vendorData) {
402         if (!SdkLevel.isAtLeastV()) {
403             throw new UnsupportedOperationException();
404         }
405         if (vendorData == null) {
406             throw new IllegalArgumentException("setVendorData received a null value");
407         }
408         mVendorData = vendorData;
409     }
410 
411     /**
412      * Get the vendor-specific information elements received as part of the discovery
413      * of the peer device.
414      *
415      * @return the list of vendor-specific information elements
416      *         The information element format is defined in the IEEE 802.11-2016 spec
417      *         Table 9-77.
418      */
getVendorElements()419     @NonNull public List<ScanResult.InformationElement> getVendorElements() {
420         if (mVendorElements == null) return Collections.emptyList();
421         return new ArrayList<>(mVendorElements);
422     }
423 
424     /**
425      * Get the device interface MAC address if the device is a part of the group; otherwise null.
426      *
427      * @return the interface MAC address if the device is a part of the group; otherwise null.
428      * @hide
429      */
getInterfaceMacAddress()430     @Nullable public MacAddress getInterfaceMacAddress() {
431         return mInterfaceMacAddress;
432     }
433 
434     /**
435      * Set the device interface MAC address.
436      * @hide
437      */
setInterfaceMacAddress(@ullable MacAddress interfaceAddress)438     public void setInterfaceMacAddress(@Nullable MacAddress interfaceAddress) {
439         mInterfaceMacAddress = interfaceAddress;
440     }
441 
442     /**
443      * Get the IP address of the connected client device.
444      * The application should listen to {@link WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION}
445      * broadcast to obtain the IP address of the connected client. When system assigns the IP
446      * address, the connected P2P device information ({@link WifiP2pGroup#getClientList()}) in the
447      * group is updated with the IP address and broadcast the group information using
448      * {@link WifiP2pManager#EXTRA_WIFI_P2P_GROUP} extra of the
449      * {@link WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} broadcast intent.
450      *
451      * Alternatively, the application can request for the group details with
452      * {@link WifiP2pManager#requestGroupInfo} and use ({@link WifiP2pGroup#getClientList()}) to
453      * obtain the connected client details.
454      *
455      * @return the IP address if the device is a part of the group; otherwise null.
456      */
457     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
getIpAddress()458     @Nullable public InetAddress getIpAddress() {
459         return mIpAddress;
460     }
461 
462     /**
463      * Set the IP address of the device.
464      * @hide
465      */
setIpAddress(InetAddress ipAddress)466     public void setIpAddress(InetAddress ipAddress) {
467         mIpAddress = ipAddress;
468     }
469 
470     @Override
equals(Object obj)471     public boolean equals(Object obj) {
472         if (this == obj) return true;
473         if (!(obj instanceof WifiP2pDevice)) return false;
474 
475         WifiP2pDevice other = (WifiP2pDevice) obj;
476         if (other == null || other.deviceAddress == null) {
477             return (deviceAddress == null);
478         }
479         return other.deviceAddress.equals(deviceAddress);
480     }
481 
482     @Override
hashCode()483     public int hashCode() {
484         return Objects.hashCode(deviceAddress);
485     }
486 
487     @Override
toString()488     public String toString() {
489         StringBuffer sbuf = new StringBuffer();
490         sbuf.append("Device: ").append(deviceName);
491         sbuf.append("\n deviceAddress: ").append(deviceAddress);
492         sbuf.append("\n interfaceMacAddress: ")
493                 .append(mInterfaceMacAddress == null ? "none" : mInterfaceMacAddress.toString());
494         sbuf.append("\n ipAddress: ")
495                 .append(mIpAddress == null ? "none" : mIpAddress.getHostAddress());
496         sbuf.append("\n primary type: ").append(primaryDeviceType);
497         sbuf.append("\n secondary type: ").append(secondaryDeviceType);
498         sbuf.append("\n wps: ").append(wpsConfigMethodsSupported);
499         sbuf.append("\n grpcapab: ").append(groupCapability);
500         sbuf.append("\n devcapab: ").append(deviceCapability);
501         sbuf.append("\n status: ").append(status);
502         sbuf.append("\n wfdInfo: ").append(wfdInfo);
503         sbuf.append("\n vendorElements: ").append(mVendorElements);
504         sbuf.append("\n vendorData: ").append(mVendorData);
505         return sbuf.toString();
506     }
507 
508     /** Implement the Parcelable interface */
509     @Override
describeContents()510     public int describeContents() {
511         return 0;
512     }
513 
514     /** copy constructor */
WifiP2pDevice(WifiP2pDevice source)515     public WifiP2pDevice(WifiP2pDevice source) {
516         if (source != null) {
517             deviceName = source.deviceName;
518             deviceAddress = source.deviceAddress;
519             mInterfaceMacAddress = source.mInterfaceMacAddress;
520             mIpAddress = source.mIpAddress;
521             primaryDeviceType = source.primaryDeviceType;
522             secondaryDeviceType = source.secondaryDeviceType;
523             wpsConfigMethodsSupported = source.wpsConfigMethodsSupported;
524             deviceCapability = source.deviceCapability;
525             groupCapability = source.groupCapability;
526             status = source.status;
527             if (source.wfdInfo != null) {
528                 wfdInfo = new WifiP2pWfdInfo(source.wfdInfo);
529             }
530             if (null != source.mVendorElements) {
531                 mVendorElements = new ArrayList<>(source.mVendorElements);
532             }
533             mVendorData = new ArrayList<>(source.mVendorData);
534         }
535     }
536 
537     /** Implement the Parcelable interface */
538     @Override
writeToParcel(Parcel dest, int flags)539     public void writeToParcel(Parcel dest, int flags) {
540         dest.writeString(deviceName);
541         dest.writeString(deviceAddress);
542         dest.writeParcelable(mInterfaceMacAddress, flags);
543         if (mIpAddress != null) {
544             dest.writeByte((byte) 1);
545             dest.writeByteArray(mIpAddress.getAddress());
546         } else {
547             dest.writeByte((byte) 0);
548         }
549         dest.writeString(primaryDeviceType);
550         dest.writeString(secondaryDeviceType);
551         dest.writeInt(wpsConfigMethodsSupported);
552         dest.writeInt(deviceCapability);
553         dest.writeInt(groupCapability);
554         dest.writeInt(status);
555         if (wfdInfo != null) {
556             dest.writeInt(1);
557             wfdInfo.writeToParcel(dest, flags);
558         } else {
559             dest.writeInt(0);
560         }
561         dest.writeTypedList(mVendorElements);
562         dest.writeList(mVendorData);
563     }
564 
565     /** Implement the Parcelable interface */
566     public static final @android.annotation.NonNull Creator<WifiP2pDevice> CREATOR =
567         new Creator<WifiP2pDevice>() {
568             @Override
569             public WifiP2pDevice createFromParcel(Parcel in) {
570                 WifiP2pDevice device = new WifiP2pDevice();
571                 device.deviceName = in.readString();
572                 device.deviceAddress = in.readString();
573                 device.mInterfaceMacAddress = in.readParcelable(MacAddress.class.getClassLoader());
574                 if (in.readByte() == 1) {
575                     try {
576                         device.mIpAddress = InetAddress.getByAddress(in.createByteArray());
577                     } catch (UnknownHostException e) {
578                         e.printStackTrace();
579                         return new WifiP2pDevice();
580                     }
581                 }
582                 device.primaryDeviceType = in.readString();
583                 device.secondaryDeviceType = in.readString();
584                 device.wpsConfigMethodsSupported = in.readInt();
585                 device.deviceCapability = in.readInt();
586                 device.groupCapability = in.readInt();
587                 device.status = in.readInt();
588                 if (in.readInt() == 1) {
589                     device.wfdInfo = WifiP2pWfdInfo.CREATOR.createFromParcel(in);
590                 }
591                 device.mVendorElements = in.createTypedArrayList(
592                         ScanResult.InformationElement.CREATOR);
593                 device.mVendorData = ParcelUtil.readOuiKeyedDataList(in);
594                 return device;
595             }
596 
597             @Override
598             public WifiP2pDevice[] newArray(int size) {
599                 return new WifiP2pDevice[size];
600             }
601         };
602 
603     //supported formats: 0x1abc, 0X1abc, 1abc
parseHex(String hexString)604     private int parseHex(String hexString) {
605         int num = 0;
606         if (hexString.startsWith("0x") || hexString.startsWith("0X")) {
607             hexString = hexString.substring(2);
608         }
609 
610         try {
611             num = Integer.parseInt(hexString, 16);
612         } catch(NumberFormatException e) {
613             Log.e(TAG, "Failed to parse hex string " + hexString);
614         }
615         return num;
616     }
617 }
618