package com.unity3d.services.core.device; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ConfigurationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.hardware.Sensor; import android.hardware.SensorManager; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.BatteryManager; import android.os.Build; import android.os.SystemClock; import android.provider.Settings; import android.telephony.TelephonyManager; import com.unity3d.services.core.log.DeviceLog; import com.unity3d.services.core.misc.Utilities; import com.unity3d.services.core.properties.ClientProperties; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.security.MessageDigest; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Device { public enum MemoryInfoType { TOTAL_MEMORY, FREE_MEMORY } public static int getApiLevel() { return Build.VERSION.SDK_INT; } public static String getOsVersion() { return Build.VERSION.RELEASE; } public static String getManufacturer () { return Build.MANUFACTURER; } public static String getModel() { return Build.MODEL; } public static int getScreenLayout () { if (ClientProperties.getApplicationContext() != null) { return ClientProperties.getApplicationContext().getResources().getConfiguration().screenLayout; } return -1; } @SuppressLint("DefaultLocale") public static String getAndroidId () { String androidID = null; try { androidID = Settings.Secure.getString(ClientProperties.getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID); } catch (Exception e) { DeviceLog.exception("Problems fetching androidId", e); } return androidID; } public static String getAdvertisingTrackingId() { return AdvertisingId.getAdvertisingTrackingId(); } public static boolean isLimitAdTrackingEnabled() { return AdvertisingId.getLimitedAdTracking(); } public static boolean isUsingWifi () { ConnectivityManager mConnectivity; if (ClientProperties.getApplicationContext() != null) { mConnectivity = (ConnectivityManager)ClientProperties.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); if (mConnectivity == null) { return false; } TelephonyManager mTelephony; mTelephony = (TelephonyManager)ClientProperties.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE); // Skip if no connection, or background data disabled NetworkInfo info = mConnectivity.getActiveNetworkInfo(); if (info == null || !mConnectivity.getBackgroundDataSetting() || !mConnectivity.getActiveNetworkInfo().isConnected() || mTelephony == null) { return false; } int netType = info.getType(); return netType == ConnectivityManager.TYPE_WIFI && info.isConnected(); } return false; } public static int getNetworkType() { if (ClientProperties.getApplicationContext() != null) { TelephonyManager tm = (TelephonyManager)ClientProperties.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE); return tm.getNetworkType(); } return -1; } public static boolean getNetworkMetered() { ConnectivityManager mConnectivity; if (ClientProperties.getApplicationContext() != null && android.os.Build.VERSION.SDK_INT >= 16) { mConnectivity = (ConnectivityManager) ClientProperties.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); if (mConnectivity == null) { return false; } return mConnectivity.isActiveNetworkMetered(); } return false; } public static String getNetworkOperator() { if(ClientProperties.getApplicationContext() != null) { TelephonyManager tm = (TelephonyManager)ClientProperties.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE); return tm.getNetworkOperator(); } return ""; } public static String getNetworkOperatorName() { if (ClientProperties.getApplicationContext() != null) { TelephonyManager tm = (TelephonyManager)ClientProperties.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE); return tm.getNetworkOperatorName(); } return ""; } public static String getNetworkCountryISO() { if (ClientProperties.getApplicationContext() != null) { TelephonyManager tm = (TelephonyManager)ClientProperties.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE); return tm.getNetworkCountryIso(); } return ""; } public static float getDisplayMetricDensity() { if (ClientProperties.getApplicationContext() != null) { return ClientProperties.getApplicationContext().getResources().getDisplayMetrics().density; } return -1; } public static int getScreenDensity() { if (ClientProperties.getApplicationContext() != null) { return ClientProperties.getApplicationContext().getResources().getDisplayMetrics().densityDpi; } return -1; } public static int getScreenWidth() { if (ClientProperties.getApplicationContext() != null) { return ClientProperties.getApplicationContext().getResources().getDisplayMetrics().widthPixels; } return -1; } public static int getScreenHeight() { if (ClientProperties.getApplicationContext() != null) { return ClientProperties.getApplicationContext().getResources().getDisplayMetrics().heightPixels; } return -1; } public static boolean isActiveNetworkConnected () { if (ClientProperties.getApplicationContext() != null) { ConnectivityManager cm = (ConnectivityManager)ClientProperties.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null) { NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnected(); } } return false; } public static String getUniqueEventId() { return UUID.randomUUID().toString(); } @SuppressWarnings("deprecation") public static boolean isWiredHeadsetOn() { if (ClientProperties.getApplicationContext() != null) { AudioManager am = (AudioManager)ClientProperties.getApplicationContext().getSystemService(Context.AUDIO_SERVICE); // Note: This method is officially deprecated but documentation gives more fine-grained approach. This method // returns whether headset is connected or not. Audio playback might be routed through other means. Since we are // not using this value for making audio routing decisions, using this is ok and supported by Android docs. return am.isWiredHeadsetOn(); } return false; } public static String getSystemProperty (String propertyName, String defaultValue) { if (defaultValue != null) { return System.getProperty(propertyName, defaultValue); } return System.getProperty(propertyName); } public static int getRingerMode () { if (ClientProperties.getApplicationContext() != null) { AudioManager am = (AudioManager)ClientProperties.getApplicationContext().getSystemService(Context.AUDIO_SERVICE); if (am != null) return am.getRingerMode(); else return -2; } return -1; } public static int getStreamVolume(int streamType) { if (ClientProperties.getApplicationContext() != null) { AudioManager am = (AudioManager)ClientProperties.getApplicationContext().getSystemService(Context.AUDIO_SERVICE); if (am != null) return am.getStreamVolume(streamType); else return -2; } return -1; } public static int getStreamMaxVolume(int streamType) { if (ClientProperties.getApplicationContext() != null) { AudioManager am = (AudioManager)ClientProperties.getApplicationContext().getSystemService(Context.AUDIO_SERVICE); if (am != null) return am.getStreamMaxVolume(streamType); else return -2; } return -1; } public static int getScreenBrightness () { if (ClientProperties.getApplicationContext() != null) { return Settings.System.getInt(ClientProperties.getApplicationContext().getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS, -1); } return -1; } public static long getFreeSpace (File file) { if (file != null && file.exists()) { return Math.round(file.getFreeSpace() / 1024); } return -1; } public static long getTotalSpace (File file) { if (file != null && file.exists()) { return Math.round(file.getTotalSpace() / 1024); } return -1; } public static float getBatteryLevel () { if (ClientProperties.getApplicationContext() != null) { Intent i = ClientProperties.getApplicationContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); if (i != null) { int level = i.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = i.getIntExtra(BatteryManager.EXTRA_SCALE, -1); return level / (float)scale; } } return -1; } public static int getBatteryStatus () { if (ClientProperties.getApplicationContext() != null) { Intent i = ClientProperties.getApplicationContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); if (i != null) { return i.getIntExtra(BatteryManager.EXTRA_STATUS, -1); } } return -1; } public static long getTotalMemory () { return getMemoryInfo(MemoryInfoType.TOTAL_MEMORY); } public static long getFreeMemory () { return getMemoryInfo(MemoryInfoType.FREE_MEMORY); } private static long getMemoryInfo (MemoryInfoType infoType) { int lineNumber = -1; switch (infoType) { case TOTAL_MEMORY: lineNumber = 1; break; case FREE_MEMORY: lineNumber = 2; break; default: break; } RandomAccessFile reader = null; String line = null; try { reader = new RandomAccessFile("/proc/meminfo", "r"); for (int i = 0; i < lineNumber; i++) { line = reader.readLine(); } return getMemoryValueFromString(line); } catch (IOException e) { DeviceLog.exception("Error while reading memory info: " + infoType, e); } finally { try { reader.close(); } catch (IOException e) { DeviceLog.exception("Error closing RandomAccessFile", e); } } return -1; } private static long getMemoryValueFromString (String memVal) { if (memVal != null) { Pattern p = Pattern.compile("(\\d+)"); Matcher m = p.matcher(memVal); String value = ""; while (m.find()) { value = m.group(1); } return Long.parseLong(value); } return -1; } public static boolean isRooted() { try { return searchPathForBinary("su"); } catch(Exception e) { DeviceLog.exception("Rooted check failed", e); return false; } } public static Boolean isAdbEnabled () { Boolean debugBridgeEnabled = null; if (getApiLevel() < 17) { debugBridgeEnabled = oldAdbStatus(); } else { debugBridgeEnabled = newAdbStatus(); } return debugBridgeEnabled; } private static Boolean oldAdbStatus () { Boolean status = null; try { status = 1 == Settings.Secure.getInt(ClientProperties.getApplicationContext().getContentResolver(), Settings.Secure.ADB_ENABLED, 0); } catch (Exception e) { DeviceLog.exception("Problems fetching adb enabled status", e); } return status; } @TargetApi(17) private static Boolean newAdbStatus () { Boolean status = null; try { status = 1 == Settings.Global.getInt(ClientProperties.getApplicationContext().getContentResolver(), Settings.Global.ADB_ENABLED, 0); } catch (Exception e) { DeviceLog.exception("Problems fetching adb enabled status", e); } return status; } private static boolean searchPathForBinary(String binary) { String[] paths = System.getenv("PATH").split(":"); for(String path : paths) { File pathDir = new File(path); if(pathDir.exists() && pathDir.isDirectory()) { File[] pathDirFiles = pathDir.listFiles(); if(pathDirFiles != null) { for (File fileInPath : pathDirFiles) { if (fileInPath.getName().equals(binary)) { return true; } } } } } return false; } public static String getGLVersion () { if (ClientProperties.getApplicationContext() != null) { final ActivityManager activityManager = (ActivityManager)ClientProperties.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); if (activityManager != null) { final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); if (configurationInfo != null) { return configurationInfo.getGlEsVersion(); } } } return null; } public static String getApkDigest() throws Exception { String apkDigest = null; String apkPath = ClientProperties.getApplicationContext().getPackageCodePath(); InputStream inputStream = null; try { inputStream = new FileInputStream(new File(apkPath)); apkDigest = Utilities.Sha256(inputStream); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { } } return apkDigest; } public static String getCertificateFingerprint() { String fingerprint = null; PackageManager pm = ClientProperties.getApplicationContext().getPackageManager(); String pkgName = ClientProperties.getApplicationContext().getPackageName(); try { PackageInfo pinfo = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNATURES); Signature[] signatures = pinfo.signatures; if (signatures != null && signatures.length >= 1) { CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream stream = new ByteArrayInputStream(signatures[0].toByteArray()); X509Certificate cert = (X509Certificate) cf.generateCertificate(stream); MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); byte[] publicKey = messageDigest.digest(cert.getEncoded()); fingerprint = Utilities.toHexString(publicKey); } } catch (Exception e) { DeviceLog.exception("Exception when signing certificate fingerprint", e); } return fingerprint; } public static String getBoard () { return Build.BOARD; } public static String getBootloader () { return Build.BOOTLOADER; } public static String getBrand () { return Build.BRAND; } public static String getDevice () { return Build.DEVICE; } public static String getHardware () { return Build.HARDWARE; } public static String getHost () { return Build.HOST; } public static String getProduct () { return Build.PRODUCT; } public static String getFingerprint() { return Build.FINGERPRINT; } public static ArrayList<String> getSupportedAbis () { if (getApiLevel() < 21) { return getOldAbiList(); } else { return getNewAbiList(); } } public static List<Sensor> getSensorList () { if (ClientProperties.getApplicationContext() != null) { SensorManager sensorManager = (SensorManager)ClientProperties.getApplicationContext().getSystemService(Context.SENSOR_SERVICE); return sensorManager.getSensorList(Sensor.TYPE_ALL); } return null; } public static boolean isUSBConnected() { if (ClientProperties.getApplicationContext() != null) { Intent intent = ClientProperties.getApplicationContext().registerReceiver(null, new IntentFilter("android.hardware.usb.action.USB_STATE")); if (intent != null) { return intent.getBooleanExtra("connected", false); } } return false; } public static long getCPUCount() { return Runtime.getRuntime().availableProcessors(); } public static long getUptime() { return SystemClock.uptimeMillis(); } public static long getElapsedRealtime() { return SystemClock.elapsedRealtime(); } public static String getBuildId() { return Build.ID; } public static String getBuildVersionIncremental() { return Build.VERSION.INCREMENTAL; } private static ArrayList<String> getOldAbiList () { ArrayList<String> abiList = new ArrayList<>(); abiList.add(Build.CPU_ABI); abiList.add(Build.CPU_ABI2); return abiList; } @TargetApi(21) private static ArrayList<String> getNewAbiList () { ArrayList<String> abiList = new ArrayList<>(); abiList.addAll(Arrays.asList(Build.SUPPORTED_ABIS)); return abiList; } public static Map<String, String> getProcessInfo () { HashMap<String, String> retData = new HashMap<>(); RandomAccessFile reader = null; try { reader = new RandomAccessFile("/proc/self/stat", "r"); String statContent = reader.readLine(); retData.put("stat", statContent); } catch (IOException e) { DeviceLog.exception("Error while reading processor info: ", e); } finally { try { reader.close(); } catch (IOException e) { DeviceLog.exception("Error closing RandomAccessFile", e); } } return retData; } }