android - using getstring to get device identifiers is not recommended
是否有唯一的Android設備ID? (20)
Android設備是否具有唯一的ID,如果是這樣,使用Java訪問它的簡單方法是什麼?
最後更新時間:2015年6月2日
在閱讀關於創建唯一ID,Google開發人員博客和Android文檔的每篇帖子後,我覺得好像'Pseudo ID'是最好的選擇。
主要問題:硬件與軟件
硬件
- 用戶可以更改他們的硬件,Android平板電腦或手機,因此基於硬件的唯一ID不是跟踪用戶的好主意
- 對於TRACKING HARDWARE ,這是一個好主意
軟件
- 如果root用戶可以擦除/更改他們的ROM
- 您可以跨平台(iOS,Android,Windows和Web)跟踪用戶
- 最好的想要在他們同意的情況下 追踪個人用戶只需讓他們登錄(使用OAuth使其無縫)
Android的總體細分
- 保證API> = 9/10的唯一性(包括root設備)(99.5%的Android設備)
- 沒有額外的權限
Psuedo代碼:
if API >= 9/10: (99.5% of devices)
return unique ID containing serial id (rooted devices may be different)
else
return unique ID of build information (may overlap data - API < 9)
感謝@stansult發布我們的所有選項 (在此問題中)。
選項列表 - 原因/為何不使用它們:
用戶電子郵件 - 軟件
- 用戶可以更改電子郵件 - 極不可能
- API 5+
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
或 - API 14+
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
( 如何獲取Android設備的主要電子郵件地址 )
用戶電話號碼 - 軟件
- 用戶可以更改電話號碼 - 極不可能
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - 硬件 (只有手機,需要
android.permission.READ_PHONE_STATE
)- 大多數用戶討厭在許可中說“電話”的事實。 一些用戶給出了不好的評級,因為他們認為你只是竊取他們的個人信息,當你真正想做的就是跟踪設備安裝。 很明顯,您正在收集數據。
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android ID - 硬件 (可以為null,可以在出廠重置時更改,可以在有根設備上更改)
- 由於它可以為'null',我們可以檢查'null'並更改其值,但這意味著它將不再是唯一的。
- 如果您的用戶具有出廠重置設備,則該設備上的值可能已更改或更改,因此如果您正在跟踪用戶安裝,則可能存在重複條目。
WLAN MAC地址 - 硬件 (需要
android.permission.ACCESS_WIFI_STATE
)- 這可能是第二個最佳選擇,但您仍在收集和存儲直接來自用戶的唯一標識符。 很明顯,您正在收集數據。
-
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
藍牙MAC地址 - 硬件 (帶藍牙的設備,需要
android.permission.BLUETOOTH
)- 市場上的大多數應用程序都不使用藍牙,因此如果您的應用程序不使用藍牙而您包含此功能,則用戶可能會產生懷疑。
-
<uses-permission android:name="android.permission.BLUETOOTH "/>
偽唯一ID - 軟件 (適用於所有Android設備)
- 很可能,可能包含碰撞 - 請參閱下面發布的方法!
- 這使您可以從用戶那裡獲得“幾乎唯一”的ID,而無需使用任何私有ID。 您可以從設備信息創建自己的匿名ID。
我知道沒有任何“完美”的方法可以在不使用權限的情況下獲取唯一ID; 但是,有時我們只需要跟踪設備安裝。 在創建唯一ID時,我們可以根據Android API提供的信息創建“偽唯一ID”,而無需使用額外的權限。 通過這種方式,我們可以向用戶展示尊重並嘗試提供良好的用戶體驗。
使用偽唯一ID,您實際上只會遇到基於類似設備這一事實可能存在重複的事實。 您可以調整組合方法,使其更加獨特; 但是,一些開發人員需要跟踪設備安裝,這將基於類似設備執行技巧或性能。
API> = 9:
如果他們的Android設備是API 9或更高版本,由於“Build.SERIAL”字段,這保證是唯一的。
請記住 ,從技術上講,只有0.5%的API <9的用戶錯過了。 所以你可以專注於其餘的:這是99.5%的用戶!
API <9:
如果用戶的Android設備低於API 9; 希望他們沒有完成工廠重置,他們的'Secure.ANDROID_ID'將被保留或不是'null'。 (見http://developer.android.com/about/dashboards/index.html )
如果一切都失敗了:
如果所有其他方法都失敗了,如果用戶確實低於API 9(低於Gingerbread),重置了他們的設備或'Secure.ANDROID_ID'返回'null',那麼返回的ID將完全基於他們的Android設備信息。 這是碰撞可能發生的地方。
變化:
- 由於工廠重置而刪除'Android.SECURE_ID'可能會導致值發生變化
- 編輯代碼以更改API
- 改變了偽
請看下面的方法:
/**
* Return pseudo unique ID
* @return ID
*/
public static String getUniquePsuedoID() {
// If all else fails, if the user does have lower than API 9 (lower
// than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
// returns 'null', then simply the ID returned will be solely based
// off their Android device information. This is where the collisions
// can happen.
// Thanks http://www.pocketmagic.net/?p=1662!
// Try not to use DISPLAY, HOST or ID - these items could change.
// If there are collisions, there will be overlapping data
String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);
// Thanks to @Roman SL!
// https://.com/a/4789483/950427
// Only devices with API >= 9 have android.os.Build.SERIAL
// http://developer.android.com/reference/android/os/Build.html#SERIAL
// If a user upgrades software or roots their device, there will be a duplicate entry
String serial = null;
try {
serial = android.os.Build.class.getField("SERIAL").get(null).toString();
// Go ahead and return the serial for api => 9
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
// String needs to be initialized
serial = "serial"; // some value
}
// Thanks @Joe!
// https://.com/a/2853253/950427
// Finally, combine the values we have found by using the UUID class to create a unique identifier
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
新功能(適用於包含廣告和Google Play服務的應用):
來自Google Play Developer的控制台:
從2014年8月1日開始,Google Play開發者計劃政策要求所有新的應用上傳和更新都使用廣告ID代替任何其他持久性標識符,以用於任何廣告目的。 學到更多
實施 :
允許:
<uses-permission android:name="android.permission.INTERNET" />
碼:
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...
// Do not call this function from the main thread. Otherwise,
// an IllegalStateException will be thrown.
public void getIdThread() {
Info adInfo = null;
try {
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
} catch (IOException exception) {
// Unrecoverable error connecting to Google Play services (e.g.,
// the old version of the service doesn't support getting AdvertisingId).
} catch (GooglePlayServicesAvailabilityException exception) {
// Encountered a recoverable error connecting to Google Play services.
} catch (GooglePlayServicesNotAvailableException exception) {
// Google Play services is not available entirely.
}
final String id = adInfo.getId();
final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}
來源/文檔:
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
重要:
當Google Play服務可用時,廣告ID旨在完全取代其他標識符的現有使用,以用於廣告目的(例如在Settings.Secure中使用ANDROID_ID)。 Google Play服務不可用的情況由getAdvertisingIdInfo()引發的GooglePlayServicesNotAvailableException指示。
警告,用戶可以重置:
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
我試圖引用我從中獲取信息的每個鏈接。 如果您遺失並需要加入,請發表評論!
Google Player服務實例ID
Android操作系統設備的唯一設備ID為String,使用TelephonyManager
和ANDROID_ID
,通過以下方式獲取:
String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId();
}
else {
deviceId = Secure.getString(
getApplicationContext().getContentResolver(),
Secure.ANDROID_ID);
}
但我強烈推薦Google建議的方法,請參閱識別應用安裝。
Google實例ID
2015年I / O發布; 在Android上需要播放服務7.5。
https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation
InstanceID iid = InstanceID.getInstance( context ); // Google docs are wrong - this requires context
String id = iid.getId(); // blocking call
Google似乎打算將此ID用於識別Android,Chrome和iOS上的安裝。
它識別安裝而不是設備,但是再次,ANDROID_ID(這是接受的答案)現在不再識別設備。使用ARC運行時,將為每個安裝生成一個新的ANDROID_ID(此處為詳細信息),就像這個新的實例ID一樣。此外,我認為識別安裝(而不是設備)是我們大多數人真正想要的。
實例ID的優點
在我看來,谷歌打算將它用於此目的(識別您的安裝),它是跨平台的,並且可以用於許多其他目的(請參閱上面的鏈接)。
如果您使用GCM,那麼您最終將需要使用此實例ID,因為您需要它以獲取GCM令牌(它替換舊的GCM註冊ID)。
缺點/問題
在當前實現(GPS 7.5)中,當您的應用請求時,將從服務器檢索實例ID。這意味著上面的調用是阻塞調用 - 在我不科學的測試中,如果設備在線則需要1-3秒,如果離線需要0.5-1.0秒(可能這是在放棄和生成之前等待的時間長度隨機ID)。這是在北美使用Android 5.1.1和GPS 7.5在Nexus 5上測試的。
如果您將ID用於他們想要的目的 - 例如。應用程序身份驗證,應用程序識別,GCM - 我認為這1-3秒可能會令人討厭(當然,取決於您的應用程序)。
TelephonyManger.getDeviceId()返回唯一的設備ID,例如,GSM的IMEI和CDMA電話的MEID或ESN。
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
但我建議使用:
Settings.Secure.ANDROID_ID,它將Android ID作為唯一的64位十六進製字符串返回。
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
有時TelephonyManger.getDeviceId()將返回null,因此為了確保您將使用此方法的唯一ID:
public String getUniqueID(){
String myAndroidDeviceId = "";
TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null){
myAndroidDeviceId = mTelephony.getDeviceId();
}else{
myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
}
return myAndroidDeviceId;
}
Android設備mac id也是一個唯一的id,如果我們格式化設備本身就不會改變,所以使用下面的代碼來獲取mac id
WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
String address = info.getMacAddress();
另外,不要忘記在AndroidManifest.xml中添加適當的權限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
以下是Reto Meier今年在Google I / O演示中使用的代碼,用於獲取用戶的唯一ID:
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
public synchronized static String id(Context context) {
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
}
}
return uniqueID;
}
如果你將這個與備份策略結合起來將偏好發送到雲端(在Reto的talk也有描述,你應該有一個與用戶綁定的ID,並在設備被擦除甚至更換後堅持使用。我計劃使用這個在未來的分析中(換句話說,我還沒有完成那一點:)。
使用下面的代碼,您可以將Android OS設備的唯一設備ID作為字符串獲取。
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
在谷歌I / O上, Reto Meier發布了一個強有力的答案,解決瞭如何解決這個問題,這應該滿足大多數開發人員在跨安裝時跟踪用戶的需求 安東尼諾蘭在他的回答中顯示了方向,但我認為我會寫出完整的方法,以便其他人可以輕鬆地看到如何做到(我花了一些時間來弄清楚細節)。
這種方法將為您提供一個匿名,安全的用戶ID,該用戶ID將針對不同設備(基於主要Google帳戶)和跨安裝的用戶持久存在。 基本方法是生成隨機用戶ID並將其存儲在應用程序的共享首選項中。 然後,您可以使用Google的備用代理存儲與雲中Google帳戶關聯的共享偏好設置。
讓我們來看看完整的方法。 首先,我們需要使用Android備份服務為SharedPreferences創建備份。 首先通過http://developer.android.com/google/backup/signup.html
註冊您的應用。
Google會為您提供備份服務密鑰,您需要將其添加到清單中。 您還需要告訴應用程序使用BackupAgent,如下所示:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="your_backup_service_key" />
</application>
然後,您需要創建備份代理並告訴它使用幫助代理進行共享優先:
public class MyBackupAgent extends BackupAgentHelper {
// The name of the SharedPreferences file
static final String PREFS = "user_preferences";
// A key to uniquely identify the set of backup data
static final String PREFS_BACKUP_KEY = "prefs";
// Allocate a helper and add it to the backup agent
@Override
public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
要完成備份,您需要在主Activity中創建一個BackupManager實例:
BackupManager backupManager = new BackupManager(context);
最後創建一個用戶ID(如果尚不存在),並將其存儲在SharedPreferences中:
public static String getUserID(Context context) {
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
MyBackupAgent.PREFS, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
//backup the changes
BackupManager mBackupManager = new BackupManager(context);
mBackupManager.dataChanged();
}
}
return uniqueID;
}
即使用戶移動設備,此User_ID現在也將在安裝期間保持不變。
有關此方法的更多信息,請參閱talk。
有關如何實施備份代理的完整詳細信息,請參閱數據備份。我特別推薦測試底部的部分,因為備份不會立即發生,所以要測試你必須強製備份。
官方的Android開發者博客現在有一篇關於這個主題的完整文章, android-developers.blogspot.com/2011/03/… 。
對於特定Android設備的硬件識別,您可以檢查MAC地址。
你可以這樣做:
在AndroidManifest.xml中
<uses-permission android:name="android.permission.INTERNET" />
現在在你的代碼中:
List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface interface : interfacesList) {
// This will give you the interface MAC ADDRESS
interface.getHardwareAddress();
}
在每個Android設備中,它們至少是一個“wlan0”接口巫婆是WI-FI芯片。即使沒有打開WI-FI,此代碼也能正常工作。
PS他們是從包含MACS的列表中獲得的一堆其他接口但這可以在手機之間切換。
我要補充一點 - 我有一個獨特的情況。
使用:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
事實證明,即使我的Viewsonic G Tablet報告的DeviceID不是Null,每個G Tablet也會報告相同的數字。
使用“Pocket Empires”讓它變得有趣,它可讓您根據“唯一”DeviceID即時訪問某人的帳戶。
我的設備沒有手機收音機。
我認為這肯定是為一個獨特的ID構建骨架的方法......檢查一下。
偽唯一ID,適用於所有Android設備某些設備沒有電話(例如平板電腦)或由於某種原因,您不希望包含READ_PHONE_STATE權限。您仍然可以閱讀ROM版本,製造商名稱,CPU類型和其他硬件詳細信息等詳細信息,如果您要將ID用於序列密鑰檢查或其他一般用途,則非常適合。以這種方式計算的ID將不是唯一的:可以找到具有相同ID的兩個設備(基於相同的硬件和ROM映像),但實際應用程序中的更改可以忽略不計。為此,您可以使用Build類:
String m_szDevIDShort = "35" + //we make this look like a valid IMEI
Build.BOARD.length()%10+ Build.BRAND.length()%10 +
Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 +
Build.DISPLAY.length()%10 + Build.HOST.length()%10 +
Build.ID.length()%10 + Build.MANUFACTURER.length()%10 +
Build.MODEL.length()%10 + Build.PRODUCT.length()%10 +
Build.TAGS.length()%10 + Build.TYPE.length()%10 +
Build.USER.length()%10 ; //13 digits
大多數構建成員都是字符串,我們在這裡做的是獲取它們的長度並通過模數轉換它。我們有13個這樣的數字,我們在前面添加兩個(35)以具有與IMEI(15位數)相同的大小ID。這裡有其他可能性很好,只需看看這些字符串。返回類似的東西355715565309247
。無需特殊許可,這種方法非常方便。
(額外信息:上面給出的技術是從here上的一篇文章中復制的。)
這裡有相當有用的信息。
它涵蓋了五種不同的ID類型:
- IMEI (僅適用於使用電話的Android設備;需要
android.permission.READ_PHONE_STATE
) - 偽唯一ID (適用於所有Android設備)
- Android ID (可以為null,可以在出廠重置時更改,可以在root電話上更改)
- WLAN MAC地址字符串(需要
android.permission.ACCESS_WIFI_STATE
) - BT MAC地址字符串(帶藍牙的設備,需要
android.permission.BLUETOOTH
)
IMEI怎麼樣?這對Android或其他移動設備來說是獨一無二的。
以下代碼使用隱藏的Android API返回設備序列號。但是,此代碼不適用於Samsung Galaxy Tab,因為此設備上未設置“ro.serialno”。
String serial = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
}
catch (Exception ignored) {
}
另一種方法是/sys/class/android_usb/android0/iSerial
在沒有任何權限的應用程序中使用。
[email protected]:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root root 4096 2013-01-10 21:08 iSerial
[email protected]:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5
要在Java中執行此操作,只需使用FileInputStream打開iSerial文件並讀出字符即可。請確保將其包裝在異常處理程序中,因為並非所有設備都具有此文件。
至少以下設備已知此文件具有全局可讀性:
- Galaxy Nexus
- Nexus S.
- 摩托羅拉Xoom 3G
- 東芝AT300
- HTC One V.
- 迷你MK802
- 三星Galaxy S II
您還可以看到我的博客發布洩漏Android硬件序列號到非特權應用程序,在那裡我討論哪些其他文件可用於獲取信息。
我使用以下代碼來獲取IMEI
或使用安全。ANDROID_ID
作為替代方案,當設備沒有電話功能時:
String identifier = null;
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
if (tm != null)
identifier = tm.getDeviceId();
if (identifier == null || identifier .length() == 0)
identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);
我的兩分錢 - 注意這是設備(錯誤)的唯一ID - 而不是Android開發人員博客中討論的安裝ID。
值得注意的是,@ emmby提供的solution會回退到每個應用程序ID,因為SharedPreferences不會跨進程同步(請參閱here和here)。所以我完全避免了這一點。
相反,我封裝了在枚舉中獲取(設備)ID的各種策略 - 更改枚舉常量的順序會影響獲取ID的各種方式的優先級。返回第一個非null ID或拋出異常(根據不給出null含義的優秀Java實踐)。所以例如我首先使用TELEPHONY - 但是一個很好的默認選擇是ANDROID_ID beta:
public static String getDeviceId(Context ctx)
{
TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
String tmDevice = tm.getDeviceId();
String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
String serial = null;
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL;
if(tmDevice != null) return "01" + tmDevice;
if(androidId != null) return "02" + androidId;
if(serial != null) return "03" + serial;
// other alternatives (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)
return null;
}
有很多不同的方法可以解決這些ANDROID_ID
問題(null
有時可能或特定模型的設備總是返回相同的ID),有利有弊:
- 實現自定義ID生成算法(基於應該是靜態且不會更改的設備屬性 - >誰知道)
- 濫用其他ID,如IMEI,序列號,Wi-Fi /藍牙MAC地址(它們不會存在於所有設備上或需要其他權限)
我自己更喜歡使用Android 的現有OpenUDID實現(請參閱https://github.com/ylechelle/OpenUDID)(請參閱https://github.com/vieux/OpenUDID)。它易於集成,並利用ANDROID_ID
上述問題的後備。
有關如何為安裝應用程序的每個Android設備獲取唯一標識符的詳細說明,請參閱官方Android開發人員博客發布android-developers.blogspot.com/2011/03/…。
看來最好的方法是在安裝時自己生成一個,然後在重新啟動應用程序時讀取它。
我個人覺得這個可以接受但不理想。Android提供的任何一個標識符都不適用於所有情況,因為大多數都取決於手機的無線電狀態(Wi-Fi開/關,手機開/關,藍牙開/關)。其他,Settings.Secure.ANDROID_ID
必須由製造商實施,並不保證是獨一無二的。
以下是將數據寫入安裝文件的示例,該文件將與應用程序在本地保存的任何其他數據一起存儲。
public class Installation {
private static String sID = null;
private static final String INSTALLATION = "INSTALLATION";
public synchronized static String id(Context context) {
if (sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists())
writeInstallationFile(installation);
sID = readInstallationFile(installation);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
return sID;
}
private static String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}
private static void writeInstallationFile(File installation) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
}