@ -43,6 +43,7 @@ import timber.log.Timber;
@SuppressWarnings ( "KotlinInternalInJava" )
@SuppressWarnings ( "KotlinInternalInJava" )
public final class AndroidacyRepoData extends RepoData {
public final class AndroidacyRepoData extends RepoData {
public static String ANDROIDACY_DEVICE_ID = null ;
public static String token = MainApplication . getPreferences ( "androidacy" ) . getString ( "pref_androidacy_api_token" , null ) ;
public static String token = MainApplication . getPreferences ( "androidacy" ) . getString ( "pref_androidacy_api_token" , null ) ;
static {
static {
@ -86,10 +87,15 @@ public final class AndroidacyRepoData extends RepoData {
// Generates a unique device ID. This is used to identify the device in the API for rate
// Generates a unique device ID. This is used to identify the device in the API for rate
// limiting and fraud detection.
// limiting and fraud detection.
public static String generateDeviceId ( ) {
public static String generateDeviceId ( ) {
// first, check if ANDROIDACY_DEVICE_ID is already set
if ( ANDROIDACY_DEVICE_ID ! = null ) {
return ANDROIDACY_DEVICE_ID ;
}
// Try to get the device ID from the shared preferences
// Try to get the device ID from the shared preferences
SharedPreferences sharedPreferences = MainApplication . getPreferences ( "androidacy" ) ;
SharedPreferences sharedPreferences = MainApplication . getPreferences ( "androidacy" ) ;
String deviceIdPref = sharedPreferences . getString ( "device_id" , null ) ;
String deviceIdPref = sharedPreferences . getString ( "device_id" , null ) ;
if ( deviceIdPref ! = null ) {
if ( deviceIdPref ! = null ) {
ANDROIDACY_DEVICE_ID = deviceIdPref ;
return deviceIdPref ;
return deviceIdPref ;
} else {
} else {
// Really not that scary - just hashes some device info. We can't even get the info
// Really not that scary - just hashes some device info. We can't even get the info
@ -119,6 +125,7 @@ public final class AndroidacyRepoData extends RepoData {
digest = MessageDigest . getInstance ( "SHA-256" ) ;
digest = MessageDigest . getInstance ( "SHA-256" ) ;
} catch ( NoSuchAlgorithmException ignored ) {
} catch ( NoSuchAlgorithmException ignored ) {
// This should never happen so we can just return the original device ID
// This should never happen so we can just return the original device ID
ANDROIDACY_DEVICE_ID = deviceId ;
return deviceId ;
return deviceId ;
}
}
byte [ ] hash = digest . digest ( deviceId . getBytes ( ) ) ;
byte [ ] hash = digest . digest ( deviceId . getBytes ( ) ) ;
@ -135,6 +142,8 @@ public final class AndroidacyRepoData extends RepoData {
SharedPreferences . Editor editor = sharedPreferences . edit ( ) ;
SharedPreferences . Editor editor = sharedPreferences . edit ( ) ;
editor . putString ( "device_id" , hexString . toString ( ) ) ;
editor . putString ( "device_id" , hexString . toString ( ) ) ;
editor . apply ( ) ;
editor . apply ( ) ;
// Set ANDROIDACY_DEVICE_ID
ANDROIDACY_DEVICE_ID = hexString . toString ( ) ;
// Return it
// Return it
return hexString . toString ( ) ;
return hexString . toString ( ) ;
}
}
@ -151,8 +160,7 @@ public final class AndroidacyRepoData extends RepoData {
// set role and permissions on userInfo property
// set role and permissions on userInfo property
userInfo = new String [ ] [ ] { { "role" , memberLevel } , { "permissions" , String . valueOf ( memberPermissions ) } } ;
userInfo = new String [ ] [ ] { { "role" , memberLevel } , { "permissions" , String . valueOf ( memberPermissions ) } } ;
return true ;
return true ;
} catch (
} catch ( HttpException e ) {
HttpException e ) {
if ( e . getErrorCode ( ) = = 401 ) {
if ( e . getErrorCode ( ) = = 401 ) {
Timber . w ( "Invalid token, resetting..." ) ;
Timber . w ( "Invalid token, resetting..." ) ;
// Remove saved preference
// Remove saved preference
@ -162,8 +170,7 @@ public final class AndroidacyRepoData extends RepoData {
return false ;
return false ;
}
}
throw e ;
throw e ;
} catch (
} catch ( JSONException e ) {
JSONException e ) {
// response is not JSON
// response is not JSON
Timber . w ( "Invalid token, resetting..." ) ;
Timber . w ( "Invalid token, resetting..." ) ;
Timber . w ( e ) ;
Timber . w ( e ) ;
@ -185,8 +192,7 @@ public final class AndroidacyRepoData extends RepoData {
editor . apply ( ) ;
editor . apply ( ) ;
return false ;
return false ;
}
}
if ( Http . needCaptchaAndroidacy ( ) )
if ( Http . needCaptchaAndroidacy ( ) ) return false ;
return false ;
// Implementation details discussed on telegram
// Implementation details discussed on telegram
// First, ping the server to check if it's alive
// First, ping the server to check if it's alive
try {
try {
@ -208,15 +214,13 @@ public final class AndroidacyRepoData extends RepoData {
}
}
return false ;
return false ;
}
}
} catch (
} catch ( Exception e ) {
Exception e ) {
Timber . e ( e , "Failed to ping server" ) ;
Timber . e ( e , "Failed to ping server" ) ;
return false ;
return false ;
}
}
String deviceId = generateDeviceId ( ) ;
String deviceId = generateDeviceId ( ) ;
long time = System . currentTimeMillis ( ) ;
long time = System . currentTimeMillis ( ) ;
if ( this . androidacyBlockade > time )
if ( this . androidacyBlockade > time ) return true ; // fake it till you make it. Basically,
return true ; // fake it till you make it. Basically,
// don't fail just because we're rate limited. API and web rate limits are different.
// don't fail just because we're rate limited. API and web rate limits are different.
this . androidacyBlockade = time + 30_000L ;
this . androidacyBlockade = time + 30_000L ;
try {
try {
@ -234,8 +238,7 @@ public final class AndroidacyRepoData extends RepoData {
} else {
} else {
Timber . i ( "Using validated cached token" ) ;
Timber . i ( "Using validated cached token" ) ;
}
}
} catch (
} catch ( IOException e ) {
IOException e ) {
if ( HttpException . shouldTimeout ( e ) ) {
if ( HttpException . shouldTimeout ( e ) ) {
Timber . e ( e , "We are being rate limited!" ) ;
Timber . e ( e , "We are being rate limited!" ) ;
this . androidacyBlockade = time + 3_600_000L ;
this . androidacyBlockade = time + 3_600_000L ;
@ -256,8 +259,7 @@ public final class AndroidacyRepoData extends RepoData {
//noinspection SuspiciousRegexArgument
//noinspection SuspiciousRegexArgument
Timber . d ( "Token: %s" , token . substring ( 0 , token . length ( ) - 4 ) . replaceAll ( "." , "*" ) + token . substring ( token . length ( ) - 4 ) ) ;
Timber . d ( "Token: %s" , token . substring ( 0 , token . length ( ) - 4 ) . replaceAll ( "." , "*" ) + token . substring ( token . length ( ) - 4 ) ) ;
memberLevel = jsonObject . getString ( "role" ) ;
memberLevel = jsonObject . getString ( "role" ) ;
} catch (
} catch ( JSONException e ) {
JSONException e ) {
Timber . e ( e , "Failed to parse token" ) ;
Timber . e ( e , "Failed to parse token" ) ;
// Show a toast
// Show a toast
Looper mainLooper = Looper . getMainLooper ( ) ;
Looper mainLooper = Looper . getMainLooper ( ) ;
@ -280,8 +282,7 @@ public final class AndroidacyRepoData extends RepoData {
editor . apply ( ) ;
editor . apply ( ) ;
Timber . i ( "Token saved to shared preference" ) ;
Timber . i ( "Token saved to shared preference" ) ;
}
}
} catch (
} catch ( Exception e ) {
Exception e ) {
if ( HttpException . shouldTimeout ( e ) ) {
if ( HttpException . shouldTimeout ( e ) ) {
Timber . e ( e , "We are being rate limited!" ) ;
Timber . e ( e , "We are being rate limited!" ) ;
this . androidacyBlockade = time + 3_600_000L ;
this . androidacyBlockade = time + 3_600_000L ;
@ -384,8 +385,7 @@ public final class AndroidacyRepoData extends RepoData {
moduleInfo . minMagisk = // Allow 24.1 to mean 24100
moduleInfo . minMagisk = // Allow 24.1 to mean 24100
( Integer . parseInt ( minMagisk . substring ( 0 , c ) ) * 1000 ) + ( Integer . parseInt ( minMagisk . substring ( c + 1 ) ) * 100 ) ;
( Integer . parseInt ( minMagisk . substring ( 0 , c ) ) * 1000 ) + ( Integer . parseInt ( minMagisk . substring ( c + 1 ) ) * 100 ) ;
}
}
} catch (
} catch ( Exception e ) {
Exception e ) {
moduleInfo . minMagisk = 0 ;
moduleInfo . minMagisk = 0 ;
}
}
moduleInfo . needRamdisk = jsonObject . optBoolean ( "needRamdisk" , false ) ;
moduleInfo . needRamdisk = jsonObject . optBoolean ( "needRamdisk" , false ) ;
@ -437,8 +437,7 @@ public final class AndroidacyRepoData extends RepoData {
private String injectToken ( String url ) {
private String injectToken ( String url ) {
// Do not inject token for non Androidacy urls
// Do not inject token for non Androidacy urls
if ( ! AndroidacyUtil . isAndroidacyLink ( url ) )
if ( ! AndroidacyUtil . isAndroidacyLink ( url ) ) return url ;
return url ;
if ( this . testMode ) {
if ( this . testMode ) {
if ( url . startsWith ( "https://production-api.androidacy.com/" ) ) {
if ( url . startsWith ( "https://production-api.androidacy.com/" ) ) {
Timber . e ( "Got non test mode url: %s" , AndroidacyUtil . hideToken ( url ) ) ;
Timber . e ( "Got non test mode url: %s" , AndroidacyUtil . hideToken ( url ) ) ;