-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8ba78ee
Showing
7 changed files
with
366 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider | ||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||
|
||
# User-specific stuff | ||
.idea/**/workspace.xml | ||
.idea/**/tasks.xml | ||
.idea/**/usage.statistics.xml | ||
.idea/**/dictionaries | ||
.idea/**/shelf | ||
|
||
# AWS User-specific | ||
.idea/**/aws.xml | ||
|
||
# Generated files | ||
.idea/**/contentModel.xml | ||
|
||
# Sensitive or high-churn files | ||
.idea/**/dataSources/ | ||
.idea/**/dataSources.ids | ||
.idea/**/dataSources.local.xml | ||
.idea/**/sqlDataSources.xml | ||
.idea/**/dynamic.xml | ||
.idea/**/uiDesigner.xml | ||
.idea/**/dbnavigator.xml | ||
|
||
# Gradle | ||
.idea/**/gradle.xml | ||
.idea/**/libraries | ||
|
||
# Gradle and Maven with auto-import | ||
# When using Gradle or Maven with auto-import, you should exclude module files, | ||
# since they will be recreated, and may cause churn. Uncomment if using | ||
# auto-import. | ||
# .idea/artifacts | ||
# .idea/compiler.xml | ||
# .idea/jarRepositories.xml | ||
# .idea/modules.xml | ||
# .idea/*.iml | ||
# .idea/modules | ||
# *.iml | ||
# *.ipr | ||
|
||
# CMake | ||
cmake-build-*/ | ||
|
||
# Mongo Explorer plugin | ||
.idea/**/mongoSettings.xml | ||
|
||
# File-based project format | ||
*.iws | ||
|
||
# IntelliJ | ||
out/ | ||
|
||
# mpeltonen/sbt-idea plugin | ||
.idea_modules/ | ||
|
||
# JIRA plugin | ||
atlassian-ide-plugin.xml | ||
|
||
# Cursive Clojure plugin | ||
.idea/replstate.xml | ||
|
||
# SonarLint plugin | ||
.idea/sonarlint/ | ||
|
||
# Crashlytics plugin (for Android Studio and IntelliJ) | ||
com_crashlytics_export_strings.xml | ||
crashlytics.properties | ||
crashlytics-build.properties | ||
fabric.properties | ||
|
||
# Editor-based Rest Client | ||
.idea/httpRequests | ||
|
||
# Android studio 3.1+ serialized cache file | ||
.idea/caches/build_file_checksums.ser |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<module type="JAVA_MODULE" version="4"> | ||
<component name="NewModuleRootManager" inherit-compiler-output="true"> | ||
<exclude-output /> | ||
<content url="file://$MODULE_DIR$"> | ||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||
</content> | ||
<orderEntry type="inheritedJdk" /> | ||
<orderEntry type="sourceFolder" forTests="false" /> | ||
</component> | ||
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package akamai.sensordata; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class DeviceInfo { | ||
static final String[] ANDROID_SCREEN_SIZE = {"128,72", "256,144", "384,216", "512,288", "640,360", "768,432", "896,504", "1024,576", "1152,648", "1280,720", "1408,792", "1536,864", "1664,936", "1792,1008", "1920,1080", "2048,1152", "2176,1224", "2304,1296", "2432,1368", "2560,1440", "2688,1512", "2816,1584", "2944,1656", "3072,1728", "3200,1800", "3328,1872", "3456,1944", "3584,2016", "3712,2088", "3840,2160", "3968,2232", "4096,2304", "4224,2376", "4352,2448", "4480,2520", "4608,2592", "4736,2664", "4864,2736", "4992,2808", "5120,2880", "5248,2952", "5376,3024", "5504,3096", "5632,3168", "5760,3240", "5888,3312", "6016,3384", "6144,3456", "6272,3528", "6400,3600", "6528,3672", "6656,3744", "6784,3816", "6912,3888", "7040,3960", "7168,4032", "7296,4104", "7424,4176", "7552,4248", "7680,4320"}; | ||
static final Map<String, String> ANDROID_VERSION = new HashMap<String, String>() { | ||
private static final long serialVersionUID = 1; | ||
|
||
{ | ||
put("5.1", "22"); | ||
put("6.0", "23"); | ||
put("7.0", "24"); | ||
put("7.1", "25"); | ||
put("8.0.0", "26"); | ||
put("8.1.0", "27"); | ||
put("9.0", "28"); | ||
put("10.0", "29"); | ||
put("11.0", "30"); | ||
} | ||
}; | ||
static final String[] ANDROID_BRAND = {"Acer", "ARCHOS", "ASUS", "Casio", "Dell", "Garmin-ASUS", "HTC", "Huawei", "Kyocera", "Lenovo", "LG", "Motorola", "Panasonic", "Philips", "Samsung", "Sony", "Sony Ericsson", "Toshiba", "ZTE"}; | ||
private static String[] ANDROID_MODEL = {"SCH-I535", "DROIDRAZR", "SAMSUNG-SGH-I747", "SGH-T999", "SPH-L710", "PC36100", "DROIDBIONIC", "ADR6400L", "SGH-T989", "SPH-D710", "ADR6300", "GT-I9300", "DROIDX", "ADR6350", "HTCOneX", "DROIDRAZRHD", "XT910", "Nexus4", "DROID4", "NexusS", "HTCVLE_U", "EVO", "SAMSUNG-SGH-I727", "SCH-I510", "Nexus7", "XT907", "PG86100", "HTCSensation4G", "NexusS4G", "SPH-D720", "HTCGlacier", "SCH-I500", "GT-I9100", "LG-P999", "ADR6425LVW", "SPH-D700", "SAMSUNG-SGH-I317", "SAMSUNG-SGH-I777", "SGH-T959V", "SCH-I405", "SCH-I415", "DROID2GLOBAL", "T-MobileG2", "DROIDPro", "SAMSUNG-SGH-I717", "SGH-I747M", "DROID3", "MB860", "ADR6410LVW", "NexusOne", "SGH-T769", "DROIDX2", "SAMSUNG-SGH-I997", "GT-I9100", "GT-I9001", "GT-I9000", "GT-I8150", "D700", "GT-S5660", "GT-5830L", "S5360", "GT-S5570", "I200", "XT894", "LG-P500", "GT-N7100", "Zio", "DesireHD", "HTCEVODesign4G", "5860E", "A100", "A200", "A500", "ADR6330VW", "ADR8995", "ADR910L", "HTCOneS", "GT-I9100P", "LG-E971", "GT-I9300T", "GT-N7000", "HTCDesireS", "HTCAmaze4G", "GT-I8190", "GT-I8190N", "GT-I9305", "SGH-I747", "HTCVision", "HTCDesire", "HTC6435LVW", "EK-GC100", "HTCIncredibleS", "HTCOneV", "GT-S5830", "SGH-T989D", "SGH-I717D", "GT-N7105", "GT-I9100M", "SGH-I317M", "XT615", "HTCChaChaA810e", "LG-E973", "ST26i", "LT26i", "GT-S5839i", "MT15i", "LG-E400", "GT-P1000", "HTCWildfireSA510e", "GT-I9100T", "LG-C555", "SAMSUNG-SGH-I896", "XT905", "HTCDesireHDA9191", "HTCDesireX", "GT-I9210T", "HTCLegend", "HTCSensationZ710e", "U9202L-1", "GT-P3100", "GT-P5100", "GT-P5110", "LT25i", "SGH-T959D", "DROIDPRO", "GT-I9305T", "HTCDesireC", "LG-P880", "LT30at", "XT603", "C5303", "GT-I9210", "GT-N7105T", "GT-N8000", "GT-P3110", "GT-P7500", "GT-S7560M", "Garmin-AsusA50", "LT29i", "MB525", "SCH-I605", "SGH-I717R", "SGH-T589W", "SGH-T999V", "T-MobileVivacity", "WIKO-CINKSLIM", "SHW-M250S1", "SHV-E250S1", "SHV-E210S", "SHV-E250K", "SHV-E210K", "SHV-E160K", "SHV-E160S", "SCH-I545", "SAMSUNG-SGH-I337", "HTCOne", "SGH-M919", "SPH-L720", "SCH-I605", "GT-I9505", "SGH-T889", "HTCONE", "HTC6435LVW", "SPH-L900", "SAMSUNG-SGH-I537", "GT-N7100", "HTC6500LVW", "LG-P769", "XT1080", "GT-I9305", "GT-I9505G", "GT-I9500", "SGH-I337M", "XT1058", "VS8404G", "GT-N7105", "C6603", "LG-MS770", "GT-N7000", "HTCOneX+", "DesireHD", "LG-LS970", "MB855", "XT1060", "MB886", "HTC_PN071", "SGH-T999L", "HTCEVOV4G", "MB865", "SCH-R530U", "SGH-T679", "SGH-T679", "HTCPH39100", "HTC_Amaze_4G", "myTouch_4G_Slide", "SPH-L300", "SCH-I200", "SPH-D710VMUB", "HTCOne801e", "SPH-D710BST", "LG-E970", "SHV-E250S", "SGH-T699", "SCH-R530M", "C6606", "XT897", "LG-E980", "LG-VM696", "PH44100", "VS9204G", "SM-N900V"}; | ||
private static String[] CPU_MANUFACTURER = {"amd", "allwinner", "amlogic", "apple", "broadcom", "cyrix", "freescale", "fujitsu", "hisilicon", "hitachi", "hygon", "ibm", "intel", "mcst", "marvell", "mediatek", "motorola", "nexgen", "nvidia", "oracle", "qualcomm", "rise technology", "rockchip", "samsung", "sifive", "sigmatel", "texas instruments", "tilera", "transmeta", "via", "wave computing", "winchip", "zhaoxin"}; | ||
private static String[] CPU_TYPE = {"qcom", "mtk", "intel", "amd", "exynos", "unisoc"}; | ||
public String brand = randomANDROIDBRAND(); | ||
public String model = randomANDROIDMODEL(); | ||
public String androidVersion = randomANDROIDVERSION(); | ||
public String manufacturer = randomMANUFACTURER(); | ||
public String cputype = randomCPU(); | ||
public String SDKINT = ANDROID_VERSION.get(this.androidVersion); | ||
|
||
public static String randomScreenSize() { | ||
return ANDROID_SCREEN_SIZE[(int) (Math.random() * ANDROID_SCREEN_SIZE.length)]; | ||
} | ||
|
||
public static String randomANDROIDVERSION() { | ||
Object[] versions = ANDROID_VERSION.keySet().toArray(); | ||
return versions[(int) (Math.random() * versions.length)].toString(); | ||
} | ||
|
||
public static String randomANDROIDBRAND() { | ||
return ANDROID_BRAND[(int) (Math.random() * ANDROID_BRAND.length)]; | ||
} | ||
|
||
public static String randomANDROIDMODEL() { | ||
return ANDROID_MODEL[(int) (Math.random() * ANDROID_MODEL.length)]; | ||
} | ||
|
||
public static String randomMANUFACTURER() { | ||
return CPU_MANUFACTURER[(int) (Math.random() * CPU_MANUFACTURER.length)]; | ||
} | ||
|
||
public static String randomCPU() { | ||
return CPU_TYPE[(int) (Math.random() * CPU_TYPE.length)]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package akamai.sensordata; | ||
|
||
import java.security.Key; | ||
import java.security.KeyFactory; | ||
import java.security.PublicKey; | ||
import java.security.spec.X509EncodedKeySpec; | ||
import java.util.Base64; | ||
import java.util.UUID; | ||
import javax.crypto.Cipher; | ||
import javax.crypto.KeyGenerator; | ||
import javax.crypto.Mac; | ||
import javax.crypto.SecretKey; | ||
import javax.crypto.spec.SecretKeySpec; | ||
|
||
|
||
public class SensorData { | ||
static long uptime = 0; | ||
static long startTime = System.currentTimeMillis(); | ||
public DeviceInfo mDeviceInfo = new DeviceInfo(); | ||
SecretKey aesKey = null; | ||
SecretKey hmacKey = null; | ||
String aesKeyEncrypted = null; | ||
String hmacKeyEncrypted = null; | ||
|
||
public SensorData() { | ||
uptime = getRandomNumber(0, 12) * 36L; | ||
} | ||
|
||
public static long getUptime() { | ||
return uptime + (System.currentTimeMillis() - startTime); | ||
} | ||
|
||
public String getSensorData() { | ||
this.mDeviceInfo = new DeviceInfo(); | ||
String model = this.mDeviceInfo.model; | ||
String androidVersion = this.mDeviceInfo.androidVersion; | ||
String brand = this.mDeviceInfo.brand; | ||
String SDKINT = this.mDeviceInfo.SDKINT; | ||
String manufacturer = this.mDeviceInfo.manufacturer; | ||
String cputype = this.mDeviceInfo.cputype; | ||
StringBuilder stringBuilder6 = new StringBuilder(); | ||
stringBuilder6.append("3.2.2-1,2,-94,-100,").append("-1,uaend,-1,").append(DeviceInfo.randomScreenSize()).append(",1,100,1,en,").append(androidVersion).append(",0,").append(model).append(",").append(manufacturer).append(",").append(cputype).append(",-1,").append("com.clairmail.fth").append(",-1,-1,").append(UUID.randomUUID()).append(",-1,1,0,REL,").append(getRandomNumber(0, 999999999)).append(",").append(SDKINT).append(",").append(brand).append(",").append(model).append(",release-keys,user,").append(brand).append(",").append(brand).append("-user/").append(androidVersion).append("/").append(getRandomNumber(0, 999999999)).append("/release-keys,").append("universal").append(getRandomNumber(0, 99999)).append(",").append(brand).append(",").append(model).append(",").append(brand).append("/").append(brand).append("/").append(model).append(":").append(androidVersion).append("/").append(getRandomNumber(0, 999999999)).append(":").append("user/release-keys,").append(getRandomNumber(0, 999999999)).append(",").append(model); | ||
int length = chrplus(stringBuilder6.toString()); | ||
stringBuilder6.append(",").append(length - 907).append(",").append(getRandomNumber(1, 9999)).append(",").append(System.currentTimeMillis() / 2).append("-1,2,-94,-101,").append("do_unr,dm_en,t_en").append("-1,2,-94,-102,").append("-1,2,-94,-108,").append(randomEvent()).append("-1,2,-94,-117,").append(touchEvent()).append("-1,2,-94,-144,").append("-1,2,-94,-142,").append("-1,2,-94,-145,").append("-1,2,-94,-143,").append("-1,2,-94,-115,").append(randomPair()).append("-1,2,-94,-70,").append("-1,2,-94,-80,").append("-1,2,-94,-120,").append("-1,2,-94,-112,").append(randomActivity2()).append("-1,2,-94,-103,").append(activities()); | ||
String sensor = encryptSensor(stringBuilder6.toString()); | ||
return sensor + "|" + SDKINT + "|" + brand + "|" + model + "|" + androidVersion + "|"; | ||
} | ||
|
||
public static String randomActivity2() { | ||
return getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000) + "," + getRandomNumber(0, 200000); | ||
} | ||
|
||
public static String activities() { | ||
StringBuilder sb = new StringBuilder(); | ||
int cycleNum = getRandomNumber(10, 50); | ||
for (int i = 0; i < cycleNum; i++) { | ||
sb.append("2"); | ||
sb.append(","); | ||
sb.append(System.currentTimeMillis() + getRandomNumber(0, 50000)); | ||
sb.append(";"); | ||
sb.append("3"); | ||
sb.append(","); | ||
sb.append(System.currentTimeMillis() + getRandomNumber(50000, 1000000)); | ||
sb.append(";"); | ||
} | ||
return sb.toString(); | ||
} | ||
|
||
public static String randomPair() { | ||
return randomEvent() + "," + touchEvent() + "," + getRandomNumber(0, 10000) + "," + getRandomNumber(0, 10000) + "," + getRandomLong(4294967295L, 999999999999999999L) + "," + System.currentTimeMillis() + ",0,0," + getRandomNumber(0, 10000) + "," + getRandomLong(4294967295L, 999999999999999999L) + "," + getRandomNumber(0, 1000000) + "," + getUptime() + "," + getRandomNumber(0, 10000) + "," + getRandomLong(4294967295L, 999999999999999999L) + "," + System.currentTimeMillis() + "," + getRandomNumber(0, 10000) + ",0"; | ||
} | ||
|
||
public static String randomEvent() { | ||
StringBuilder sb = new StringBuilder(); | ||
int maxScreenSize = getMaxScreenSize(); | ||
int cycleNum = getRandomNumber(10, 50); | ||
for (int i = 0; i < cycleNum; i++) { | ||
sb.append("2"); | ||
sb.append(","); | ||
sb.append(getRandomNumber(0, maxScreenSize)); | ||
sb.append(","); | ||
sb.append(getRandomNumber(0, maxScreenSize)); | ||
sb.append(";"); | ||
} | ||
return sb.toString(); | ||
} | ||
|
||
public static String touchEvent() { | ||
StringBuilder sb = new StringBuilder(); | ||
int maxScreenSize = getMaxScreenSize(); | ||
int cycleNum = getRandomNumber(10, 50); | ||
for (int i = 0; i < cycleNum; i++) { | ||
sb.append(getRandomNumber(2, 3)); | ||
sb.append(","); | ||
sb.append(getRandomNumber(0, maxScreenSize)); | ||
sb.append(",0,0,1,1,1,-1;"); | ||
} | ||
return sb.toString(); | ||
} | ||
|
||
public static int getRandomNumber(int min, int max) { | ||
return (int) ((Math.random() * (max - min)) + min); | ||
} | ||
|
||
public static long getRandomLong(long min, long max) { | ||
return (long) ((Math.random() * (max - min)) + min); | ||
} | ||
|
||
public static int getMaxScreenSize() { | ||
String[] screenInt = DeviceInfo.randomScreenSize().split(","); | ||
int b1 = Integer.parseInt(screenInt[0]); | ||
int b2 = Integer.parseInt(screenInt[1]); | ||
return Math.max(b1, b2); | ||
} | ||
|
||
public static int chrplus(String paramString) { | ||
if (paramString != null && !paramString.trim().equalsIgnoreCase("")) { | ||
char c = 0; | ||
for (int b = 0; b < paramString.length(); b++) { | ||
try { | ||
char c2 = paramString.charAt(b); | ||
if (c2 < 128) { | ||
c += c2; | ||
} | ||
} catch (Exception e) { | ||
return -2; | ||
} | ||
} | ||
return c; | ||
} | ||
return -1; | ||
} | ||
|
||
public String encryptSensor(String str) { | ||
String result = null; | ||
try { | ||
initEncryptKey(); | ||
long uptimeMillis = getUptime(); | ||
Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding"); | ||
instance.init(1, this.aesKey); | ||
byte[] doFinal = instance.doFinal(str.getBytes()); | ||
long aesUptime = (getUptime() - uptimeMillis) * 1000; | ||
byte[] iv = instance.getIV(); | ||
byte[] obj = new byte[doFinal.length + iv.length]; | ||
System.arraycopy(iv, 0, obj, 0, iv.length); | ||
System.arraycopy(doFinal, 0, obj, iv.length, doFinal.length); | ||
long uptimeMillis2 = getUptime(); | ||
Key secretKeySpec = new SecretKeySpec(this.hmacKey.getEncoded(), "HmacSHA256"); | ||
Mac instance2 = Mac.getInstance("HmacSHA256"); | ||
instance2.init(secretKeySpec); | ||
byte[] iv2 = instance2.doFinal(obj); | ||
byte[] doFinal2 = new byte[obj.length + iv2.length]; | ||
long hmackUptime = (getUptime() - uptimeMillis2) * 1000; | ||
System.arraycopy(obj, 0, doFinal2, 0, obj.length); | ||
System.arraycopy(iv2, 0, doFinal2, obj.length, iv2.length); | ||
long uptimeMillis3 = getUptime(); | ||
String encryptedData = Base64.getEncoder().encodeToString(doFinal2); | ||
long b64uptime = 1000 * (getUptime() - uptimeMillis3); | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append("2,a,"); | ||
sb.append(this.aesKeyEncrypted); | ||
sb.append(","); | ||
sb.append(this.hmacKeyEncrypted); | ||
sb.append("$"); | ||
sb.append(encryptedData); | ||
sb.append("$"); | ||
sb.append(aesUptime).append(",").append(hmackUptime).append(",").append(b64uptime); | ||
sb.append("$$"); | ||
result = sb.toString(); | ||
} catch (Exception e) { | ||
} | ||
return result; | ||
} | ||
|
||
private void initEncryptKey() { | ||
if (this.aesKey != null) { | ||
return; | ||
} | ||
try { | ||
KeyGenerator keyGen = KeyGenerator.getInstance("AES"); | ||
this.aesKey = keyGen.generateKey(); | ||
KeyGenerator hmacKeyGen = KeyGenerator.getInstance("HmacSHA256"); | ||
this.hmacKey = hmacKeyGen.generateKey(); | ||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4sA7vA7N/t1SRBS8tugM2X4bByl0jaCZLqxPOql+qZ3sP4UFayqJTvXjd7eTjMwg1T70PnmPWyh1hfQr4s12oSVphTKAjPiWmEBvcpnPPMjr5fGgv0w6+KM9DLTxcktThPZAGoVcoyM/cTO/YsAMIxlmTzpXBaxddHRwi8S2NvwIDAQAB")); | ||
KeyFactory factory = KeyFactory.getInstance("RSA"); | ||
PublicKey rsaKey = factory.generatePublic(keySpec); | ||
Cipher rsaInstance = Cipher.getInstance("RSA/ECB/PKCS1PADDING"); | ||
rsaInstance.init(1, rsaKey); | ||
this.aesKeyEncrypted = Base64.getEncoder().encodeToString(rsaInstance.doFinal(this.aesKey.getEncoded())); | ||
this.hmacKeyEncrypted = Base64.getEncoder().encodeToString(rsaInstance.doFinal(this.hmacKey.getEncoded())); | ||
} catch (Exception e) { | ||
} | ||
} | ||
|
||
public static void main(String[] args) { | ||
System.out.println(new SensorData().getSensorData()); | ||
} | ||
} |